diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 5c4b78f3bc..0000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "marine/prettier/node", - "ignorePatterns": ["dist/*", "**/*.js"], - "parserOptions": { - "project": "./tsconfig.eslint.json" - }, - "rules": { - "no-eq-null": "off" - } -} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..c83f3371a5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @discord/devrel diff --git a/.github/ISSUE_TEMPLATE/app-subscriptions-bug-report.yml b/.github/ISSUE_TEMPLATE/app-subscriptions-bug-report.yml new file mode 100644 index 0000000000..8af1faa61c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/app-subscriptions-bug-report.yml @@ -0,0 +1,49 @@ +name: App Subscriptions Bug Report +description: A bug has been found in Discord's App Subscriptions. +labels: ["bug", "premium-apps"] +body: + - type: markdown + attributes: + value: "Before opening a new issue, please search existing issues: https://github.com/discord/discord-api-docs/issues?q=is%3Aissue+label%3A%22premium-apps%22" + - type: textarea + id: description + attributes: + label: Description + description: Provide a clear and concise description of what the problem is. + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + description: Provide clear and concise steps for us to reliably reproduce this issue. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What is the behavior you expect to occur that is not? + validations: + required: true + - type: textarea + id: current + attributes: + label: Current Behavior + description: What is the behavior you are currently seeing instead? + validations: + required: true + - type: textarea + id: screenshots + attributes: + label: Screenshots/Videos + description: Provide a screenshot and/or video demonstrating the issue being experienced. + validations: + required: false + - type: textarea + id: information + attributes: + label: Client and System Information + description: What is the browser/library/client you are using? What operating system and version? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/developer-site-bug-report.yml b/.github/ISSUE_TEMPLATE/developer-site-bug-report.yml index 043a1d4c94..604576d393 100644 --- a/.github/ISSUE_TEMPLATE/developer-site-bug-report.yml +++ b/.github/ISSUE_TEMPLATE/developer-site-bug-report.yml @@ -1,10 +1,10 @@ name: Developer Site Bug Report description: A bug has been found with Discord's Developer site or documentation. -labels: ["bug"] +labels: ["bug", "developer portal"] body: - type: markdown attributes: - value: "Before opening a new issue, please search existing issues: https://github.com/discord/discord-api-docs/issues" + value: "Before opening a new issue, please search existing issues: https://github.com/discord/discord-api-docs/issues?q=is%3Aissue+label%3A%22developer+portal%22" - type: textarea id: description attributes: diff --git a/.github/ISSUE_TEMPLATE/message-components-bug-report.yml b/.github/ISSUE_TEMPLATE/message-components-bug-report.yml index d430cc53cd..d914ec9033 100644 --- a/.github/ISSUE_TEMPLATE/message-components-bug-report.yml +++ b/.github/ISSUE_TEMPLATE/message-components-bug-report.yml @@ -1,10 +1,10 @@ name: Message Components Bug Report description: A bug has been found in Discord's Message Components. -labels: ["message components"] +labels: ["bug", "message components"] body: - type: markdown attributes: - value: "Before opening a new issue, please search existing issues: https://github.com/discord/discord-api-docs/issues?q=is%3Aissue+label%3A%22message+components%22+" + value: "Before opening a new issue, please search existing issues: https://github.com/discord/discord-api-docs/issues?q=is%3Aissue+label%3A%22message+components%22" - type: textarea id: description attributes: diff --git a/.github/ISSUE_TEMPLATE/slash-command-bug-report.yml b/.github/ISSUE_TEMPLATE/slash-command-bug-report.yml index 555a7c22b8..5927d3acf7 100644 --- a/.github/ISSUE_TEMPLATE/slash-command-bug-report.yml +++ b/.github/ISSUE_TEMPLATE/slash-command-bug-report.yml @@ -1,10 +1,10 @@ name: Slash Commands Bug Report description: A bug has been found in Discord's Slash Commands and Interactions. -labels: ["slash commands"] +labels: ["bug", "slash commands"] body: - type: markdown attributes: - value: "Before opening a new issue, please search existing issues: https://github.com/discord/discord-api-docs/issues?q=is%3Aissue+label%3A%22slash+commands%22+" + value: "Before opening a new issue, please search existing issues: https://github.com/discord/discord-api-docs/issues?q=is%3Aissue+label%3A%22slash+commands%22" - type: textarea id: description attributes: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1..56baa6b7d4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,11 @@ updates: directory: "/" schedule: interval: "weekly" + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + ignore: + - dependency-name: "*" + update-types: + ["version-update:semver-patch", "version-update:semver-minor"] diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 0000000000..69f8419138 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,33 @@ +name: Verify Docs Formatting + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + markdown_tables: + name: Check Markdown Tables + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Install Node v20 + uses: actions/setup-node@v5 + with: + node-version: 20 + cache: npm + cache-dependency-path: tools/package-lock.json + + - name: Install dependencies + run: npm ci + working-directory: tools + + - name: Check Markdown Tables + run: npm run test:tables + working-directory: tools diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 3532280c1f..9bb68aa6f0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,48 +1,42 @@ -name: Verify Links +name: ci -on: [push, pull_request] +on: + push: + branches: + - main + pull_request: permissions: contents: read jobs: lint: - name: ESLint + name: Lint runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Install Node v16 - uses: actions/setup-node@v3 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 16 + node-version: 20 cache: npm - - - name: Install dependencies - run: npm ci - - - name: Run ESLint - run: npm run lint - - links: - name: Check Links + cache-dependency-path: tools/package-lock.json + - run: npm ci + working-directory: tools + - run: npm run lint + working-directory: tools + - run: npm run build + working-directory: tools + mdx: + name: Validate mdx runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Install Node v16 - uses: actions/setup-node@v3 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 16 + node-version: 20 cache: npm - - - name: Install dependencies - run: npm ci - - - name: Build - run: npm run build - - - name: Run Link Checks - run: npm run test:links + cache-dependency-path: tools/package-lock.json + - run: npm ci + working-directory: tools + - run: npm run test:build + working-directory: tools diff --git a/.gitignore b/.gitignore index b508004b1c..162dc1edf1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .DS_Store *.swp node_modules/ -dist/ \ No newline at end of file +dist/ +.idea +xml_output/ diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 96c0ecc381..0000000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -docs/ \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 246fdf0468..0000000000 --- a/.prettierrc.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "quoteProps": "consistent", - "endOfLine": "lf", - "printWidth": 120, - "useTabs": true, - "overrides": [ - { - "files": "*.md", - "options": { - "useTabs": false, - "tabWidth": 4 - } - } - ] -} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 0da2efdb0f..8086a7cd50 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,5 @@ +# Code of Conduct + ## **Our Pledge** We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. @@ -8,19 +10,19 @@ We pledge to act and interact in ways that contribute to an open, welcoming, div Examples of behavior that contributes to a positive environment for our community include: -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -- Focusing on what is best not just for us as individuals, but for the overall community +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -- The use of sexualized language or imagery, and sexual attention or advances of any kind -- Trolling, insulting or derogatory comments, hate speech, and personal or political attacks -- Public or private harassment -- Publishing others’ private information, such as a physical or email address, without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Trolling, insulting or derogatory comments, hate speech, and personal or political attacks +- Public or private harassment +- Publishing others’ private information, such as a physical or email address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## **Enforcement Responsibilities** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 579460a0b7..84a1020936 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,16 +1,34 @@ -## Wanted Changes +# Contributing + +See the [README](https://github.com/discord/discord-api-docs/blob/main/README.md) for licensing and legal information. + +## Types of Changes + +### Wanted Changes 1. Fixes to incorrect statements or inaccuracies within the documentation. 1. Rewording or extending documentation to clarify unclear wording or complicated explanations. 1. Additions that fill gaps or missing pieces in the current documentation. 1. Fixing of spelling and grammatical errors in the documentation. -## Unwanted Changes +### Unwanted Changes 1. Whitespace or formatting changes. 1. Subjective wording changes. 1. Modifications to the overall structure and format of the API docs. 1. Additions that replicate or needlessly restructure current documentation. 1. Additions that document unreleased product functionality. +1. Changes that modify [Community Resources](https://discord.com/developers/docs/topics/community-resources#community-resources) (see [guidelines](https://github.com/discord/discord-api-docs/discussions/4456) for more detail). + +## Markdown Syntax + +Our documentation uses standard [Markdown](https://www.markdownguide.org/basic-syntax/) syntax with some additional [MDX](https://mdxjs.com/) features. + +We also support the following formatting from Mintlify: -By submitting pull requests to this repository, you waive any rights or ownership of the included contents to Discord. Contributions to this repository must conform to the [Discord App TOS](https://discord.com/terms). +- [Format Text](https://www.mintlify.com/docs/create/text) +- [Format Code](https://www.mintlify.com/docs/create/code) +- [Images & Embeds](https://www.mintlify.com/docs/create/image-embeds) +- [Files](https://www.mintlify.com/docs/create/files) +- [Lists & Tables](https://www.mintlify.com/docs/create/list-table) +- [Available Components](https://www.mintlify.com/docs/components/accordions) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..3b7b82d0da --- /dev/null +++ b/LICENSE @@ -0,0 +1,427 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + + including for purposes of Section 3(b); and + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSE-CODE b/LICENSE-CODE new file mode 100644 index 0000000000..3569941d8d --- /dev/null +++ b/LICENSE-CODE @@ -0,0 +1,24 @@ +MIT License + +Copyright (c) 2016 Discord + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 36c26c3c89..678ad3ee77 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,35 @@ -# Discord Official API Documentation +# Discord for Developers Documentation -This repo contains the official Discord API documentation, which can be viewed online [HERE](https://discord.com/developers/docs/intro). Before submitting pull-requests, please remember to _fully_ read the [Contributing](CONTRIBUTING.md) guidelines. +This repo contains the official Discord for Developers documentation, which can be viewed online [HERE](https://discord.com/developers/docs/intro). Before submitting pull-requests, please remember to _fully_ read the [Contributing](CONTRIBUTING.md) guidelines. -This repository reflects the Discord API as it is meant to be accessed by third-party applications. It omits features and capabilities that are not generally available, or are not fully supported for third-party usage. +This repository reflects the Discord Developer Platform as it is meant to be accessed by third-party applications. It omits features and capabilities that are not generally available, or are not fully supported for third-party usage. We welcome your contributions! -- Issue tracker: Discord API bugs -- Discussions: Discord API feature and improvement requests -- Pull Requests: See [Contributing.md](https://github.com/discord/discord-api-docs/blob/main/CONTRIBUTING.md) +- Issue tracker: [Discord API bugs](https://github.com/discord/discord-api-docs/issues) +- Discussions: [Discord API feature and improvement requests](https://github.com/discord/discord-api-docs/discussions/categories/api-feature-requests-ideas) +- Pull Requests: See [Contributing.md](https://github.com/discord/discord-api-docs/blob/main/CONTRIBUTING.md) for types of changes accepted and specific markdown syntax used in the documentation. -## Markdown Syntax +### Local Preview -This repository uses special markdown syntax that helps style the resulting web version of the documentation. +Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) to preview the documentation changes locally. To install, use the following command -### H6 Headings - -H6 headings should be used above tables and code blocks to properly label them. - -### Linking +``` +npm i -g mintlify +``` -Links between docs can be achieved by using a hash symbol (#), plus the markdown file name, plus a slash, and finally the dash-separated anchor. For instance, to link to the above H6 heading section: +Run the `mintlify dev` in the `discord` directory to see your changes locally. -```md -[Links to README.md H6](#README/h6-headings) +``` +cd discord +mintlify dev ``` -### Alert Boxes +Your local browser should open automatically to the correct page, but if not, navigate to: -Alert boxes are achieved by using a block quote that has one of 'warn', 'danger' or 'info' on the first line. +``` +http://localhost:3000/developers/docs/intro +``` ## Need some help? @@ -37,3 +38,7 @@ Here are some Discord servers that can help you out with everything Discord API: The [Official Discord Developers server](https://discord.gg/discord-developers) has plenty of help channels with knowledgeable people and Discord's developers to get you help with something you need, and get updates straight from the developers. However do keep in mind this is a purely on-topic server. If you are looking for a community, join the server below. The [Unofficial Discord API server](https://discord.gg/discord-api) is a common hangout for library and bot developers alike. It's a great starting point for those looking to dive in and learn bot-creation with the Discord API. + +## License + +Except as otherwise noted, the Discord API Documentation and other content in this repository is licensed under the Creative Commons Attribution-ShareAlike 4.0 License (see [LICENSE](https://github.com/discord/discord-api-docs/blob/main/LICENSE)), and code samples in this repository are licensed under the MIT License (see [LICENSE-CODE](https://github.com/discord/discord-api-docs/blob/main/LICENSE-CODE)). These licenses do not grant you rights to use any of Discord’s trademarks or other brand features. Please see the [Discord Developer Terms of Service](https://support-dev.discord.com/hc/articles/8562894815383-Discord-Developer-Terms-of-Service) for more information about use of Discord’s brand features and APIs. diff --git a/ci/checkLinks.ts b/ci/checkLinks.ts deleted file mode 100644 index 1e1a481a96..0000000000 --- a/ci/checkLinks.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { readdirSync, statSync, readFileSync } from "node:fs"; -import path from "node:path"; -import chalk from "chalk"; -import * as github from "@actions/core"; -const cwd = process.env.GITHUB_ACTIONS ? process.env.GITHUB_WORKSPACE! : process.cwd(); - -function importDirectory(directory: string, extension: string, subdirectories = true) { - try { - const output = new Map(); - const files = readdirSync(directory); - for (const fileOrPath of files) { - const currentPath = path.join(directory, fileOrPath); - if (statSync(currentPath).isDirectory()) { - if (!subdirectories) continue; - const subdir = importDirectory(currentPath, extension, subdirectories); - if (!subdir) continue; - for (const [name, read] of subdir) { - output.set(`/${fileOrPath}${name}`, read); - } - continue; - } - if (!fileOrPath.endsWith(extension)) continue; - const read = readFileSync(currentPath, "utf8"); - output.set(`/${fileOrPath}`, read); - } - return output; - } catch { - // Directory likely does not exist, we should be able to safely discard this error - return null; - } -} - -function printResults(resultMap: Map): void { - let output = "\n"; - let total = 0; - for (const [resultFile, resultArr] of resultMap) { - if (resultArr.length <= 0) continue; - const filePath = path.join(cwd, resultFile); - output += `${chalk.underline(filePath)}\n`; - output += resultArr.reduce((result, props) => { - total += 1; - return `${result} ${props.startLine ?? ""}:${props.startColumn ?? ""}-${props.endColumn ?? ""} ${chalk.yellow( - "warning" - )} ${props.title ?? ""}\n`; - }, ""); - output += "\n"; - } - output += "\n"; - if (total > 0) { - output += chalk.red.bold(`\u2716 ${total} problem${total === 1 ? "" : "s"}\n`); - } - console.log(output); -} - -function annotateResults(resultMap: Map): void { - let total = 0; - for (const [resultFile, resultArr] of resultMap) { - if (resultArr.length <= 0) continue; - github.startGroup(resultFile); - for (const result of resultArr) { - total += 1; - console.log( - `::warning file=${resultFile},title=Invalid Link,line=${result.startLine ?? 0},endLine=${ - result.startLine ?? 0 - },col=${result.startColumn ?? 0},endColumn=${result.endColumn ?? result.startColumn ?? 0}::${ - result.title ?? "Invalid Link" - }` - ); - } - github.endGroup(); - } - if (total > 0) { - github.setFailed("One or more links are invalid!"); - } -} - -const docFiles = importDirectory(path.join(cwd, "docs"), ".md"); - -if (!docFiles) { - console.error("No doc files found!"); - process.exit(1); -} - -const validLinks = new Map([ - ["APPLICATIONS", []], - ["SERVERS", []], - ["TEAMS", []], -]); - -const extLength = ".md".length; - -// Gather valid links -for (const [name, raw] of docFiles) { - const keyName = `DOCS${name.slice(0, -extLength).replaceAll("/", "_").toUpperCase()}`; - if (!validLinks.has(keyName)) { - validLinks.set(keyName, []); - } - const validAnchors = validLinks.get(keyName)!; - - let parentAnchor = ""; - let multilineCode = false; - for (const line of raw.split("\n")) { - if (line.trim().startsWith("```")) { - multilineCode = !multilineCode; - if (line.trim().length > 3 && line.trim().endsWith("```")) multilineCode = !multilineCode; - } - if (multilineCode || !line.startsWith("#")) continue; - const anchor = line - .split("%")[0] - .replace(/[^ A-Z0-9]/gi, "") - .trim() - .replace(/ +/g, "-") - .toLowerCase(); - if (/^#{1,5}(?!#)/.test(line.trim())) { - parentAnchor = `${anchor}-`; - validAnchors.push(anchor); - continue; - } - validAnchors.push(`${parentAnchor}${anchor}`); - } -} - -const results = new Map(); - -// Check Links -for (const [name, raw] of docFiles) { - const fileName = `docs${name}`; - const file = raw.split("\n"); - if (!results.has(fileName)) { - results.set(fileName, []); - } - const ownResults = results.get(fileName)!; - let multilineCode = false; - file.forEach((line, lineNum) => { - if (line.trim().startsWith("```")) { - multilineCode = !multilineCode; - if (line.trim().length > 3 && line.trim().endsWith("```")) multilineCode = !multilineCode; - } - if (multilineCode) return; - const matches = line.matchAll(/(? a.includes(anchor)); - const suggestionText = suggestions.length > 0 ? ` Did you mean one of (${suggestions.join(", ")})?` : ""; - ownResults.push({ - title: `Anchor ${chalk.cyan(anchor)} does not exist on ${chalk.blueBright(page)}${suggestionText}`, - startLine: lineNum + 1, - startColumn: match.index, - endColumn: (match.index ?? 0) + match[0].length, - }); - } - } - }); -} - -if (results.size > 0) { - if (process.env.GITHUB_ACTIONS) { - annotateResults(results); - } else { - printResults(results); - } -} diff --git a/discord/.vale.ini b/discord/.vale.ini new file mode 100644 index 0000000000..8f3499328e --- /dev/null +++ b/discord/.vale.ini @@ -0,0 +1,38 @@ +# Top level styles +StylesPath = ./vale +MinAlertLevel = suggestion +IgnoredScopes = code, tt, img, url, a +SkippedScopes = script, style, pre, figure, code + +# Vocabularies +Vocab = Mintlify, Discord + +# This is required since Vale doesn't officially support MDX +[formats] +mdx = md + +# MDX support +[*.mdx] +BasedOnStyles = Vale +Vale.Terms = NO # Enforces really harsh capitalization rules, keep off + +# `import ...`, `export ...` +# `` +# `...` +# `{ ... }` +# Words with underscores +# Markdown links [text](url) +# Content within square brackets (markdown link text) +TokenIgnores = (?sm)((?:import|export) .+?$), \ +(?)(?!`), \ +(<[A-Z]\w+>.+?<\/[A-Z]\w+>), \ +(\w*_\w*), \ +(\[.+?\]\(.+?\)), \ +(\[.+?\]) + +# Exclude: +# `` +BlockIgnores = (?sm)^(<\w+\n .*\s\/>)$, \ +(?sm)^({.+.*}) + +CommentDelimiters = {/*, */} diff --git a/discord/developers/docs/activities/building-an-activity.mdx b/discord/developers/docs/activities/building-an-activity.mdx new file mode 100644 index 0000000000..1f6140d102 --- /dev/null +++ b/discord/developers/docs/activities/building-an-activity.mdx @@ -0,0 +1,716 @@ +--- +title: Building Your First Activity in Discord +sidebarTitle: Quickstart +description: Step-by-step tutorial for creating your first Discord Activity. +--- + +import {LinkButton} from '/snippets/linkbutton.jsx' + +[Activities](/developers/docs/activities/overview) are web-based games and apps that can be run within Discord. Activities are embedded in iframes within the Discord client, and can be launched from the App Launcher or when responding to interactions. + +If this is your first time learning about Activities, check out the [Activities Overview](/developers/docs/activities/overview) for more information and a collection of more advanced [sample projects](/developers/docs/activities/overview#sample-projects). + +## Introduction + +In this guide, we'll be building a Discord app with a basic Activity that handles user authentication and fetches data using the API. + +It assumes an understanding of [JavaScript](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics) and [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), and a basic understanding of frontend frameworks like [React](https://react.dev/) and [Vue](https://vuejs.org/). If you are still learning to program, there are many free education resources to explore like [The Odin Project](https://www.theodinproject.com/paths/foundations/courses/foundations), [Codecademy](https://www.codecademy.com/learn/paths/web-development), and [Khan Academy](https://www.khanacademy.org/computing/computer-programming/programming). + + + +![Building Your First Activity Tutorial](/images/activities/tutorial-hero.png) + + + + +- **[discord/getting-started-activity](https://github.com/discord/getting-started-activity)**, a project template to get you started +- **[@discord/embedded-app-sdk](https://github.com/discord/embedded-app-sdk)**, the SDK used to communicate between your app and Discord when building Activities +- **[Node.js](https://nodejs.org)**, latest version +- **[Express](https://expressjs.com)**, a popular JavaScript web framework we'll use to create a server to handle authentication and serve our app +- **[Vite](https://vite.dev/)**, a build tool for modern JavaScript projects that will make your application easier to serve +- **[cloudflared](https://github.com/cloudflare/cloudflared?tab=readme-ov-file#installing-cloudflared)**, for bridging your local development server to the internet + + +--- + +## Step 0: Enable Developer Mode + +Before getting started, you need to enable Developer Mode for your Discord account if you don't already have it enabled. Developer Mode will allow you to run in-development Activities and expose resource IDs (like users, channels, and servers) in the client which can simplify testing. To enable Developer Mode: +1. Go to your **User Settings** in your Discord client. On Desktop, you can access **User Settings** by clicking on the cogwheel icon near the bottom-left, next to your username. +2. Click on **Advanced** tab from the left-hand sidebar and toggle on `Developer Mode`. + +## Step 1: Setting up the project + +Before creating an app, let's set up our project code from the [`discord/getting-started-activity`](https://github.com/discord/getting-started-activity) repository. + +Open a terminal window and clone the project code: + +``` +git clone git@github.com:discord/getting-started-activity.git +``` + +The sample project you cloned is broken into two parts: +- `client` is the sample Activity's frontend, built with vanilla JavaScript and integrated with [Vite](https://vitejs.dev/) to help with local development. +- `server` is a backend using vanilla JavaScript, Node.js, and Express. However, as you're building your own Activity, you can use whichever backend you prefer. + + + +``` +├── client +├────── main.js -> your Activity frontend +├────── index.html +├────── package.json +├────── rocket.png +├────── vite.config.js +├── server +├────── package.json +├────── server.js -> your Activity backend +└── .env -> your credentials, IDs and secrets +``` + + + +### Install project dependencies + +Before creating our Discord app, let's quickly install your project's frontend dependencies. + +Navigate to your project folder's `client` directory, which is where all the sample Activity's frontend code lives: + +``` +cd getting-started-activity/client +``` + +Then install the project's dependencies and start up the frontend for the sample Activity: + +``` +# install project dependencies +npm install + +# start frontend +npm run dev +``` + +If you visit http://localhost:5173/ you should see a vanilla JS frontend template running with [Vite](https://vitejs.dev/). + +While it's not much at the moment, in the following steps we'll connect it to the backend services, make it runnable in Discord, and power it up by populating it with data we pull from Discord APIs. + + + +By the end of Step 1, you should have: +- An understanding of what Discord [Activities](/developers/docs/activities/overview) are +- Developer Mode enabled on your Discord account +- Cloned the [sample project](https://github.com/discord/getting-started-activity) to your development environment +- Installed the front-end dependencies (in the `client` folder) + + +--- + +## Step 2: Creating an app + +With our project set up, let's create our app and configure the Activity. Create a new app in the developer portal if you don't have one already: + +Create App + +Enter a name for your app, select a development team, then press **Create**. + + +**Development Team Access** + +Launching a non-distributed Activity is limited to you or members of the developer team, so if you're collaborating with others during development, create a [developer team](https://discord.com/developers/teams) and set it to the owner when you create the app. + + +After you create your app, you'll land on the **General Overview** page of the app's settings, where you can update basic information about your app like its description and icon. + +### Choose installation contexts + +Apps in Discord can be installed to different **[installation contexts](/developers/docs/resources/application#installation-context)**: servers, user accounts, or both. + +The recommended *and* default behavior for apps is supporting both installation contexts, which lets the installer to choose the context during the installation flow. However, you can change the default behavior by changing the supported installation contexts in your app's settings. + + + +As mentioned, installation contexts determine where your app can be installed. The installation context affect things like who can manage the installation, where the app's commands can appear, and the data returned in response to interactions. + +- Apps installed in a **[server context](/developers/docs/resources/application#server-context)** (server-installed apps) must be authorized by a server member with the `MANAGE_GUILD` permission, and are visible to all members of the server. +- Apps installed in a **[user context](/developers/docs/resources/application#user-context)** (user-installed apps) are visible only to the authorizing user, and therefore don't require any server-specific permissions. Apps installed to a user context are visible across all of the user's servers, DMs, and GDMs—however, they're limited to using commands. + +Details about installation contexts is in the [Application documentation](/developers/docs/resources/application#installation-context) and the [Developing a User-Installable App tutorial](/developers/docs/tutorials/developing-a-user-installable-app). + + +Click on **Installation** in the left sidebar, then under **Installation Contexts** make sure both "User Install" and "Guild Install" are selected. This will make sure users can launch our app's Activity across Discord servers, DMs, and Group DMs. + +### Add a Redirect URI + +Next, we'll add a Redirect URI, which is where a user is typically redirected to after authorizing with your app when going through the standard OAuth flow. While setting up a Redirect URI is required, the Embedded App SDK automatically handles redirecting users back to your Activity when the RPC [`authorize` command](/developers/docs/developer-tools/embedded-app-sdk#authorize) is called. + +You can learn more about the OAuth flow and redirect URIs in the [OAuth2 documentation](/developers/docs/topics/oauth2), but since we're only authorizing in an Activity, we'll just use a placeholder value (`https://127.0.0.1`) and let the Embedded App SDK handle the rest. + +Click on **OAuth2** on the sidebar in your app's settings. Under **Redirects**, enter `https://127.0.0.1` as a placeholder value then click **Save Changes**. + +![Redirect URI in Activity Settings](/images/activities/oauth2-redirect.png) + +### Fetch Your OAuth2 Credentials + +To use information related to a user (like their username) or a server (like the server's avatar), your app must be granted specific OAuth **scopes**. + +For our sample app, we'll be requesting three scopes: `identify` to access basic information about a user, `guilds` to access basic information about the servers a user is in, and `applications.commands` to install [commands](/developers/docs/interactions/overview#commands). We'll request these later on in the guide, but a full list of scopes you can request is in the [OAuth2 documentation](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes). + +When requesting scopes later on, you'll need to pass your app's OAuth2 identifiers. For now, we'll copy these identifiers into your project's environment file. + +In the root of your project, there is an `example.env` file. From the root of your project, run the following to copy it into a new `.env` file: + +``` +cp example.env .env +``` + + +**Secure Your Secrets** + +Your `DISCORD_CLIENT_SECRET` and `DISCORD_BOT_TOKEN` are *highly* sensitive secrets. Never share either secrets or check them into any kind of version control. + + +Back in your app's settings, click on **OAuth2** on the sidebar: +1. **Client ID**: Copy the value for Client ID and add it to your `.env` file as **`VITE_CLIENT_ID`**. This is the public ID that Discord associates with your app, and is almost always the same as your App ID. +2. **Client Secret**: Copy the value for Client Secret and add it to your `.env` as **`DISCORD_CLIENT_SECRET`**. This is a private, sensitive identifier that your app will use to grant an OAuth2 `access_token`, and should never be shared or checked into version control. + + +**Why is there a VITE_ prefix before our Client ID?** + +Prefixing the `CLIENT_ID` environment variable with `VITE_` makes it accessible to our client-side code. This security measure ensures that only the variables you intend to be accessible in the browser are available, and all other environment variables remain private. You can read details in the [Vite documentation](https://vitejs.dev/guide/env-and-mode). + + + + +By the end of Step 2, make sure you have: +- Set up a placeholder Redirect URI +- Added your app's Client ID and Client Secret to your project's `.env` file. + + +## Step 3: Setting Up the Embedded App SDK + +With our project and app set up, we're going to install and configure the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) which we'll use extensively through the rest of this guide. + +The Embedded App SDK is a first-party SDK that handles the communication between Discord and your Activity with [commands](/developers/docs/developer-tools/embedded-app-sdk#sdk-commands) to interact with the Discord client (like fetching information about the channel) and [events](/developers/docs/developer-tools/embedded-app-sdk#sdk-events) to listen for user actions and changes in state (like when a user starts or stops speaking). + + +The events and commands available in the Embedded App SDK are a subset of the [RPC API](/developers/docs/topics/rpc) ones, so referencing the RPC documentation can be helpful to understand what's happening under the hood when developing Activities. + + +### Install the SDK + +Back in our project's `client` directory from before (`getting-started-activity/client`), install the Embedded App SDK [via NPM](https://www.npmjs.com/package/@discord/embedded-app-sdk): + +``` +npm install @discord/embedded-app-sdk +``` + +This will add `@discord/embedded-app-sdk` to `getting-started-activity/client/package.json` and install the SDK in your `node_modules` folder. + +### Import the SDK in your Project + +Once installed, we need to import it into our client code and instantiate it to start the handshake between our app and the Discord client. + +To instantiate the SDK, we will use the environment variables we set up in Step 2. + +We also set up a check for the [`ready` event](/developers/docs/developer-tools/embedded-app-sdk#ready) with an async/await function which allows us to output a log or perform other actions once the handshake was successful. + + + +In `getting-started-activity/client/main.js`, let's import and instantiate the SDK: + +```js +// Import the SDK +import { DiscordSDK } from "@discord/embedded-app-sdk"; + +import "./style.css"; +import rocketLogo from '/rocket.png'; + +// Instantiate the SDK +const discordSdk = new DiscordSDK(import.meta.env.VITE_DISCORD_CLIENT_ID); + +setupDiscordSdk().then(() => { + console.log("Discord SDK is ready"); +}); + +async function setupDiscordSdk() { + await discordSdk.ready(); +} + +document.querySelector('#app').innerHTML = ` +
+ +

Hello, World!

+
+`; +``` + +
+ + +**Time to leave your browser behind** + +Once you add the SDK to your app, you will *not* be able to view your app inside your web browser. In the next step, we will run your Activity inside of Discord. In the next step, we will go over how to view your app in Discord. + + + + +By the end of Step 3, make sure you have: +- Installed the Embedded App SDK to your project +- Imported the SDK in your project's `client/main.js` file + + + +--- + +## Step 4: Running your app in Discord + +Let's ensure everything is wired up correctly, enable activities via the dev portal, and then run the Activity in Discord. + +### Run your app + +First, we'll restart the sample app. Open a terminal window and navigate to your project directory's `client` folder, then start the client-side app: + +``` +cd client +npm run dev +``` + +Your app should start and you should see output similar to the following: + +``` +VITE v5.0.12 ready in 100 ms + +➜ Local: http://localhost:5173/ +➜ Network: use --host to expose +➜ press h + enter to show help +``` + +We'll use the Local URL as our publicly-accessible URL in the next step. + +### Set up a public endpoint + +Next, we'll need to set up the public endpoint that serves the Activity's frontend. To do that, we'll create a tunnel with a reverse proxy. While we'll be using [`cloudflared`](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) in this guide, you can use [ngrok](https://ngrok.com/docs) or another reverse proxy solution if you prefer. + +While your app is still running, open another terminal window and start a network tunnel that listens to the port from the last step (in this case, port `5173`): + +``` +cloudflared tunnel --url http://localhost:5173 +``` + +When you run `cloudflared`, the tunnel will generate a public URL and you'll see output similar to the following: + +``` +Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): +https://funky-jogging-bunny.trycloudflare.com +``` + +Copy the URL from the output, as we'll need to add it to our app's settings. + +### Set up your Activity URL Mapping + +Because Activities are in a sandbox environment and go through the Discord proxy, you'll need to add a public URL mapping to serve your application and make external requests in your Activity. Since we're developing locally, we'll use the public endpoint we just set up. + +Back in your app's settings, click on the **URL Mappings** page under **Activities** on the left-hand sidebar. Enter the URL you generated from `cloudflared` in the previous step. + +![Configuring your URL Mapping](/images/activities/url-mapping-tutorial.png) + +| PREFIX | TARGET | +|--------|-----------------------------------------| +| `/` | `funky-jogging-bunny.trycloudflare.com` | + +Read details about URL Mapping [in the development guide](/developers/docs/activities/development-guides/local-development#url-mapping). + +### Enable Activities + +Next, we'll need to enable Activities for your app. On the left hand sidebar under **Activities**, click **Settings**. + +Find the first checkbox, labeled `Enable Activities`. Turn it on 🎉 + +![Enabling Activities in Settings](/images/activities/enable-activities.png) + +#### Default Entry Point Command + +When you enable Activities for your app, a [default Entry Point command](/developers/docs/interactions/application-commands#default-entry-point-command) called "Launch" is automatically created. This [Entry Point command](/developers/docs/interactions/application-commands#entry-point-commands) is the primary way for users to launch your Activity in Discord. + +By default, interactions with this command will result in Discord opening your Activity for the user and posting a message in the channel where it was launched from. However, if you prefer to handle the interactions in your app, you can update the [`handler` field](/developers/docs/interactions/application-commands#entry-point-handlers) or create your own. Additional details are in the Entry Point command [documentation](/developers/docs/interactions/application-commands#entry-point-commands) and [development guide](/developers/docs/activities/development-guides/user-actions#setting-up-an-entry-point-command). + + +### Running your Activity in Discord + +Now that we are pointing Discord to our locally running app, we can launch the Activity in Discord! + +Navigate to your Discord test server and, in any voice and or text channel, open the App Launcher where your in-development Activity should be present. If you don't see your Activity, you should try searching for its name. + +Clicking on your app will launch your locally running app from inside Discord! + +![Running your activity](/images/activities/start-activity.png) + + +**Customizing your Activity** + +If you'd like to set images for your Activity, you can learn how to do that [here](/developers/docs/activities/development-guides/assets-and-metadata#setting-up-activity-metadata). + + +We're looking pretty good so far, but we haven't wired up any Discord functionality yet. Let's do that next. + + + +By the end of Step 4, make sure you have: +- Set up a public endpoint +- Added an Activity URL Mapping in your app's settings +- Enabled Activities for your app +- Successfully launched your Activity in Discord + + +--- + +## Step 5: Authorizing & authenticating users + +To authenticate your Activity with the users playing it, we must finish implementing our server-side app and get it talking to the client-side app. + +We will use `express` for this example, but any backend language or framework will work here. + + + +This diagram illustrates the common pattern for granting a user an OAuth2 access_token: + +![Flow diagram for Oauth2](/images/activities/oauth-flow-diagram.svg) + +We will be implementing this pattern in this tutorial, but more example implementations can also be found in this sample project: + +- [Back-end code](https://github.com/discord/embedded-app-sdk-examples/blob/main/discord-activity-starter/packages/server/src/app.ts) +- [Front-end code](https://github.com/discord/embedded-app-sdk-examples/blob/main/discord-activity-starter/packages/client/src/main.ts) + + + +``` +# move into our server directory +cd server + +# install dependencies +npm install +``` + +We aren't going to edit the server code here, but it consists of a single POST route for `/api/token` that allows us to perform the OAuth2 flow from the server securely. + + + +In the `getting-started-activity/server/server.js` file, the following code should already be present: + +```javascript +import express from "express"; +import dotenv from "dotenv"; +import fetch from "node-fetch"; +dotenv.config({ path: "../.env" }); + +const app = express(); +const port = 3001; + +// Allow express to parse JSON bodies +app.use(express.json()); + +app.post("/api/token", async (req, res) => { + + // Exchange the code for an access_token + const response = await fetch(`https://discord.com/api/oauth2/token`, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + client_id: process.env.VITE_DISCORD_CLIENT_ID, + client_secret: process.env.DISCORD_CLIENT_SECRET, + grant_type: "authorization_code", + code: req.body.code, + }), + }); + + // Retrieve the access_token from the response + const { access_token } = await response.json(); + + // Return the access_token to our client as { access_token: "..."} + res.send({access_token}); +}); + +app.listen(port, () => { + console.log(`Server listening at http://localhost:${port}`); +}); +``` + + +Now, start the project's backend server: + +``` +npm run dev +``` + +You should output similar to the following: + +``` +> server@1.0.0 dev +> node server.js + +Server listening at http://localhost:3001 +``` + +We can now run our server and client-side apps in separate terminal windows. You can see other ways to set this up in the other [sample projects](/developers/docs/activities/overview#sample-projects). + +### Calling external resources from your activity + +Before we call your backend activity server, we need to be aware of the Discord proxy and understand how to avoid any Content Security Policy (CSP) issues. + +Learn more about this topic in the guides for [Constructing a Full URL](/developers/docs/activities/development-guides/networking#construct-a-full-url) and [Using External Resources](/developers/docs/activities/development-guides/networking#using-external-resources). + +### Calling your backend server from your client + +We're almost there! Now, we need our client application to communicate with our server so we can start the OAuth process and get an access token. + + +**What is vite.config.js?** + +To allow our frontend app to call our Express server, Vite requires us to set up a proxy for `/api/*` to our backend server, which is running on port 3001. In their docs, you can learn more about [Vite](https://vitejs.dev/). + + + + +Copy the following code in your project's `getting-started-activity/client/main.js` file: + +```javascript +import { DiscordSDK } from "@discord/embedded-app-sdk"; + +import rocketLogo from '/rocket.png'; +import "./style.css"; + +// Will eventually store the authenticated user's access_token +let auth; + +const discordSdk = new DiscordSDK(import.meta.env.VITE_DISCORD_CLIENT_ID); + +setupDiscordSdk().then(() => { + console.log("Discord SDK is authenticated"); + + // We can now make API calls within the scopes we requested in setupDiscordSDK() + // Note: the access_token returned is a sensitive secret and should be treated as such +}); + +async function setupDiscordSdk() { + await discordSdk.ready(); + console.log("Discord SDK is ready"); + + // Authorize with Discord Client + const { code } = await discordSdk.commands.authorize({ + client_id: import.meta.env.VITE_DISCORD_CLIENT_ID, + response_type: "code", + state: "", + prompt: "none", + scope: [ + "identify", + "guilds", + "applications.commands" + ], + }); + + // Retrieve an access_token from your activity's server + const response = await fetch("/api/token", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + code, + }), + }); + const { access_token } = await response.json(); + + // Authenticate with Discord client (using the access_token) + auth = await discordSdk.commands.authenticate({ + access_token, + }); + + if (auth == null) { + throw new Error("Authenticate command failed"); + } +} + +document.querySelector('#app').innerHTML = ` +
+ +

Hello, World!

+
+`; +``` +
+ +Now if we relaunch our app, we'll be prompted to authorize with Discord using the `identify`, `guilds`, and `applications.commands` scopes. + +![Prompt to authorize Activity](/images/activities/tutorial-auth.png) + + +**Safe storage of tokens** + +Access tokens and refresh tokens are powerful, and should be treated similarly to passwords or other highly-sensitive data. Store both types of tokens securely and in an encrypted manner. + + + + +By the end of Step 5, make sure you have: +- Updated your `client/main.js` to call the backend to support user authorization and authentication +- Been able to successfully complete the authorization flow for your app when opening your Activity + + +--- + +## Step 6: Use the SDK to fetch the channel + +Now that we have authenticated our users, we can start interacting with contextual Discord information that we can use in our application. + +Let's use the SDK to get details about the channel that our activity is running in. We can do this by writing a new async function that uses the `commands.getChannel` SDK method. + + + +In the same `getting-started-activity/client/main.js` file, paste the following function: + +```javascript +async function appendVoiceChannelName() { + const app = document.querySelector('#app'); + + let activityChannelName = 'Unknown'; + + // Requesting the channel in GDMs (when the guild ID is null) requires + // the dm_channels.read scope which requires Discord approval. + if (discordSdk.channelId != null && discordSdk.guildId != null) { + // Over RPC collect info about the channel + const channel = await discordSdk.commands.getChannel({channel_id: discordSdk.channelId}); + if (channel.name != null) { + activityChannelName = channel.name; + } + } + + // Update the UI with the name of the current voice channel + const textTagString = `Activity Channel: "${activityChannelName}"`; + const textTag = document.createElement('p'); + textTag.textContent = textTagString; + app.appendChild(textTag); +} +``` + +Now, update the callback after `setupDiscordSdk()` to call the function you just added: + +```javascript +setupDiscordSdk().then(() => { + console.log("Discord SDK is authenticated"); + + appendVoiceChannelName(); +}); +``` + + + +If you close and rejoin the Activity, you should now see the name of the current channel. + +![Discord Activities](/images/activities/tutorial-channel-name.png) + + + +By the end of Step 6, make sure you have: +- Updated your `client/main.js` code to fetch the channel name using the SDK +- Added a call to the new function in the callback for `setupDiscordSdk()` + + +--- + +## Step 7: Use the API to fetch the guild + +Since we requested the `identify` and `guilds` scopes, you can also use the authorized `access_token` we received earlier to fetch those resources via the API. + +In the following code block, we will: +1. Call the [`GET /users/@me/guilds`](/developers/docs/resources/user#get-current-user-guilds) endpoint with `auth.access_token` to get a list of the guilds the authorizing user is in +2. Iterate over each guild to find the guild we are in based on the `guildId` defined in discordSdk +3. Create a new HTML image element with the guild avatar and append it to our frontend + + +In this example, we use a pure `fetch` request to make the API call, but you can us one of the JavaScript [community-built libraries](/developers/docs/developer-tools/community-resources) if you prefer. + + + + +In the same `client/main.js` file, add the following function: + +```javascript +async function appendGuildAvatar() { + const app = document.querySelector('#app'); + + // 1. From the HTTP API fetch a list of all of the user's guilds + const guilds = await fetch(`https://discord.com/api/v10/users/@me/guilds`, { + headers: { + // NOTE: we're using the access_token provided by the "authenticate" command + Authorization: `Bearer ${auth.access_token}`, + 'Content-Type': 'application/json', + }, + }).then((response) => response.json()); + + // 2. Find the current guild's info, including it's "icon" + const currentGuild = guilds.find((g) => g.id === discordSdk.guildId); + + // 3. Append to the UI an img tag with the related information + if (currentGuild != null) { + const guildImg = document.createElement('img'); + guildImg.setAttribute( + 'src', + // More info on image formatting here: https://discord.com/developers/docs/reference#image-formatting + `https://cdn.discordapp.com/icons/${currentGuild.id}/${currentGuild.icon}.webp?size=128` + ); + guildImg.setAttribute('width', '128px'); + guildImg.setAttribute('height', '128px'); + guildImg.setAttribute('style', 'border-radius: 50%;'); + app.appendChild(guildImg); + } +} +``` + +Then, call the new function in the callback for `setupDiscordSdk`: + +```javascript +setupDiscordSdk().then(() => { + console.log("Discord SDK is authenticated"); + + appendVoiceChannelName(); + appendGuildAvatar(); +}); +``` + + +If we relaunch our Activity, we will see the current server's avatar render in our Activity. + +![Discord Activities](/images/activities/tutorial-hero.png) + + + +At this point, you should have your Activity up and running. For Step 7, you should have: +- Updated your `client/main.js` code to fetch the guild information using the [`GET /users/@me/guilds`](/developers/docs/resources/user#get-current-user-guilds) API endpoint +- Added a call to the new function in the callback for `setupDiscordSdk()` + + +--- + +## Next Steps + +Congrats on building your first Activity! 🎉 + +This is an intentionally simple example to get you started with the communication between your Activity and Discord using the Embedded App SDK and APIs. From here, you can explore the [Activities documentation](/developers/docs/activities/overview) and other resources. + +import {WrenchIcon} from '/snippets/icons/WrenchIcon.jsx' +import {ForumIcon} from '/snippets/icons/ForumIcon.jsx' +import {GlobeEarthIcon} from '/snippets/icons/GlobeEarthIcon.jsx' + + + }> + Follow our Activities Development Guides for suggested development practices and considerations. + + }> + Try out the full range of Embedded App SDK features in the playground app, or explore some of the other examples + + }> + Join our community to ask questions about the API, attend events hosted by the Discord platform team, and interact with other Activities developers + + diff --git a/discord/developers/docs/activities/design-patterns.mdx b/discord/developers/docs/activities/design-patterns.mdx new file mode 100644 index 0000000000..0ab4836e8a --- /dev/null +++ b/discord/developers/docs/activities/design-patterns.mdx @@ -0,0 +1,223 @@ +--- +title: Activity Design Patterns +sidebarTitle: Design Patterns +description: Best practices and design principles for creating engaging Discord Activities. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +Before building your Activity, here are some guiding principles for you to consider for a successful user experience. + +## Guiding Principles + +### Interaction over Isolation +- Activities are not for screen-sharing, we already have that built-in! +- Give users a way to interact with one another. +- User actions should impact the experiences of other participants. + +### Expression over Monotony +- Our users want to create memorable moments. +- Create experiences where users craft moments and react in ways they want to share with others. + +### Accessibility over Exclusion +- Inclusivity is key. +- Reduce the bar to entry for your Activity and reward people for engaging deeply with your experience. + +--- + +## Providing a "Solo-Plus" Experience + +Successful experiences will be built to accommodate small group sessions ("Plus"), but also playable and enjoyable for the case of single-player ("Solo") sessions. + +### Solo or single-player +- While most people use Discord with small groups of friends. Consider experiences that are compelling alone, but better together! +- If you don't support solo play, expect to see a lot of users peeking into the Activity on their own. Consider what you can do to preview the Activity and compel them to come back with their friends. + +### Solo-Plus or small groups of users +- Small group sessions (3-8 people) show more engagement and retention from users than single-player experiences. +- Letting players join with their friends keeps them coming back for more. + +### Large groups of users +- While you can set a "max participants" suggestion to users, the only real limit is the number of people who can join a Voice call. +- Be aware of how your Activity will behave when there are 25 or more people in the call. + +--- + +## User Presence & Privacy +When in an Activity with others, make the actions and presence of the others visible to each player. + +### Make actions and presence of the others visible to each player + - Users enjoy participating in a space that feels active. + - If a user has customized their server nickname or avatar, use their server nickname or avatar in game. + - Show when a user is speaking in the voice call, or whether they're active or inactive. + +![Speech bubbles in Bobble League](/images/activities/bobble-bash.png) + +### Respect user privacy + - For Activity sessions that match users from various servers, usernames and avatars should be anonymized by default. + - Discord personal identifiers should only be populated into the Activity with user confirmation. + - Only use these identifiers when the people who can see them already have access via the server or DM. + +--- + +## Deliver a Quality Experience + +Make your app fast, easy to join, and maximize fun to launch a crowd favorite. + +### Surprise and delight users +- Surprise and delight is about caring about the small details of how a person experiences your work. +- Put the right emotion in when they least expect it to deliver the magic. + +![Bobble League](/images/activities/bobble-league.png) + +### Keep load times as low as possible +- This allows for easier drop-in drop-out behavior for the large portion of mobile users on Discord. +- See the below [Quality & Testing Recommendations](/developers/docs/activities/design-patterns#quality-and-testing) for key areas of minimum quality support and testing recommendations. +- See the below [Technical Considerations](/developers/docs/activities/design-patterns#technical-considerations) for recommendations on how to partition loading and work with various development tools to reduce load times. +- Consider different screen sizes and orientations across desktop and mobile devices and make sure UI elements scale appropriately. + +### Support drop-in, drop-out behavior + +- Activities are frictionless to join and easy to discover, so you can expect that users will join mid-experience. Give those users something to do, even if it's just letting them spectate until they can join without being disruptive. In the same vein, users can leave without notice or become afk (away from keyboard). Handle these cases gracefully. +- Create a case for users who have joined a call but have not yet started playing or engaging. Allow these users to "spectate" other users who are playing. This can also be helpful for Activities that have an ideal number in mind for play. + +![Support drop-in, drop-out behavior in your Activity](/images/activities/eights.png) + +### Make your app as available as possible + +#### Cross-Platform Support +- Supporting desktop and mobile devices will expand your user-base to the widest number of users. +- Discord device usage is split between mobile and desktop platforms. + +#### Discord is a Global Audience + - Consider supporting a multitude of languages and cultures to make your app more usable for all Discord users. + +#### Implement Invites +- The Embedded App SDK allows for sending invites from within an Activity out to other friended users. +- Add invite buttons to a screen or flow with intentionality, not only on the start screen and at the beginning. Examples of intentional Invite prompting include: + - Cases where players leave the activity session + - Cases where the minimum number of participants has not been reached (e.g. any activity that needs 2+ to start, or is more fun with more people, or when you need an opponent in a game like Chess.) + +#### Implement Sharing + +Discord is a social platform where users talk to each other. Sharing and invites lets your app live and engage in those shared spaces, making it visible and accessible to everyone not currently playing. Things like sharing and invites are important ways to reach into those private, shared spaces and attract other players. + +- The Embedded App SDK allows for sending an image or gif from within an Activity. + - Share photos or GIFs that capture moments of fun and memorable, or something to brag about. Don't make things shareable just to feature the activity. + - Sharing a high score alone may not be very engaging, but sharing a really good move made in a game, or a collaborative drawing that creates a memory is a conversation starter and may make others want to join in on the fun. + +![Shared Moment from Chess in the Park](/images/activities/chess-victory.png) + +#### Activities in Text Channels +- The Activity user interface, copy and user flows should not rely on people in voice to explain, organize, clarify, or instruct about how the activity works. +- All controls, CTAs, and instructions should be clear enough that folks (especially first time users) playing in a text channel are able to quickly start using the app without needing to talk on voice to learn about it. +- Lean on dynamic first-time user experience and "How To Play" instructions, toast messages, Call To Action buttons, etc. but be careful to not over-clutter with copy. + +### Monetization Considerations + + +Monetization in Activities will be available soon. Keep the following considerations in mind as you design your Activity. + + +- Avoid prohibitive gates in front of participation (e.g. login wall / paywall), especially early in the user journey. +- Avoid monetized unlocks that give unfair advantage to other non-paying players or users (i.e. "pay to win"). +- Take advantage of Discord as a social platform: look for opportunities to offer users customizable skins, aesthetic themes, new avatars, etc. + +--- + +## Game Design Considerations + +### Build games that are easy to learn, hard to master +- Players can often feel pressure when joining a multiplayer game (voice/video) in the moment if they’ve never played before. +- Games that have a simple quick reference control scheme that are easily approachable help ease players into an experience without dropping them into the deep end. +- An easy to understand game loop combined with easy to digest controls tends to work best. If you have to go through a tutorial to understand the game then it will be intimidating to join a game in the moment. + +### Discord users who engage with voice are among our most engaged users +- Activity engagement and retention increases when users are in a voice call with friends. + +### Consider existing game expectations and tropes +- If you're developing a game, you're more likely to attract users who are gamers. +- Take advantage of existing tropes and expectations for games of the genre you're developing. +- Appeal to the player interests that your game (or game genre) provides, and support those interests as a means to get them coming back. + +### Activities close when the last participant in the Activity leaves it +- The next time the Activity is launched, even if it’s in the same DM or Voice Channel, it will be a different instance and could have different participants. + + +### If a game is launched, what % of the time do users reach different phases of the game? +- This includes launch → start, as there can be a large dropoff between these two (especially if you don’t support solo play or small group sizes). +- This also includes various check points in the game, including what % of sessions that start a game reach the end of that game! +- If your Activity has different settings you can start a game with, see if some are more popular or more successful — you may want to change your defaults! + - For non-games, you may want to analyze a certain action instead (such as queueing a video / song for a co-watching / co-listening Activity). +- If drop-off is really high at a certain point, see if you can figure out why or change flows. + + +### How many games are played in a session? +- More games per session isn’t inherently better (you may have an Activity that is meant to be one long game), but is a good baseline to understand. +- If you expect to see a lot of repeat plays per session and don’t, it can be worth digging in to understand more. + + +### How does the group size impact various key metrics? +- For example, are larger sessions more or less likely to reach the end of a game? To replay? Etc. +- This can help you catch if your Activity has unexpected weak points in different group sizes — maybe the game drags on if there are too many people or isn’t compelling enough if there’s only two. +- Not every Activity needs to be built for robust group sizes, but if you have the option to play with X # of players, it’s good practice to make sure that experience is enjoyable for all involved! + +--- + +## Co-Watching / Co-Listening + +- Co-watching and co-listening should be more than just screen-sharing. Consider creative ways to make each user's experience impacted by other participants. +- Consider providing host-controls so that playback isn't overly chaotic. If host controls are provided, make sure everyone can participate in some way, even if it's just recommending content to the host. +- If content is not available for everyone in the activity (for example, geo-restricted content), make sure that information is known when the content is queued and when it's playing. The host is generally interested in creating a good experience for their friends. Playback of content should be in sync with others in a local channel or call. +- Familiar design patterns can help to get people to participate in your Activity right away. Most Discord users are extremely familiar with media browsing (Spotify, YouTube, Netflix, etc.) and will immediately understand your content if you follow these patterns. +- Mobile device volume can be controlled by a device's built-in controls (volume buttons on the side of the device) and work as expected across desktop, mobile, and browsers. + +--- + +## Technical Considerations + +### Developing for the iframe + +- Remember that you need to contend with the Discord Client itself for resources (CPU, RAM and GPU) as that client is still fully running while executing your Activity. +- Prioritize time-to-first-interaction. +- Minimize the amount of Web Assembly (WASM) as much as possible in your Activity. + - Older iOS devices are especially affected by WebKit's optimization compilation pass of WASM that occurs during the first few minutes of usage of an Activity. To varying degrees, this will cause noticeable stutters, device thermal issues, and possibly degraded Discord's AV quality during those early minutes. +- All network traffic is routed through the Discord Proxy for various security reasons. +- Create multiple versions of your Activity in the Developer Portal for less and more stable development versioning (e.g. Development, Staging, Production). This allows you to show and test the last stable build while also having development and staging environments. + +### Developing in Unity + +Developing for the iframe in Unity is possible, but will likely require extensive min/maxing of all facets of your game to meet performance and quality standards. Other, web-first, game engines are generally more performant out of the box. + +--- + +## Quality and Testing + +### Verify User Interface and User Experience +- Ensure all buttons and UI elements function correctly without any blocking issues for standard Activity use or gameplay. +- Test Discord usernames handling, ensuring they display appropriately based on the chosen format (Discord username or server identity). + +### Test for Performance +- Evaluate performance to ensure it is satisfactory for the majority of users across phones, tablets, and desktop machines and across multiple operating systems. +- Lag or slowness should not significantly impact the Activity or gameplay experience. + +### Test for Audio +- Check that sound effects and background music work as intended, with volume settings functioning correctly. +- Being able to hear when playing on a voice call is important. + +### Validate Activity Controls +- Confirm that all game controls, including clicks, drag, key controls, swiping, and hovers, work as expected and are suitable for the platform. +- Test in-app tutorials and instructions for accuracy and relevance to the current activity functionality and user platform (desktop or mobile). + +### Mobile Device UI Considerations +- Watch out for potential issues on devices with top notches or cameras in specific corners that may hide or cut off UI elements and buttons, rendering them untappable. +- Be mindful of Android curved screen edges that could obscure or make elements and buttons inaccessible. +- Take into account iOS/Android swipe gestures that may interfere with UI elements or buttons located at the bottom of the screen. +- Ensure that Android devices with a back button do not overlap with elements or buttons at the bottom of the screen, preventing users from tapping them. +- Respect the safe area defined by the platform to prevent any buttons or content from being cut off or non-functional. + +### Desktop Accessibility Guidelines +- Enable users to cycle through Discord buttons and fields on desktop by pressing TAB repeatedly, ensuring that the activity does not capture control. +- Implement the functionality for the ESC key to close any open in-activity modals, providing a consistent user experience. +- Opt for color-blind friendly colors with high contrast, especially for crucial game elements that need to be distinguishable based on color. +- Avoid using similar colors for objects that are essential for gameplay but differ only in color, as this may pose challenges for color-blind users. diff --git a/discord/developers/docs/activities/development-guides.mdx b/discord/developers/docs/activities/development-guides.mdx new file mode 100644 index 0000000000..3bf845c5cb --- /dev/null +++ b/discord/developers/docs/activities/development-guides.mdx @@ -0,0 +1,138 @@ +--- +title: Activity Development Guides Glossary +sidebarTitle: Glossary +description: Definitions and explanations of key terms used in Discord Activities development. +--- + +These guides include suggested development practices, SDK commands, and user flows for you to consider while building your Activity. These will help to provide your users with a consistent and clear experience while interacting with your application. + +## Local Development + + + Get up and running with a local development application. + + + How to launch your app on mobile and desktop Discord clients. + + + Configure the Discord proxy to allow network requests to necessary external endpoints. + + + How to use the various levels of logging while building your application. + + + +## User Actions + + + Open an external link from within your app. + + + Open the Application Channel Invite dialog within Discord. + + + Open a dialog to share media from your application to a channel, DM, or GDM. + + + Configure a command that allows users to open your Activity from the App Launcher. + + + Open a dialog to enable hardware acceleration for compute-intensive applications. + + + +## Mobile + + + Update your application settings to support iOS and Android. + + + Ensure that your app's assets fall within mobile-safe areas. + + + Respond to thermal state changes surfaced from iOS and Android. + + + +## Layout + + + Configure and subscribe to changes in application orientation. + + + Subscribe to layout mode changes to update your application's user interface. + + + +## Networking + + + Working with our Activity Proxy + + + Generate a full URL when working with network requests. + + + Allow network requests to external resources from inside the Discord proxy. + + + Keep things safe and secure in your Activity. + + + +## Multiplayer Experience + + + Managing instances to ensure users join the same instance as their friends. + + + Use the SDK to fetch the users currently connected to an instance. + + + Retrieve and render the usernames and avatars of users connected to your application. + + + Validating activity sessions are via a Discord client before adding them to an instance's session. + + + +## Growth and Referrals + + + Encourage your users to share links to your activity by adding tracking and offering rewards for engagement. + + + + For off-platform sharing of rewards, promotions, or limited time experiences. + + + + An API to be able to generate ephemeral links with a customizable embed. + + + +## Assets & Metadata + + + Best practices for configuring how your application shows up in Discord. + + + Best practices for configuring how your application shows up in Discord. + + + +## Production Readiness + + + Manage asset caching in your application and the Discord Activity proxy. + + + Stay within rate limits to keep the fun going in your Activity. + + + Network routing considerations when preparing your Activity for production use. + + + Future-proof your application and support new commands as they become available in the SDK. + + diff --git a/discord/developers/docs/activities/development-guides/assets-and-metadata.mdx b/discord/developers/docs/activities/development-guides/assets-and-metadata.mdx new file mode 100644 index 0000000000..6d358261bd --- /dev/null +++ b/discord/developers/docs/activities/development-guides/assets-and-metadata.mdx @@ -0,0 +1,57 @@ +--- +title: Assets and Metadata +description: Guide to managing Activity assets, metadata, and configuration. +--- + +## Setting Up Activity Metadata + +The Activity Shelf is where users can see what Activities can be played. It has various metadata and art assets that can be configured. + +To update your app's metadata in the Discord Developer Portal, navigate to the `Settings -> General Information` tab of your app. + +- **Application Name:** The publicly visible name of your app. +- **Application Icon:** The publicly visible icon for your app. +- **Application Description:** The application description is shown in the view of the Activity Shelf Item. +- **Max Participants:** The max participants indicate the maximum number of players for your application. + - Max Participants is displayed above the name in the 1-up view: `Up to X participants`. + - Leaving this field empty defaults to `Unlimited participants`. + - Max Participants is also displayed under the name in the 2-up view. + + +An app can have a different application name and avatar from the application's bot username and avatar. Both sets of metadata are public-facing and may be visible in various situations when a user interacts with your app. You can view your bot's username on the `Settings -> Bot` tab. + + +--- + +## Setting Up Activity Art Assets + +The Activity Shelf is where users can see what Activities can be played. It has various metadata and art assets that can be configured. + +To update your app's embedded-specific art assets in the Discord Developer Portal, navigate to the `Activities -> Art Assets` tab of your app. + +## Embedded Background + +Used as a background overlay in Grid view. Artwork should be clustered around the edges of the image leaving space in the center of the image so the UI does not clash with it. + +#### Specifications +- 16:9 aspect ratio +- At least 1024 pixels wide + +## Cover Art + +Used as the main image in the Activity Shelf. It is suggested that this image contain the title and some art in the background. + +#### Specifications: +- Image can be displayed at both 16:9 and 13:11 aspect ratios +- At least 1024 pixels wide + + +## App Tile + +There are two views of an application tile. The regular size tile (2-up tile) and the larger "featured" application tile (1-up tile). + +## Video Preview + +Hovering over the cover image should start playing a preview video of the Application. The preview videos should be no more than 10 seconds long. If no video is provided, nothing will happen as you hover over the application. + +#### Specifications: 640 x 360, mp4 format, under 10 seconds long, under 1 MB in size diff --git a/discord/developers/docs/activities/development-guides/growth-and-referrals.mdx b/discord/developers/docs/activities/development-guides/growth-and-referrals.mdx new file mode 100644 index 0000000000..145d2a9bf2 --- /dev/null +++ b/discord/developers/docs/activities/development-guides/growth-and-referrals.mdx @@ -0,0 +1,183 @@ +--- +title: Growth and Referrals +description: Implement growth strategies and referral systems in Discord Activities. +--- + +## Prompting Users to Share Incentivized Links + +Incentivized sharing can help grow your Activity through network effects. You can use links in several different ways such as: + +- **Referral links.** Users can copy referral links inside your Activity, which include their Discord user ID (`https://discord.com/activities/?referrer_id=123456789`), and they can send to their friends. If their friend accepts and starts playing your game, then you gift the referrer something inside your game. +- **Promotions.** You can run a temporary promotion on social media, where you offer a reward if they start playing now. Share a custom link on your social media (`https://discord.com/activities/?custom_id=social012025` ). Anyone who clicks that specific link receives something inside your game. +- **Social deep-links.** Currently, when users launch an Activity, they all land in the same place. Instead, you can start deep-linking to contextually relevant points in your game. For example, user A can copy a link inside your Activity for engaging other users (`https://discord.com/activities/?referrer_id=123456789&custom_id=visit-location`), and sends the link to their friends in a DM or channel. Then, user B who clicks the link gets taken directly to user A’s location. +- **Turn-based deep-links.** When you send an “it’s your turn” DM to a user, you can include a link which takes them directly to the right game instance and the turn they need to take. +- **Affiliate marketing.** You can work with affiliates (influencers, companies, etc) to advertise your game to their followings, and reward them via a custom link (`https://discord.com/activities/?custom_id=influencer1`). Then, for every user that starts playing because of said influencer, you can then pay out to the influencer. +- **Source attribution.** You can use the `custom_id` parameter to figure out how much traffic you’re getting from different marketing sources. + +This guide covers implementing a referral link which will feature a reward system for users who share links and those who click them. + +#### Implementation Overview + +1. Create and track an incentivized link for a promotional campaign, then prompt users to share the link +2. Handle incoming referrals and grant valid rewards + +#### Sharing Links + +When implementing sharing, you'll need to: +1. Generate a unique ID for tracking the promotion +2. Call the [`shareLink`](/developers/docs/developer-tools/embedded-app-sdk#sharelink) command +3. Track the share attempt + +```javascript +// Generate a unique ID for this promotion +// This could be per-campaign, per-user, or per-share depending on your needs +const customId = await createPromotionalCustomId(); + +try { + const { success } = await discordSdk.commands.shareLink({ + message: 'Click this link to redeem 5 free coins!', + custom_id: customId, + }); + + if (success) { + // Track successful share for analytics/limiting + await trackSuccessfulShare(customId); + } +} catch (error) { + // Handle share failures appropriately + console.error('Failed to share link:', error); +} +``` + +#### Handling Incoming Referrals + +When a user clicks a shared link, your activity will launch with referral data available through the SDK: + +```javascript +// Early in your activity's initialization +async function handleReferral() { + // Validate the referral data + if (!discordSdk.customId || !discordSdk.referrerId) { + return; + } + + try { + // Verify this is a valid promotion and hasn't expired + const promotion = await validatePromotion(discordSdk.customId); + if (!promotion) { + console.log('Invalid or expired promotion'); + return; + } + + // Prevent self-referrals + if (discordSdk.referrerId === currentUserId) { + console.log('Self-referrals not allowed'); + return; + } + + // Grant rewards to both users + await grantRewards({ + promotionId: discordSdk.customId, + referrerId: discordSdk.referrerId, + newUserId: currentUserId + }); + } catch (error) { + console.error('Failed to process referral:', error); + } +} +``` + +#### Link Sharing Best Practices + +- Generate unique, non-guessable `customId`s +- Track and validate referrals to prevent abuse +- Handle edge cases like expired promotions gracefully +- Consider implementing cool-down periods between shares +- Do not override the `referrer_id` query parameter directly. When present, `referrer_id` is expected to be a Discord snowflake-type user ID, otherwise it will be set to the message's author id. + +--- + +## Creating and Managing Custom Incentivized Links + +This guide covers creating a customizable [Incentivized Link](/developers/docs/activities/development-guides/growth-and-referrals#prompting-users-to-share-incentivized-links) through the dev portal, and then retrieving the link to be able to share it off-platform. Incentivized Links are used to customize how the embed appears to users. + +#### Creating a Link + +1. In your Application's portal, visit the Custom Links page under the Activities heading in the navigation pane. +2. On the Custom Links page, click `Create New` to create a new link. +3. You will need to upload an image with an aspect ratio of 43:24. +4. Title, and description are also required. +5. `custom_id` is an optional field, an explicit `custom_id` query parameter on the link itself will always override the set `custom_id`. +6. Click Save. + +#### Editing a Link + +1. Click on a row to open up the modal with all of the data loaded in ready for your edits. +2. Change the description to something else. +3. Click Update. + +#### Copying a Link + +Once you're satisfied with your changes you can click on the copy icon on the row, it'll change colors to green indicating that it copied to your clipboard. You are now able to share this link anywhere. The link will look like: `https://discord.com/activities/?link_id=0-123456789`. Even if you've set a `custom_id`, it won't be explicitly included in the link but will be loaded once a user clicks on the link. You can then further shorten this URL if you'd like. + +#### Deleting a Link + +1. Click on the trash icon on the row of the link you're trying to delete. +2. You'll have a confirm dialog pop up. + + +Deleting is irreversible and immediate. Ensure that your link isn't in active use before deleting and/or that your activity gracefully handles any click-throughs from the link. + + +#### Best Practices + +- Generate unique, non-guessable `customId`s +- Track and validate referrals to prevent abuse +- Gracefully handle expirations in your activity for any custom links that are limited time but still live off-platform. + +#### User Experience + +![custom-link-embed](/images/activities/custom-link-embed.png) + +Users will see an embed with your information displayed. Clicking "Play" opens the activity and passes through the `custom_id` you've set. A `referrer_id` will be present for links shared on Discord. + +--- + +## Generating a Custom Link Within Your Activity + +This guide covers creating a customizable [Incentivized Link](/developers/docs/activities/development-guides/growth-and-referrals#prompting-users-to-share-incentivized-links) within your activity, and using the `shareLink` API to share the link. + +* Allows you to customize the way the link is presented to users via the embed +* Can be generated on-demand within your activity +* Ephemeral, 30 day TTL +* Does not show up in the developer portal + +#### Generating a Link + +```javascript +// Convert an image array buffer to base64 string +const image = base64EncodedImage; + +// Generate the quick activity link +const linkIdResponse = await fetch(`${env.discordAPI}/applications/${env.applicationId}/quick-links/`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: { + custom_id: 'user_123/game_456', + description: 'I just beat level 10 with a perfect score', + title: 'Check out my high score!', + image, + } +}); +const {link_id} = await linkIdResponse.json(); + +// Open the Share modal with the generated link +const {success} = await discordSdk.commands.shareLink({ + message: 'Check out my high score!', + link_id, +}); +success ? console.log('User shared link!') : console.log('User did not share link!'); +``` diff --git a/discord/developers/docs/activities/development-guides/layout.mdx b/discord/developers/docs/activities/development-guides/layout.mdx new file mode 100644 index 0000000000..bc76d41d31 --- /dev/null +++ b/discord/developers/docs/activities/development-guides/layout.mdx @@ -0,0 +1,74 @@ +--- +title: Layout +description: Design effective layouts and user interfaces for Discord Activities. +--- + +## Application Orientation + +#### Locking Application Orientation + +This SDK provides APIs for locking the application to specific orientations. The possible lock states are `UNLOCKED`, `PORTRAIT`, and `LANDSCAPE`. `lock_state` is the default lock state, and it affects the app orientation when the application is focused. `picture_in_picture_lock_state` determines the PIP aspect ratio, and `grid_lock_state` determines the grid tile aspect ratio for the application. When `picture_in_picture_lock_state` is not set, the application PIP falls back to `lock_state` to determine the aspect ratio. When `grid_lock_state` is not set, the application grid tile falls back to `picture_in_picture_lock_state` to determine its aspect ratio, and if `picture_in_picture_lock_state`is not set, it uses `lock_state`. + +Calling `setOrientationLockState` with an `undefined` or omitted value for `picture_in_picture_lock_state` or `grid_lock_state` will not change the corresponding lock states for the application. Calling `setOrientationLockState` with a null value for `picture_in_picture_lock_state` or `grid_lock_state` will clear the application's corresponding lock states such that those layout modes will use the fallback lock states. + +```javascript +import {DiscordSDK, Common} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(clientId); +await discordSdk.ready(); + +// Set a default lock state +discordSdk.commands.setOrientationLockState({lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE}); + +// or set both a default lock state and a picture-in-picture lock state +discordSdk.commands.setOrientationLockState({ + lock_state: Common.OrientationLockStateTypeObject.PORTRAIT, + picture_in_picture_lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE, + grid_lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE, +}); +``` + +#### Configuring Default Orientation Lock State Through the Developer Portal + +It's also possible to configure an application with a default orientation lock state via the Developer Portal. Using this method, the Discord app will apply the orientation lock when launching the application before the SDK has been initialized. This can create a smoother application launch flow where the application starts in the correct orientation rather than switching to the correct orientation after some delay after the application requests an orientation lock via the SDK. The Developer Portal supports setting a different default orientation lock states for phones versus tablets. + +![default-orientation-lock-state](/images/activities/default_orientation_lock_state.png) + +#### Subscribing to Screen Orientation Updates + +To listen to the screen orientation (which is sometimes different from the physical device orientation), subscribe to the `ORIENTATION_UPDATE` event. Discord will publish the current orientation upon event subscription, and it'll also publish any orientation changes that happen afterward. + +```javascript +const handleOrientationUpdate = (update: {screen_orientation: number}) => { + switch (update.screen_orientation) { + case Common.OrientationTypeObject.PORTRAIT: + ... + case Common.OrientationTypeObject.LANDSCAPE: + ... + default: + ... + } +} + +discordSdk.subscribe('ORIENTATION_UPDATE', handleOrientationUpdate); +``` + +--- + +## Application Layout Mode + +There are three layout modes that an application can be in: focused, picture-in-picture (PIP), or grid mode. Activities can subscribe to the layout mode to determine when to optionally change their layouts to optimize for each layout mode. Old Discord clients only support the `ACTIVITY_PIP_MODE_UPDATE` event, while new Discord clients support both `ACTIVITY_PIP_MODE_UPDATE` and `ACTIVITY_LAYOUT_MODE_UPDATE`. Use `subscribeToLayoutModeUpdatesCompat` and `unsubscribeFromLayoutModeUpdatesCompat` to subscribe to both events with backward compatibility for old Discord clients that only support `ACTIVITY_PIP_MODE_UPDATE`. Here's an example using React: + +```javascript +export default function LayoutMode() { + const handleLayoutModeUpdate = React.useCallback((update: {layout_mode: number}) => { + ... + }, []); + + React.useEffect(() => { + discordSdk.subscribeToLayoutModeUpdatesCompat(handleLayoutModeUpdate); + return () => { + discordSdk.unsubscribeFromLayoutModeUpdatesCompat(handleLayoutModeUpdate); + }; + }, [handleLayoutModeUpdate]); +} +``` diff --git a/discord/developers/docs/activities/development-guides/local-development.mdx b/discord/developers/docs/activities/development-guides/local-development.mdx new file mode 100644 index 0000000000..920466c62f --- /dev/null +++ b/discord/developers/docs/activities/development-guides/local-development.mdx @@ -0,0 +1,194 @@ +--- +title: Local Development +sidebarTitle: Local Development +description: Set up local development environment for Discord Activities. +--- + +## Run Your Application Locally + +It is possible to load your application via a localhost port or other unique URL. This URL must support an HTTPS connection to load on the web/desktop Discord app (HTTPS is not required for mobile). The downside to this flow is that your application's network traffic will not pass through Discord's proxy, which means any requests made by the application will need to use a full URL instead of a ["mapped"](/developers/docs/activities/development-guides/local-development#url-mapping) URL. + +To run your locally hosted application, follow the instructions for [Launching your Application from the Discord Client](/developers/docs/activities/development-guides/local-development#launch-your-application-from-the-discord-client) and set the Application URL Override to the address of your application's web server. + +### Running Your Application Through A Network Tunnel + +Although it is possible to test your application locally, we recommend developing and testing against the Discord proxy. This is helpful to make sure all URLs behave as expected before your application runs in production. One technique to enable testing locally against the proxy is to use a network tunneling tool, such as [cloudflared](https://github.com/cloudflare/cloudflared#installing-cloudflared). A typical pattern is for each developer to have their own "development-only" application. To set up a local environment to run through Discord's proxy, you will need to do the following: + +1. Create a new application in the Discord Developer portal. +2. Enable Activities for your app. +3. Set up the application's [URL mapping](/developers/docs/activities/development-guides/local-development#url-mapping). +4. Locally, spin up your web server. +5. Install and run a tunnel solution, such as [cloudflared](https://github.com/cloudflare/cloudflared#installing-cloudflared). You will point it to your local web server. + + +Your web server can be HTTP and your network tunnel can upgrade the connection to HTTPS. + + +If using cloudflared, you will run the following command, replace `3000` with your web server's port. + +``` +cloudflared tunnel --url http://localhost:3000 +``` + +Once you run this command, you will receive your publicly accessible network tunnel address from cloudflared. + +``` +Your quick Tunnel has been created! Visit it at (it may take some time to be reachable): +https://funky-jogging-bunny.trycloudflare.com +``` + +In the Discord Developer Portal, update the Application URL mapping for `/` url to `funky-jogging-bunny.trycloudflare.com` to match your network tunnel address and save your changes. + +![Configuring your URL Mapping](/images/activities/url-mapping-tutorial.png) + + +If you do not own the URL that you are using to host the application (i.e. ngrok's free tier), someone else could claim that domain and host a malicious site in its place. Please be aware of these risks, and if you have to use a domain you do not own, be sure to reset your URL mapping when you are done using the tunnel. + + +Follow the instructions for [Launching your Application from the Discord Client](/developers/docs/activities/development-guides/local-development#launch-your-application-from-the-discord-client). Application URL Override should not be enabled. + +### Running Your Application In Production + +The flow for setting up your production application is very similar: + +1. If not made yet, create a new application. +2. Enable Activities for your app. +3. Set up the application's [URL Mapping](/developers/docs/activities/development-guides/local-development#url-mapping). The URL for your application's html should be set to the `/` route. +4. Follow the instructions for [Launching your Application from the Discord Client](/developers/docs/activities/development-guides/local-development#launch-your-application-from-the-discord-client). Application URL Override should not be enabled. + +This application now uses the same configuration it will use once it is fully published ✨. + +![application-test-mode-prod](/images/activities/application-test-mode-prod.gif) + +--- + +### Launch your application from the Discord Client + +You will be able to see and launch all activities owned by you or any teams you are a member of via the Developer Activity Shelf. One caveat is that the activity will not be shown on the current platform (web/ios/android) unless you have checked your platform in `Settings/Supported Platforms` on the developer portal. + +To see you app inside of Discord in the Activity Shelf: + +#### Web + +1. Select ⚙️ User Settings > App Settings > Advanced and toggle on `Developer Mode` +3. Close the settings window and enter a voice channel. +4. From either the RTC Panel or the Center Control Tray, click on the "Rocket Button" to open the Activity shelf. You should now see all of the same applications that you have access to in the developer portal. Note: The shelf will only include applications which have been flagged as "Embedded". +6. Click on an activity to launch it! + +#### Mobile + +1. From your User Profile, select Appearance, and then toggle "On" `Developer Mode` +2. Enter a voice channel +5. Click on an activity to launch it! + +--- + +### URL Mapping + +Activities in Discord are "sandboxed" via a Discord proxy. This is done to hide the users' IP addresses, your application's IP addresses, and to block URLs from known malicious endpoints. As an application owner, you can configure the proxy to allow network requests to external endpoints. + +Because your application is "sandboxed", it will be unable to make network requests to external URLs. Let's say you want request `https://some-api.com`. To enable reaching this url from inside your application, you will create a new url mapping, with the `PREFIX` set to +`/api` and `TARGET` set to `some-api.com`. Now you can make requests to `/api` from inside of your application, which will be forwarded, via Discord's proxy to `some-api.com`. + +#### How to set a URL Mapping + +To add or modify your application's URL mappings, click on `Activities -> URL Mappings` and set the prefix and target values for each mapping as needed. + +![Configuring your URL Mapping](/images/activities/url-mapping-tutorial.png) + +#### Prefix/Target formatting rules + +- URL mappings can utilize any url protocol, (https, wss, ftp, etc...), which is why the URL target should not include a protocol. For example, for a URL target, do not put `https://your-url.com`, instead, omit `https://` and use `your-url.com`. +- Parameter matching can be used to help map external domain urls. For example, if an external url has many subdomains, such as `foo.google.com`, `bar.google.com`, then you could use the following mapping: + | PREFIX | TARGET | + |-----------------------|--------------------------| + | `/google/{subdomain}` | `{subdomain}.google.com` | +- Targets must point to a directory; setting a target to a file (e.g. `example.com/index.html`) is unsupported and may lead to unexpected behavior. +- Because of how URL globbing works, if you have multiple prefix urls with the same initial path, you must place the shortest of the prefix paths last in order for each url mapping to be reachable. For example, if you have `/foo` and `/foo/bar`, you must place the url `/foo/bar` before `/foo` or else the mapping for `/foo/bar` will never be reached. + +| ✅ DO | ❌ DON'T | +|--------------------------------------------------------------|------------------------------------------------------------------| +| Requests mapped correctly | Requests to /foo/bar will incorrectly be sent to `foo.com` | +| ![url-mapping-do.png](/images/activities/url-mapping-do.png) | ![url-mapping-dont.png](/images/activities/url-mapping-dont.png) | + +#### Exceptions + +The aforementioned "sandbox" is enforced by a [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). We have some notable exceptions to our CSP, meaning application clients may make requests to these URLs without hitting the proxy and therefore without establishing mappings. Notable exceptions include: + +- `https://discord.com/api/` +- `https://canary.discord.com/api/` +- `https://ptb.discord.com/api/` +- `https://cdn.discordapp.com/attachments/` +- `https://cdn.discordapp.com/avatars/` +- `https://cdn.discordapp.com/icons/` +- `https://media.discordapp.net/attachments/` +- `https://media.discordapp.net/avatars/` +- `https://media.discordapp.net/icons/` + +--- + +### Logging + +By default, the SDK will send any console `log`, `warn`, `error`, `info`, and `debug` events triggered by your app to the Discord application. + +#### Viewing Logs on Desktop + +Desktop logs are viewable through the console tab inside a browser's Developer Tools. See the [Troubleshooting Console Log Errors](https://support.discord.com/hc/en-us/articles/115001239472-Troubleshooting-Console-Log-Errors) support article for more information. + +The Public Test Build (PTB) Discord client also allows inspecting your logs from the `View -> Developer -> Toggle Developer Tools` menu. It can be downloaded at [https://discord.com/downloads](https://discord.com/downloads). + +#### Viewing Logs on Mobile + +Mobile logs are viewable via the `Debug Logs` option inside User Settings on the mobile App. It is only discoverable when you have `Developer Mode` enabled. + +1. On the bottom navigation, tap on your avatar and then the gear icon to open your `User Settings`. +2. Tap `Appearance`. +3. Slide the `Developer Mode` toggle to ON. +4. The `Debug Logs` option will be available under the `DEV ONLY` section. + +#### Filtering for Application Logs + +Inside the Debug Logs view, you can search for your own application logs with the possible keywords: + +- `RpcApplicationLogger` +- Your Application ID + +Each log line is formatted as: `[RpcApplicationLogger] - message` + +The first section of Debug Logs are not your application logs but Discord specific app startup info which is not relevant to your application. + +When you scroll down the page, your application logs should be visible. + +![debug-logs-filtering](/images/activities/debug-logs-filtering.gif) + +#### Sharing Application Logs from Mobile + +With `Developer Mode` enabled, you can share your application logs from within a Voice Channel. + +1. In the voice channel, swipe from the bottom to see the expanded voice controls. Tap on `Share Application Logs`. +2. You'll be presented with a native share sheet where you can save the logs to a file or share it as a message. + +#### Disabling Logging + +If you do not want logs to be forwarded to the browser, you can disable it with the optional configuration object. + +```javascript +import {DiscordSDK} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(clientId, { + disableConsoleLogOverride: true, +}); +``` + +#### Forwarding Log Messages + +You can forward specific log messages via the SDK command `captureLog` as shown below. + +```javascript +import {DiscordSDK} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(clientId); +await discordSdk.ready(); +discordSdk.commands.captureLog({ + level: 'log', + message: 'This is my log message!', +}); +``` diff --git a/discord/developers/docs/activities/development-guides/mobile.mdx b/discord/developers/docs/activities/development-guides/mobile.mdx new file mode 100644 index 0000000000..630cc63b1a --- /dev/null +++ b/discord/developers/docs/activities/development-guides/mobile.mdx @@ -0,0 +1,89 @@ +--- +title: Mobile +description: Design and optimize Discord Activities for mobile devices. +--- + +## Supported Platforms: Web, iOS, Android + +By default, your Activity will be launchable on web/desktop. To enable or disable support for Web/iOS/Android, do the following: + +- Visit the developer portal +- Select your application +- Select `Activities` -> `Settings` in the left-side of the developer portal, or visit `https://discord.com/developers//embedded/settings` +- From check the appropriate checkboxes in the developer portal, and save your changes + +![supported-platforms](/images/activities/supported-platforms.png) + +--- + +## Mobile Safe Areas + +As an example, you can define your safe area insets as below in CSS: + +```css +:root { + --sait: var(--discord-safe-area-inset-top, env(safe-area-inset-top)); + --saib: var(--discord-safe-area-inset-bottom, env(safe-area-inset-bottom)); + --sail: var(--discord-safe-area-inset-left, env(safe-area-inset-left)); + --sair: var(--discord-safe-area-inset-right, env(safe-area-inset-right)); +} +``` + +This prefers the `--discord-safe-area-inset-*` variable and will fallback to the env values for iOS + any local dev testing that is done outside of Discord. + +You can then reference these values: + +```css +body { + padding-left: var(--sail); + padding-right: var(--sair); + padding-top: var(--sait); + padding-bottom: var(--saib); +} +``` + +--- + +## Mobile Thermal States + +You may need to respond to thermal state changes using recommendations from [thermal states surfaced by mobile devices](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html) to improve the user experience. + +Discord's Embedded App SDK provides an abstraction over [Apple's thermal state APIs](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html) and [Android's thermal state APIs](https://source.android.com/docs/core/power/thermal-mitigation#thermal-api). + +Here's how Discord's abstraction maps to Apple's thermal states and Android's thermal states. + +```javascript +enum ThermalState { + NOMINAL = 0, // maps to "nominal" on iOS and "none" on Android + FAIR = 1, // maps to "fair" on iOS and "light" / "moderate" on Android + SERIOUS = 2, // maps to "serious" on iOS and "severe" on Android + CRITICAL = 3, // maps to "critical" on iOS and "critical" / "emergency" / "shutdown" on Android +} +``` + +The Embedded App SDK allows developers to subscribe to these thermal state changes. + +```javascript +const handleThermalStateUpdate = (update: {thermal_state: number}) => { + switch (thermalState) { + case Common.ThermalStateTypeObject.NOMINAL: + ... + case Common.ThermalStateTypeObject.FAIR: + ... + case Common.ThermalStateTypeObject.SERIOUS: + ... + case Common.ThermalStateTypeObject.CRITICAL: + ... + default: + ... + } +} + +discordSdk.subscribe('THERMAL_STATE_UPDATE', handleThermalStateUpdate); +``` + +Discord will publish the current thermal state upon event subscription, and it will also publish any thermal state changes that happen afterward. + + +On Android devices, the thermal state updates will only be available on Android 10 and higher. + diff --git a/discord/developers/docs/activities/development-guides/multiplayer-experience.mdx b/discord/developers/docs/activities/development-guides/multiplayer-experience.mdx new file mode 100644 index 0000000000..38f608adc8 --- /dev/null +++ b/discord/developers/docs/activities/development-guides/multiplayer-experience.mdx @@ -0,0 +1,248 @@ +--- +title: Multiplayer Experience +description: Design and implement engaging multiplayer experiences in Discord Activities. +--- + +## Activity Instance Management + +When a user clicks "Join Application", they expect to enter the same application that their friends are participating in. Whether the application is a shared drawing canvas, board game, collaborative playlist, or first-person shooter; the two users should have access to the same shared data. In this documentation, we refer to this shared data as an **application instance**. + +![join-application](/images/activities/join-application.png) + +The Embedded App SDK allows your app to talk bidirectionally with the Discord Client. The `instanceId` is necessary for your application, as well as Discord, to understand which unique instance of an application it is talking to. + +#### Using instanceId + +The `instanceId` attribute is available as soon as the SDK is constructed, and does not require the SDK to receive a `ready` payload from the Discord client. + +```javascript +import {DiscordSDK} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(clientId); +// available immediately +const instanceId = discordSdk.instanceId; +``` + +The `instanceId` should be used as a key to save and load the shared data relevant to an application. This ensures that two users who are in the same application instance have access to the same shared data. + +##### Semantics of instanceId + +Instance IDs are generated when a user launches an application. Any users joining the same application will receive the same `instanceId`. When all the users of an application in a channel leave or close the application, that instance has finished its lifecycle, and will not be used again. The next time a user opens the application in that channel, a new `instanceId` will be generated. + +--- + +## Instance Participants + +Instance Participants are any Discord user actively connected to the same Application Instance. This data can be fetched or subscribed to. + +```javascript +import {DiscordSDK, Events, type Types} from '@discord/embedded-app-sdk'; + +const discordSdk = new DiscordSDK('...'); +await discordSdk.ready(); + +// Fetch +const participants = await discordSdk.commands.getInstanceConnectedParticipants(); + +// Subscribe +function updateParticipants(participants: Types.GetActivityInstanceConnectedParticipantsResponse) { + // Do something really cool +} +discordSdk.subscribe(Events.ACTIVITY_INSTANCE_PARTICIPANTS_UPDATE, updateParticipants); +// Unsubscribe +discordSdk.unsubscribe(Events.ACTIVITY_INSTANCE_PARTICIPANTS_UPDATE, updateParticipants); +``` + +--- + +## Render Avatars and Names + +Check out detailed documentation on where and how Discord stores common image assets [here](/developers/docs/reference#image-formatting-cdn-endpoints). + +Here's a basic example for retrieving a user's avatar and username + +```javascript +// We'll be referencing the user object returned from authenticate +const {user} = await discordSdk.commands.authenticate({ + access_token: accessToken, +}); + +let avatarSrc = ''; +if (user.avatar) { + avatarSrc = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png?size=256`; +} else { + const defaultAvatarIndex = (BigInt(user.id) >> 22n) % 6n; + avatarSrc = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarIndex}.png` +} + +const username = user.global_name ?? `${user.username}#${user.discriminator}`; + +// Then in your HTML/JSX/etc... +avatar +

{username}

+``` + +#### Rendering guild-specific avatars and nicknames + +In order to retrieve a user's guild-specific avatar and nickname, your application must request the `guilds.members.read` scope. Note, this only grants the information for that instance of the application's user. To display the guild-specific avater/nickname for all application users, any info retrieved from `guilds.members.read` scope'd API calls must be shared via your application's server. + +Here's an example of how to retrieve the user's guild-specific avatar and nickname: + +```javascript +// We'll be referencing the user object returned from authenticate +const {user} = await discordSdk.commands.authenticate({ + access_token: accessToken, +}); + +// When using the proxy, you may instead replace `https://discord.com` with `/discord` +// or whatever url mapping you have chosen via the developer portal +fetch(`https://discord.com/api/users/@me/guilds/${discordSdk.guildId}/member`, { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, +}) + .then((response) => { + return response.json(); + }) + .then((guildsMembersRead) => { + let guildAvatarSrc = ''; + // Retrieve the guild-specific avatar, and fallback to the user's avatar + if (guildsMembersRead?.avatar) { + guildAvatarSrc = `https://cdn.discordapp.com/guilds/${discordSdk.guildId}/users/${user.id}/avatars/${guildsMembersRead.avatar}.png?size=256`; + } else if (user.avatar) { + guildAvatarSrc = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png?size=256`; + } else { + const defaultAvatarIndex = (BigInt(user.id) >> 22n) % 6n; + avatarSrc = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarIndex}.png`; + } + + // Retrieve the guild-specific nickname, and fallback to the username#discriminator + const guildNickname = guildsMembersRead?.nick ?? (user.global_name ?? `${user.username}#${user.discriminator}`); + }); +``` + +This example is being done entirely on the client, however, a more common pattern is to instead, do the following: + +- Store the user's access token on the application server +- Retrieve the user's guild-specific avatar and nickname via the application's server +- Serve all of the application's avatar/nicknames via the application's server + +--- + +## Preventing unwanted activity sessions + +Activities are surfaced through iframes in the Discord app. The activity website itself is publicly reachable at `.discordsays.com`. Activities will expect to be able to communicate with Discord's web or mobile client via the Discord SDK's RPC protocol. If a user loads the activity's website in a normal browser, the Discord RPC server will not be present, and the activity will likely fail in some way. + +It is theoretically possible for a malicious client to mock Discord's RPC protocol or load one activity website when launching another. Because the activity is loaded inside Discord, the RPC protocol is active, and the activity is none the wiser. + +### Using the Activity Instance API + +To enable an activity to "lock down" activity access, we encourage utilizing the `get_activity_instance` API, found at `discord.com/api/applications//activity-instances/'`. The route requires a Bot token of the application. It returns a serialized active activity instance for the given application, if found, otherwise it returns a 404. Here are two example responses: + +```javascript +curl https://discord.com/api/applications/1215413995645968394/activity-instances/i-1234567890-gc-912952092627435520-912954213460484116 -H 'Authorization: Bot ' +{"message": "404: Not Found", "code": 0} + +curl https://discord.com/api/applications/1215413995645968394/activity-instances/i-1276580072400224306-gc-912952092627435520-912954213460484116 -H 'Authorization: Bot ' +{"application_id":"1215413995645968394","instance_id":"i-1276580072400224306-gc-912952092627435520-912954213460484116","launch_id":"1276580072400224306","location":{"id":"gc-912952092627435520-912954213460484116","kind":"gc","channel_id":"912954213460484116","guild_id":"912952092627435520"},"users":["205519959982473217"]} +``` + +With this API, the activity's backend can verify that a client is in fact in an instance of that activity before allowing the client to participate in any meaningful gameplay. How an activity implements "session verification" is left to the developer's discretion. The solution can be as granular as gating specific features or as binary as not returning the activity HTML except for valid sessions. + +###### Validating Proxy Request Headers + +For apps that want additional security validation, Discord provides an optional proxy authentication system. When your embedded app makes requests through Discord's proxy, each request can include cryptographic headers that prove the request's authenticity. + +Each proxy-authenticated request is sent with the following headers: + +- `X-Signature-Ed25519` as a cryptographic signature +- `X-Signature-Timestamp` as a Unix timestamp +- `X-Discord-Proxy-Payload` as a base64-encoded payload containing user context + +If you choose to use proxy authentication, you can validate these headers to ensure requests are legitimate. If the signature fails validation, your app should respond with a `401` error code. + + +Below are some code examples that show how to validate the headers sent in proxy-authenticated requests. + +**JavaScript** + +```js +const nacl = require("tweetnacl"); + +// Your public key can be found on your application in the Developer Portal +const PUBLIC_KEY = "APPLICATION_PUBLIC_KEY"; + +const signature = req.get("X-Signature-Ed25519"); +const timestamp = req.get("X-Signature-Timestamp"); +const payload = req.get("X-Discord-Proxy-Payload"); + +// Decode the base64 payload +const payloadBytes = Buffer.from(payload, "base64"); +const payloadString = payloadBytes.toString("utf-8"); +const payloadData = JSON.parse(payloadString); + +// Verify timestamp matches payload +if (payloadData.created_at.toString() !== timestamp) { + return res.status(401).end("invalid request timestamp"); +} + +// Check if token has expired +if (payloadData.expires_at < Math.floor(Date.now() / 1000)) { + return res.status(401).end("expired proxy token"); +} + +// Verify the signature using tweetnacl +const isVerified = nacl.sign.detached.verify( + payloadBytes, + Buffer.from(signature, "base64"), + Buffer.from(PUBLIC_KEY, "hex") +); + +if (!isVerified) { + return res.status(401).end("invalid request signature"); +} +``` + +**Python** + +```py +import json +import base64 +import time +from nacl.signing import VerifyKey +from nacl.exceptions import BadSignatureError + +# Your public key can be found on your application in the Developer Portal +PUBLIC_KEY = 'APPLICATION_PUBLIC_KEY' + +verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY)) + +signature = request.headers["X-Signature-Ed25519"] +timestamp = request.headers["X-Signature-Timestamp"] +payload = request.headers["X-Discord-Proxy-Payload"] + +# Decode the base64 payload +payload_bytes = base64.b64decode(payload) +payload_string = payload_bytes.decode('utf-8') +payload_data = json.loads(payload_string) + +# Verify timestamp matches payload +if str(payload_data['created_at']) != timestamp: + abort(401, 'invalid request timestamp') + +# Check if token has expired +if payload_data['expires_at'] < int(time.time()): + abort(401, 'expired proxy token') + +try: + verify_key.verify(payload_bytes, bytes.fromhex(signature)) +except BadSignatureError: + abort(401, 'invalid request signature') +``` + + +Proxy authentication is entirely optional and provided as an additional security layer for apps that choose to implement it. + +In the below flow diagram, we show how the server can deliver the activity website, only for valid users in a valid activity instance: + +![activity-instance-validation](/images/activities/activity-instance-validation.jpg) diff --git a/discord/developers/docs/activities/development-guides/networking.mdx b/discord/developers/docs/activities/development-guides/networking.mdx new file mode 100644 index 0000000000..f46e34a6d3 --- /dev/null +++ b/discord/developers/docs/activities/development-guides/networking.mdx @@ -0,0 +1,103 @@ +--- +title: Networking +description: Learn networking patterns and best practices for Discord Activities. +--- + +## Activity Proxy Considerations + +All network traffic is routed through the Discord Proxy for various security reasons. + +Under the hood we utilize Cloudflare Workers, which brings some restrictions, outlined below. + +#### WebTransport + +While we currently only support websockets, we're working with our upstream providers to enable WebTransport. + +#### WebRTC + +WebRTC is not supported. + +--- + +## Construct A Full URL + +There are scenarios where instead of using a relative url (`/path/to/my/thing`) you may want or need to reference the full url when making a network request. The URL is a combination of the following + +1. The protocol you wish to use +2. Your application's client id +3. The discord proxy domain +4. Whatever you need to list + +Here's an example of how to build a full url, using the URL constructor: + +```javascript +const protocol = `https`; +const clientId = ''; +const proxyDomain = 'discordsays.com'; +const resourcePath = '/foo/bar.jpg'; +const url = new URL(`${protocol}://${clientId}.${proxyDomain}${resourcePath}`); +``` + +In other words, given an application client id of `12345678` +| Relative Path | Full Path | +|---------------|----------------------------------------------| +| /foo/bar.jpg | https://12345678.discordsays.com/foo/bar.jpg | + +--- + +## Using External Resources + +Activities in Discord are "sandboxed" via a Discord proxy. This is done to hide the users' IP addresses as well as block URLs from known malicious endpoints. To achieve this, the Discord Developer Portal has a section for [configuring URL Mappings](/developers/docs/activities/development-guides/local-development#url-mapping) for your application. + +One edge-case of URL mappings is that third-party NPM modules or other resources may reference external (non-sandboxed) urls. + +For example, if your application has an npm module that attempts to make an http request to https://foo.library.com, the request will fail with a `blocked:csp` error. + +To get around this limitation there are several options to consider: + +- Fork the library (to use mapped urls) +- Utilize a post-install utility such as [patch-package](https://www.npmjs.com/package/patch-package) +- Use our Embedded App SDK's `patchUrlMappings` API + +In the above scenario, we recommend using the `patchUrlMappings` API, as it will allow a smooth transition from the non-sandboxed dev environment to the production environment. + +This method call takes an array of "mappings" which will transform any external network requests to the mappings you've defined. + +See the example below: + +- In this example, imagine you have a third-party library which makes an HTTP request to foo.com +- In the developer portal, create a mapping like this: `/foo` -> `foo.com` +- Then in your code, when initializing the SDK, you will make a function call. + + +```javascript +import {patchUrlMappings} from '@discord/embedded-app-sdk'; +const isProd = process.env.NODE_ENV === 'production'; // Actual dev/prod env check may vary for you + +async function setupApp() { + if (isProd) { + patchUrlMappings([{prefix: '/foo', target: 'foo.com'}]); + } + // start app initialization after this.... +} +``` + + +Note: `patchUrlMappings` is modifying your browser's `fetch`, `WebSocket`, and `XMLHttpRequest.prototype.open` global variables. Depending on the library, you may see side effects from using this helper function. It should be used only when necessary. + + +--- + +## Security Considerations + +#### Trusting Client Data + +Do not trust data coming from the Discord client as truth. It's fine to use this data in your application locally, but assume any data coming from the Discord Client could be falsified. That includes data about the current user, their nitro status, their current channel, etc. If you need this information in a trusted manner, contact Discord API directly from your application's server, with the user token you received from completing the OAuth2 flow. + +Furthermore, data coming from the Discord client is not sanitized beforehand. Things like usernames and channel names are arbitrary user input. Make sure to sanitize these strings or use `.textContent` (for example) to display them safely in your UI. + +#### Using Cookies + +To set a cookie for your activity to use in network requests through the proxy, make sure the cookie's domain matches your app's full `{clientId}.discordsays.com` domain. You will also need to explicitly set `SameSite=None Partitioned` on the cookie. `SameSite=None` is needed as browsers refuse to store or send cookies with higher restriction levels for any navigation within an iframe. `Partitioned` then limits the use of that cookie to only Discord's iframes. + +Rest assured: other activities will not be able to make requests with your activity's cookie, thanks to the Content Security Policy (CSP) limiting requests only to your own app's proxy. diff --git a/discord/developers/docs/activities/development-guides/production-readiness.mdx b/discord/developers/docs/activities/development-guides/production-readiness.mdx new file mode 100644 index 0000000000..f3e541e962 --- /dev/null +++ b/discord/developers/docs/activities/development-guides/production-readiness.mdx @@ -0,0 +1,47 @@ +--- +title: Production Readiness +description: Prepare your Discord Activity for production deployment. +--- + +## Cache Busting + +All assets loaded by your application will respect [cache headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control). One exception is that Discord's application proxy will remove any cache headers for assets whose `content-type` headers include `text/html`. For all non-`text/html` content that your application plans to serve, be sure your application has a cache-busting strategy. This is often built into build processes. If your application has a static filename for its javascript or css, please be sure to implement cache busting techniques, for example [webpack enables creating dynamic file names](https://webpack.js.org/guides/caching/). + +---- + +## Handling Rate Limits + +Be sure network requests made by your application's client and server will be able to respect Discord API's rate limiting [as described here](/developers/docs/topics/rate-limits). + +See [this implementation in the Activity Starter project](https://github.com/discord/embedded-app-sdk-examples/blob/main/discord-activity-starter/packages/server/src/utils.ts) for an example of how to respect the `retry_after` header when you receive a 429 error. + +--- + +## Static IP Addresses + +If your application's server is utilizing a dynamically assigned IP address (this is standard for cloud functions), there is a non-zero chance that you will inherit from a previous bad actor an IP address which has been banned by Cloudflare. In this scenario any egress traffic from the IP address directed towards Discord's API will be banned for up-to an hour. The best way to mitigate this situation is to set up a static IP address for all of your application server's egress traffic to be routed through. + +--- + +## Backward Compatibility + +#### New Commands + +When new commands become available in the embedded-app-sdk, those commands won't be supported by all Discord app versions. The new command will typically only be supported by newer Discord app versions. When an application tries to use a new command with an old Discord app version that doesn't support the command, the Discord app will respond with error code `INVALID_COMMAND` which the application can handle like this: + +```javascript +try { + const {permissions} = await discordSdk.commands.getChannelPermissions(); + + // check permissions + ... +} catch (error) { + if (error.code == RPCErrorCodes.INVALID_COMMAND) { + // This is an expected error. The Discord client doesn't support this command + ... + } else { + // Unexpected error + ... + } +} +``` diff --git a/discord/developers/docs/activities/development-guides/user-actions.mdx b/discord/developers/docs/activities/development-guides/user-actions.mdx new file mode 100644 index 0000000000..7c8e55f192 --- /dev/null +++ b/discord/developers/docs/activities/development-guides/user-actions.mdx @@ -0,0 +1,181 @@ +--- +title: User Actions +description: Implement user interactions and actions in Discord Activities. +--- + +## Open External Link + +Since Activities are sandboxed, your app will need to perform a command in order for users to launch any external links. Users will be prompted inside Discord whether or not they want to open the external link. + +#### Usage + +```javascript +import {DiscordSDK} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(clientId); +await discordSdk.ready(); +// Once the sdk has established the connection with the discord client, external +// links can be launched +discordSdk.commands.openExternalLink({ + url: 'https://google.com', +}); +``` + +#### User Experience + +![external-link-modal](/images/activities/external-link-modal.png) + +Users will see a modal inside the Discord app notifying them whether or not they want to proceed. By clicking **_Trust this Domain_**, users will not see a modal for that specific domain again. + +--- + +## Open Invite Dialog + +Getting an Application Channel Invite, as outlined in [these docs](/developers/docs/resources/invite#get-invite), is not granted by any OAuth scopes. Nonetheless, the `openInviteDialog` command is available via the SDK. This command opens the Application Channel Invite UI within the discord client without requiring additional OAuth scopes. + +This command returns an error when called from DM (Direct Message) contexts, so should only be called in Guild Voice or Text Channels. Similarly, this command returns an error if the user has invalid permissions for the channel, so using `getChannelPermissions` (requires OAuth scope `'guilds.members.read'`) is highly recommended. + +#### Usage + +```javascript +import {DiscordSDK, Permissions, PermissionUtils} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(clientId); +await discordSdk.ready(); + +try { + const {permissions} = await discordSdk.commands.getChannelPermissions(); + if (PermissionUtils.can(Permissions.CREATE_INSTANT_INVITE, permissions)) { + await discordSdk.commands.openInviteDialog(); + // successfully opened dialog + } else { + console.warn('User does not have CREATE_INSTANT_INVITE permissions'); + } +} catch (err) { + // failed to fetch permissions or open dialog + console.warn(err.message); +} +``` + +User Experience + +![Invite Dialog UI](/images/activities/invite-dialog.png) + +Users will see a modal inside the Discord app allowing them to send an invite to a channel, friend, or copy an invite link to share manually. + +--- + +## Open Share Moment Dialog + +The easiest way for an application to share media to a channel or DM is to use the `openShareMomentDialog` command. This command accepts a Discord CDN `mediaUrl` (eg `https://cdn.discordapp.com/attachments/...`) and opens a dialog on the discord client that allows the user to select channels, DMs, and GDMs to share to. This requires no additional OAuth scopes, but does require the application to be authenticated. + +Since `mediaUrl` must be a Discord CDN URL, it is encouraged to use the activities attachment API endpoint (`discord.com/api/applications/${applicationId}/attachment`) to create an ephemeral CDN URL. This endpoint accepts bearer tokens for any scopes, so it can be called from the application client using the authorized user's bearer token. The endpoint returns a serialized attachment, which includes a `url` attribute, which should then be passed to the DiscordSDK command as `mediaUrl`. + +#### Usage + +```javascript +import {discordSdk} from './wherever-you-initialize-your-sdk'; +import {accessToken} from './wherever-you-store-your-access-token'; + +// some image +const imageURL = 'https://i.imgur.com/vaSWuKr.gif'; + +// get image data +const response = await fetch(imageURL); +const blob = await response.blob(); +const mimeType = blob.type; + +// image data as buffer +const buf = await blob.arrayBuffer(); + +// image as file +const imageFile = new File([buf], 'example.gif', {type: mimeType}); + +const body = new FormData(); +body.append('file', imageFile); + +const attachmentResponse = await fetch(`${env.discordAPI}/applications/${env.applicationId}/attachment`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + body, +}); +const attachmentJson = await attachmentResponse.json(); + +// mediaUrl is an ephemeral Discord CDN URL +const mediaUrl = attachmentJson.attachment.url; + +// opens dialog in Discord client +await discordSdk.commands.openShareMomentDialog({mediaUrl}); +``` + +User Experience + +![Example of the sharing dialog](/images/activities/share-moment-dialog-example.png) + +--- + +## Setting Up an Entry Point Command + +An [Entry Point command](/developers/docs/interactions/application-commands#entry-point-commands) is required for users to be able to launch your Activity from the [App Launcher menu](https://support.discord.com/hc/articles/21334461140375-Using-Apps-on-Discord#h_01HRQSA6C8TRHS722P1H3HW1TV) in Discord. + +When you enable Activities in your [app's settings](http://discord.com/developers/applications), a [default Entry Point command](/developers/docs/interactions/application-commands#default-entry-point-command) is automatically created for your app. The default Entry Point command will use the `DISCORD_LAUNCH_ACTIVITY` (`2`) [handler type](/developers/docs/interactions/application-commands#application-command-object-entry-point-command-handler-types), which means that Discord automatically launches your Activity for the user and posts a follow-up message into the channel where it was launched from. + +If you want to handle sending messages yourself, you can update the handler to be `APP_HANDLER` (`1`). Details about Entry Point command handlers is in the [Entry Point command documentation](/developers/docs/interactions/application-commands#entry-point-handlers). + +#### Customizing the Default Entry Point Command + +Entry Point commands can be customized in the same way as other [commands](/developers/docs/interactions/application-commands). Since Entry Point commands can only be [global](/developers/docs/interactions/application-commands#making-a-global-command), you'll use the HTTP endpoints for global commands: +- **Edit your existing Entry Point command's name or details** using the [Edit Global Application Command](/developers/docs/interactions/application-commands#edit-global-application-command) endpoint. If you don't know the ID for your app's Entry Point command, use the [Get Global Application Commands](/developers/docs/interactions/application-commands#get-global-application-commands) endpoint to retrieve it. +- **Make a different (option-less) command your Entry Point command** by updating its [command `type`](/developers/docs/interactions/application-commands#application-command-object-application-command-types) to `PRIMARY_ENTRY_POINT` (type `4`). Your app can only have one Entry Point command, so if your app already has one, you must first [delete](/developers/docs/interactions/application-commands#delete-global-application-command) it or [update](/developers/docs/interactions/application-commands#edit-global-application-command) its [command `type`](/developers/docs/interactions/application-commands#application-command-object-application-command-types). + +#### Creating an Entry Point Command + +To create a new Entry Point command, you can call the [Create Global Application Command](/developers/docs/interactions/application-commands#create-global-application-command) endpoint and set the [command `type`](/developers/docs/interactions/application-commands#application-command-object-application-command-types) to `PRIMARY_ENTRY_POINT` (type `4`). + +Your command payload may look something like this: + +```json +{ + "name": "launch", + "description": "Launch Realms of Wumpus", + // PRIMARY_ENTRY_POINT is type 4 + "type": 4, + // DISCORD_LAUNCH_ACTIVITY is handler value 2 + "handler": 2, + // integration_types and contexts define where your command can be used (see below) + "integration_types": [0, 1], + "contexts": [0, 1, 2] +} +``` + +In addition to the `type` and `handler` values, the command payload includes `integration_types` and `contexts` which let you configure when and where your command can be used: +- `integration_types` defines the [installation contexts](/developers/docs/resources/application#installation-context) where your command is available (to a server, to a user's account, or both). If you don't set `integration_types` when creating a command, it will default to your app's [currently-supported installation contexts](/developers/docs/resources/application#setting-supported-installation-contexts). +- `contexts` defines the [interaction contexts](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) where a command can be run in Discord (in a server, in a DM with your app, and/or in DMs and Group DMs with other users). + +Details about both of these fields are in the [command contexts](/developers/docs/interactions/application-commands#contexts) documentation. + +--- + +## Encourage Hardware Acceleration + +Activities that are compute intensive may benefit from encouraging users to enable hardware acceleration. When an application invokes the `encourageHardwareAcceleration` command the current status of the setting will be returned and the user will be prompted to update the setting, if applicable. + +Users will see a modal inside the Discord app if Hardware Acceleration is disabled, encouraging them to change the setting. By clicking **Don't show me this again** they will not see the modal for _any application_ on this device again. + +#### Best Practices + +Switching the Hardware Acceleration setting causes the Discord client to quit and re-launch, so it is best practice to invoke this command as soon as possible, so users do not begin the experience of an application before restarting. Ideally, this is immediately after `await discordSdk.ready()`. + +#### Usage + +```javascript +import {DiscordSDK} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(clientId); +await discordSdk.ready(); +const {enabled} = await discordSdk.commands.encourageHardwareAcceleration(); +console.log(`Hardware Acceleration is ${enabled === true ? 'enabled' : 'disabled'}`); +``` + +#### User Experience + +![encourage-hardware-acceleration-modal](/images/activities/encourage-hardware-acceleration-modal.png) diff --git a/discord/developers/docs/activities/how-activities-work.mdx b/discord/developers/docs/activities/how-activities-work.mdx new file mode 100644 index 0000000000..7abfd12851 --- /dev/null +++ b/discord/developers/docs/activities/how-activities-work.mdx @@ -0,0 +1,68 @@ +--- +title: How Activities Work +description: Understand the technical architecture and lifecycle of Discord Activities. +--- + + +Activities are web applications that run in an iframe within Discord on desktop, mobile and web. In order to achieve this, we use the [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) protocol to enable secure communication between your application and Discord. + +The [Embedded App SDK](https://github.com/discord/embedded-app-sdk) simplifies this process by managing the `postMessage` protocol on your behalf. For details on available commands and their usage, consult the [SDK Reference](/developers/docs/developer-tools/embedded-app-sdk). Our [Sample Projects](/developers/docs/activities/overview#sample-projects) provide practical examples of how to implement these features. + +## Designed for Single-Page Apps (SPAs) + +This SDK is intended for use by a single-page application. We recognize developers may be using frameworks or approaches that are not an exact fit for single-page applications. We recommend nesting those frameworks inside your Activity's top-level single-page application and passing messages as you see fit. Please refer to the [Nested Messages App](/developers/docs/activities/overview#sample-projects) sample project for guidance on this approach. + +## Activity Lifecycle + +1. **Initialization:** When your iframe is loaded within Discord, it will include unique query parameters in its URL. These parameters are identifiable by your application using the Discord SDK. +2. **Handshake Process:** Constructing the SDK instance begins a handshake process with the Discord client. Once the connection is established, the iframe receives a `[FRAME, {evt: 'READY', ...}]` message. The `ready()` method of the SDK instance resolves once a successful connection has been established. +3. **Authorization and Authentication:** After receiving the `READY` payload, your application should perform authorization and authentication to acquire necessary permissions (scopes). This step is crucial for utilizing specific features or scopes, such as `rpc.activities.write`. +4. **Interacting with Discord Client:** Post-authentication, your application can subscribe to events and send commands to the Discord client. Note that attempting to use commands or subscribe to events outside your granted scope will result in errors. Adding new scopes may prompt an OAuth modal for user permission re-confirmation. +5. **Disconnection and Errors:** Receiving a `[CLOSE, {message: string, code: number}]` message indicates an error or a need to restart the connection process. +6. **Sending Errors or Close Requests:** To communicate an error or request a close from the Discord client, send `[CLOSE, {message?: string, code: number}]`. A code other than CLOSE_NORMAL will display the message to the user, while CLOSE_NORMAL results in a silent closure. + +## Sample Code and Activity Lifecycle Diagram + + +Below is a minimal example of setting up the SDK. Please see our [Sample Projects](/developers/docs/activities/overview#sample-projects) for more complete sample applications. + + +```javascript +import {DiscordSDK} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(YOUR_OAUTH2_CLIENT_ID); + +async function setup() { + // Wait for READY payload from the discord client + await discordSdk.ready(); + + // Pop open the OAuth permission modal and request for access to scopes listed in scope array below + const {code} = await discordSdk.commands.authorize({ + client_id: YOUR_OAUTH2_CLIENT_ID, + response_type: 'code', + state: '', + prompt: 'none', + scope: ['identify'], + }); + + // Retrieve an access_token from your application's server + const response = await fetch('/.proxy/api/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + code, + }), + }); + const {access_token} = await response.json(); + + // Authenticate with Discord client (using the access_token) + auth = await discordSdk.commands.authenticate({ + access_token, + }); +} +``` + +This diagram illustrates the communication flow between your application and Discord in the sample code above. + +![Diagram of how Activities communicate with Discord](/images/activities/embedded-app-flow-diagram.svg) diff --git a/discord/developers/docs/activities/overview.mdx b/discord/developers/docs/activities/overview.mdx new file mode 100644 index 0000000000..07e971259e --- /dev/null +++ b/discord/developers/docs/activities/overview.mdx @@ -0,0 +1,92 @@ +--- +title: Overview of Activities +sidebarTitle: Overview +description: Learn about Discord Activities - multiplayer games and social experiences. +--- + +![Building Discord Activities](/images/activities/activities-hero.png) + +**Activities** are multiplayer games and social experiences that can be launched in Discord. Activities can integrate with Discord features like user identity, voice and chat, profile data like Rich Presence, and native monetization. + +Under the hood, Activities are single page web apps hosted in an iframe and use the [Embedded App SDK](/developers/docs/activities/overview#embedded-app-sdk) to communicate with Discord clients. This page focuses on how Activities are launched and built, but you can explore real-world Activities by reading some of our [developer case studies](https://discord.com/build#case-studies), or by trying a few out in Discord. You can also jump right into building using some of the resources below. + + + + Follow the guide to build your first Activity using the Embedded App SDK. + + + Explore common development patterns and practices to make building Activities simpler. + + + Learn more about the lifecycle of Activities and how they run in Discord clients. + + + +--- + +## Launching Activities + +After you have Activities enabled in your [app's settings](https://discord.com/developers/applications), your app can launch Activities in two ways: +1. When a user invokes your app's Entry Point command in the App Launcher +2. By responding to an interaction with the `LAUNCH_ACTIVITY` callback type + +Each of these are covered in more detail in the below sections. + +### Entry Point Command + +Activities are primarily opened when users invoke your app's [Entry Point command](/developers/docs/interactions/application-commands#entry-point-commands) in the App Launcher. + +When you enable Activities for your app, a [default Entry Point command](/developers/docs/interactions/application-commands#default-entry-point-command) called "Launch" is created for you. By default, Discord automatically handles opening your Activity when your Entry Point command is run by a user. + +Read more about setting up Entry Point commands in the [development guide](/developers/docs/activities/development-guides/user-actions#setting-up-an-entry-point-command). + +### Interaction Response + +Activities can be launched in response to [command](/developers/docs/interactions/overview#commands), [message component](/developers/docs/interactions/overview#message-components), and [modal submission](/developers/docs/interactions/overview#modals) interactions. To open an Activity, set the callback type to `LAUNCH_ACTIVITY` (type `12`) when [responding to the interaction](/developers/docs/interactions/receiving-and-responding#responding-to-an-interaction). + +--- + +## Developing Activities + +Whether you're developing a multiplayer game, a new social experience, or another creative idea, your Activity will be built as a web app that is run in an iframe in Discord on desktop, mobile, and web. You can get started by following the guide on [Building an Activity using the Embedded App SDK](/developers/docs/activities/building-an-activity) or exploring the [sample projects](/developers/docs/activities/overview#sample-projects) + +The sections below provide an overview of the Embedded App SDK, but technical details about how Activities are run in Discord is covered in [How Activities Work](/developers/docs/activities/how-activities-work). + +### Embedded App SDK + +The [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) handles all of the communication between Discord and your app, making it easier to do common tasks like managing connected users, supporting mobile clients, and debugging your Activity. + +The Embedded App SDK offers different events and commands to handle the communication between Discord and your Activity, which are covered more below. + +#### Commands + +The SDK has a set of commands you can call to interact with a Discord client, given you have the appropriate scopes. This is helpful when you want to do authorize and authenticate users, fetch information about your Activity, or update information for your Activity or an authenticated user. + +Read the [Embedded App SDK documentation](/developers/docs/developer-tools/embedded-app-sdk#sdk-commands) for a full list of commands, and details about each command. + +#### Events + +The SDK also has events you can subscribe (or unsubscribe) to for things happening in a connected client that are relevant to your Activity, given you have the appropriate scopes. This is helpful when you want to do things like handle initial connection and thrown errors, listen to updates about a connected user, and listen to events related to your Activity instance. + +Read the [Embedded App SDK documentation](/developers/docs/developer-tools/embedded-app-sdk#sdk-events) for a full list of events, and details for each event. + + +--- + +## Sample Projects + +Use the sample projects we've built to help you get started building quickly and learn from common development patterns. + +You can also find a list of community-maintained samples on [GitHub](https://github.com/discord/embedded-app-sdk-examples?tab=readme-ov-file#community-curated-examples), which includes more framework-specific examples. + + + + This TypeScript starter template is perfect for getting your own Activity up and running quickly. + + + This reference example implements the commands and events available to you within the Embedded App SDK. + + + This reference example demonstrates an Activity using a nested framework like a game engine. + + diff --git a/discord/developers/docs/change-log.mdx b/discord/developers/docs/change-log.mdx new file mode 100644 index 0000000000..b191ba913d --- /dev/null +++ b/discord/developers/docs/change-log.mdx @@ -0,0 +1,2391 @@ +--- +title: "Change Log" +description: "Discover the latest updates, new features, and bug fixes on the Discord Developer Platform. To join in on the conversation, join the [Discord Developers Server](https://discord.com/invite/discord-developers)." +--- + +import {Route} from '/snippets/route.jsx' + +--- + + + + ## Next Generation Docs Project + + We're excited to announce the launch of our Next Gen Docs project! This initiative aims to improve the way developers interact with Discord's developer documentation, making it more accessible, comprehensive, and user-friendly. + + ## Our Goal + + The Discord API has evolved far beyond its origins as a platform for bots. Today, we're a comprehensive developer platform supporting: + + - **Discord Apps and Bots** - The foundation of our ecosystem, extending Discord's functionality with custom apps, commands, and integrations + - **Discord Activities** - Real-time games and social experiences that users can launch directly inside Discord + - **Social SDK for Games** - Bringing Discord-powered features like voice, chat, rich presence, and social graph to games + + Our documentation needs to evolve with us. With this project, we're not just updating our docs. We're aiming to improve the entire developer experience for learning and building on Discord as it exists in 2025 and beyond. + + ## Our Next Gen Docs Journey + + **What we've done so far:** + - Migrated to [Mintlify](https://mintlify.com) for a modern documentation experience + - For this migration, we preserved our existing content and familiar design but plan to evolve this over time + - We no longer have to maintain and build our own documentation platform and can instead focus on making content and improving our developer experience + - Gained new capabilities: + - **PR Previews** - See documentation changes before they go live! This works locally as well as in your pull requests + - **More Components and Docs Features** - We now have access to all of Mintlify's documentation components that will continue to grow over time + - **AI features & improved search** - You can now send our docs to your LLM of choice or access them via MCP (more on this soon!) + + If you're reading this, the migration is complete! 🎉 + + + This migration did require us to reorganize the entire repository and make a lot of formatting changes. If you have an open PR, it will be a pretty gnarly merge conflict to resolve. We will be focusing on getting our PR backlog down over the next few weeks. + + + + We combed through the existing documentation to ensure everything was migrated correctly, but if you spot anything that looks off, please let us know by opening an issue or submitting feedback [here](https://github.com/discord/discord-api-docs/discussions/categories/next-gen-docs-feedback). We will be actively monitoring feedback to quickly address any issues. + + + ## Why This Matters + + Great documentation isn't just about having the right information, it's about presenting it in a way that helps developers succeed. + + In the coming months, we'll be shipping changes to our documentation to ensure that it: + + - **Reduces time-to-first-success** for new developers + - **Scales with complexity** as your projects grow + - **Stays current** with our rapidly evolving platform + - **Serves all skill levels** from beginners to experts + + ## Contributing to Our Vision + + This transformation is happening with the developer community at its heart. We welcome: + + - **Feedback** on what's working and what isn't + - **Content contributions** through our existing PR process + - **Bug reports** when documentation doesn't match reality + + We have created a Github Discussion topic for [Next Gen Docs Feedback](https://github.com/discord/discord-api-docs/discussions/categories/next-gen-docs-feedback) to collect feedback. + + See our [Contributing Guidelines](https://github.com/discord/discord-api-docs/blob/main/CONTRIBUTING.md) for how to get involved. + + + + + ## Discord Social SDK Release 1.6.12170 + + A new release of the Discord Social SDK is now available, with the following updates: + + ## New Features + + This small update to v1.6 adds some highly requested features from our partners. We found some time to sneak them in… + + - You can now provide custom art to display as a banner image in game invites that appear in Discord. This is done one + of two ways: + - By specifying a URL or an `asset key` to an image in the `activity.assets.inviteCoverImage` parameter when + calling `Client::UpdateRichPresence`. This method even affords you the ability to set a unique image + on each invite if you want! + - By uploading cover image art in the `Rich Presence` tab in the Developer Portal for your Application. Note: This + will be used as the fallback image if you don't specify one via `activity.assets.inviteCoverImage` + - It's now possible to customize the displayed name for your Application in Discord's Rich Presence. To do so, set + the `activity.name` parameter when calling [`Client::UpdateRichPresence`] + - Added support for Microsoft Xbox GDK version 2025.04 + - Fixed a crash when calling [`Client::GetUserGuilds`] on iOS devices + + + + Have you ever wanted to collect more than text from a user through a modal? With the new [**File Upload**](/developers/docs/components/reference#file-upload) component you can! You can specify a min and max number of files accepted between 0 and 10, and if uploading files within that limit is required before submitting. Any file types are accepted, and the max file size is based on the user's upload limit in that channel. + + #### The New Component: + + - [**File Upload**](/developers/docs/components/reference#file-upload) + + #### Developer Resources + + - [Using Modal Components](/developers/docs/components/using-modal-components) - Dive into creating a modal + - Check out our [Component Reference](/developers/docs/components/reference) for details on all available components. + + + + + ## Discord Social SDK Release 1.6 + + A new release of the Discord Social SDK is now available, with the following updates: + + ### ✨ New Features + + - When calling [`Client::GetGuildChannels`], channels are now sorted by their `position` field, which matches how they are sorted in the Discord client. + - Messages received via the Social SDK, no longer create notifications in a Discord client for the same user, to avoid double notification on the same machine. + + **This release adds features to support upcoming Discord experiments that will enhance how games integrate with user profiles and authentication:** + + - **Game Profile Integration**: New functionality to display game data on Discord user profiles. This includes `UserApplicationProfile` support with two new methods: `Client::GetUserApplicationProfiles` and `Client::GetUserApplicationProfilesNoLock` on the Users class, which retrieve game identity data from external authentication providers. + - **In-Discord Authentication Flow**: Support for users to start account linking directly from Discord (rather than having to initiate it from within your game). Added [`Client::RegisterAuthorizeRequestCallback`] and [`Client::RemoveAuthorizeRequestCallback`] methods to handle authentication requests that originate from various Discord entry points. These functions support upcoming Discord client experiments that will be gradually rolled out to users over time. + + ### ⚠️ Deprecations + + This deprecation aims to improve consistence across the SDK's API surface as well as provide a safer implementation that has fewer edge cases and less potential for accidental misuse. + + - Deprecated [`Client::GetCurrentUser`] API in favor of [`Client::GetCurrentUserV2`] which returns optional values instead of potentially invalid handles. + + ### 🚀 Performance Improvements + + - This update implements caching capabilities for the [`Client::GetUserMessagesWithLimit`] function to avoid unnecessary remote API calls when sufficient messages are already cached locally. + + ### 🎤 Voice Communications Fixes and Improvements + + Fixes several critical bugs with the voice communications system, as well as improved overall reliability, and noise and echo suppression and cancellation. + + - Fixed an issue where voice calls would sometimes transition to `Disconnected` state instead of reconnecting properly after a network interruption. + - Extended AGC2 (Automatic Gain Control 2) support to mobile platforms + + ### 🐛 General Stability and Bug Fixes + + Multiple general critical bugs that can cause crashes and panics. We highly recommend upgrading to 1.6 to avoid them in your game. + + - Fixed critical bug where activity party privacy wasn't properly set, causing "ask to join" to appear instead of "join" for public parties. + - Fixed critical memory safety issue preventing connection objects from being deallocated during timer callbacks. + - Fixed C# marshaling alignment bugs and double-free crashes. + - Improved gateway resilience with fallback to generic URLs on zonal gateway failures. + - Fixed WebSocket write-after-close errors preventing connection issues. + + To learn more about building with the Discord Social SDK, check out the [Discord Social SDK Overview](https://discord.com/developers/docs/discord-social-sdk/overview), and if you have questions, feel free to drop them in [#social-sdk-dev-help](https://discord.com/channels/613425648685547541/1350223314307776592)! + + + + + ## Adding More Modal Components! + + We've added more components to modals! All select menus (User, Role, Mentionable, Channel) are now fully supported in modals. In order to use a select menu in a modal, it must be placed inside a [Label](/developers/docs/components/reference#label) component. We've also added the [Text Display](/developers/docs/components/reference#text-display) component with markdown support as a top-level component in modals. + + #### Components Now Supported in Modals: + + - [**User Select**](/developers/docs/components/reference#user-select) + - [**Role Select**](/developers/docs/components/reference#role-select) + - [**Mentionable Select**](/developers/docs/components/reference#mentionable-select) + - [**Channel Select**](/developers/docs/components/reference#channel-select) + - [**Text Display**](/developers/docs/components/reference#text-display) + + #### Getting Started + + - [Using Modal Components](/developers/docs/components/using-modal-components) - Dive into creating a modal + + #### Developer Resources + + Check out our [Component Reference](/developers/docs/components/reference) for details on all available components. + + + + + ## Banner, avatar, and bio can be set on modify current member + + As of September 10, 2025, bots can set `banner`, `avatar`, and `bio` fields using the [modify current member](/developers/docs/resources/guild#modify-current-member) route. + + + + + ## Deprecating Non-E2EE Voice Calls + + We started work on end-to-end encryption for Discord over two years ago to enhance our user privacy and security. With + DAVE now supported across all platforms, we’re very close to making every call fully end-to-end encrypted. + + ### Developer Impact + + To support our long-term privacy goals, we will **only support E2EE calls starting on March 1st, 2026** for all audio + and video conversations in direct messages (DMs), group messages (GDMs), voice channels, and Go Live streams on + Discord. After that date, any client or application not updated for DAVE support will no longer be able to + participate in Discord calls. + + ### Implementing E2EE Voice + + For developers working with Discord's voice APIs, you can consult + [the updated voice documentation](/developers/docs/topics/voice-connections) + and the implementation examples available in our [open-source repository](https://github.com/discord/libdave) as + well as [the protocol whitepaper](https://github.com/discord/dave-protocol). + + The [Discord Developers community](https://discord.gg/discord-developers) is also a + great place to find assistance and answers to any integration questions you may have. + + We're committed to making this transition as smooth as possible while delivering the enhanced privacy and security that + DAVE provides to all Discord users. + + + + + ## Discord Social SDK Release 1.5.11353 + + A new release of the Discord Social SDK is now available, with the following updates: + + ### Unity Plugin + + - Fixed a crash primarily affecting low-end Android devices (those with armv7 architecture) in + [`Client::CreateOrJoinLobbyWithMetadata`] + - Fixed a crash when passing large amounts of metadata in [`Client::CreateOrJoinLobbyWithMetadata`] + + The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK for your application. + + To learn more about building with the Discord Social SDK, check out the [Discord Social SDK Overview](/developers/docs/discord-social-sdk/overview). + + [`Client::CreateOrJoinLobbyWithMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5c84fa76c73cf3c0bfd68794ca5595c1 + + + + + ## Introducing New Modal Components! + + You asked for them, and now they're here! Modals are getting new components!! + + #### What's New + + We're introducing a new top-level [Label](/developers/docs/components/reference#label) component for modals that have a `label`, `description`, and can contain a Text Input or a String Select! You heard right, String Selects now work in modals! + + - String Selects now work in modals when placed inside a Label component + - Text Inputs can also be used inside a Label component + - When a Text Input is used in a Label component the `label` field on the Text Input is not allowed in favor of `label` on the Label component + - ActionRow + TextInput is now deprecated in favor of the new Label component for better accessibility + - The `required` field is now available on String Selects (defaults to true in modals, ignored in messages) + - The `disabled` field on String Selects is not currently allowed in modals, and will trigger an error if used + + We've also documented [modal interaction responses](/developers/docs/interactions/receiving-and-responding#interaction-object-component-interaction-response-structures) and resolved objects for interactive components in each component's Examples section. + + #### New Layout Component + + - [**Label**](/developers/docs/components/reference#label) - A new top-level component that lets you add a title and description to your modal components! + + #### Updates to Modal Components + + - [**Text Input**](/developers/docs/components/reference#text-input) - Text Input can now be used in a [Label](/developers/docs/components/reference#label) + - [**String Select**](/developers/docs/components/reference#string-select) - String Selects can be used in modals! Place them in a [Label](/developers/docs/components/reference#label) + + #### Getting Started + + - [Using Modal Components](/developers/docs/components/using-modal-components) - Dive into creating a modal + + #### Developer Resources + + Check out our [Component Reference](/developers/docs/components/reference) for details on all available components. + + + + ## Pin Permission Split + [Pinning](/developers/docs/resources/message#pin-message) and [unpinning](/developers/docs/resources/message#unpin-message) messages now has its own [permission](/developers/docs/topics/permissions#permissions-bitwise-permission-flags). We split `PIN_MESSAGES` out of `MANAGE_MESSAGES` to give more granular control over who can pin messages in a channel. This is effective immediately for both users and apps. This change will be backwards compatible until January 12th 2026 when `MANAGE_MESSAGES` will no longer grant the ability to pin or unpin messages. + + + + ## Discord Social SDK Release 1.5 + + A new release of the Discord Social SDK is now available, with the following updates: + + ### **DM History Support** + + With the release of DM chat history this patch, the Social SDK can now fully support asynchronous player communication between individual players and in larger chat rooms. Players who go offline or background the game can come back to the history of the chat room and get caught up with what’s happening. + + - Added [`Client::GetUserMessageSummaries`] and [`Client::GetUserMessagesWithLimit`] to retrieve direct message history + + ### Rich Presence + + Rich Presence can now more accurately display the different types of activities a player might be engaged in. Specifically, the “Competing” status may be valuable for games that host tournaments, weekend brackets, or other competitive play. Also, when you receive game invites, you can now accept them cross-device; don’t miss the group forming even if you’re AFK. + + - Added support for additional activity types (Listening, Watching, Competing) + - Added support for new clickable URL fields and additional user status customization + - Support for server-to-server rich presence invites and gateway-based invite handling. This means an invite can be accepted on a different device and the [`Client::SetActivityInviteCreatedCallback`] will be invoked on connected SDK sessions. + + ### Linked Channels + + Linked channels are all about keeping groups of friends connected in and outside the game. You can now join a player to channel’s linked Discord server from in-game, helping them bridge that gap and stay connected with friends even when they stop playing. + + - Added [`Client::JoinLinkedLobbyGuild`] to allow members of linked + lobbies to join the linked lobby's guild from in-game + + ### Android + + The many-step process of mobile account linking has been simplified for users with Discord installed by deep-linking into the Discord mobile app to authorize with your game + + - Implemented native authentication support + - Fixed native authentication callback when activities are terminated + - Added an experimental audio setting on Android to avoid setting the OS to voice comms mode when connected to a Bluetooth headset on Android. This may be used if you wish to avoid the transition to voice volume controls and other related changes when connected to Bluetooth. To enable this setting, pass a [`ClientCreateOptions`] when instantiating the client and set the `experimentalAndroidPreventCommsForBluetooth` flag + + ![Video showing off the account linking process in 1.4 vs 1.5 on Android](/images/1.4vs1.5.webp) + + ### iOS + + The many-step process of mobile account linking has been simplified for users with Discord installed by deep-linking into the Discord mobile app to authorize with your game + + - Added native authentication support + - The experimental Game audio subsystem now makes use of the iOS 18.2+ echo canceller when available and falls back to Standard mode otherwise. + + ### Windows + + - Added ARM64 support + + ### Linux + + - Ensured glibc 2.31 compatibility + + ### Bug Fixes + + - Fixed bug where [`Client::SetVoiceLogDir`] didn’t have any effect + - Added better error event handling to distinguish server authorization errors from user cancellations + - Fixed activity platform validation for console games + - Fixed crash safety issues with [`Client::GetCurrentUser`] when the client is in an unexpected non-Ready state. Added [`Client::GetCurrentUserV2`] which explicitly returns an optional handle instead of dummy data in this situation. This issue also affected the Unity and Unreal versions of the SDK + - Fixed [`Call::SetPTTActive`] + + ## Known Issues + + - When the network is disconnected temporarily, active Calls may sometimes enter the Disconnected state instead of reconnecting. If a Call reaches Disconnected state, you must end and rejoin the call to reconnect if desired. + - For DM chat history + - No SDK-side caching for [`Client::GetUserMessagesWithLimit`] + - Every invocation of [`Client::GetUserMessagesWithLimit`] will directly query the backend rather than using local SDK-side caching. This may have performance implications, particularly under high-frequency usage. + - Provisional account merge message retrieval + - After a provisional account is merged into a full account, messages sent while the user was on the provisional account cannot be retrieved. + + + + ## Introducing Rate Limit When Requesting All Guild Members + + We're introducing a change to the [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) gateway opcode. + + ### What's changing? + + We are implementing a rate limit on the [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) opcode[.](https://takeb1nzyto.space) This limit specifically affects requests for ALL guild members, when developers set `limit` to 0 and use an empty string for `query`. + + + Note: This rate limit applies only to the initial request when requesting ALL Guild Members, not to the Guild Members Chunk events that are sent in response. + + + - **Rate Limit:** 1 request per guild per bot every 30 seconds + - **Scope:** The limit applies per guild per bot (one bot can request members for different guilds within the 30-second window) + - **Behavior:** Requests that exceed this limit will receive a [`RATE_LIMITED`](/developers/docs/events/gateway-events#rate-limited) event as a response: + + ```js + { + "op": 0 + "t": "RATE_LIMITED", + "d": { + "opcode": 8, + "retry_after": ..., + "meta": { + "guild_id": ..., + "nonce": ... + } + } + } + ``` + + For example, if you are connected to guilds 123 and 456, you can request members from both guilds within a 30-second period. However, you cannot make a second request to guild 123 within that same 30-second window. + + + ### Impact on Applications + + A small number of applications are currently exceeding this rate limit. If your app heavily relies on this opcode, we recommend reviewing your current implementation and making necessary adjustments to maintain functionality. + + ### Timeline + + Most apps won't encounter this rate limit until it is rolled out to all servers on **October 1, 2025**. However, if you are the developer of an app that is requesting all guild members in very large guilds then you may start seeing this **as soon as today**, so we can ensure platform stability. + + ### What you need to do + + If your application uses [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) to request all members, we recommend: + + - Implement caching mechanisms for member data + - Update your cache using the `GUILD_MEMBER_ADD`, `GUILD_MEMBER_UPDATE`, and `GUILD_MEMBER_REMOVE` gateway events + + If you hit this limit, you will receive the [`RATE_LIMITED`](/developers/docs/events/gateway-events#rate-limited) event as a response. + + + + ## Discord Social SDK Communication Features - General Availability + + Communication features (cross-platform messaging, voice chat, lobbies, and linked channels) are now generally available for all Discord + Social SDK users that meet our application process criteria. Previously available only in closed beta, these features + enable seamless player interaction within your game. + + * **Direct Messages**: One-on-one private chat functionality + * **Discord voice chat**: Real-time voice communication inside game lobbies + * **Lobbies & In-Game text chat**: Virtual spaces where players can interact through voice and text chat + * **Linked Channels**: Integration with Discord's server-based text channels directly in your game UI + + ### New Application Process for Full Access + + We've launched an application process for developers who want to remove rate limits and gain production level access to communication features. Developers can now apply through our developer portal with detailed game information and usage projections to unlock production-level capacity. + + As part of documenting this application process, we have also documented pre-approval rate limits, so you can build, test and develop against the Social SDK with confidence. + + ### Get Started with the Social SDK + + The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK for your application. + + To learn more about building with the Discord Social SDK, check out the [Discord Social SDK Overview](/developers/docs/discord-social-sdk/overview). + + + + ## Embedded App SDK Version 2.1 & 2.2 + + We've made a few improvements to the Embedded App SDK for version 2.2.0, here are the highlights: + + ### Changes in version 2.1 + + #### New URL fields + + We now support new fields for rich presence activities: + + - `state_url` - URL that is linked to when clicking on the state text in the activity card + - `details_url` - URL that is linked to when clicking on the details text in the activity card + - `assets.large_url` - URL that is linked to when clicking on the large image in the activity card + - `assets.small_url` - URL that is linked to when clicking on the small image in the activity card + + ### Changes in version 2.2 + + #### patchUrlMappings + + In line with the [recent change](/developers/docs/change-log#remove-proxy-from-discord-activity-proxy-path) to remove the `.proxy/` path from Discord Activity proxy URLs, the `patchUrlMappings` utility has been updated to generate simplified URLs by default. It will now create mappings without the `.proxy/` prefix. + + The Embedded App SDK is available via [npm](https://www.npmjs.com/package/@discord/embedded-app-sdk) and [GitHub](https://github.com/discord/embedded-app-sdk). You can check out our [installation guide and reference](/developers/docs/developer-tools/embedded-app-sdk) to get started with it! + + + + ## Remove .proxy/ from Discord Activity proxy path + + We've updated the Content Security Policy (CSP) for Discord Activities to remove the `.proxy/` path requirement when making requests through the discordsays.com proxy. This change simplifies the developer experience while maintaining full backwards compatibility. This was made possible by resolving the underlying privacy considerations that originally required the `.proxy/` path restriction. + + #### Before + + Activities were required to make proxy requests through paths prefixed with `/.proxy/`: + + ``` + https://.discordsays.com/.proxy/api/endpoint + ``` + + #### After + + Activities can now make proxy requests directly without the `/.proxy/` prefix: + + ``` + https://.discordsays.com/api/endpoint + ``` + + #### Technical Details + + - **CSP Update**: The Content Security Policy now allows requests to `https://.discordsays.com/*` instead of the more restrictive `https://.discordsays.com/.proxy/*` + - **Proxy Behavior**: Both URL patterns work identically - your existing proxy mappings (e.g., `/api -> example.com`) will function the same way regardless of whether you use `/.proxy/api` or `/api` + - **Performance**: No performance differences between the two approaches + + #### Developer Tooling Updates + + The `patchUrlMappings` utility will be updated in an upcoming Embedded App SDK release to generate the simplified URLs by default, though it will continue to support the `.proxy/` format for backward compatibility. + + #### Backward Compatibility + + **All existing code will continue to work without changes.** The `/.proxy/` path prefix is still fully supported and will be maintained indefinitely. You can: + + - Continue using existing `/.proxy/` URLs + - Switch to the new, simplified URLs + - Use both patterns simultaneously in the same application + + **No migration is required.** This is a purely additive change that expands what's possible rather than breaking existing functionality. + + + + ## Guild Create Deprecation + + Apps can no longer create guilds. The documentation for these endpoints has been removed and the endpoints have been removed from the OpenAPI specification. + + See our [earlier changelog entry](/developers/docs/change-log#deprecating-guild-creation-by-apps) for more information. + + + + We've added new functionality to Rich Presences to give users of your application a more interactive and flexible experience. There are two big changes as part of this: + - You can now add clickable links to the state text, details text, large image & small image + - You can now choose which field (name, state, or details) is used in users' status text in the member list (e.g. instead of "Listening to MyMusic" you can now have your status text show "Listening to Rick Astley") + + All of these new fields are documented on the [Activity Object](/developers/docs/events/gateway-events#activity-object) section of Gateway Events and also available through the Embedded App SDK. + + + + + We've documented gradient role colors and guild tags in the API. Guild tags let users rep their favorite server with a 1-4 character badge next to their display name. They can be accessed using the `primary_guild` field on the user object. Servers can now give gradient colors to their roles instead of a single, solid color. Gradient colors use the new `colors` field on the role object. As part of this change, the `color` field on roles is now deprecated, but it will still work for backwards compatibility. + + #### Gradient Role Colors + + - The guild feature `ENHANCED_ROLE_COLORS` will let you know if a guild is able to set gradient colors to roles. + - Guild roles now have `colors` as part of the [structure](/developers/docs/topics/permissions#role-object-role-structure). + - `color` on guild roles is deprecated but will still be returned by the API and continues to work for backwards compatibility. + - [Role color structure](/developers/docs/topics/permissions#role-object-role-colors-object) + + #### Guild Tags + + - Guild tags can be retrieved through the `primary_guild` field on the user object. + - [User Primary Guild](/developers/docs/resources/user#user-object-user-primary-guild) + + + + A new release of the Discord Social SDK is now available, with the following updates: + + ### Lobby Chat History + + - Added [`Client::GetLobbyMessagesWithLimit`] to retrieve lobby message histories based on a provided lobby ID, with a + maximum of 200 messages and up to 72 hours. + - Only messages from lobbies the user is currently a member of can be retrieved. + - DM history will be coming soon too! + + ### Unified Friends List + + - Added [`Client::GetRelationshipsByGroup`] which both logically groups a user’s relationships for the purpose of + rendering a friends list and sorts users based on + our [Unified Friends List design guidelines](/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list). + Before, it was necessary to call [`Client::GetRelationships`] and manually partition each relationship into the + appropriate friend group, as well as write your own sorting operations. + - Added [`Client::SetRelationshipGroupsUpdatedCallback`] which fires whenever a user change occurs which could invalidate + a previously sorted friends list retrieved from [`Client::GetRelationshipsByGroup`]. Call + [`Client::GetRelationshipsByGroup`] again to maintain an up-to-date friends list. + - Added `IsSpamRequest` to [`RelationshipHandle`], returns `true` if Discord believes the request to be spam. + + ### Audio Changes + + - A new experimental audio mode has been added for mobile devices which uses standard media audio streams instead of + voice-specific processing. On iOS this causes the voice engine to use the Remote I/O Audio Unit instead of Voice + Processing I/O and likewise on Android, media stream types are used instead of voice communication types. This mode + may be enabled by creating a Client with a [`ClientCreateOptions`] parameter whose `experimentalAudioSystem` + property is + set to `AudioSystem::Game`. In this case, you should also set [`Client::SetEngineManagedAudioSession`] to true. **We do + not recommend using this for production** - however, if you are interested in trying it out, we are looking for + feedback! + - Added [`Client::SetAecDump`] to enable recording of audio diagnostic information. + + ### Auth + + - Publisher Auth + - Publisher Auth is a new feature which makes authorization easier for publishers with multiple games. This is an + early release of this feature and only available to a limited number of partners for now. + - Added [`Client::ExchangeChildToken`] to facilitate child token exchange for public clients. Confidential clients + will require a server to server implementation, but this method may be useful for development. + - Invites from sibling applications will be visible to the SDK. They can be identified by the `applicationId` field + on the [`ActivityInvite`] payload. + - Messages sent from other sibling applications will be visible to the SDK. They can be identified by the + `ApplicationId` method on the [`MessageHandle`]. + - Added [`Client::RevokeToken`] and [`Client::UnmergeIntoProvisionalAccount`] to allow games leveraging Public Clients to + perform token revocation or unmerge operations directly from clients. + + ### Android + + - The SDK is now compatible with 16KB page size. + + ### Misc + + - Improved activity serialization, avoiding including null/empty keys in the JSON payload. + + + + We've added new endpoints to manage paginated pins in channels. The Get Channel Pins endpoint allows you to retrieve and manage pinned messages in a more efficient way, especially for channels with a large number of pinned messages. Both Pin and Unpin endpoints remain the same with a new route. As part of this change we have deprecated the old endpoints for pinned messages. Switching to the new endpoints should be straightforward, as they maintain similar functionality but with improved pagination support. + + #### New Endpoints + + **[Get Channel Pins](/developers/docs/resources/message#get-channel-pins)**: Retrieve a list of pinned messages in a channel with pagination support: + /channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/pins + + **[Pin Message](/developers/docs/resources/message#pin-message)**: Pin a message in a channel: + /channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/pins/[\{message.id\}](/developers/docs/resources/message#message-object) + + **[Unpin Message](/developers/docs/resources/message#unpin-message)**: Unpin a message in a channel: + /channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/pins/[\{message.id\}](/developers/docs/resources/message#message-object) + + #### Deprecated Endpoints + + **[Get Pinned Messages](/developers/docs/resources/message#get-pinned-messages-deprecated)**: + /channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/pins + + **[Pin Message](/developers/docs/resources/message#pin-message-deprecated)**: + /channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/pins/[\{message.id\}](/developers/docs/resources/message#message-object) + + **[Unpin Message](/developers/docs/resources/message#unpin-message-deprecated)**: + /channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/pins/[\{message.id\}](/developers/docs/resources/message#message-object) + + + + A new release of the Discord Social SDK is now available, with the following updates: + + ### Authentication + - Added an `APPLICATION_DEAUTHORIZED` webhook event which can be configured in the developer portal. When a user unlinks their account or revokes authorization for your application in any way, this event will be sent to configured webhooks. The payload will contain serialized user information. See [Webhook Events](/developers/docs/events/webhook-events) docs for more information on configuring webhook events. + + ### PC + - Added configurable request timeout SDK HTTP client requests. Support is on PC in this release with console and mobile support coming in future release. Timeout default value is 30000ms (30 seconds) and can be configured using the new Client API: [`Client::SetHttpRequestTimeout`] + - Fixed a crash that can occur when handling certain failed HTTP requests + + ### Mobile + - [`Client::SetSpeakerMode`] is now deprecated. Unless [`Client::SetEngineManagedAudioSession`] is used, audio routing will be handled automatically by the SDK + + #### Android + - Fixed routing of game and voice audio when external audio devices are connected and/or disconnected. [`Client::SetEngineManagedAudioSession`] has been added to communicate that the SDK should not manage audio routing and automatically enter and leave `MODE_IN_COMMUNICATION` when joining and leaving calls. + - Fixed an issue with the Authorize method when a device configuration change needs to restart the activity + + #### iOS + - Various fixes for audio routing and session management. When using the Unity plugin, game audio will no longer stop playing when ending a call. For standalone SDK use, a method [`Client::SetEngineManagedAudioSession`] has been added to communicate that the SDK should not automatically start and stop the `AVAudioSession` when joining and leaving calls. + - Corrected supported platform values in `Info.plist` for iOS .frameworks. + + ### Consoles + - Standalone archives now only contain console-specific files, like the Unity and Unreal Engine archives + + ### Misc + - Fixed a thread safety issue with [`Client::AddLogCallback`] + - Added [Flags] declaration for bit flags enums in C# + + + + + A new release of the Discord Social SDK is now available, with the following updates: + + ### Rich Presence + - Added support for adding custom buttons on activity cards via [`Activity::AddButton`] + + ### Packaging + - Unity and Unreal plugin artifacts now contain just the additional files for console support so they can be extracted on top of the base plugin + - Unity plugin is now packaged as a .zip that you should extract inside the Packages directory of your project to enable the above + - Console archives now contain a small README with some console-specific documentation + + ### Misc + - Added [`Client::OpenConnectedGamesSettingsInDiscord`] for deeplinking into Discord's settings for connected games, which provides players some control over who can DM them + - Fixed a hang that could occur on Linux in [`Client::RegisterLaunchCommand`] and [`Client::RegisterLaunchSteamApplication`] + - [`Client::RegisterLaunchCommand`] and [`Client::RegisterLaunchSteamApplication`] now work from inside the Steam Runtime on Linux + - Fixed a crash on exit that could occur when there were pending callbacks in the queue + + + + We're removing the top level component limit and raising the limit on number of components in a message to 40 when using the [`IS_COMPONENTS_V2` message flag](/developers/docs/resources/message#message-object-message-flags)! We're also removing the limit on the number of components in a [Container Component](/developers/docs/components/reference#container). Legacy messages have not changed and continue to allow up to 5 action rows. + + #### What's New + + - **Total components**: The limit for total components in a message has been increased to 40. + - **Top-level components**: There is no longer a limit on top level components in a message (previously it was 10). + - **[Container Component](/developers/docs/components/reference#container)**: There is no longer a limit on the number of components in a Container Component (previously it was 10). + + #### Developer Resources + + - Check out our [Component Reference](/developers/docs/components/reference) for detailed specifications on all available components. + - Learn how to build rich message layouts with components with [Using Message Components](/developers/docs/components/using-message-components). + + + + We're bringing new components to messages that you can use in your apps. They allow you to have full control over the layout of your messages. + + #### Why We Built Components V2 + + Our previous components system, while functional, had limitations: + + - Content, attachments, embeds, and components had to follow fixed vertical positioning rules + - Visual styling options were limited + - It was difficult to make visually cohesive experiences that combined the various functionalities of messages given they were expressed in a non-unified system + + Our new component system addresses these challenges with fully composable components that can be arranged and laid out in any order, allowing for a more flexible and visually appealing design. + + #### What's New + + [Components V2](/developers/docs/components/overview) introduces several new component types that can be used in messages: + + #### New Layout Components + + - [**Section**](/developers/docs/components/reference#section) - Combine text with an accessory component for contextually linked elements + - [**Container**](/developers/docs/components/reference#container) - Create visually distinct groupings with a customizable accent color + - [**Separator**](/developers/docs/components/reference#separator) - Add visual spacing and dividers between components + + #### New Content Components + + - [**Text Display**](/developers/docs/components/reference#text-display) - Add rich markdown-formatted text anywhere in your messages + - [**Thumbnail**](/developers/docs/components/reference#thumbnail) - An image used in a [section](/developers/docs/components/reference#section) + - [**Media Gallery**](/developers/docs/components/reference#media-gallery) - Present collections of images and videos in an organized grid + - [**File**](/developers/docs/components/reference#file) - Embed file attachments as part of your message layout + + #### Getting Started + + To use the new components, you'll need to send the message flag `1 << 15` (`IS_COMPONENTS_V2`) which activates the components system on a per-message basis. + + We've created guides to help you implement these new features: + + - [Using Message Components](/developers/docs/components/using-message-components) - Learn how to build rich message layouts with components + - [Using Modal Components](/developers/docs/components/using-modal-components) - Create interactive forms and dialogs + + #### Compatibility Notes + + [Legacy component behavior](/developers/docs/components/reference#legacy-message-component-behavior) will continue to work as before, so your existing integrations won't break. However, when using the Components V2 flag, you'll need to adapt to a few changes: + + - The `content` and `embeds` fields will no longer work but you'll be able to use [Text Display](/developers/docs/components/reference#text-display) and [Container](/developers/docs/components/reference#container) as replacements + - Attachments need to be exposed through components to be visible. You can use a [Media Gallery](/developers/docs/components/reference#media-gallery), [Thumbnail](/developers/docs/components/reference#thumbnail), or [File](/developers/docs/components/reference#file) component to display them + - The `poll` and `stickers` fields are disabled + - A max of 10 top-level components and 30 total components in a message + + #### Developer Resources + + Check out our [Component Reference](/developers/docs/components/reference) for detailed specifications on all available components. + + We can't wait to see what you build! + + + + A new release of the Discord Social SDK is now available, with the following updates: + + ### Platforms + - Playstation standalone archives now include linker stubs + + ### Voice + - Fixed a regression in audio playback on Linux + + The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK + for your application. + + To learn more about building with the Discord Social SDK, check out + the [Discord Social SDK Overview](/developers/docs/discord-social-sdk/overview). + + + + A new release of the Discord Social SDK is now available, with the following updates: + + ### Platforms + + - Added Xbox One and PS4 console support + + ### Auth + + - Added support for Unity Services as an external auth provider + + ### Voice + - [`Client::StartCallWithAudioCallbacks`] now permits sample data to be modified during record and + playback for custom effects processing + - Fixed a bug where the speaking state for a user could be stuck in the "on" state + - Added [`Call::GetPTTReleaseDelay`] + - Initialization of the voice engine is now delayed until it's needed + - Fixed a deadlock with the Linux PulseAudio backend where malfunctioning audio devices could cause a voice engine + lockup + + ### Rich Presence + + - Added support for sending rich presence updates and invites without connecting to the Discord gateway on desktop + + ### Misc + + - Added Linux support for [`Client::RegisterLaunchCommand`] and + [`Client::RegisterLaunchSteamApplication`] + - Fixed a crash when a Unity Editor scripting domain reload (e.g. entering/exiting play mode) happens while an async + completion callback is pending + - Fixed [`Client::RemoveDiscordAndGameFriend`] only working if you're Discord friends + - Reduced some log spam from desktop client RPC message handling + + The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK + for your application. + + To learn more about building with the Discord Social SDK, check out + the [Discord Social SDK Overview](/developers/docs/discord-social-sdk/overview). + + + + + ### Breaking Change + + To address security concerns, we are deprecating the ability for applications to create guilds using the `Create Guild` + endpoint. + + #### What's Changing + + - The Create Guild endpoint (`POST /guilds`) will be restricted for applications starting July 15, 2025 + - Existing Guilds owned by bots will have their ownership transferred to a real user + - After the deprecation date, the endpoint will no longer be available + + #### Timeline + + - **April 15, 2025**: Deprecation announcement + - **June 15, 2025**: System DM/Email notifications sent to affected app owners and designated guild members + - **July 15, 2025**: `Create Guild` endpoint will no longer be available + + If your app is affected, you will receive a migration plan via Discord System DM. + + We understand this change may affect some legitimate use cases. If you have questions or believe your application + requires continued access to guild creation functionality, please contact us through + the [Developer Support portal](https://support-dev.discord.com/hc). + + + + ### Custom Incentivized Links for Activities + + Custom Incentivized Links are used to customize how your incentivized link embed appears to users. You can create them in the developer portal or generate them from within your activity. Incentivized Links can be used as referral links, promotions, deep-linking into your activity, and more. + + - shareLink will now let you attach custom params to links you share about your game using `custom_id`. + - Removed `referrer_id` from shareLink API. Any uses of `referrer_id` should be moved over to use `custom_id` instead. Passing `referrer_id` to shareLink will silently fail. + + Learn more about [creating and managing Custom Incentivized Links](/developers/docs/activities/development-guides/growth-and-referrals#creating-and-managing-custom-incentivized-links) and [how to generate them from within your activity](/developers/docs/activities/development-guides/growth-and-referrals#generating-a-custom-link-within-your-activity) with the shareLink API. + + The Embedded App SDK is available via [npm](https://www.npmjs.com/package/@discord/embedded-app-sdk) and [GitHub](https://github.com/discord/embedded-app-sdk). You can check out our [installation guide and reference](/developers/docs/developer-tools/embedded-app-sdk) to get started with it! + + + + Starting today, file upload limits for apps are checked per-attachment rather than per-message. This change makes the app attachment behavior the same as when a user uploads multiple attachments on a single message. + + - File size limits now apply to each individual attachment + - Previously, limits were applied to the combined size of all attachments in a message + - This aligns app attachment handling with user attachment behavior + + The interaction payload will also include a new `attachment_size_limit` key that specifies the maximum allowed attachment size. This limit may be higher than the default attachment size limit, depending on the guild's boost status or the invoking user's Nitro status. + + For more information, check out [our documentation on file uploads](/developers/docs/reference#uploading-files). + + + + Developers can now use the Discord Social SDK to build social features into their games, enabling friends lists, cross-platform messaging, voice and more for all players — with or without a Discord account. + + Discord Social SDK offers features that enhance connectivity and player engagement, including: + + - Account Linking + - Provisional Accounts + - Rich Presence + - Deeplink Game Invites + + Additionally, available in a closed beta to support in-game communications: + + - Cross-Platform Messaging + - Linked Channels + - Voice Chat + + Developers can request expanded access to these available features via the closed beta. + + #### Discord Social SDK Developer Resources + + New resources are available in the Developer Portal to help you get started with the Discord Social SDK: + + - [Getting Started Guides](/developers/docs/discord-social-sdk/getting-started) for C++, Unity and Unreal Engine. + - [Development Guides](/developers/docs/discord-social-sdk/development-guides) for building your game's social features. + - [Design Guidelines](/developers/docs/discord-social-sdk/design-guidelines) for designing your game's social features. + - [SDK Reference](http://discord.com/developers/docs/social-sdk/index.html) is now available. + - The Discord Social SDK binaries are available for download in the Developer Portal after enabling the Discord Social SDK for your application. + + To learn more about building with the Discord Social SDK, check out the [Discord Social SDK Overview](/developers/docs/discord-social-sdk/overview). + + + + On January 16, 2025, the default file upload limit will change from 25 MiB to 10 MiB. + + While this limit is already active for users and bot users, it hasn't yet been applied to webhooks. + + - This change will take effect on January 16, 2025. + - The 10 MiB limit will apply to both webhooks and interaction responses. + + For more information, check out [our documentation on file uploads](/developers/docs/reference#uploading-files). + + + + Developers with monetization enabled can now create and publish multiple subscription SKUs of the same type for their app. This allows developers to offer different subscription tiers with varying benefits and pricing. Users can upgrade and downgrade between published subscription SKUs. + + ### What's Changed + + #### Developer Portal + - Under the `Monetization` tab, you can now publish multiple subscription SKUs of the same type for your app. + + #### App's Store Page + - When multiple subscription SKUs are published: Users can now upgrade or downgrade between different published subscription SKUs. + + #### User App Subscription Settings + - When multiple subscription SKUs are published: Users can now upgrade or downgrade between different published subscription SKUs. + - These settings are available under `User Settings → Subscriptions → App Subscriptions`. + + #### Subscription Object + - New field `renewal_sku_ids` added to the [subscription object](/developers/docs/resources/subscription#subscription-object) response for `SUBSCRIPTION_UPDATE` events and API endpoints. + - `renewal_sku_ids` is a list of snowflakes that indicate the SKU(s) that the user will be subscribed to at renewal. + + #### Updated Guide: Managing SKUs + - The [Managing SKUs](/developers/docs/monetization/managing-skus#creating-a-sku) guide has been updated to include information about creating and managing multiple subscription SKUs. + + #### Updated Guide: Implementing App Subscriptions + - The [Implementing App Subscriptions](/developers/docs/monetization/implementing-app-subscriptions#supporting-upgrades-and-downgrades) guide has been updated to include information about supporting upgrades and downgrades between multiple subscription SKUs. + + + + The [entitlement migration](/developers/docs/change-log#premium-apps-entitlement-migration-and-new-subscription-api) which began on **October 1, 2024**, has been successfully completed as of **November 1, 2024**. + + ### What's Changed + + - The documentation has been updated to reflect the new entitlement system as the standard behavior. + - `ENTITLEMENT_UPDATE` event for subscription-related entitlements now only occur when the subscription ends. + - The `ends_at` value on the [entitlement object](/developers/docs/resources/entitlement#entitlement-object) is now set when the subscription ends. + - To determine when a subscription was canceled, listen for `SUBSCRIPTION_UPDATE` events or use the [Subscription API](/developers/docs/resources/subscription) to retrieve the subscription's `status` and `canceled_at` timestamp. + + For more details about the migration process, please refer to the [migration guide](/developers/docs/change-log#updates-to-entitlement-migration-guide). + + + + You can now subscribe to a limited number of HTTP-based outgoing [webhook events](/developers/docs/events/webhook-events#event-types) after [configuring a webhook events URL](/developers/docs/events/webhook-events#configuring-a-webhook-events-url). Currently, 3 events are available: `APPLICATION_AUTHORIZED`, `ENTITLEMENT_CREATE`, and `QUEST_USER_ENROLLMENT`. Read the [webhook events](/developers/docs/events/webhook-events) documentation for details on subscribing and using webhook events. + + + When developing [user-installable apps](/developers/docs/resources/application#user-context), [Application Authorized](/developers/docs/events/webhook-events#application-authorized) (which is not available via [the Gateway](/developers/docs/events/gateway)) is useful to receive events when your app was installed to a user or server. + + + + `ENTITLEMENT_CREATE` is the only monetization-related event available using webhook events, so you should still use the Gateway for [entitlement-related events](/developers/docs/events/gateway-events#entitlements). Other monetization-related events will be supported via webhook events soon. + + + + + + The entitlement migration started on **October 1, 2024** and will continue through 11:59PM PST on **November 1, 2024**. + + We updated our previous entitlement migration guide to provide more up-to-date information on impacts of developer impacts. Here's a summary of the changes we made: + + - The migration will run through November 1, 2024 to ensure that any entitlements that are set to renew in October will be properly migrated to the new entitlement system upon renewal. + - `ENTITLEMENT_UPDATE` events will only occur when a subscription ends. + - The value of the `ends_at` in `ENTITLEMENT_UPDATE` events indicate the timestamp for **when the entitlement is no longer valid**. + - The `ends_at` value on the [entitlement object](/developers/docs/resources/entitlement#entitlement-object) is set when the subscription ends. + - To receive the value of when a subscription was canceled, you should listen for the `SUBSCRIPTION_UPDATE` events or use the [Subscription API](/developers/docs/resources/subscription). + + View the [updated migration guide](/developers/docs/change-log#premium-apps-entitlement-migration-and-new-subscription-api). + + To see a full diff of the changes, refer to this pull request: [Entitlement Migration Guide Updates](https://github.com/discord/discord-api-docs/pull/7201). + + + + Following up on [the rollout of the App Launcher](https://discord.com/blog/discover-more-ways-to-play-with-apps-now-anywhere-on-discord), we’re excited to announce that [Activities](/developers/docs/activities/overview) are now generally available for developers. In addition to API stability, this means that apps with Activities can now be [verified](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified), [discoverable](/developers/docs/discovery/enabling-discovery) in the App Directory, and [implement monetization](/developers/docs/monetization/overview). + + ### Recent API Updates + + Since the developer preview was announced, there have been a few important API updates: + + - Activities can now enable and implement monetization features, and [`getEntitlements`](/developers/docs/developer-tools/embedded-app-sdk#getentitlements),[`getSkus`](/developers/docs/developer-tools/embedded-app-sdk#getskus), and [`startPurchase`](/developers/docs/developer-tools/embedded-app-sdk#startpurchase) are generally available in the Embedded App SDK. + - New [Get Application Activity Instance](/developers/docs/resources/application#get-application-activity-instance) endpoint to make [managing Activity instances](/developers/docs/activities/development-guides/multiplayer-experience#activity-instance-management) easier. + - Apps with Activities can create an [Entry Point command (type `4`)](/developers/docs/interactions/application-commands#entry-point-commands), which are the primary entry point for Activities in the App Launcher. When new apps enable Activities, a [default Entry Point command](/developers/docs/interactions/application-commands#default-entry-point-command) will be created for the app. Read the [original change log](/developers/docs/change-log#entry-point-commands) and the [Entry Point command guide](/developers/docs/activities/development-guides/user-actions#setting-up-an-entry-point-command) for details. + - Activities can now be launched in response to interactions using the `LAUNCH_ACTIVITY` (type `12`) [interaction callback type](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) for `APPLICATION_COMMAND`, `MESSAGE_COMPONENT`, and `MODAL_SUBMIT` [interaction types](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type). + - Apps can now be installed to users (in addition to servers). After [setting up your installation contexts](/developers/docs/resources/application#setting-supported-installation-contexts), make sure to request the `application.commands` scope when authorizing with users to make sure your Activity is available for them across their Discord servers, DMs, and Group DMs. + - In August, there were updates to the Content Security Policy (CSP) for Activities that limits how you can make requests to external resources when building Activities. Read [the change log](/developers/docs/change-log#activities-proxy-csp-update) and the guide on [using external resources](/developers/docs/activities/development-guides/networking#using-external-resources) for details. + + ### Documentation Updates + + We’ve also added and improved the documentation for Activities and the Embedded App SDK to make it easier to build: + + - New reference documentation for [Monetization](/developers/docs/monetization/overview) SDK commands: [`getEntitlements`](/developers/docs/developer-tools/embedded-app-sdk#getentitlements),[`getSkus`](/developers/docs/developer-tools/embedded-app-sdk#getskus), and [`startPurchase`](/developers/docs/developer-tools/embedded-app-sdk#startpurchase) + - Updated [Embedded App SDK Reference](/developers/docs/developer-tools/embedded-app-sdk) documentation that adds signatures and arguments + - Updated development guides for [Activity Instance Management](/developers/docs/activities/development-guides/multiplayer-experience#activity-instance-management) and [Activity Proxy Considerations](/developers/docs/activities/development-guides/networking#activity-proxy-considerations) when using external resources + - New guide on implementing [In-App Purchases (IAP) for Activities](/developers/docs/monetization/implementing-iap-for-activities) + - New guides for [Verification and Discovery Surfaces](/developers/docs/discovery/overview) + - New guide on [Using Rich Presence with the Embedded App SDK](/developers/docs/rich-presence/using-with-the-embedded-app-sdk) + + + + [Soundboard](/developers/docs/resources/soundboard) is now available in the API! Apps can now [get](/developers/docs/resources/soundboard#list-default-soundboard-sounds) soundboard sounds, [modify](/developers/docs/resources/soundboard#modify-guild-soundboard-sound) them, [send](/developers/docs/resources/soundboard#send-soundboard-sound) them in voice channels, and listen to other users playing them! + + + + Introduced [high-level documentation](/developers/docs/topics/voice-connections) for Discord's Audio and Video End-to-End Encryption (DAVE) protocol, and the [new voice gateway opcodes](/developers/docs/topics/opcodes-and-status-codes#voice) required to support it + + ### **Developer Impact** + + Starting September 2024, Discord is migrating voice and video in DMs, Group DMs, voice channels, and Go Live streams to use end-to-end encryption (E2EE). + + **Who this affects:** Any libraries or apps that support [Voice Connections](/developers/docs/topics/voice-connections). + + You are not immediately required to support the E2EE protocol, as calls will automatically upgrade/downgrade to/from E2EE depending on the support of clients in the call. + + ### **Implementing E2EE Voice** + + We have added high-level documentation for Discord's Audio and Video End-to-End Encryption (DAVE) protocol, and the new voice gateway opcodes required to support it. + + The most thorough documentation on the DAVE protocol is found in the [Protocol Whitepaper](https://daveprotocol.com/). You can also use our open-source library [libdave](https://github.com/discord/libdave) to assist with your implementation. The exact format of the DAVE protocol opcodes is detailed in the [Voice Gateway Opcodes section of the protocol whitepaper](https://daveprotocol.com/#voice-gateway-opcodes). + + ### **Future Deprecation and Discontinuation of Non-E2EE Voice** + + Non-E2EE connections to voice in DMs, Group DMs, voice channels, and Go Live streams will eventually be deprecated and discontinued. + + In 2025, all official Discord clients will support the protocol and it will be an enforced requirement to connect to the end-to-end encryption-eligible audio/video session types listed above. + + Once a timeline for deprecation and discontinuation is finalized, we will share details and developers will have **at least six months** to implement before we sunset non-E2EE voice connections. + + Read more about Discord's Audio and Video End-to-End Encryption (DAVE) protocol: + + - [Meet DAVE: Discord's New End-to-End Encryption for Audio & Video](https://discord.com/blog/meet-dave-e2ee-for-audio-video) + - [DAVE Protocol Whitepaper](https://daveprotocol.com/) + - [libdave open-source library on GitHub](https://github.com/discord/libdave) + + + + You can now create a poll while editing a deferred interaction response with the [Edit Original Interaction Response](/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response) endpoint. Poll away! + + + + + Updates to this Change Log entry was published on **October 7, 2024** to reflect up-to-date information. See the [new Change Log entry](/developers/docs/change-log#updates-to-entitlement-migration-guide) for details on updates. + + + We are migrating our entitlement system to a new behavior where entitlements will not end until explicitly canceled, representing a breaking change for subscription management. We are introducing a [Subscription API](/developers/docs/resources/subscription) and [Subscription Events](/developers/docs/events/gateway-events#subscriptions) to allow handling subscription-related events. + + + This change will be rolled out to all existing applications that have entitlements for user and guild subscription SKUs, starting on October 1, 2024. + + + #### Entitlement Migration Details + + - `ENTITLEMENT_CREATE` events will now be triggered with a null `ends_at` value for all ongoing subscriptions, indicating an indefinite entitlement. + - `ENTITLEMENT_UPDATE` events will occur only when a subscription ends, with the `ends_at` value indicating the end date. + - Discord-managed Subscription entitlements will have an `type` value of `PURCHASE` (type `1`) instead of `APPLICATION_SUBSCRIPTION` (type `8`). + + ### Migration Plan & Guide: + + As of **October 1, 2024**, all existing entitlements that grant access to user-subscription and guild-subscription SKUs will automatically transfer to the new system on their renewal date. This means we will have a month-long migration window to allow all of your entitlements to migrate to the new system upon renewal. + + Developers are advised to update their systems to handle the new `ENTITLEMENT_CREATE` and `ENTITLEMENT_UPDATE` events according to the following migration guide before the rollout date to avoid service disruptions. + + ### Introducing a New Subscription API + + With the new entitlement behavior, entitlements for subscription SKUs will no longer emit events at the start of a new subscription billing period. Instead, subscription lifecycle management can be handled through the new [Subscription API](/developers/docs/monetization/implementing-app-subscriptions#using-the-subscription-api). + Developers should refer to the [Subscription resource](/developers/docs/resources/subscription) for information on calling the Subscription API and responding to Subscription events. For in-depth implementation details, see our [Implementing App Subscriptions](/developers/docs/monetization/implementing-app-subscriptions#using-the-subscription-api) guide. You can start using this API now. + + ### Monetization Documentation Updates + + As part of these changes, we've updated the documentation for Premium Apps. + + - Created a new [Enabling Monetization](/developers/docs/monetization/enabling-monetization) page to cover setting up your team, managing payouts, and enabling monetization for your apps + - Created a new [Managing SKUs](/developers/docs/monetization/managing-skus#creating-a-sku) page to document how to create, update, publish, and promote your SKUs + - Moved and added [Entitlement](/developers/docs/resources/entitlement), [SKU](/developers/docs/resources/sku) and [Subscription](/developers/docs/resources/subscription) resources to the **Resources** section + - Updated guides for [Implementing App Subscriptions](/developers/docs/monetization/implementing-app-subscriptions) and [Implementing One-Time Purchases](/developers/docs/monetization/implementing-one-time-purchases) + + ### Subscription Entitlement Migration Guide + + Starting on **October 1, 2024**, we will be migrating our existing entitlement system to a new behavior where **entitlements do not expire until explicitly canceled**. This migration guide outlines the changes and impacts of this migration on developers and guides how to manage these changes effectively. + + + With this update, entitlements for subscription SKUs will no longer emit events when a new subscription billing period begins. If you need to know when a subscription has been renewed, use the new [Subscription API](/developers/docs/resources/subscription) and related [Subscription Gateway Events](/developers/docs/events/gateway-events#subscriptions). + + + ### Current System + + Currently, entitlements for Subscription SKUs purchased through Discord have: + + - An `ends_at` date that corresponds to the subscription interval. This date is updated at each billing cycle. + - A entitlement `type` value of `APPLICATION_SUBSCRIPTION` (type `8`). + - An `ENTITLEMENT_UPDATE` event is triggered at the start of each new subscription period. + + ### New System + + Post-migration, entitlements for Subscription SKUs purchased through Discord will: + + - No longer have an end date (`ends_at` will be `null`) until the user decides to cancel the subscription. + - Now have an entitlement `type` value of `PURCHASE` (type `1`). + - No `ENTITLEMENT_UPDATE` events will be triggered until the subscription is canceled. + + ### Migration Timeline + + - **Migration Start Date:** October 1, 2024 + - **Migration End Date:** November 1, 2024 + + ### Migration Impacts + + ### 1) Existing Entitlements Scheduled to Renew + + - **During Migration Window:** + - These will automatically transfer to the new system. + - A new `ENTITLEMENT_CREATE` event will be triggered to indicate the migration. This does not indicate a net new entitlement. + - No further events will be generated until the entitlement ends, which will then trigger an `ENTITLEMENT_UPDATE` event. + - The `ends_at` value in the `ENTITLEMENT_UPDATE` event and in the Entitlement API will indicate the timestamp when the entitlement ends. + + ### 2) Existing Entitlements Set to End + + - **During Migration Window:** + - These entitlements will naturally expire and not renew under the new system. + - No new entitlement events will be generated for these cases. + + ### Developer Actions + + - **Pre-Migration:** + - Review and understand the new entitlement event structure. + - Adjust your system to handle `ends_at` being null, which now indicates an indefinite entitlement. + - Adjust your system not to expect type `APPLICATION_SUBSCRIPTION` (type `8`) for Discord-managed subscription entitlements. + - **Post-Migration:** + - Monitor for `ENTITLEMENT_CREATE`, `ENTITLEMENT_UPDATE`, `SUBSCRIPTION_CREATE`, and `SUBSCRIPTION_UPDATE` events. + - Update any references to an entitlement `ends_at` timestamps, which now indicate the ending of an entitlement. If you need to know when a subscription's period ends, use the [Subscription API](/developers/docs/resources/subscription) and related [Subscription Gateway Events](/developers/docs/events/gateway-events#subscriptions). + + + + [Activities](/developers/docs/activities/overview) can now be launched as a response to interactions using the `LAUNCH_ACTIVITY` (type `12`) [interaction callback type](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type). `LAUNCH_ACTIVITY` can be used in response to `APPLICATION_COMMAND`, `MESSAGE_COMPONENT`, and `MODAL_SUBMIT` [interaction types](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type). + + + + Apps with [Activities](/developers/docs/activities/overview) enabled can now create [Entry Point commands](/developers/docs/interactions/application-commands#entry-point-commands) using the `PRIMARY_ENTRY_POINT` (type `4`) [command type](/developers/docs/interactions/application-commands#application-command-object-application-command-types). Apps are limited to one globally-scoped Entry Point command, which appears in the App Launcher. + + When creating or updating an Entry Point command, an [Entry Point handler](/developers/docs/interactions/application-commands#application-command-object-entry-point-command-handler-types) can be defined using the [`handler` field](/developers/docs/interactions/application-commands#application-command-object-application-command-structure). The `handler` field determines whether your app wants to manually handle responding to the interaction: + - If the value is `DISCORD_LAUNCH_ACTIVITY` (`2`), Discord will automatically handle the interaction and send a follow-up message to the channel where the Entry Point command was invoked from. + - If the value is `APP_HANDLER` (`1`), your app will receive an interaction token and will be responsible for responding to the interaction. In this case, you can launch your Activity using the `LAUNCH_ACTIVITY` (type `12`) [interaction callback](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type). + + More details about Entry Point commands can be found in the [Application Commands documentation](/developers/docs/interactions/application-commands#entry-point-commands) and in the [Activity development guide](/developers/docs/activities/development-guides/user-actions#setting-up-an-entry-point-command). + + ### Default Entry Point Commands + + Starting today, when you enable Activities in your [app's settings](http://discord.com/developers/applications), a [default Entry Point command](/developers/docs/interactions/application-commands#default-entry-point-command) called "Launch" will automatically be created for your app. This can be customized or deleted like other commands if you want to update the name or handler type. + + + + Added documentation for voice [encryption modes](/developers/docs/topics/voice-connections#transport-encryption-modes) `aead_aes256_gcm_rtpsize` and `aead_xchacha20_poly1305_rtpsize` while announcing the deprecation of all `xsalsa20_poly1305*` variants and `aead_aes256_gcm`. Deprecated encryption modes will be discontinued as of November 18th, 2024. + + + Deprecated encryption modes will be discontinued as of November 18th, 2024. + + + + + We are officially deprecating some very old voice gateway versions (> 7 years ago). + + * The voice gateway now supports a resume which re-sends lost messages. Use voice gateway version 8 and refer to [Buffered Resume](/developers/docs/topics/voice-connections#buffered-resume). + * We have removed the default option for voice gateway version. Once this is deprecated, you must pass a voice gateway version. + + + You will be required to pass a voice gateway version and deprecated voice gateway versions will be discontinued as of November 18th, 2024. See [Voice Gateway Versioning](/developers/docs/topics/voice-connections#voice-gateway-versioning) for further details. + + + + + Need to get just one role, not the whole role list? Use the new [Get Guild Role](/developers/docs/resources/guild#get-guild-role) endpoint to fetch a single role by ID. + + + + We've added an approximate user install count to the [Application object](/developers/docs/resources/application#application-object) for user-installable apps. You can also view an app's install counts in the developer portal. + + + + Voice states can now be accessed over the HTTP API! Apps can use the new [Get Current User Voice State](/developers/docs/resources/voice#get-current-user-voice-state) and [Get User Voice State](/developers/docs/resources/voice#get-user-voice-state) endpoints to fetch a user's voice state without a Gateway connection. + + + + The [`SET_ACTIVITY` RPC command](/developers/docs/topics/rpc#setactivity) has been updated to support 3 additional [activity types](/developers/docs/events/gateway-events#activity-object-activity-types): Listening (`2`), Watching (`3`), and Competing (`5`). Previously, it only accepted Playing (`0`). + + + The [Game SDK](/developers/docs/developer-tools/game-sdk#activities) has not been updated to support setting [`ActivityType`](/developers/docs/developer-tools/game-sdk#activitytype-enum), and is still limited to read-only (to handle events that you receive from Discord). + + + + + You can now upload emojis for your application in your [app's settings](https://discord.com/developers/applications) and use them as custom emojis anywhere on Discord. + + * Up to 2000 emojis per app + * Support for user-installable apps + * Can be [managed via the API](/developers/docs/resources/emoji#create-application-emoji) with a bot token + + + + + This change is outdated. We have since updated the Activities Proxy CSP and the use of `/.proxy/` is no longer required. For the latest information, please refer to [this changelog](/developers/docs/change-log#remove-proxy-from-discord-activity-proxy-path). + + + This change will be rolled out to all existing applications on **August 28, 2024**. + + We will be updating our Content Security Policy (CSP) for the Activities Domain (`https://.discordsays.com`). This represents a **breaking change** for **all Activities**, and as such we have a migration plan in order. + + our CSP will be updated as follows: + + * all requests must be made through `https://.discordsays.com/.proxy/`, and requests to other paths on the `discordsays.com` domain will be blocked. + * requests to `https://discord.com/api/` will be permitted, but other paths on the `discord.com` domain will be blocked. + * Only allowed paths on `cdn.discordapp.com` and `media.discordapp.net` will be permitted such as `/attachments/`, `/icons/`, and `/avatars/`. + * nested child iframes must also mount paths prepended by `/.proxy/` + + As of [embedded-app-sdk v1.4.0](https://github.com/discord/embedded-app-sdk/releases/tag/v1.4.0) we have updated `patchUrlMappings` to automatically route requests through `/.proxy/`, so updating your SDK version calling `patchUrlMappings` is a good first step. If you are unfamiliar with `patchUrlMappings`, please consult the [documentation](/developers/docs/activities/development-guides/networking#using-external-resources). + + All Application IDs created after `07/17/2024 12:00:00` UTC (applicationID greater than `1263102905548800000`) will also automatically have the new CSP applied. Testing your production code on a new application created after this date is a suggested way for developers to test compliance with this new CSP. + + + + Apps can now access guild member profile banners via the API and Gateway! The [guild member object](/developers/docs/resources/guild#guild-member-object) now includes a `banner` field which can be used to create the [guild member banner URL](/developers/docs/reference#image-formatting). + + + + We are slowly rolling out the message forwarding feature to users. This feature allows callers to create a message using `message_reference.type = FORWARD` and have the API generate a `message_snapshot` for the sent message. The feature has [some limitations](/developers/docs/resources/message#message-reference-types) and the snapshot is a minimal version of a standard `MessageObject`, but does capture the core parts of a message. + + The resulting message will look something like: + + ```json + { + "id": "1255957733279273083", + "message_reference": { + "type": 1, // Forward + ... + } + "message_snapshots": [ + { + "message": { + "content": "original message", + "embeds": [...], + "attachments": [...], + ... + } + } + ], + ... + } + ``` + + We have applied stricter rate limits for this feature based on the following: + + * number of forwards sent by the user + * total attachment size + + ###### API Updates since preview + + This was [previously announced](https://discord.com/channels/613425648685547541/697138785317814292/1233463756160503859) but note that the final API has a few changes since the API was first previewed: + + * [`message snapshot`](/developers/docs/resources/message#message-snapshot-object) objects don't include a `guild` field anymore since the `message_reference` already provides that information + * forwarded messages have a distinctive `message_reference` type of `FORWARD` now + + + + [`GET /users/@me/guilds`](/developers/docs/resources/user#get-current-user-guilds) now includes each guild's `banner` field! This enables apps using OAuth2 with the `guilds` scope to display guild banners. + + + + Back in March, we announced [the beta for user-installed apps](/developers/docs/change-log#userinstallable-apps-preview). After listening and making updates based on feedback from developers and modmins, we're excited to announce that user-installed apps are now considered generally available and can be used in all servers (regardless of size). + + With this update, there are a few API and behavioral updates for user-installed apps. + + ###### API Updates + + * `user_id` has been removed from the `interaction_metadata` field on messages. Instead, you can use the `id` field in the nested `user` object. See the [Message Interaction Metadata Object](/developers/docs/resources/message#message-interaction-metadata-object) for details. + * User-installed apps are now limited to creating a maximum of 5 [follow-ups](/developers/docs/interactions/receiving-and-responding#followup-messages) when responding to interactions. This only affects the [Create Followup Message endpoint](/developers/docs/interactions/receiving-and-responding#create-followup-message), and apps installed to the server are unaffected. + * On [Interactions](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure), the value of `authorizing_integration_owners` is now correctly serialized as a string. Previously, the `"0"` value was incorrectly serialized as a number. + * `app_permissions` on [Interactions](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) now correctly represents the permissions for user-installed apps. Previously, the value was incorrect for user-installed apps. + * Updating a message can result in a `400` response if the content of the message was blocked by AutoMod, which may be particularly important for [deferred messages](/developers/docs/interactions/receiving-and-responding#responding-to-an-interaction). + * Interaction responses are no longer forced to be ephemeral for servers with over 25 members. + + ###### New `Use External Apps` Permission + + A new [`USE_EXTERNAL_APPS` (`1 << 50`) permission](/developers/docs/topics/permissions#permissions-bitwise-permission-flags) was added, and is enabled for servers by default. The new permission lets modmins control whether user-installed apps can post public replies in a server. If `Use External Apps` is disabled and your app is *not* installed to the server, your app’s responses will be ephemeral for the end user. + + Read more in the [Moderating Apps on Discord Help Center article](https://support.discord.com/hc/en-us/articles/23957313048343-Moderating-Apps-on-Discord#h_01HZQQQEADYVN2CM4AX4EZGKHM). + + ###### Updated Defaults for New Apps + + * Newly-created apps now default to having both "User Install" *and* "Guild Install" [installation contexts](/developers/docs/resources/application#installation-context) enabled. This can be updated in the **Installation** tab in an [app's settings](https://discord.com/developers/applications). + * Newly-created apps now default to using the "Discord Provided Link" [install link](/developers/docs/resources/application#install-links). This can be updated in the **Installation** tab in an [app's settings](https://discord.com/developers/applications). + * If Discord Provided Link is selected as the install link type, `application.commands` scope is added to both installation contexts. + + + + **New Premium Button Style** + + Introduces a new `premium` [button style](/developers/docs/components/reference#button-button-styles) to be used with a `sku_id` which points to an active [SKU](/developers/docs/resources/sku#sku-object). This allows developers to customize their premium experience by returning specific subscription or one-time purchase products. + + Learn more about using [button components with interactions](/developers/docs/components/reference#button). + + + This change deprecates Interaction Response Type 10 + + + The `PREMIUM_REQUIRED (10)` interaction response type is now deprecated in favor of using custom premium buttons. This will continue to function but may be eventually unsupported. It is recommended to migrate your bots to use the more flexible [premium button component](/developers/docs/components/reference#button-button-styles). + + Learn more about [gating features with premium interactions](/developers/docs/monetization/implementing-app-subscriptions#prompting-users-to-subscribe). + + **Deep Linking URL Schemes for SKUs and Store** + + Introduces two new url schemes for linking directly to the Application Directory. When these links are used in chat, they are rendered as rich embeds that users can interact with to launch an app's store or open a SKU detail modal. + + * New [Store URL Scheme](/developers/docs/monetization/managing-skus#linking-to-your-store): `https://discord.com/application-directory/:appID/store` + * New [SKU URL Scheme](/developers/docs/monetization/managing-skus#linking-to-a-specific-sku): `https://discord.com/application-directory/:appID/store/:skuID` + + + + * Add Auto Moderation `MEMBER_PROFILE` rule [trigger\_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types). This rule type will check if a member's profile contains disallowed keywords. + * Add Auto Moderation `BLOCK_MEMBER_INTERACTION` [action type](/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types) currently available for the `MEMBER_PROFILE` rule [trigger\_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types). This action will "quarantine" the member to some extent and prevent them from performing most interactions within a specific server. + + + + Two new features are now available for Premium Apps: One-Time Purchases and Stores. + + **One-Time Purchases** + + * **Durable Items**: A one-time purchase that is permanent and is not subject to either renewal or consumption, such as lifetime access to an app's premium features. + * **Consumable Items**: A one-time, non-renewable purchase that provides access, such as a temporary power-up or boost in a game. + + Learn more about [Implementing One-Time Purchases](/developers/docs/monetization/implementing-one-time-purchases). + + **A Store for Your Premium App** + + We have also introduced a Store for your Premium App to showcase your app subscriptions and one-time purchase items. You can now create a unique Store page within the developer portal and add your published subscription SKUs or one-time purchase SKUs to your store view, allowing your users to buy these items from your App Directory or Bot User Profile. + + To explore these features, eligibility details, and how to enable monetization for your app, check out the [Monetization Overview](/developers/docs/monetization/overview). + + **API Documentation Updates** + + The following were added to our public Monetization documentation with this update: + + * New [SKU Object Types](/developers/docs/resources/sku#sku-object-sku-types) + * New [Entitlement Object Types](/developers/docs/resources/entitlement#entitlement-object-entitlement-types) + * [Consume an Entitlement](/developers/docs/resources/entitlement#consume-an-entitlement) API endpoint + * `consumed` field on the [Entitlement](/developers/docs/resources/entitlement) resource + + + + Update permissions necessary to modify the `flags` field when calling the [Modify Guild Member](/developers/docs/resources/guild#modify-guild-member) endpoint. + + + + For apps with [Monetization](/developers/docs/monetization/overview) enabled, we have released the ability to export your SKU analytics to CSV. These exports allow you to use your preferred data tools to report on your premium offerings. + + You can find the export at the bottom of the `Monetization → Analytics` tab of your app to export data points such as `sales_count`, `sales_amount`, `sales_currencies`, `cancellation_count`, `refund_amount`, and `refund_count`, aggregated by each of your offerings for the selected month. + + + + Discord Developers can now build Activities! + + Activities are interactive, multiplayer experiences that run in an iframe in Discord. In order to make the communication between your experience and Discord, we've introduced the Embedded App SDK to assist in communicating between your app and the Discord client. + + * New [Discord Activities](/developers/docs/activities/overview) developer docs with a tutorial, code samples, development guides, and design principles. + * The Embedded App SDK is now available via [npm](https://npmjs.com/package/@discord/embedded-app-sdk) and [GitHub](http://github.com/discord/embedded-app-sdk). + * The [Embedded App SDK Reference](/developers/docs/developer-tools/embedded-app-sdk) is now available. + + To learn more about how to get started building your own Activity, check out the [Activities Overview](/developers/docs/activities/overview). + + + + Apps can now be installed to users—making them easier to install, discover, and access across Discord. User-installed apps can be used across all of a user's servers, within their (G)DMs, and in DMs with the app's bot user. + + When creating or updating your app, you can choose which installation types your app supports on the **Installation** page in your [app's settings](https://discord.com/developers/applications). To quickly get started, you can follow the new [Developing a User-Installable App tutorial](/developers/docs/tutorials/developing-a-user-installable-app) or read details about the new changes below. + + This change introduces new concepts and fields across the API that apps will now encounter. + + ###### API Changes + + **Concepts:** + + * [Installation context](/developers/docs/resources/application#installation-context) defines how an app was installed: to a user, a guild (server), or both. Currently, apps will default to only support the guild installation context, but the default may change in the future. + * Commands can also support one or both installation contexts, with the default being the same as the app's supported installation context(s) at the time of command creation. + * [Interaction context](/developers/docs/interactions/application-commands#interaction-contexts) defines where a command can be used in Discord—within guilds, DM with your app's bot user, and/or within group DMs and DMs other than with your app's bot user. + * The installation flow for apps have been updated so users can select whether they want to install an app to their account or to a server. + + **API Fields:** + + * New `integration_types_config` field for [Applications](/developers/docs/resources/application#application-object) include the default scopes and permissions for app's supported installation contexts + * New `integration_types` and `contexts` fields for [Commands](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) are the supported [installation](/developers/docs/interactions/application-commands#installation-context) and [interaction](/developers/docs/interactions/application-commands#interaction-contexts) contexts (respectively) for the command. Read [command contexts](/developers/docs/interactions/application-commands#contexts) documentation for details. + * New `context` field for [Interactions](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) indicates the [interaction context](/developers/docs/interactions/application-commands#interaction-contexts) where an interaction was triggered from. + * New `authorizing_integration_owners` field for [Interactions](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) includes a mapping of installation contexts that the interaction was authorized for, to related snowflakes for that context. Read [Authorizing Integration Owners Object](/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) for details. + * `app_permissions` is now always serialized for interactions to indicate what [permissions](/developers/docs/topics/permissions#permissions-bitwise-permission-flags) your app has access to in the context its' responding. For (G)DMs with other users, it will include the `ATTACH_FILES | EMBED_LINKS | MENTION_EVERYONE`, and for DMs with the app's bot user it will also contain `USE_EXTERNAL_EMOJIS` for the bot’s DM + * New `interaction_metadata` on [Messages](/developers/docs/resources/message#message-object) that are created as part of an interaction response (either a response or follow-up). See [Message Interaction Metadata Object](/developers/docs/resources/message#message-interaction-metadata-object) for details. + * `dm_permission` field for [Commands](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) is deprecated. Apps should use `contexts` instead. + * `interaction` field for [Messages](/developers/docs/resources/message#message-object) is deprecated. Apps should use `interaction_metadata` instead. + + ###### Limitations and Known Issues + + * During the preview, interaction responses for the user installation context will be forced to be ephemeral in servers with over 25 members. Forced ephemerality is enforced at the client-level, so your app does not need to manually pay attention to server size, and will not receive errors via the API. + * All [follow-up messages](/developers/docs/interactions/receiving-and-responding#followup-messages) are currently forced to be ephemeral in DMs + * Follow-up messages have a bug where they will not correctly respect user permissions + + + + The [Get Guild Prune Count](/developers/docs/resources/guild#get-guild-prune-count) and [Begin Guild Prune](/developers/docs/resources/guild#begin-guild-prune) + endpoints now require the `MANAGE_GUILD` permission alongside the existing `KICK_MEMBERS` requirement. + + + + The [Create message](/developers/docs/resources/message#create-message) endpoint now supports an `enforce_nonce` parameter. When set to true, the message will be deduped for the same sender within a few minutes. If a message was created with the same nonce, no new message will be created and the previous message will be returned instead. This behavior will become the default for this endpoint in a future API version. + + + + [Embed objects](/developers/docs/resources/message#embed-object) are now limited more explicitly to 25 [embed fields](/developers/docs/resources/message#embed-object-embed-field-structure). If you pass more than 25 fields within the an embed's `fields` property, an error will be returned. + + Previously, only the first 25 embed fields would be displayed within the embed but no error was returned. + + + + + The existing behavior for `MANAGE_GUILD_EXPRESSIONS` and `MANAGE_EVENTS` will **not be changing**. These permissions will continue to allow your bot users to create, update and delete expressions/events. No action will be needed if you plan to continue using these permissions. + + + To support added controls for expressions and events, new [permissions](/developers/docs/topics/permissions) were added for users and roles in July 2023: + + * `CREATE_GUILD_EXPRESSIONS`: `1 << 43` + * `CREATE_EVENTS`: `1 << 44` + + These allow for creating new expressions and events, as well as editing and deleting those created by the current user. + + + These were rolled out in July 2023 to users and roles and have been added to our developer documentation but **are not yet available to app developers**. We will share an update here when these new permissions are available in your apps. + + + + + #### What’s Happening? + + As outlined in [a blog post earlier this year](https://discord.com/blog/encryption-for-voice-and-video-on-discord), we are experimenting with end-to-end encryption (e2ee) for voice and video channels. + + End-to-end encryption is designed to only allow the participants in a call to decipher its contents. One of the protocols we’re experimenting with is called Messaging Layer Security, which we believe would allow us to deliver end-to-end encryption at scale. Intermediaries, including platforms like Discord, are unable to access the content of communications encrypted with end-to-end encryption. + + #### How do I prepare for the changes? + + During this testing phase, there is nothing developers need to do to support end-to-end encryption. Voice channels will automatically downgrade to documented, non-e2ee protocols when a bot user joins the channel. This is transparent to the connecting client but may result in a slight delay between establishing a connection and receiving audio. + + #### What is planned for the future? + + We will be continuing our testing and will share updates along with developer documentation and sample code once it is available. + + Once this information is published, we will provide developers with a substantial timeframe to implement end-to-end encryption when interacting with voice and video. + + + + Following feedback on Premium App Subscriptions, we've made it easier for developers to test their app subscriptions. The goal is to provide you with flexibility during testing and prevent you from having to use live payment methods. + + * Team members will automatically receive a 100% discount on a subscription for your app, allowing you to test the end-to-end payment flow + * Developers can create and delete [test entitlements](/developers/docs/resources/entitlement#create-test-entitlement) to toggle access to an application's premium features + + Read more about [Testing your App Subscriptions Implementation](/developers/docs/monetization/implementing-app-subscriptions#testing-your-app-subscription-implementation) for details. + + + + Behavior for message edit interaction response actions like [updating interaction responses](/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response) and [sending follow-up messages](/developers/docs/interactions/receiving-and-responding#followup-messages) have been updated to follow a bot user's permissions. + + Previously, some message edit interaction response actions would use the default permissions rather than a bot user's permissions. + + + + Starting today, eligible developers based in EU and UK can now monetize their verified apps with App Subscriptions. [App Subscriptions](/developers/docs/monetization/overview) let you to charge your users for premium functionality with a recurring, monthly subscription. + + + New features for Premium App Subscriptions are documented in the [App Subscriptions overview](/developers/docs/monetization/overview) and in [the changelog for the previous App Subscriptions release](/developers/docs/change-log#premium-app-subscriptions-available-in-the-us). + + + To learn more about eligibility details and how to enable monetization for your app, check out the [Monetization Overview](/developers/docs/monetization/overview). + + + + We have added a global rate limit for API requests made to `discordapp.com/*` and may further restrict requests in the future. + + To limit impact on your app, please make sure you are making calls to `discord.com/*`. + + This does **not** apply for `cdn.discordapp.com`. + + Refer to the [API Reference](/developers/docs/reference) for more info on which url(s) to use when building on the REST API + + * [February 14, 2022 Change Log](/developers/docs/change-log#api-v10): Requests to v10 and higher will no longer be supported on `discordapp.com` (this does not affect `cdn.discordapp.com`) + * [May 4, 2020 #api-announcements](https://discord.com/channels/613425648685547541/697138785317814292/706944540971630662) + + + + Starting today, eligible US-based developers can monetize their verified apps with App Subscriptions. [App Subscriptions](/developers/docs/monetization/overview) let you to charge your users for premium functionality with a recurring, monthly subscription. + + * Manage subscription SKUs in the Developer Portal + * View monetization analytics in the Developer Portal + * Team owners can setup and manage payouts in Developer Portal + * New endpoints for working with [SKUs](/developers/docs/resources/sku) and [Entitlements](/developers/docs/resources/entitlement): + * [List SKUs](/developers/docs/resources/sku#list-skus) `GET /applications//skus` + * [List Entitlements](/developers/docs/resources/entitlement#list-entitlements) `GET /applications//entitlements` + * [Create Test Entitlement](/developers/docs/resources/entitlement#create-test-entitlement) `POST /applications//entitlements` + * [Delete Test Entitlement](/developers/docs/resources/entitlement#delete-test-entitlement) `DELETE /applications//entitlements/` + * [Gateway Events](/developers/docs/events/gateway-events#entitlements) for working with entitlements: `ENTITLEMENT_CREATE`, `ENTITLEMENT_UPDATE`, `ENTITLEMENT_DELETE` + * New [`PREMIUM_REQUIRED (10)` interaction response type](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) is available to prompt users to upgrade + * New `entitlements` field, which is an array of [entitlement](/developers/docs/resources/entitlement) objects, available in interaction data payloads when [receiving and responding to interactions](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) + + To learn more about eligibility details and how to enable monetization for your app, check out the [Monetization Overview](/developers/docs/monetization/overview). + + + + A new `default_values` field was added for user (`5`), role (`6`), mentionable (`7`), and channel (`8`) [select menu components](/developers/docs/components/reference). `default_values` is a list of [default value objects](/developers/docs/components/reference#user-select-select-default-value-structure), which each include an `id` (the snowflake value for the resource), as well as a corresponding `type` (either `"user"`, `"role"`, or `"channel"`). + + + + You can now select roles other than admin when inviting users or configuring members of a team. There are four [role types](/developers/docs/topics/teams#team-member-roles-team-member-role-types) that a team member can be assigned: owner, admin, developer, or read-only. The team member object now has an additional [`role` field](/developers/docs/topics/teams#data-models-team-member-object), which is a string representing the member's current role. + + Details about team member roles are in the updated [Teams documentation](/developers/docs/topics/teams#team-member-roles). + + + + We've released a new [Embed Debugger tool](https://discord.com/developers/embeds) that shows you how a URL's metadata will be parsed and rendered as a link embed within the Discord client. Use it to preview your site's embed, or debug why your site's link embed isn't working as expected. + + + + The `state` field in [activity objects](/developers/docs/events/gateway-events#activity-object) can now be set when [updating presence](/developers/docs/events/gateway-events#update-presence) for a bot user. The value of `state` will appear as a custom status for the bot user when an [activity's `type`](/developers/docs/events/gateway-events#activity-object-activity-types) is set to `4`, or as additional data under an activity's name for other activity types. + + + + We're introducing an [OpenAPI 3.1 spec](https://github.com/discord/discord-api-spec) in public preview to make it easier and more reliable to develop with the HTTP API. While our current developer documentation requires manual reviews and updates, the OpenAPI spec is generated from the source code which means it better reflects the nooks, crannies, and nuances of the Discord API. + + + The public preview of the OpenAPI spec is subject to breaking changes without advance notice, and should not be used within production environments. If you see something that looks incorrect or can be improved, you can [open an issue](https://github.com/discord/discord-api-spec/issues). + + + The public spec can be found in the new [`discord-api-spec` repository on GitHub](https://github.com/discord/discord-api-spec). + + + + * Add the [`GUILD_MEDIA` (16) channel type](/developers/docs/resources/channel#channel-object-channel-types). `GUILD_MEDIA` channels only support threads, similar to `GUILD_FORUM` channels. + + Read the [media channel topic](/developers/docs/topics/threads#media-channels) for more information on the relevant APIs and technical details, or the [media channel Help Center Article](https://creator-support.discord.com/hc/en-us/articles/14346342766743) for more about the feature. + + + + * Add Auto Moderation `mention_raid_protection_enabled` [trigger\_metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) field for the `MENTION_SPAM` [trigger\_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types). If this field and its parent `MENTION_SPAM` rule are enabled, Auto Moderation provides baseline detection against sudden spikes in mention activity that are normally indicative of mention raids. + * Add `safety_alerts_channel_id` [guild](/developers/docs/resources/guild#guild-object) field and [`RAID_ALERTS_DISABLED` guild feature flag](/developers/docs/resources/guild#guild-object-guild-features) which are associated with join raid protection + + + + + Bot users will stay on the legacy username system for now. More details can be found on the [Developer Help Center article](https://dis.gd/app-usernames). + + + Discord’s username system is changing. Discriminators are being removed and new, unique usernames and display names are being introduced. You can read more details about how changes to the username system affects non-bot users in the [general Help Center article](https://dis.gd/usernames). To learn how it impacts bot users specifically, you can read the [Developer Help Center article](https://dis.gd/app-usernames). + + This changelog focuses only on the technical changes to be aware of to update your app's code. + + ### Identifying migrated users + + The new username system will rollout to users over time rather than all at once. The value of a single zero (`"0"`) in the [`discriminator` field](/developers/docs/resources/user#user-object-user-structure) on a user will indicate that the user has been migrated to the new username system. Note that the discriminator for migrated users will *not* be 4-digits like a standard discriminator (it is `"0"`, not `"0000"`). The value of the `username` field will become the migrated user's unique username. + + After migration of all users is complete, the `discriminator` field may be removed. + + #### Example migrated user + + ```json + { + "id": "80351110224678912", + "username": "nelly", + "discriminator": "0", + "global_name": "Nelly", + "avatar": "8342729096ea3675442027381ff50dfe", + "verified": true, + "email": "nelly@discord.com", + "flags": 64, + "banner": "06c16474723fe537c283b8efa61a30c8", + "accent_color": 16711680, + "premium_type": 1, + "public_flags": 64 + } + ``` + + ### Display names + + As part of the new username system, standard Discord users can define a non-unique display name. This value will be a new `global_name` field with a max length of 32 characters. If the user has not set a display name, `global_name` will be null. + + ### Default avatars + + For users with migrated accounts, default avatar URLs will be based on the user ID instead of the discriminator. The URL can now be calculated using `(user_id >> 22) % 6`. Users on the legacy username system will continue using `discriminator % 5`. + + + + Starting today, [bot users](/developers/docs/topics/oauth2#bot-vs-user-accounts) will be added to all newly-created apps. Settings and configuration options for bot users remain the same, and can still be accessed on the **Bot** page within your [app's settings](https://discord.com/developers/applications). + + If your app doesn't need or want a bot user associated with it, you can refrain from adding the [`bot` scope](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) when installing your app. + + + + Interactions now contain a `channel` field which is a partial channel object and guaranteed to contain `id` and `type`. We recommend that you begin using this channel field to identify the source channel of the interaction, and may deprecate the existing `channel_id` field in the future. See the [interaction documentation](/developers/docs/interactions/receiving-and-responding#interaction-object) for more details. + + + + Add new `custom_message` [action metadata](/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata) for the `BLOCK_MESSAGE` [action type](/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types)). You can now specify a custom string for every Auto Moderation rule that will be shown to members whenever the rule blocks their message. This can be used as an additional explanation for why a message was blocked and as a chance to help members understand your server's rules and guidelines. + + + + ### Upcoming Changes + + Currently, threads in Discord (including forum posts) can either be archived or both locked and archived. Starting on **March 6, 2023**, threads will be able to be locked *without* being archived, which will slightly change the meaning of the [`locked` field](/developers/docs/resources/channel#thread-metadata-object-thread-metadata-structure). + + `locked` currently indicates that a thread cannot be reopened by a user without the [`MANAGE_THREADS` (`1 << 34`) permission](/developers/docs/topics/permissions#permissions-bitwise-permission-flags), but it doesn't restrict user activity within active (meaning non-archived) threads. After this change, users (including bot users) without the `MANAGE_THREADS` permission will be more restricted in locked threads. Users won't be able to create or update messages in locked threads, or update properties like its title or tags. Additionally, some user activity like deleting messages and adding or removing reactions will *only* be allowed in locked threads if that thread is also active (or un-archived). + + If a user or bot user has the `MANAGE_THREADS` permission, they will still be able to make changes to the thread and messages. The upcoming change does not affect the meaning of the [`archived` field](/developers/docs/resources/channel#thread-metadata-object-thread-metadata-structure) or the behavior of a thread that is both locked and archived. + + ### How do I prepare for this change? + + If your app is interacting with threads (including forum posts), it should check the state of the `locked` and/or `archived` field for the thread to understand which actions it can or cannot perform. It should also be prepared to handle any errors that it may receive when a thread is locked. + + + + * Increase maximum number of rules with `KEYWORD` [trigger\_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) per guild from 5 to 6 + * Increase maximum length for each keyword in the `keyword_filter` and `allow_list` [trigger\_metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) fields from 30 to 60. + + + + At long last, a new [`GUILD_AUDIT_LOG_ENTRY_CREATE`](/developers/docs/events/gateway-events#guild-audit-log-entry-create) event has been added to the gateway, allowing your application to react to moderation actions in guilds. The `VIEW_AUDIT_LOG` permission is required in order to receive these events, and the [`GUILD_MODERATION` intent](/developers/docs/events/gateway#gateway-intents) needs to be set when connecting to the gateway. + + + + A new `member` field was added to the [thread member object](/developers/docs/resources/channel#thread-member-object). `member` is a [guild member object](/developers/docs/resources/guild#guild-member-object) that will be included within returned thread member objects when the new `with_member` field is set to `true` in the [List Thread Members](/developers/docs/resources/channel#list-thread-members) (`GET /channels//thread-members`) and [Get Thread Member](/developers/docs/resources/channel#get-thread-member) (`GET /channels//thread-members/`) endpoints. + + Setting `with_member` to `true` will also enable pagination for the [List Thread Members](/developers/docs/resources/channel#list-thread-members) endpoint. When the results are paginated, you can use the new `after` and `limit` fields to fetch additional thread members and limit the number of thread members returned. By default, `limit` is 100. + + #### Upcoming Changes + + Starting in API v11, [List Thread Members](/developers/docs/resources/channel#list-thread-members) (`GET /channels//thread-members`) will *always* return paginated results, regardless of whether `with_member` is passed or not. + + + + `default_forum_layout` is an optional field in the [channel object](/developers/docs/resources/channel) that indicates the default layout for posts in a [forum channel](/developers/docs/topics/threads#forums). A value of 1 (`LIST_VIEW`) indicates that posts will be displayed as a chronological list, and 2 (`GALLERY_VIEW`) indicates they will be displayed as a collection of tiles. If `default_forum_layout` hasn't been set, the value will be `0`. + + Setting `default_forum_layout` requires the `MANAGE_CHANNELS` permission. + + + + Introducing [linked roles](https://discord.com/blog/connected-accounts-functionality-boost-linked-roles) as well as the ability for all developers to set up their own linked roles with an application. This includes: + + * New [`role_connections_verification_url`](/developers/docs/resources/application#application-object) that can be set in the developer portal in order for the application to render as potential verification option for linked roles. + * [Application metadata](/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object) to specify more detailed linked role requirements. + * New endpoints to [retrieve](/developers/docs/resources/application-role-connection-metadata#get-application-role-connection-metadata-records) (`GET /applications//role-connections/metadata`) and [update](/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records) (`PUT /applications//role-connections/metadata`) application connection metadata. + * New [`role_connections.write`](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) OAuth2 scope required to authenticate the below requests. + * Endpoints to [retrieve](/developers/docs/resources/user#get-current-user-application-role-connection) (`GET /users/@me/applications/{application.id}/role-connection`) and [update](/developers/docs/resources/user#update-current-user-application-role-connection) (`PUT /users/@me/applications/{application.id}/role-connection`) a user's role connections, both of which return an [application role connection](/developers/docs/resources/user#application-role-connection-object) object. + + + For a quick rundown on how to get started using linked roles, refer to the [tutorial](/developers/docs/tutorials/configuring-app-metadata-for-linked-roles). + + + + + * Auto Moderation rules with [trigger\_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) `KEYWORD` now support an `allow_list` field in its [trigger\_metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata). Any message content that matches an `allow_list` keyword will be ignored by the Auto Moderation `KEYWORD` rule. Each `allow_list` keyword can be a multi-word phrase and can contain [wildcard symbols](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-matching-strategies). + * Increase maximum number of rules with `KEYWORD` [trigger\_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) per guild from 3 to 5 + * Increase maximum length for each regex pattern in the `regex_patterns` [trigger\_metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) field from 75 to 260. + + + + Based on feedback, we’re updating permissions for [application commands](/developers/docs/interactions/application-commands) to simplify permission management and to make command permissions more closely resemble other permissions systems in Discord. + + Server admins can begin to opt-in to the command permission changes outlined here on a per-server basis **starting on December 16, 2022**. However, changes will not be applied to all servers **until late January or early February**. + + + Current permissions behavior is documented in [the application commands documentation](/developers/docs/interactions/application-commands#permissions) and in [the changelog for the previous permissions update](/developers/docs/change-log#updated-command-permissions) + + + These changes are focused on how configured permissions are used by Discord clients, so most apps will be unaffected. However, if your app uses the [Update Permissions endpoint](/developers/docs/interactions/application-commands#edit-application-command-permissions) (`PUT /applications//guilds//commands//permissions`), you may need to make updates and should read these changes carefully. + + #### Types of command permission configurations + + + The following information isn’t changing, but it’s helpful context to understand the changes. + + + Discord’s clients determine whether a user can see or invoke a command based on three different permission configurations: + + * **Command-level permissions** are set up by an admin for a specific *command* in their server. These permissions affect only a specific command. + * **App-level permissions** are set up by an admin for a specific *app* in their server. These permissions affect all commands for an app. + * **`default_member_permissions`** are set up by an app when creating or updating a command. `default_member_permissions` apply to that command in *all* servers (unless an override exists). More information about `default_member_permissions` is [in the documentation](/developers/docs/interactions/application-commands#application-command-permissions-object-using-default-permissions). + + The concepts of these permission configurations are not changing. But then of course, the question becomes… + + ### What's changing? + + There are two changes around command permissions: + + 1. The logic used to apply permission configurations to a user in a given context within Discord clients + 2. New `APPLICATION_COMMAND_PERMISSIONS_V2` guild feature flag to indicate whether that guild is using the old permissions logic or the new (upcoming) logic. + + Let's go deeper into both of these. + + #### 1. How permission configurations are applied in Discord + + ##### Current behavior: + + Currently, these systems are **mutually-exclusive**, meaning that only one type of permission configuration is used to determine whether a user can invoke a command. + + With this current system, there is a clear hierarchy: command-level permission configurations take precedence (if present), then app-level permission configurations (if present), and finally `default_member_permissions` if neither are present. + + The implication of the current permissions system means that: + + * If any command-level permissions are configured, all app-level permissions and `default_member_permissions` are ignored for that command. + * If any app-level permissions are configured, `default_member_permissions` is ignored for *all* of that app’s commands. + + This system leads to unintentional permission escalations, and can force moderators to manually re-define their app-level configurations to make small tweaks on the command-level. + + ##### Upcoming behavior: + + The new system removes the mutual exclusion aspect, meaning that the different types of permission configurations work together rather than independently—specifically, more than one may be used to determine whether a user can invoke a command. + + **`default_member_permissions` continues to act as a “default” that a developer can set when creating or updating a command.** + + **App-level permission configurations now act as the "base" configuration.** + + App-level configurations define who is allowed to use the app and where. These will work *together* with `default_member_permissions`, meaning if a user is granted access via an app-level permission configuration, they will still be restricted to the `default_member_permissions` for each command (by default). No more accidentally granting `/ban` which requires `BAN_MEMBERS` to `@BotMemers` just because you gave them access to the app! + + **Command-level permission configurations now act as an “override” of the app-level.** + + Command-level configurations override what is present at the app-level *and* any restrictions set by `default_member_permissions`. This means that an admin can explicitly grant a user access to a specific command even if they are denied access on the app-level *or* if they don't have permissions that meet that command's `default_member_permissions`. + + If a command-level configuration does not exist for the given context, the system will fall back to looking at the app-level configuration. + + ##### Flowchart for command permissions logic + + Below is a simplified flowchart that illustrates how permissions will be applied by the Discord client after the new changes take effect. + + ![Flowchart with an overview of the new permissions configurations logic](/images/new-permissions-flowchart.svg) + + #### 2. `APPLICATION_COMMAND_PERMISSIONS_V2` Guild Feature + + We added a new [`APPLICATION_COMMAND_PERMISSIONS_V2` feature flag](/developers/docs/resources/guild#guild-object-guild-features) which indicates whether that server is using **the current permissions logic**. + + * If the flag *is* present, that server is using the old command permissions behavior. + * If the flag *is not* present, that server has migrated from the old command permissions behavior to the new behavior. + + ### Am I affected? + + Your app will only be affected if it uses the [`PUT /applications//guilds//commands//permissions`](/developers/docs/interactions/application-commands#edit-application-command-permissions) endpoint. This is a pretty restricted endpoint used to manage and update application command permissions on behalf of admins, meaning that it requires the `applications.commands.permissions.update` scope. + + **If your app doesn’t use this endpoint, there’s nothing you need to prepare for these changes.** + + If your app does use this endpoint, you should read the section on preparing for changes below. + + ### How do I prepare for the changes? + + To prepare for these changes, you should take two steps: + + **1. Use the `APPLICATION_COMMAND_PERMISSIONS_V2` flag** + + Use this flag to determine which permissions logic that server is using. While the transition from the old behavior to the new behavior is happening, you may need two code paths depending on if the flag is present or not. + + ```py + if 'APPLICATION_COMMAND_PERMISSIONS_V2' in guild.features: + # Use current behaviors when interacting with endpoint + else: + # Use new permissions behaviors when interacting with endpoint + ``` + + + If you don’t have access to guild features already through Gateway events, you can fetch that information using the [`GET /guilds/` endpoint](/developers/docs/resources/guild#get-guild). + + + **2. Modify the behavior based on your use case** + + After you know what permissions behavior the server is using, you should update how you handle that server specifically. + + To understand what changes you need to make, you should look at the assumptions users have when your app updates their server’s commands permissions. Do you have a web dashboard where admins update permissions? If so, analyze the logic of that dashboard and what your permission configurations are trying to do to map them to the new permissions behavior. Do you document what your app is doing in regards to certain command permissions you’re configuring on behalf of the admin? If so, map that documentation to the new behavior. + + If you are unsure, you can communicate with your admin users to ask if your new logic meets their expectations. + + #### What happens if I don’t update my app? + + If your app is affected and you don’t update it, permissions behavior that your app configures may not match what you or the users of your app expect. + + #### How long do I have to update my app? + + The new `APPLICATION_COMMAND_PERMISSIONS_V2` flag is already live, and you should start seeing it in guilds’ feature flags. + + The new permissions behavior will roll out **on December 16, 2022**. On this date, admins will begin to see a banner that allows them to *optionally* move their server to the new behavior. + + In **late January or early February**, all servers will be migrated to the new behavior. We'll post another changelog at this point, at which time you can remove any logic around the old permissions behavior. + + + + To help keep us focused on the features, improvements, and gaming-related experiences that Discord users love, we are deprecating the following pieces of the GameSDK **starting today**, and decommissioning them on **Tuesday, May 2, 2023**: + + * [Achievements](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Achievements.md) + * [Applications](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Applications.md) + * [Voice](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Discord_Voice.md) + * [Images](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Images.md) + * [Lobbies](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Lobbies.md) + * [Networking](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Networking.md) + * [Relationships](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Relationships.md) + * [Storage](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Storage.md) + * [Store](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Store.mdmd) + + This deprecation period will last until **Tuesday May 2, 2023**, after which these pieces will be decommissioned and no longer work. The other pieces of the GameSDK will continue to be supported. + + We know that Discord is an important place for people to find belonging, and that using your Discord identity in games is a crucial part of that sense of belonging. You’ll still be able to use the GameSDK to integrate Rich Presence, relationships, entitlements, basic user information, and the overlay. + + + + Auto Moderation rules with [trigger\_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) `KEYWORD` now support + a `regex_patterns` field in its [trigger\_metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types). + Regex patterns are a powerful way to describe many keywords all at once using one expression. Only Rust flavored regex is supported, which can be tested in online editors such as [Rustexp](https://rustexp.lpil.uk/). + + + + Ephemeral interaction responses and follow-ups can now be deleted with a valid interaction token using the following endpoints: + + * [`DELETE /webhooks///messages/@original`](/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response) + * [`DELETE /webhooks///messages/`](/developers/docs/interactions/receiving-and-responding#delete-followup-message) + + As a reminder, interaction tokens stay valid for up to 15 minutes after the interaction occurs. Details can be found in the [interaction documentation](/developers/docs/interactions/receiving-and-responding). + + + + Four new select menu [component types](/developers/docs/components/reference#component-object-component-types) have been added to make it easier to populate selects with common resources in Discord: + + * User select (type `5`) + * Role select (type `6`) + * Mentionable (user *and* role) select (type `7`) + * Channel select (type `8`) + + The new select menu components are defined similarly to the existing string select menu—with the exception of not including the `options` field and, within channel select menus, having the option to include a `channel_types` field. The [select menu interaction](/developers/docs/components/reference#string-select-string-select-interaction) apps receive also contain a [`resolved` field](/developers/docs/components/reference#string-select-select-menu-resolved-object) for the new components. + + More details can be found in the updated [select menu documentation](/developers/docs/components/reference#component-object-component-types). + + + + `default_sort_order` is an optional field in the [channel object](/developers/docs/resources/channel) that indicates how the threads in a [forum channel](/developers/docs/topics/threads#forums) will be sorted for users by default. Setting `default_sort_order` requires the `MANAGE_CHANNELS` permission. + + If `default_sort_order` hasn't been set, its value will be `null`. + + + + Two new [trigger types](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) were added to Auto Moderation: + + * `MENTION_SPAM` blocks messages that mention more than a set number of unique server members or roles. Apps can define the number (up to 50) using the `mention_total_limit` field in the [trigger metadata object](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) when creating or updating an Auto Moderation rule. + * `SPAM` blocks links and messages that are identified as spam. + + More information can be found in the [Auto Moderation documentation](/developers/docs/resources/auto-moderation). + + + + Forum channels ([`GUILD_FORUM` or `15`](/developers/docs/resources/channel#channel-object-channel-types)) have been released to all community servers. `GUILD_FORUM` channels are a new channel type that only supports threads, which display differently than in text (`GUILD_TEXT`) channels. + + Check out the [forums topic](/developers/docs/topics/threads#forums) for more information on the relevant APIs and technical details, and the [Forums FAQ](https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ#h_01G69FJQWTWN88HFEHK7Z6X79N) for more about the feature. + + + + As of today, [message content](/developers/docs/events/gateway#message-content-intent) is a privileged intent for all verified apps *and* apps eligible for verification. More details about why it's becoming a privileged intent and how to apply for it is in the [Help Center FAQ](https://support-dev.discord.com/hc/articles/4404772028055-Message-Content-Privileged-Intent-FAQ). + + Any app that does not have the message content intent configured in its app's settings within the Developer Portal will receive empty values in fields that expose message content across Discord's APIs (including the `content`, `embeds`, `attachments`, and `components` fields). These restrictions do not apply for messages that a bot or app sends, in DMs that it receives, or in messages in which it is mentioned. + + #### If your app is verified + + Verified apps and verification-eligible apps must be approved for the message content intent to receive message content. If your verified app isn’t approved, or doesn’t account for the new message content restrictions, it will break for users. + + ##### Temporary Message Content Intent + + Verified apps or apps that have submitted for verification can temporarily opt-in to a grace period which will allow your app to continue receiving message content until October 1. However, if you opt-in to the grace period, your app will be prevented from joining any additional servers until you opt-out. More details are in the [Help Center article](https://support-dev.discord.com/hc/en-us/articles/8561391080471). + + #### If your app is unverified + + Unverified apps must still must enable the intent in your app’s settings within the Developer Portal. + + Existing unverified apps will automatically have the message content intent toggled on in their settings. New unverified apps will have to manually toggle the intent in the Developer Portal. + + + + This week, [Slash Command mentions](/developers/docs/reference#message-formatting) are rolling out across all Discord clients (for Android, mentions are limited to the [React Native client](https://discord.com/blog/android-react-native-framework-update)). Clicking a Slash Command mention will auto-populate the command in the user's message input. + + Slash Command mentions use the following format: ``. You can also use `` and `` for subcommands and subcommand groups. + + + + + Starting on **September 12, 2022**, apps that aren’t using the new `resume_gateway_url` field to resume gateway sessions will be disconnected significantly faster than normal. + + + A new `resume_gateway_url` field has been added to the [Ready](/developers/docs/events/gateway-events#ready) gateway event to support session-specific gateway connections. The value of `resume_gateway_url` is a session-specific URL that should be used when resuming the gateway session after a disconnect. Previously, `wss://gateway.discord.gg` was used to connect *and* resume sessions, but should now only be used during the connection. + + At the moment, the value of `resume_gateway_url` will always be `wss://gateway.discord.gg` to give developers more time to adopt the new field. In the near future, the value will change to the session-specific URLs. + + + + On August 8th, 2022 we will begin requiring the `VIEW_CHANNEL (1 << 10)` permission for webhook routes which require `MANAGE_WEBHOOKS (1 << 29)`, to align with our documented behavior. We don't expect that many applications will be affected by this, but in case you are, please ensure you have updated permissions needed for accessing the following routes: + + * [`GET /webhooks/{webhook.id}`](/developers/docs/resources/webhook#get-webhook) + * [`DELETE /webhooks/{webhook.id}`](/developers/docs/resources/webhook#delete-webhook) + * [`PATCH /webhooks/{webhook.id}`](/developers/docs/resources/webhook#modify-webhook) + * [`GET /channels/{channel.id}/webhooks`](/developers/docs/resources/webhook#get-channel-webhooks) + * [`POST /channels/{channel.id}/webhooks`](/developers/docs/resources/webhook#create-webhook) + + + + While this is a breaking change, most apps only rely on interaction responses (`INTERACTION_CREATE`), *not* message interaction objects (`MESSAGE_CREATE`). [Interaction responses](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-data) are unaffected by this change. + + #### Upcoming Changes + + Starting **July 18, 2022**, the `name` field for [message interaction objects](/developers/docs/interactions/receiving-and-responding#message-interaction-object) will now include subcommands and subcommand groups in the value (along with the existing top-level command). In the future, we recommend not relying on this message interaction field. + + The format of the value will be the different command levels (if they exist), separated by spaces: + ` ` + + The `name` field is only seen on messages that are a response to an interaction without an existing message, so interaction objects for message components don’t include this field. + + #### Updating your app + + Most apps only rely on interaction responses, not message interaction objects. + + We don't recommend that your app relies on the `name` field for message interactions objects, but if it does you should update your app to handle subcommands and subcommand groups that your app may encounter. + + As an example of the change, pretend your app had a command `/role` with subcommands `add` and `remove`. Currently, the `name` field in the original interaction payload would contain `role`. If you responded to that interaction with a message then fetched its contents, the `name` field for that message interaction object would contain `role` as well. + + After this change, the `name` field for the original interaction payload will still contain `role`. However, now if you responded to that interaction with a message then fetched its contents, the `name` field for that message interaction object would contain `role add` or `role remove`. + + + + Application [command options](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) of type `STRING` now includes optional `min_length` and `max_length` fields to control the length of text a user can input. + + The value of `min_length` must be greater or equal to `0`, and the value of `max_length` must be greater or equal to `1`. + + + + Interaction payloads now contain an `app_permissions` field whose value is the computed [permissions](/developers/docs/topics/permissions#permissions-bitwise-permission-flags) for a bot or app in the context of a specific interaction (including any channel overwrites). Similar to other permission fields, the value of `app_permissions` is a bitwise OR-ed set of permissions expressed as a string. Read details in the [interactions documentation](/developers/docs/interactions/receiving-and-responding#interaction-object). + + For apps without a bot user (or without the `bot` scope), the value of `app_permissions` will be the same as the permissions for `@everyone`, but limited to the permissions that can be used in interaction responses (currently `ATTACH_FILES`, `EMBED_LINKS`, `MENTION_EVERYONE`, and `USE_EXTERNAL_EMOJIS`). + + + + #### Upcoming Changes + + + `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` are the only permissions that will be affected by this change. In a previous version of this changelog, it was indicated that `ATTACH_FILES` and `EMBED_LINKS` would be affected but this is no longer the case. + + + Starting **August 3, 2022**, the way some of a bot's `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` [permissions](/developers/docs/topics/permissions) are calculated is changing in two cases: + + * When **responding to an [interaction](/developers/docs/interactions/receiving-and-responding)** (like application commands or message components) + * When **executing a [webhook](/developers/docs/resources/webhook) that the bot created** + + Going forward, in the above cases, a bot’s `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` permissions will be calculated based on the permissions its granted, *including* any [overwrites](/developers/docs/topics/permissions#permission-overwrites). Previously, a bot’s permissions in these cases relied only on those granted to `@everyone`. + + This change *only* applies to bots. The permissions for an app without a bot user (or without the `bot` scope) will still depend on `@everyone`. + + #### Updating Your App + + If your bot wants to use the `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` or `USE_EXTERNAL_EMOJIS` permissions when responding to interactions or executing a webhook, **ensure that the bot was installed (or explicitly granted) with them**. + + Note that even if your bot is installed with certain permissions, they can be changed using overwrites. For interactions, you can use the [`app_permissions` field](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) to determine your app or bot's contextual permissions before replying. + + + + In API v10, the `MESSAGE_CONTENT` (`1 << 15`) intent is now required to receive non-empty values for the `content` and `matched_content` fields in [`AUTO_MODERATION_ACTION_EXECUTION`](/developers/docs/events/gateway-events#auto-moderation-action-execution) gateway events. This matches the intended behavior for message content across the API. + + + + The `$` prefix in [identify connection properties](/developers/docs/events/gateway-events#identify-identify-connection-properties) are deprecated. The new field names are `os`, `browser`, and `device`. When passed, the `$`-prefixed names will resolve to the new ones. + + In API v11, support for the previous field names (`$os`, `$browser`, and `$device`) will be removed. + + + + Add new [Auto Moderation feature](/developers/docs/resources/auto-moderation) which enables guilds to moderate message content based on keywords, harmful links, and unwanted spam. This change includes: + + * New endpoints for [creating](/developers/docs/resources/auto-moderation#create-auto-moderation-rule), [updating](/developers/docs/resources/auto-moderation#modify-auto-moderation-rule), and [deleting](/developers/docs/resources/auto-moderation#delete-auto-moderation-rule) Auto Moderation rules + * New gateway events emitted when Auto Moderation rules are [created](/developers/docs/events/gateway-events#auto-moderation-rule-create) (`AUTO_MODERATION_RULE_CREATE`), [updated](/developers/docs/events/gateway-events#auto-moderation-rule-update) (`AUTO_MODERATION_RULE_UPDATE `), and [deleted](/developers/docs/events/gateway-events#auto-moderation-rule-delete) (`AUTO_MODERATION_RULE_DELETE `). Requires the `AUTO_MODERATION_CONFIGURATION` (`1 << 20`) intent + * New gateway event emitted when an [action is executed](/developers/docs/events/gateway-events#auto-moderation-action-execution) (`AUTO_MODERATION_ACTION_EXECUTION`). Requires the `AUTO_MODERATION_EXECUTION` (`1 << 21`) intent + * New [audit log entries](/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events) when rules are created (`AUTO_MODERATION_RULE_CREATE`), updated (`AUTO_MODERATION_RULE_UPDATE`), or deleted (`AUTO_MODERATION_RULE_DELETE`), or when Auto Moderation performs an action (`AUTO_MODERATION_BLOCK_MESSAGE`) + + + + Application command permissions have been updated to add more granular control and access to commands in Discord. You can read the major changes below, and [the updated documentation](/developers/docs/interactions/application-commands#permissions) for details. + + #### Breaking changes + + * Bearer tokens are now required to edit command permissions. Bearer tokens are tokens tied to an authenticating user's permissions, and can be [retrieved using OAuth](/developers/docs/topics/oauth2). The user must have permission to manage the guild and roles. + * [`applications.commands.permissions.update`](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) scope was added as a requirement to edit command permissions. + * Disabled the batch editing endpoint ([`PUT /applications/{application.id}/guilds/{guild.id}/commands/permissions`](/developers/docs/interactions/application-commands#batch-edit-application-command-permissions)). + + #### Other changes + + * Created a [`CHANNEL` command permission type](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permission-type) + * Increase permission limit from `10` to `100` + * [constant (`guild_id - 1`)](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-constants) to represent all channels in command permissions + * Added `default_member_permissions` field, which is a bitwise OR-ed set of [permissions](/developers/docs/topics/permissions#permissions-bitwise-permission-flags), expressed as a string. This replaces the `default_permission` field, which will soon be deprecated. + * Added `dm_permission`, which is a boolean flag used to indicate whether a command is available in DMs (only for global application commands). If no value is passed, the global command will be visible in DMs. + * Added `APPLICATION_COMMAND_PERMISSIONS_UPDATE` [gateway](/developers/docs/events/gateway-events#application-command-permissions-update) event and `APPLICATION_COMMAND_PERMISSION_UPDATE` [audit log](/developers/docs/resources/audit-log) event. + + + + Added new channel type, `GUILD_FORUM` (15). A `GUILD_FORUM` channel is an unreleased feature that is very similar (from an API perspective) to a `GUILD_TEXT` channel, except only threads can be created in that channel; messages cannot be sent directly in that channel. Check out the [forums topic](/developers/docs/topics/threads#forums) for more information. + + + + The `GET /guilds/{guild.id}/bans` endpoint has been migrated to require pagination to improve reliability and stability. Check out the [endpoint docs](/developers/docs/resources/guild#get-guild-bans) for more information. + + + + * API v8 is now deprecated. + * `GET /channels/{channel.id}/threads/active` is decommissioned in favor of [`GET /guilds/{guild.id}/threads/active`](/developers/docs/resources/guild#list-active-guild-threads). + * Starting in v10, you must specify the message content intent (`1 << 15`) to receive content-related fields in message dispatches. Read more in the [Gateway Intents documentation](/developers/docs/events/gateway#gateway-intents). + * To specify a reason for an administrative action in audit logs, apps must now pass the `X-Audit-Log-Reason` header rather than the `reason` parameter for all endpoints. Read more in the [Audit Logs documentation](/developers/docs/resources/audit-log). + * Message routes (like [`POST /channels/{channel.id}/messages`](/developers/docs/resources/message#create-message)) now use the `embeds` field (an array of embed objects) instead of `embed`. + * The `summary` field for [applications](/developers/docs/resources/application) now returns an empty string for all API versions. + * The `name` and `description` fields for [Achievements](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Achievements.md#achievement-struct) are now strings, and localization info is now passed in new `name_localizations` and `description_localizations` dictionaries. This change standardizes localization to match [Application Commands](/developers/docs/interactions/application-commands#localization). Read details in the [Achievements documentation](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Achievements.md#achievement-struct). + * Existing attachments must be specified when [`PATCH`ing messages with new attachments](/developers/docs/reference#editing-message-attachments). Any attachments not specified will be removed and replaced with the specified list + * Requests to v10 and higher will no longer be supported on `discordapp.com` (this does **not** affect `cdn.discordapp.com`) + + #### Upcoming changes + + * API v6 and v7 will be decommissioned **in early 2023** + * `MESSAGE_CONTENT` is becoming a privileged intent for verified bots in 75+ servers **on August 31, 2022**. Read details in [the FAQ](https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Privileged-Intent-FAQ) or follow [the guide](/developers/docs/tutorials/upgrading-to-application-commands) on updating your app. + * The `summary` field for applications will be removed in the next API version (v11) + + + + Interaction modals are now available, allowing applications to prompt users for further detailed input. Check out [the modal docs](/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal) for more information. + + Application Commands can now add an attachment option type. See [the option type table](/developers/docs/interactions/application-commands#application-command-object-application-command-option-type) for more information. + + + + Add new documentation for the recently released guild member timeout feature. + + + + * Add official support for `guild_scheduled_events` field on `Guild` resource sent with `GUILD_CREATE` event + + #### Nov 18, 2021 + + * Breaking change for return type for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}/users` + * Add `with_user_count` query param for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}` + * Return additional `creator` field by default in response for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}` + * More details and clarification for the guild scheduled events feature. + * Document support for `before` and `after` query params for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}/users` + + #### Nov 15, 2021 + + Add new documentation for the recently released Guild Scheduled Events feature. + + + + Autocomplete interactions are now available, allowing application commands to provide server completed options. Check out [the autocomplete interaction docs](/developers/docs/interactions/application-commands#autocomplete) for more information. + + + + Thread permissions have been updated and simplified: + + * "Use Public Threads" is now "Create Public Threads", which allows users to create public threads and announcement threads in a channel, even if they cannot send messages in that channel. + * "Use Private Threads" is now "Create Private Threads", which allows users to create private threads in a channel, even if they cannot send messages in that channel. + + A new permission has also been added: + + * "Send Messages in Threads", which allows users to send a message in a thread. The "Send Messages" permission has no effect in threads: users **must** have "Send Messages in Threads" to send a message in a thread. This allows for setups where a user can participate in a thread but cannot send a message in the parent channel (like a thread on an announcement post). + + + + [User commands](/developers/docs/interactions/application-commands#user-commands) and [message commands](/developers/docs/interactions/application-commands#message-commands) are now live! These commands appear on context menus for users and messages, with more to come in the future. + + Context menu commands are a type of application command. The "Slash Commands" documentation page has been renamed to "Application Commands" and split out by type to show this. + + + + Select Menus are now part of the components API! They're the greatest thing since the invention of buttons yesterday. Select menus allow you to offer users a choice of one or many options in a friendly UI-based way. + + Select menus can be used like other [message components](/developers/docs/components/overview). Learn all the specifics in the [documentation](/developers/docs/components/reference#string-select). + + + + Message routes now accept an embeds array in addition to the existing embed field. Bots can now send up to 10 embeds per message, to be consistent with webhook behavior. The existing embed field is considered deprecated and will be removed in the next API version. + + + + Message components are now available with our first two components: a layout-based `ActionRow` and...buttons! + + You can now include buttons on messages sent by your app, whether they're bot messages or responses to interactions. [Learn more about message components](/developers/docs/components/overview). + + The addition of message components means new fields and response types: + + * An optional `components` field has been added to the [message object](/developers/docs/resources/message#message-object) + * New response types `6` and `7` have been added for [interaction responses](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type), valid only for component-based interactions + + + + API v9 is now available. + + API v9 includes support for [threads](/developers/docs/topics/threads), an upcoming feature. Older API versions will not receive any Gateway Events for threads, so it is important to update soon! We've prepared a [migration guide](/developers/docs/topics/threads) to help make the upgrade process very straightforward. + + This documentation is being published early so bots can have at least two months to upgrade before threads launch. + + Additionally, API v9 also removes the `/channels/:id/messages/:id/suppress-embeds` route. + + + + Need to keep some of your commands safe from prying eyes, or only available to the right people? Commands now support [command permissions](/developers/docs/interactions/application-commands#permissions)! + + You can enable or disable a command (guild or global) for a specific user or role in a guild. For now, users will still be able to see the commands, but won't be able to use them. + + New routes have been added to support this functionality: + + * [`GET Guild Application Command Permissions`](/developers/docs/interactions/application-commands#get-guild-application-command-permissions) + * [`GET Application Command Permissions`](/developers/docs/interactions/application-commands#get-application-command-permissions) + * [`PUT Application Command Permissions`](/developers/docs/interactions/application-commands#batch-edit-application-command-permissions) + + A `default_permission` field has also been added to the [ApplicationCommand](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) model. This field allows you to disable commands for everyone in a guild by default, if you prefer to make some of your commands an opt-in experience. + + + + There have been reports that sessions have higher frequency of errors when starting if a bot has joined too many guilds (the gateway connection times out). To account for this we have lowered the requirement for large bot sharding down to 150,000 guilds in order to improve reliability. + + + + Changes to interaction response types have been made to support better designs for application commands: + + * Type `2` `Acknowledge` has been deprecated + * Type `3` `ChannelMessage` has been deprecated + * Type `5` `AcknowledgeWithSource` has been renamed to `DeferredChannelMessageWithSource` + + These deprecated types will be removed and break on **April 9, 2021**. + + Additionally, `flags` has been documented on [InteractionApplicationCommandCallbackData](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-data-structure). Setting `flags` to `64` will make the interaction response ephemeral. + + + + Slash Commands are now supported in DMs with bots. Due to this change, some of the fields on the [Interaction object](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) have been made optional. Newly optional fields don't reflect any behavior changes in Slash Commands within guilds; they are to support commands in the context of a DM only. + + + + Permission overwrites in the guild channel creation endpoint are now validated against the permissions your bot has in the guild. Permission overwrites specified in the request body when creating guild channels will now require your bot to also have the permissions being applied. Setting `MANAGE_ROLES` permission in channel overwrites is only possible for guild administrators or users with `MANAGE_ROLES` as a permission overwrite in the channel. + + + + Slash Commands are here! There's a *lot* to cover, so go check out specific documentation under [Slash Commands](/developers/docs/interactions/application-commands). + + Slash Commands include some new features for webhooks as well: + + * Webhooks can now update previously-sent messages from the same webhook using [Edit Webhook Message](/developers/docs/resources/webhook#edit-webhook-message) and [Delete Webhook Message](/developers/docs/resources/webhook#delete-webhook-message) + + This PR also documents the `application` field on the `READY` gateway event, which is a partial [application object](/developers/docs/resources/application#application-object) containing `id` and `flags`. + + + + Inline Replies have been added to our documentation. They behave differently in v6 and v8, so be cautious in your implementation: + + * Inline replies are type `19` in v8, but remain type `0` in v6 + * You can now add a `message_reference` on message create to create a reply + * A new field `referenced_message` has been added to the [Message Object](/developers/docs/resources/message#message-object) + * A new field `replied_user` has been added to the [Allowed Mentions Object](/developers/docs/resources/message#allowed-mentions-object) + * [Message Create](/developers/docs/events/gateway-events#message-create) gateway event is guaranteed to have a `referenced_message` if the message created is a reply. Otherwise, that field is not guaranteed. + + + + Stickers are now documented as part of the [message](/developers/docs/resources/message#message-object) object. + + + + The v6 gateway now applies the restrictions for gateway intents. This means the new chunking limitations are now in effect, regardless of intents being used. See [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) for further details. + Additionally, if privileged intents are not enabled in the application dashboard the bot will not receive the events for those intents. + + All other intents are always enabled by default unless specified otherwise by the identify payload. We have made a support article to explain some of the changes and resulting issues with more details: [Gateway Update FAQ](https://dis.gd/gwupdate) + + + + We've introduced API and Gateway v8! Changes are noted throughout the documentation, and you can also read [this commit in our docs repo](https://github.com/discord/discord-api-docs/commit/545ff4a7883e5eee7ee91d19a5e5d760a0730033) for a full diff. + + The changes are: + + * API and Gateway v8 are now available. v6 is still the default for the time being. + * [Gateway Intents](/developers/docs/events/gateway#gateway-intents) are now required + * Removed `guild_subscriptions` in identify in favor of [Gateway Intents](/developers/docs/events/gateway#gateway-intents). + * All permissions have been converted to strings-serialized numbers. As such, `permissions_new`, `allow_new`, and `deny_new` have been removed + * The `game` field has been removed. If you need a direct replacement, you can instead reference the first element of `activities` + * Channel Permission Overwrite `type`s are now numbers (0 and 1) instead of strings ("role" and "member"). However due to a current technical constraint, they are string-serialized numbers in audit log `options`. + * `embed_enabled` and `embed_channel_id` have been removed. Use `widget_enabled` and `widget_channel_id` instead. + * Form body errors have been improved to include more helpful messaging on validation. [See more here](/developers/docs/reference#error-messages) + * The `Retry-After` header value and `retry_after` body value is now based in seconds instead of milliseconds (e.g. `123` means 123 seconds) + * The `X-RateLimit-Precision` header is no longer respected. `X-RateLimit-Reset` and `X-RateLimit-Reset-After` are always returned at millisecond precision (e.g. `123.456` instead of `124`) + * Bots no longer receive [Channel Create Gateway Event](/developers/docs/events/gateway-events#channel-create) for DMs + * `delete-message-days` is no longer available. Use `delete_message_days`. + * Removed `roles`, `premium_since`, and `nick` from [Presence Update Gateway Event](/developers/docs/events/gateway-events#presence-update) + * Removed some [integration object](/developers/docs/resources/guild#integration-object) fields for Discord application integrations + * Removed `include_applications` from [Get Guild Integrations](/developers/docs/resources/guild#get-guild-integrations). Application integrations are always included. + * The following deprecated routes have been removed for better naming conventions: + + Removed in favor of `/guilds//widget`: + + * `/guilds//embed` + + Removed in favor of `/guilds//widget.json`: + + * `/servers//embed.json` + * `/servers//widget.json` + * `/guilds//embed.json` + + Removed in favor of `/guilds//widget.png`: + + * `/guilds//embed.png` + + Removed in favor of `/channels//messages/bulk-delete`: + + * `/channels//messages/bulk_delete/` + + Removed in favor of `/invites//`: + + * `/invite//` + + + + Documented `permissions_new`, `allow_new`, and `deny_new` as string-serialized permission bitfields. + + + + The legacy mention behavior for bots is now removed, and granular control of mentions should use the [Allowed Mentions](/developers/docs/resources/message#allowed-mentions-object) API moving forwards. + + + + The [Guild Members Chunk](/developers/docs/events/gateway-events#guild-members-chunk) gateway event now contains two properties: `chunk_index` and `chunk_count`. These values can be used to keep track of how many events you have left to receive in response to a [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) command. + + + + We've added a way to specify mentions in a more granular form. This change also begins the start of a 60 day deprecation cycle on legacy mention behavior. Read more: + + * [Allowed mentions object](/developers/docs/resources/message#allowed-mentions-object) + + + + We've added a new endpoint for deleting all reactions of a specific emoji from a message, as well as some new invite and reaction gateway events. Read more: + + * [Delete All Reactions for Emoji](/developers/docs/resources/message#delete-all-reactions-for-emoji) + * [Invite Create](/developers/docs/events/gateway-events#invite-create) + * [Invite Delete](/developers/docs/events/gateway-events#invite-delete) + * [Message Reaction Remove Emoji](/developers/docs/events/gateway-events#message-reaction-remove-emoji) + + + + The [Spectate](/developers/docs/developer-tools/game-sdk#onactivityspectate) functionality of Rich Presence no longer requires whitelisting or approval. + + + + We've added documentation around a brand new feature: [Gateway Intents!](/developers/docs/events/gateway#gateway-intents) Gateway Intents are a great way to specify which events you want to receive from our gateway. Go on, save yourself some bandwidth and CPU usage. + + Using Intents will change the behavior of some existing events and commands, so please refer to: + + * [Guild Create](/developers/docs/events/gateway-events#guild-create) + * [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) + * [Guild Member Add](/developers/docs/events/gateway-events#guild-member-add) + * [Guild Member Remove](/developers/docs/events/gateway-events#guild-member-remove) + * [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) + * [Presence Update](/developers/docs/events/gateway-events#presence-update) + * [List Guild Members](/developers/docs/resources/guild#list-guild-members) + + + + Updated our [IP discovery message](/developers/docs/topics/voice-connections#ip-discovery). The old message is deprecated and will be removed in the future. + + + + Fixed a bug from the 2.5.5 release that caused network handshakes to fail, resulting in no networking data being sent. The networking manager and integrated lobby networking should be full operational again after updating. + + + + We've shipped some updates to the GameSDK, including support for Linux as well as the IL2CPP backend system for Unity. These changes also fixed a bug in the [`SetUserAchievement()`](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Achievements.md#setuserachievement) method. + + Get the latest at the top of the [Getting Started](/developers/docs/developer-tools/game-sdk#step-1-get-the-sdk) documentation. If you're looking for help interacting with the GameSDK or want to report a bug, join us on the [official Discord](https://discord.gg/discord-developers). + + + + News Channels are now changed to Announcement Channels. Developer License owners will continue to get access to them (both existing and new). Underlying channel type (GUILD\_NEWS = 5) remains the same. + + + + You can now get more precise rate limit reset times, via a new request header. Check out the [rate limits](/developers/docs/topics/rate-limits) documentation for more information. + + + + You can now use Bot tokens for authorization headers against the HTTP API for [Achievements](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Achievements.md#the-api-way). + + + + Additional information around Teams has been added to both the API and the documentation. The [Teams](/developers/docs/topics/teams) page now includes information about the team and team member objects. Additionally, the [Get Current Application Information](/developers/docs/topics/oauth2#get-current-bot-application-information) endpoint now returns a `team` object if that application belongs to a team. That documentation has also been updated to includes fields that were missing for applications that are games sold on Discord. + + + + Additional information has been documented to support [Server Nitro Boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting). This includes the addition of a few [message types](/developers/docs/resources/message#message-object-message-types), as well as some [new fields on guilds](/developers/docs/resources/guild#guild-object-premium-tier). Please note that this feature is currently under experimentation, and these fields may be subject to change. + + + + The [Discord-RPC](https://github.com/discord/discord-rpc) implementation of Rich Presence has been deprecated in favor of Discord's new GameSDK. If you're interested in using Rich Presence, please read our [SDK Starter Guide](/developers/docs/developer-tools/game-sdk#getting-started) and check out the relevant functions in the [Activity Manager](/developers/docs/developer-tools/game-sdk#activities). + + + + The [Invite Object](/developers/docs/resources/invite#invite-object) now includes two additional fields, `target_user` and `target_user_type`. + + + + Ask to Join no longer requires approval or whitelisting to use. You are welcome to create in-game UI, but all Ask to Join requests are also now handled by the Discord overlay. + + There have also been some small additions to the Rich Presence SDK. The previously undocumented `UpdateHandlers()` function is now documented. + + + + Dispatch documentation around store listings has been removed. Store pages for the Discord Store are now managed entirely within the [Developer Portal](https://discord.com/developers). + + + + The [User object](/developers/docs/resources/user#user-object) now includes two new additional fields, `premium_type` and `flags`. These can be used to know the Nitro status of a user, or determine which HypeSquad house a user is in. + + + + The documentation has been updated to correctly note that the `private_channels` field in the [Ready](/developers/docs/events/gateway-events#ready) should be an empty array, as well as the response from `/users/@me/channels` for a bot user. This change has been in effect for a long time, but the documentation was not updated. + + + + We released server changes that allow guilds to represent an incomplete state of the member list in our clients, which results in inaccurate member lists and online counts over RPC. These fields are now deprecated and will now return an empty members array and an online count of 0 moving forward. + + + + Additional `activity` and `application` fields—as well as corresponding object documentation—have been added to the [Message](/developers/docs/resources/message#message-object) object in support of our newly-released [Spotify integration](https://support.discord.com/hc/en-us/articles/360000167212-Discord-Spotify-Connection) and previous Rich Presence enhancements. + + + + The [Get Guild Emoji](/developers/docs/resources/emoji#get-guild-emoji) response now also includes a user object if the emoji was added by a user. + + + + The [Accept Invite](/developers/docs/resources/invite) endpoint is deprecated starting today, and will be discontinued on March 23, 2018. The [Add Guild Member](/developers/docs/resources/guild#add-guild-member) endpoint should be used in its place. + + + + Additional sharding requirements and information for bots in over 100,000 guilds has been added. This requires a small change in numbers of shards for affected bots. See the [documentation](/developers/docs/events/gateway#sharding-for-large-bots) for more information. + + + + Rich Presence is now live and available for all developers! Rich Presence allows developers to closely integrate with Discord in a number of new, cool ways like: + + * Showing more information about a user's current game in their profile + * Allowing users to post invitations to join their party or spectate their game in chat + * Displaying "Spectate" and "Ask to Join" buttons on users' profiles + + For more information, check out our [Rich Presence site](https://discord.com/rich-presence). To get started on development, [read the docs](/developers/docs/rich-presence/overview)! + + + + [API](/developers/docs/reference#api-versioning) and Gateway versions below v6 are now discontinued after being previously deprecated. Version 6 is now the default API and Gateway version. Attempting to use a version below 6 will result in an error. + + + + Changes have been made throughout the documentation to reflect the addition of channel categories to Discord. These includes an additional field—`parent_id`—to the base [channel](/developers/docs/resources/channel#channel-object) object and a new [channel category example](/developers/docs/resources/channel#channel-object-example-channel-category). + + + + [Emoji endpoints](/developers/docs/resources/emoji) have been added to the API. Bots can now manage guild emojis to their robo-hearts' content! + + + + The `type` field in the [activity object](/developers/docs/events/gateway-events#activity-object) for [Gateway Status Update](/developers/docs/events/gateway-events#update-presence) and [Presence Update](/developers/docs/events/gateway-events#presence-update) payloads is no longer optional when the activity object is not null. + + + + After today, we are changing how default channels function. The "default" channel for a given user is now the channel with the highest position that their permissions allow them to see. New guilds will no longer have a default channel with the same id as the guild. Existing guilds will not have their #general channel id changed. It is possible, if permissions are set in such a way, that a user will not have a default channel in a guild. + + We saw a use case in many servers where the previously-default #general channel was being repurposed as an announcement-only, non-writable channel for new members by using bots to clear the entire message history. Now, that channel can simply be deleted and re-created with the desired permissions. This change also allows dynamic default channels for users based on permissions. + + We are also rolling out a change in conjunction that will allow Discord to remember your last-visited channel in a guild across sessions. Newly-joined users will be directed to the guild's default channel on first join; existing members will return to whichever channel they last visited. + + + + Audit logs are here! Well, they've been here all along, but now we've got [documentation](/developers/docs/resources/audit-log) about them. Check it out, but remember: with great power comes great responsibility. + + + + * [Channel](/developers/docs/resources/channel#channel-object) Object + * `is_private` removed + * [`type`](/developers/docs/resources/channel#channel-object-channel-types) is now an integer + * `recipient` is now `recipients`, an array of [user](/developers/docs/resources/user#user-object) objects + * [Message](/developers/docs/resources/message#message-object) Object + * [`type`](/developers/docs/resources/message#message-object-message-types) added to support system messages + * [Status Update](/developers/docs/events/gateway-events#update-presence-gateway-presence-update-structure) Object + * `idle_since` renamed to `since` + + +{/* Autogenerated Reference Links */} +[`Activity::AddButton`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#aab07650fcff18565eb78a1e2df46627e +[`ActivityInvite`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#af980f140c1459e1cd8f6ef3f3c07547c +[`Call::GetPTTReleaseDelay`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ab8dc6b1527728fecb17f266d5b3e9e6e +[`Call::SetPTTActive`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ac442b1d69b9256abbb188583c0c81c41 +[`Client::AddLogCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af78996cff24a40f5dc7066beed16692c +[`Client::CreateOrJoinLobbyWithMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5c84fa76c73cf3c0bfd68794ca5595c1 +[`Client::ExchangeChildToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a59f5d9d14f79eb318bf4d57f4e87a5c1 +[`Client::GetCurrentUser`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7edea2a3dfe9ae560d5fa5ba8663b5cc +[`Client::GetCurrentUserV2`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae52259570ba657252d91f5580636fe5d +[`Client::GetGuildChannels`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adba1e5a83c219a9c4f6dab1657778017 +[`Client::GetLobbyMessagesWithLimit`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a0586192e85caf548b8b321f1cb21301f +[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d +[`Client::GetRelationshipsByGroup`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9f7898d3f3d1ec92b06c662df70746d5 +[`Client::GetUserGuilds`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aac1ec02df6074ed9213ce230e6a42fe1 +[`Client::GetUserMessageSummaries`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a32dafc20ff1f99b019e40bdc81f46dde +[`Client::GetUserMessagesWithLimit`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a054a758a76c5873b38a4d79651a5f26c +[`Client::JoinLinkedLobbyGuild`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a54ec764e72e168de419ac14e24e8fc60 +[`Client::OpenConnectedGamesSettingsInDiscord`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a24f268f5eebe9919a3f774354eb577e0 +[`Client::RegisterAuthorizeRequestCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5f34b873e127a446c9ab549e4588ccd7 +[`Client::RegisterLaunchCommand`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a024d7222931fdcb7d09c2b107642ecab +[`Client::RegisterLaunchSteamApplication`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a45b2c791c5b06f77d457dacb53dfba40 +[`Client::RemoveAuthorizeRequestCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab7e48864b0cedf3e8572a228ca401f2a +[`Client::RemoveDiscordAndGameFriend`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa6d393a3d98ec5d06faef49a57d1a89b +[`Client::RevokeToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a30ccea6366efaf0b884efcdcc28a6f2d +[`Client::SetActivityInviteCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3b4e37a222a8662506d763514774bedc +[`Client::SetAecDump`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3a05b2cafaa546d915a5249c63f4059f +[`Client::SetEngineManagedAudioSession`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ade08897214152b9acfa79c263e77e366 +[`Client::SetHttpRequestTimeout`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae5fe2518b0b1b05ee1745ab0a79096b9 +[`Client::SetRelationshipGroupsUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af12441ef091298f968075b7190851098 +[`Client::SetSpeakerMode`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac269ab57407e3b83e2bb2d30895e666d +[`Client::SetVoiceLogDir`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a48c6b7e8bbc2b632a935acafc6a5f7a7 +[`Client::StartCallWithAudioCallbacks`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#abcaa891769f9e912bfa0e06ff7221b05 +[`Client::UnmergeIntoProvisionalAccount`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2da21ae8a3015e0e5e42c1a7226b256f +[`Client::UpdateRichPresence`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af0a85e30f2b3d8a0b502fd23744ee58e +[`ClientCreateOptions`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#ae655ad66ba64f443496c158307cc77b4 +[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f +[`RelationshipHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a7da36b15ad0b7d38ba658a622e9ded77 \ No newline at end of file diff --git a/discord/developers/docs/components/overview.mdx b/discord/developers/docs/components/overview.mdx new file mode 100644 index 0000000000..b84391d11a --- /dev/null +++ b/discord/developers/docs/components/overview.mdx @@ -0,0 +1,48 @@ +--- +title: Components Overview +sidebarTitle: Overview +mode: wide +description: Learn about Discord's interactive message components. +--- + +Components allow you to add interactive elements to modals and the messages your app sends. They're accessible, customizable, and easy to use. + +![Image showing examples of components UI](/images/components/hero.png) + +Discord apps support three types of message components: layout components, content components, and interactive components. Each type of component has its own purpose and use cases. + +- **Layout Components**: These components are used to organize and structure the content of your message. They help create a visually appealing and user-friendly layout. +- **Content Components**: These components are used to display information and content in your message. They can include text, images, and other media. +- **Interactive Components**: These components allow users to interact with your message. They can include buttons, select menus, and other interactive elements. + +All components are customizable and can be organized using layout components to create rich, interactive message experiences. + +To use components, messages must be sent with the `IS_COMPONENTS_V2` flag (`1<<15`). Note that using this flag disables traditional content and embeds - all content must be sent as components instead. + + +[Legacy message component behavior](/developers/docs/components/reference#legacy-message-component-behavior) will **not** be deprecated and will continue to be available to your apps on a message-by-message basis. However, we recommend using the new components for new projects and features. + + +import {ChatDotsIcon} from '/snippets/icons/ChatDotsIcon.jsx' +import {CompassIcon} from '/snippets/icons/CompassIcon.jsx' +import {BrowserIcon} from '/snippets/icons/BrowserIcon.jsx' + + + }> + A guide on sending Message Components with examples. + + }> + A guide on sending Modal Components with examples. + + }> + Explore the Components reference documentation. + + + +--- + +## Get Help & Join the Community + +Do you have a question or want to connect with other app developers? + +- Join our [DDevs Discord Server](https://discord.gg/discord-developers) and get help from the community, share best practices, and discover new ways to enhance your apps. \ No newline at end of file diff --git a/discord/developers/docs/components/reference.mdx b/discord/developers/docs/components/reference.mdx new file mode 100644 index 0000000000..e5535646d9 --- /dev/null +++ b/discord/developers/docs/components/reference.mdx @@ -0,0 +1,2232 @@ +--- +title: Component Reference +sidebar_label: Component Reference +description: Complete reference for all Discord message components. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +This document serves as a comprehensive reference for all available components. It covers three main categories: + +- **Layout Components** - For organizing and structuring content (Action Rows, Sections, Containers) +- **Content Components** - For displaying static text, images, and files (Text Display, Media Gallery, Thumbnails) +- **Interactive Components** - For user interactions (Buttons, Select Menus, Text Input) + +To use these components, you need to send the [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (IS_COMPONENTS_V2) which can be sent on a per-message basis. Once a message has been sent with this flag, it can't be removed from that message. This enables the new components system with the following changes: + +- The `content` and `embeds` fields will no longer work but you'll be able to use [Text Display](/developers/docs/components/reference#text-display) and [Container](/developers/docs/components/reference#container) as replacements +- Attachments won't show by default - they must be exposed through components +- The `poll` and `stickers` fields are disabled +- Messages allow up to 40 total components + + +[Legacy component behavior](/developers/docs/components/reference#legacy-message-component-behavior) will continue to work but provide less flexibility and control over the message layout. + + +For a practical guide on implementing these components, see our [Using Message Components](/developers/docs/components/using-message-components) and [Using Modal Components](/developers/docs/components/using-modal-components) documentation. + +--- + +## What is a Component + +Components allow you to style and structure your messages, modals, and interactions. They are interactive elements that can create rich user experiences in your Discord applications. + +Components are a field on the [message object](/developers/docs/resources/message#message-object) and [modal](/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal). You can use them when creating messages or responding to an interaction, like an [application command](/developers/docs/interactions/application-commands). + +### Component Object + +###### Component Types + +The following is a complete table of available components. Details about each component are in the sections below. + +| Type | Name | Description | Style | Usage | +|------|--------------------------------------------------------------------------------|----------------------------------------------------------------|-------------|----------------| +| 1 | [Action Row](/developers/docs/components/reference#action-row) | Container to display a row of interactive components | Layout | Message | +| 2 | [Button](/developers/docs/components/reference#button) | Button object | Interactive | Message | +| 3 | [String Select](/developers/docs/components/reference#string-select) | Select menu for picking from defined text options | Interactive | Message, Modal | +| 4 | [Text Input](/developers/docs/components/reference#text-input) | Text input object | Interactive | Modal | +| 5 | [User Select](/developers/docs/components/reference#user-select) | Select menu for users | Interactive | Message, Modal | +| 6 | [Role Select](/developers/docs/components/reference#role-select) | Select menu for roles | Interactive | Message, Modal | +| 7 | [Mentionable Select](/developers/docs/components/reference#mentionable-select) | Select menu for mentionables (users *and* roles) | Interactive | Message, Modal | +| 8 | [Channel Select](/developers/docs/components/reference#channel-select) | Select menu for channels | Interactive | Message, Modal | +| 9 | [Section](/developers/docs/components/reference#section) | Container to display text alongside an accessory component | Layout | Message | +| 10 | [Text Display](/developers/docs/components/reference#text-display) | Markdown text | Content | Message, Modal | +| 11 | [Thumbnail](/developers/docs/components/reference#thumbnail) | Small image that can be used as an accessory | Content | Message | +| 12 | [Media Gallery](/developers/docs/components/reference#media-gallery) | Display images and other media | Content | Message | +| 13 | [File](/developers/docs/components/reference#file) | Displays an attached file | Content | Message | +| 14 | [Separator](/developers/docs/components/reference#separator) | Component to add vertical padding between other components | Layout | Message | +| 17 | [Container](/developers/docs/components/reference#container) | Container that visually groups a set of components | Layout | Message | +| 18 | [Label](/developers/docs/components/reference#label) | Container associating a label and description with a component | Layout | Modal | +| 19 | [File Upload](/developers/docs/components/reference#file-upload) | Component for uploading files | Interactive | Modal | + +-------------- +## Anatomy of a Component + +All components have the following fields: + +| Field | Type | Description | +|-------|---------|-----------------------------------------------------------------------------------------------------| +| type | integer | The [type](/developers/docs/components/reference#component-object-component-types) of the component | +| id? | integer | 32 bit integer used as an optional identifier for component | + +The `id` field is optional and is used to identify components in the response from an interaction. The `id` must be unique within the message and is generated sequentially if left empty. Generation of `id`s won't use another `id` that exists in the message if you have one defined for another component. Sending components with an `id` of `0` is allowed but will be treated as empty and replaced by the API. + +###### Custom ID + +Additionally, interactive components like buttons and selects must have a `custom_id` field. The developer defines this field when sending the component payload, and it is returned in the interaction payload sent when a user interacts with the component. For example, if you set `custom_id: click_me` on a button, you'll receive an interaction containing `custom_id: click_me` when a user clicks that button. + +`custom_id` is only available on interactive components and must be unique per component. Multiple components on the same message must not share the same `custom_id`. This field is a string of a maximum of 100 characters and can be used flexibly to maintain state or pass through other important data. + +| Field | Type | Description | +|-----------|--------|--------------------------------------------------| +| custom_id | string | Developer-defined identifier, max 100 characters | + +-------------- +## Action Row + +An Action Row is a top-level layout component. + +Action Rows can contain one of the following: + +- Up to 5 contextually grouped [buttons](/developers/docs/components/reference#button) +- A single select component ([string select](/developers/docs/components/reference#string-select), [user select](/developers/docs/components/reference#user-select), [role select](/developers/docs/components/reference#role-select), [mentionable select](/developers/docs/components/reference#mentionable-select), or [channel select](/developers/docs/components/reference#channel-select)) + + +[Label](/developers/docs/components/reference#label) is recommended for use over an Action Row in modals. Action Row with Text Inputs in modals are now deprecated. + + +###### Action Row Structure + +| Field | Type | Description | +|------------|----------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| type | integer | `1` for action row component | +| id? | integer | Optional identifier for component | +| components | array of [action row child components](/developers/docs/components/reference#component-object-component-types) | Up to 5 interactive [button](/developers/docs/components/reference#button) components or a single [select](/developers/docs/components/reference#user-select) component | + +###### Action Row Child Components + +| Available Components | Description | +|--------------------------------------------------------------------------------|-------------------------------------------| +| [Button](/developers/docs/components/reference#button) | An Action Row can contain up to 5 Buttons | +| [String Select](/developers/docs/components/reference#string-select) | A single String Select | +| [User Select](/developers/docs/components/reference#user-select) | A single User Select | +| [Role Select](/developers/docs/components/reference#role-select) | A single Role Select | +| [Mentionable Select](/developers/docs/components/reference#mentionable-select) | A single Mentionable Select | +| [Channel Select](/developers/docs/components/reference#channel-select) | A single Channel Select | + +###### Examples + + + + + ![Example of an Action Row with three buttons](/images/components/action-row.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 1, // ComponentType.ACTION_ROW + "components": [ + { + "type": 2, // ComponentType.BUTTON + "custom_id": "click_yes" + "label": "Accept", + "style": 1, + }, + { + "type": 2, // ComponentType.BUTTON + "label": "Learn More", + "style": 5, + "url": "http://watchanimeattheoffice.com/" + }, + { + "type": 2, // ComponentType.BUTTON + "custom_id": "click_no" + "label": "Decline", + "style": 4, + } + ] + } + ] + } + ``` + + +-------------- +## Button + +A Button is an interactive component that can only be used in messages. It creates clickable elements that users can interact with, sending an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object) to your app when clicked. + +Buttons must be placed inside an [Action Row](/developers/docs/components/reference#action-row) or a [Section](/developers/docs/components/reference#section)'s `accessory` field. + +###### Button Structure + +| Field | Type | Description | +|-----------|----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| +| type | integer | `2` for a button | +| id? | integer | Optional identifier for component | +| style | integer | A [button style](/developers/docs/components/reference#button-button-styles) | +| label? | string | Text that appears on the button; max 80 characters | +| emoji? | partial [emoji](/developers/docs/resources/emoji#emoji-object) | `name`, `id`, and `animated` | +| custom_id | string | Developer-defined identifier for the button; max 100 characters | +| sku_id? | snowflake | Identifier for a purchasable [SKU](/developers/docs/resources/sku#sku-object), only available when using premium-style buttons | +| url? | string | URL for link-style buttons; max 512 characters | +| disabled? | boolean | Whether the button is disabled (defaults to `false`) | + +Buttons come in various styles to convey different types of actions. These styles also define what fields are valid for a button. + +- Non-link and non-premium buttons **must** have a `custom_id`, and cannot have a `url` or a `sku_id`. +- Link buttons **must** have a `url`, and cannot have a `custom_id` +- Link buttons do not send an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object) to your app when clicked +- Premium buttons **must** contain a `sku_id`, and cannot have a `custom_id`, `label`, `url`, or `emoji`. +- Premium buttons do not send an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object) to your app when clicked + +###### Button Styles + +| Name | Value | Action | Required Field | +|-----------|-------|----------------------------------------------------------------|----------------| +| Primary | 1 | The most important or recommended action in a group of options | `custom_id` | +| Secondary | 2 | Alternative or supporting actions | `custom_id` | +| Success | 3 | Positive confirmation or completion actions | `custom_id` | +| Danger | 4 | An action with irreversible consequences | `custom_id` | +| Link | 5 | Navigates to a URL | `url` | +| Premium | 6 | Purchase | `sku_id` | + + +###### Examples + + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 1, // ComponentType.ACTION_ROW + "components": [ + { + "type": 2, // ComponentType.BUTTON, + "custom_id": "click_me", + "label": "Click me!", + "style": 1 + } + ] + } + ] + } + ``` + + + + + When a user interacts with a Button in a message, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 3, // InteractionType.MESSAGE_COMPONENT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "component_type": 2, // ComponentType.BUTTON + "id": 2, + "custom_id": "click_me", + }, + } + ``` + + +### Button Design Guidelines + +###### General Button Content + +- 34 characters max with icon or emoji. +- 38 characters max without icon or emoji. +- Keep text concise and to the point. +- Use clear and easily understandable language. Avoid jargon or overly technical terms. +- Use verbs that indicate the outcome of the action. +- Maintain consistency in language and tone across buttons. +- Anticipate the need for translation and test for expansion or contraction in different languages. + +###### Multiple Buttons + +Use different button styles to create a hierarchy. Use only one `Primary` button per group. + +![Example showing one primary button per button group](/images/components/multiple-buttons-example-1.webp) + +If there are multiple buttons of equal significance, use the `Secondary` button style for all buttons. + +![Example showing multiple buttons in a group with equal significance](/images/components/multiple-buttons-example-2.webp) + +###### Premium Buttons + +Premium buttons will automatically have the following: + +- Shop Icon +- SKU name +- SKU price + +![A premium button](/images/components/premium-button.webp) + +-------------- +## String Select + +A String Select is an interactive component that allows users to select one or more provided `options`. + +String Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s) your app receives an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure). + +String Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/docs/components/reference#action-row) in messages and a [Label](/developers/docs/components/reference#label) in modals. + +###### String Select Structure + +| Field | Type | Description | +|---------------|--------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| +| type | integer | `3` for string select | +| id? | integer | Optional identifier for component | +| custom_id | string | ID for the select menu; max 100 characters | +| options | array of [select options](/developers/docs/components/reference#string-select-select-option-structure) | Specified choices in a select menu; max 25 | +| placeholder? | string | Placeholder text if nothing is selected or default; max 150 characters | +| min_values? | integer | Minimum number of items that must be chosen (defaults to 1); min 0, max 25 | +| max_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 | +| required?\* | boolean | Whether the string select is required to answer in a modal (defaults to `true`) | +| disabled?\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) | + +\* The `required` field is only available for String Selects in modals. It is ignored in messages. + +\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them. + +###### Select Option Structure + +| Field | Type | Description | +|--------------|-----------------------------------------------------------------------|----------------------------------------------------------| +| label | string | User-facing name of the option; max 100 characters | +| value | string | Dev-defined value of the option; max 100 characters | +| description? | string | Additional description of the option; max 100 characters | +| emoji? | partial [emoji](/developers/docs/resources/emoji#emoji-object) object | `id`, `name`, and `animated` | +| default? | boolean | Will show this option as selected by default | + +###### String Select Interaction Response Structure + +| Field | Type | Description | +|------------------|------------------|----------------------------------------------------------------| +| type\* | integer | `3` for a String Select | +| component_type\* | integer | `3` for a String Select | +| id | integer | Unique identifier for the component | +| custom_id | string | Developer-defined identifier for the input; max 100 characters | +| values | array of strings | The text of the selected options | + +\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned. + +###### Examples + + + + + ![Example of a String Select with three options](/images/components/string-select.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 1, // ComponentType.ACTION_ROW, + "id": 1, + "components": [ + { + "type": 3, // ComponentType.STRING_SELECT + "id": 2, + "custom_id": "favorite_bug", + "placeholder": "Favorite bug?", + "options": [ + { + "label": "Ant", + "value": "ant", + "description": "(best option)", + "emoji": {"name": "🐜"} + }, + { + "label": "Butterfly", + "value": "butterfly", + "emoji": {"name": "🦋"} + }, + { + "label": "Caterpillar", + "value": "caterpillar", + "emoji": {"name": "🐛"} + } + ] + } + ] + } + ] + } + ``` + + + + + + When a user interacts with a StringSelect in a message, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 3, // InteractionType.MESSAGE_COMPONENT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "component_type": 3, // ComponentType.STRING_SELECT + "custom_id": "favorite_bug", + "values": [ + "butterfly", + ] + }, + } + ``` + + + + + + + ![Example of a modal with a String Select](/images/components/modal-string-select.webp) + + + ```json + { + "type": 9, // InteractionCallbackType.MODAL + "data": { + "custom_id": "bug_modal", + "title": "Bug Survey", + "components": [ + { + "type": 18, // ComponentType.LABEL + "id": 1, + "label": "Favorite bug?", + "component": { + "type": 3, // ComponentType.STRING_SELECT + "id": 2, + "custom_id": "favorite_bug", + "placeholder": "Ants are the best", + "options": [ + { + "label": "Ant", + "value": "ant", + "description": "(best option)", + "emoji": {"name": "🐜"} + }, + { + "label": "Butterfly", + "value": "butterfly", + "emoji": {"name": "🦋"} + }, + { + "label": "Caterpillar", + "value": "caterpillar", + "emoji": {"name": "🐛"} + } + ] + } + } + ] + } + } + ``` + + + + + When a user submits a modal that contains a String Select, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 5, // InteractionType.MODAL_SUBMIT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "custom_id": "bug_modal", + "components": [ + { + "type": 18, // ComponentType.LABEL + "id": 1, + "component": { + "type": 3, // ComponentType.STRING_SELECT + "id": 2, + "custom_id": "favorite_bug", + "values": [ + "butterfly", + ] + } + } + ] + }, + } + ``` + + +------------ +## Text Input + +Text Input is an interactive component that allows users to enter free-form text responses in modals. It supports both short, single-line inputs and longer, multi-line paragraph inputs. + +Text Inputs can only be used within modals and must be placed inside a [Label](/developers/docs/components/reference#label). + + +We no longer recommend using Text Input within an [Action Row](/developers/docs/components/reference#action-row) in modals. Going forward all Text Inputs should be placed inside a [Label](/developers/docs/components/reference#label) component. + + +###### Text Input Structure + +| Field | Type | Description | +|--------------|---------|--------------------------------------------------------------------------------------------| +| type | integer | `4` for a text input | +| id? | integer | Optional identifier for component | +| custom_id | string | Developer-defined identifier for the input; max 100 characters | +| style | integer | The [Text Input Style](/developers/docs/components/reference#text-input-text-input-styles) | +| min_length? | integer | Minimum input length for a text input; min 0, max 4000 | +| max_length? | integer | Maximum input length for a text input; min 1, max 4000 | +| required? | boolean | Whether this component is required to be filled (defaults to `true`) | +| value? | string | Pre-filled value for this component; max 4000 characters | +| placeholder? | string | Custom placeholder text if the input is empty; max 100 characters | + + +The `label` field on a Text Input is deprecated in favor of `label` and `description` on the [Label](/developers/docs/components/reference#label) component. + + +###### Text Input Styles + +| Name | Value | Description | +|-----------|-------|-------------------| +| Short | 1 | Single-line input | +| Paragraph | 2 | Multi-line input | + +###### Text Input Interaction Response Structure + +| Field | Type | Description | +|-----------|---------|----------------------------------------------------------------| +| type | integer | `4` for a Text Input | +| id | integer | Unique identifier for the component | +| custom_id | string | Developer-defined identifier for the input; max 100 characters | +| value | string | The user's input text | + +###### Examples + + + + + ![A modal with Text Input in a Label](/images/components/modal-label.webp) + + + ```json + { + "type": 9, // InteractionCallbackType.MODAL + "data": { + "custom_id": "game_feedback_modal", + "title": "Game Feedback", + "components": [ + { + "type": 18, // ComponentType.LABEL + "label": "What did you find interesting about the game?", + "description": "Please give us as much detail as possible so we can improve the game!", + "component": { + "type": 4, // ComponentType.TEXT_INPUT + "custom_id": "game_feedback", + "style": 2, + "min_length": 100, + "max_length": 4000, + "placeholder": "Write your feedback here...", + "required": true + } + } + ] + } + } + ``` + + + + + When a user submits a modal that contains a TextInput, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 5, // InteractionType.MODAL_SUBMIT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "custom_id": "game_feedback_modal", + "components": [ + { + "type": 18, // ComponentType.LABEL + "id": 1, + "component": { + "type": 4, // ComponentType.TEXT_INPUT + "id": 2, + "custom_id": "game_feedback", + "value": "The recent changes to acceleration feel much better, but shadows still need help" + } + } + ] + }, + } + ``` + + +-------------- +## User Select + +A User Select is an interactive component that allows users to select one or more users in a message or modal. Options are automatically populated based on the server's available users. + +User Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s) your app receives an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure). + +User Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/docs/components/reference#action-row) in messages and a [Label](/developers/docs/components/reference#label) in modals. + +###### User Select Structure + +| Field | Type | Description | +|-----------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| type | integer | `5` for user select | +| id? | integer | Optional identifier for component | +| custom_id | string | ID for the select menu; max 100 characters | +| placeholder? | string | Placeholder text if nothing is selected; max 150 characters | +| default_values? | array of [default value objects](/developers/docs/components/reference#user-select-select-default-value-structure) | List of default values for auto-populated select menu components; number of default values must be in the range defined by `min_values` and `max_values` | +| min_values? | integer | Minimum number of items that must be chosen (defaults to 1); min 0, max 25 | +| max_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 | +| required?\* | boolean | Whether the user select is required to answer in a modal (defaults to `true`) | +| disabled?\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) | + +\* The `required` field is only available for User Selects in modals. It is ignored in messages. + +\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them. + +###### Select Default Value Structure + +| Field | Type | Description | +|-------|-----------|-------------------------------------------------------------------------------| +| id | snowflake | ID of a user, role, or channel | +| type | string | Type of value that `id` represents. Either `"user"`, `"role"`, or `"channel"` | + +###### User Select Interaction Response Structure + +| Field | Type | Description | +|------------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------| +| type\* | integer | `5` for a User Select | +| component_type\* | integer | `5` for a User Select | +| id | integer | Unique identifier for the component | +| custom_id | string | Developer-defined identifier for the input; max 100 characters | +| resolved | [resolved data](/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options | +| values | array of snowflakes | IDs of the selected users | + +\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned. + +###### Examples + + + + + ![Example of a User Select with two people and an app in a server](/images/components/user-select.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 1, // ComponentType.ACTION_ROW + "components": [ + { + "type": 5, // ComponentType.USER_SELECT + "custom_id": "user_select", + "placeholder": "Select a user" + } + ] + } + ] + } + ``` + + + + + When a user interacts with a User Select in a message, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + + `members` and `users` may both be present in the `resolved` object when a user is selected. + + + ```json + { + "type": 3, // InteractionType.MESSAGE_COMPONENT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "component_type": 5, // ComponentType.USER_SELECT + "id": 2, + "custom_id": "user_select", + "values": [ + "1111111111111111111", + ], + "resolved": { + "members": { + "1111111111111111111": { + "avatar": null, + "banner": null, + "collectibles": null, + "communication_disabled_until": null, + "flags": 0, + "joined_at": "2025-05-16T22:51:16.692000+00:00", + "nick": null, + "pending": false, + "permissions": "2248473465835073", + "premium_since": null, + "roles": [ + "2222222222222222222" + ], + "unusual_dm_activity_until": null + } + }, + "users": { + "1111111111111111111": { + "avatar": "d54e87d20539fe9aad2f2cebe56809a2", + "avatar_decoration_data": null, + "bot": true, + "clan": null, + "collectibles": null, + "discriminator": "9062", + "display_name_styles": null, + "global_name": null, + "id": "1111111111111111111", + "primary_guild": null, + "public_flags": 524289, + "username": "ExampleBot" + } + } + } + }, + } + ``` + + + + + + ![Example of a modal with a User Select](/images/components/modal-user-select.webp) + + + ```json + { + "type": 9, + "data": { + "custom_id": "user_modal", + "title": "User Chooser", + "components": [ + { + "type": 18, // ComponentType.LABEL + "label": "Choose your users", + "component": { + "type": 5, // ComponentType.USER_SELECT + "custom_id": "user_selected", + "max_values": 5, + "required": true + } + } + ] + } + } + ``` + + + + + When a user submits a modal that contains a User Select, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 5, // InteractionType.MODAL_SUBMIT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "custom_id": "user_modal", + "components": [ + { + "component": { + "custom_id": "user_selected", + "id": 2, + "type": 5, + "values": [ + "11111111111111111" + ] + }, + "id": 1, + "type": 18 + } + ], + "resolved": { + "members": { + "11111111111111111": { + "avatar": null, + "banner": null, + "collectibles": null, + "communication_disabled_until": null, + "flags": 0, + "joined_at": "2025-04-02T23:07:21.476000+00:00", + "nick": "Ant", + "pending": false, + "permissions": "4503599627370495", + "premium_since": null, + "roles": [ + "1357409927680889032" + ], + "unusual_dm_activity_until": null + } + }, + "users": { + "11111111111111111": { + "avatar": "a_b15bd8ee42e3c3d9a7de129fee60bc84", + "avatar_decoration_data": null, + "clan": null, + "collectibles": { + "nameplate": { + "asset": "nameplates/spell/white_mana/", + "expires_at": null, + "label": "COLLECTIBLES_SPELL_WHITE_MANA_NP_A11Y", + "palette": "bubble_gum", + "sku_id": "1379220459203072050" + } + }, + "discriminator": "0", + "display_name_styles": { + "colors": [ + 16777215 + ], + "effect_id": 4, + "font_id": 3 + }, + "global_name": "Anthony", + "id": "11111111111111111", + "primary_guild": null, + "public_flags": 65, + "username": "actuallyanthony" + } + } + } + } + } + ``` + + +-------------- +## Role Select + +A Role Select is an interactive component that allows users to select one or more roles in a message or modal. Options are automatically populated based on the server's available roles. + +Role Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s) your app receives an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure). + +Role Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/docs/components/reference#action-row) in messages and a [Label](/developers/docs/components/reference#label) in modals. + +###### Role Select Structure + +| Field | Type | Description | +|-----------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| type | integer | `6` for role select | +| id? | integer | Optional identifier for component | +| custom_id | string | ID for the select menu; max 100 characters | +| placeholder? | string | Placeholder text if nothing is selected; max 150 characters | +| default_values? | array of [default value objects](/developers/docs/components/reference#user-select-select-default-value-structure) | List of default values for auto-populated select menu components; number of default values must be in the range defined by `min_values` and `max_values` | +| min_values? | integer | Minimum number of items that must be chosen (defaults to 1); min 0, max 25 | +| max_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 | +| required?\* | boolean | Whether the role select is required to answer in a modal (defaults to `true`) | +| disabled?\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) | + +\* The `required` field is only available for Role Selects in modals. It is ignored in messages. + +\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them. + +###### Role Select Interaction Response Structure + +| Field | Type | Description | +|------------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------| +| type\* | integer | `6` for a Role Select | +| component_type\* | integer | `6` for a Role Select | +| id | integer | Unique identifier for the component | +| custom_id | string | Developer-defined identifier for the input; max 100 characters | +| resolved | [resolved data](/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options | +| values | array of snowflakes | IDs of the selected roles | + +\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned. + +###### Examples + + + + + ![Example of a Role Select allowing up to 3 choices](/images/components/role-select.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 1, // ComponentType.ACTION_ROW + "components": [ + { + "type": 6, // ComponentType.ROLE_SELECT + "custom_id": "role_ids", + "placeholder": "Which roles?", + "min_values": 1, + "max_values": 3 + } + ] + } + ] + } + ``` + + + + + When a user interacts with a Role Select in a message, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 3, // InteractionType.MESSAGE_COMPONENT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "component_type": 6, // ComponentType.ROLE_SELECT + "id": 2, + "custom_id": "role_ids", + "values": [ + "222222222222222222", + ], + "resolved": { + "roles": { + "222222222222222222": { + "color": 12745742, + "colors": { + "primary_color": 12745742, + "secondary_color": null, + "tertiary_color": null + }, + "description": null, + "flags": 0, + "hoist": false, + "icon": null, + "id": "222222222222222222", + "managed": false, + "mentionable": true, + "name": "Developer", + "permissions": "0", + "position": 2, + "unicode_emoji": "🔧" + } + } + } + }, + } + ``` + + + + + + ![Example of a modal with a Role Select](/images/components/modal-role-select.webp) + + + ```json + { + "type": 9, + "data": { + "custom_id": "role_modal", + "title": "Role Select", + "components": [ + { + "type": 18, + "label": "Select which roles to assign", + "component": { + "type": 6, + "custom_id": "roles_selected", + "max_values": 10, + "required": true + } + } + ] + } + } + ``` + + + + + When a user submits a modal that contains a Role Select, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 5, // InteractionType.MODAL_SUBMIT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "custom_id": "role_modal", + "components": [ + { + "component": { + "custom_id": "roles_selected", + "id": 2, + "type": 6, + "values": [ + "1362213912946147499", + "1357409927680889032" + ] + }, + "id": 1, + "type": 18 + } + ], + "resolved": { + "roles": { + "1357409927680889032": { + "color": 7419530, + "colors": { + "primary_color": 7419530, + "secondary_color": null, + "tertiary_color": null + }, + "description": null, + "flags": 0, + "hoist": true, + "icon": null, + "id": "1357409927680889032", + "managed": false, + "mentionable": true, + "name": "Player", + "permissions": "2249596494938111", + "position": 3, + "unicode_emoji": "🎮" + }, + "1362213912946147499": { + "color": 11342935, + "colors": { + "primary_color": 11342935, + "secondary_color": null, + "tertiary_color": null + }, + "description": null, + "flags": 0, + "hoist": false, + "icon": null, + "id": "1362213912946147499", + "managed": false, + "mentionable": false, + "name": "Mod", + "permissions": "0", + "position": 1, + "unicode_emoji": "🔨" + } + } + } + } + } + ``` + + +--------------------- +## Mentionable Select + +A Mentionable Select is an interactive component that allows users to select one or more mentionables in a message or modal. Options are automatically populated based on available mentionables in the server. + +Mentionable Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s), your app receives an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure). + +Mentionable Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/docs/components/reference#action-row) in messages and a [Label](/developers/docs/components/reference#label) in modals. + +###### Mentionable Select Structure + +| Field | Type | Description | +|-----------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| type | integer | `7` for mentionable select | +| id? | integer | Optional identifier for component | +| custom_id | string | ID for the select menu; max 100 characters | +| placeholder? | string | Placeholder text if nothing is selected; max 150 characters | +| default_values? | array of [default value objects](/developers/docs/components/reference#user-select-select-default-value-structure) | List of default values for auto-populated select menu components; number of default values must be in the range defined by `min_values` and `max_values` | +| min_values? | integer | Minimum number of items that must be chosen (defaults to 1); min 0, max 25 | +| max_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 | +| required?\* | boolean | Whether the mentionable select is required to answer in a modal (defaults to `true`) | +| disabled?\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) | + +\* The `required` field is only available for Mentionable Selects in modals. It is ignored in messages. + +\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them. + +###### Mentionable Select Interaction Response Structure + +| Field | Type | Description | +|------------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------| +| type\* | integer | `7` for a Mentionable Select | +| component_type\* | integer | `7` for a Mentionable Select | +| id | integer | Unique identifier for the component | +| custom_id | string | Developer-defined identifier for the input; max 100 characters | +| resolved | [resolved data](/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options | +| values | array of snowflakes | IDs of the selected mentionables | + +\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned. + +###### Examples + + + + + ![Example of a Mentionable Select](/images/components/mentionable-select.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 1, // ComponentType.ACTION_ROW + "components": [ + { + "type": 7, // ComponentType.MENTIONABLE_SELECT + "custom_id": "who_to_ping", + "placeholder": "Who?", + } + ] + } + ] + } + ``` + + + + + When a user interacts with a Mentionable Select in a message, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + + `members` and `users` may both be present in the `resolved` object when a user is selected. + + + ```json + { + "type": 3, // InteractionType.MESSAGE_COMPONENT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "component_type": 7, // ComponentType.MENTIONABLE_SELECT + "id": 2, + "custom_id": "who_to_ping", + "values": [ + "111111111111111111", + "222222222222222222", + ], + "resolved": { + "members": { + "1111111111111111111": { + "avatar": null, + "banner": null, + "collectibles": null, + "communication_disabled_until": null, + "flags": 0, + "joined_at": "2025-05-16T22:51:16.692000+00:00", + "nick": null, + "pending": false, + "permissions": "2248473465835073", + "premium_since": null, + "roles": [ + "2222222222222222222" + ], + "unusual_dm_activity_until": null + } + }, + "users": { + "1111111111111111111": { + "avatar": "d54e87d20539fe9aad2f2cebe56809a2", + "avatar_decoration_data": null, + "bot": true, + "clan": null, + "collectibles": null, + "discriminator": "9062", + "display_name_styles": null, + "global_name": null, + "id": "1111111111111111111", + "primary_guild": null, + "public_flags": 524289, + "username": "ExampleBot" + } + }, + "roles": { + "222222222222222222": { + "color": 12745742, + "colors": { + "primary_color": 12745742, + "secondary_color": null, + "tertiary_color": null + }, + "description": null, + "flags": 0, + "hoist": false, + "icon": null, + "id": "222222222222222222", + "managed": false, + "mentionable": true, + "name": "Developer", + "permissions": "0", + "position": 2, + "unicode_emoji": "🔧" + } + } + } + }, + } + ``` + + + + + + ![Example of a modal with a Mentionable Select](/images/components/modal-mentionable-select.webp) + + + ```json + { + "type": 9, + "data": { + "custom_id": "mentionable_modal", + "title": "Unmentionables", + "components": [ + { + "type": 18, + "label": "Who gets mentioned?", + "component": { + "type": 7, + "custom_id": "mentionables_selected", + "required": true + } + } + ] + } + } + ``` + + + + + When a user submits a modal that contains a Mentionable Select, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 5, // InteractionType.MODAL_SUBMIT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "custom_id": "mentionable_modal", + "components": [ + { + "component": { + "custom_id": "mentionables_selected", + "id": 2, + "type": 7, + "values": [ + "1361539726405926952" + ] + }, + "id": 1, + "type": 18 + } + ], + "resolved": { + "roles": { + "1361539726405926952": { + "color": 12745742, + "colors": { + "primary_color": 12745742, + "secondary_color": null, + "tertiary_color": null + }, + "description": null, + "flags": 0, + "hoist": false, + "icon": null, + "id": "1361539726405926952", + "managed": false, + "mentionable": true, + "name": "Developer", + "permissions": "0", + "position": 2, + "unicode_emoji": "🔧" + } + } + } + } + } + ``` + + +----------------- +## Channel Select + +A Channel Select is an interactive component that allows users to select one or more channels in a message or modal. Options are automatically populated based on available channels in the server and can be filtered by channel types. + +Channel Selects can be configured for both single-select and multi-select behavior. When a user finishes making their choice(s) your app receives an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure). + +Channel Selects are available in messages and modals. They must be placed inside an [Action Row](/developers/docs/components/reference#action-row) in messages and a [Label](/developers/docs/components/reference#label) in modals. + +###### Channel Select Structure + +| Field | Type | Description | +|-----------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| +| type | integer | `8` for channel select | +| id? | integer | Optional identifier for component | +| custom_id | string | ID for the select menu; max 100 characters | +| channel_types? | array of [channel types](/developers/docs/resources/channel#channel-object-channel-types) | List of channel types to include in the channel select component | +| placeholder? | string | Placeholder text if nothing is selected; max 150 characters | +| default_values? | array of [default value objects](/developers/docs/components/reference#user-select-select-default-value-structure) | List of default values for auto-populated select menu components; number of default values must be in the range defined by `min_values` and `max_values` | +| min_values? | integer | Minimum number of items that must be chosen (defaults to 1); min 0, max 25 | +| max_values? | integer | Maximum number of items that can be chosen (defaults to 1); max 25 | +| required?\* | boolean | Whether the channel select is required to answer in a modal (defaults to `true`) | +| disabled?\*\* | boolean | Whether select menu is disabled in a message (defaults to `false`) | + +\* The `required` field is only available for Channel Selects in modals. It is ignored in messages. + +\*\* Using `disabled` in a modal will result in an error. Modals can not currently have disabled components in them. + +###### Channel Select Interaction Response Structure + +| Field | Type | Description | +|------------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------| +| type\* | integer | `8` for a Channel Select | +| component_type\* | integer | `8` for a Channel Select | +| id | integer | Unique identifier for the component | +| custom_id | string | Developer-defined identifier for the input; max 100 characters | +| resolved | [resolved data](/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options | +| values | array of snowflakes | IDs of the selected channels | + +\* In message interaction responses `component_type` will be returned and in modal interaction responses `type` will be returned. + +###### Examples + + + + + ![Example of a Channel Select for text channels](/images/components/channel-select.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 1, // ComponentType.ACTION_ROW + "components": [ + { + "type": 8, // ComponentType.CHANNEL_SELECT + "custom_id": "notification_channel", + "channel_types": [0], // ChannelType.TEXT + "placeholder": "Which text channel?" + } + ] + } + ] + } + ``` + + + + + When a user interacts with a Channel Select in a message, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 3, // InteractionType.MESSAGE_COMPONENT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "component_type": 8, // ComponentType.CHANNEL_SELECT + "id": 2, + "custom_id": "notification_channel", + "values": [ + "333333333333333333", + ], + "resolved": { + "channels": { + "333333333333333333": { + "flags": 0, + "guild_id": "44444444444444444", + "id": "333333333333333333", + "last_message_id": null, + "name": "playtesting", + "nsfw": false, + "parent_id": "5555555555555555", + "permissions": "4503599627370495", + "position": 1, + "rate_limit_per_user": 0, + "topic": null, + "type": 0 // ChannelType.TEXT + } + } + } + }, + } + ``` + + + + + + ![Example of a modal with a Channel Select](/images/components/modal-channel-select.webp) + + + ```json + { + "type": 9, + "data": { + "custom_id": "channel_modal", + "title": "Lockdown", + "components": [ + { + "type": 18, + "label": "Which channel should be locked?", + "component": { + "type": 8, + "custom_id": "channel_selected", + "required": true + } + } + ] + } + } + ``` + + + + + When a user submits a modal that contains a Channel Select, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 5, // InteractionType.MODAL_SUBMIT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "custom_id": "channel_modal", + "components": [ + { + "component": { + "custom_id": "channel_selected", + "id": 2, + "type": 8, + "values": [ + "1357483683627663450" + ] + }, + "id": 1, + "type": 18 + } + ], + "resolved": { + "channels": { + "1357483683627663450": { + "flags": 0, + "guild_id": "1111111111111111", + "id": "1357483683627663450", + "last_message_id": null, + "name": "playtesting", + "nsfw": false, + "parent_id": "1357129309164404938", + "permissions": "4503599627370495", + "position": 1, + "rate_limit_per_user": 0, + "topic": null, + "type": 0 + } + } + } + } + } + ``` + + +--------- +## Section + +A Section is a top-level layout component that allows you to contextually associate content with an accessory component. +The typical use-case is to contextually associate [text content](/developers/docs/components/reference#text-display) with +an [accessory](/developers/docs/components/reference#section-section-accessory-components). + +Sections are currently only available in messages. + + + To use this component in messages you must send the [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (IS_COMPONENTS_V2) which can be activated on a per-message basis. + + +###### Section Structure + +| Field | Type | Description | +|------------|-------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| +| type | integer | `9` for section component | +| id? | integer | Optional identifier for component | +| components | array of [section child components](/developers/docs/components/reference#section-section-child-components) | One to three child components representing the content of the section that is contextually associated to the accessory | +| accessory | [section accessory component](/developers/docs/components/reference#section-section-accessory-components) | A component that is contextually associated to the content of the section | + + + Don't hardcode `components` to contain only text components. We may add other components in the future. Similarly, `accessory` may be expanded to include other components in the future. + + +###### Section Child Components + +| Available Components | +|--------------------------------------------------------------------| +| [Text Display](/developers/docs/components/reference#text-display) | + +###### Section Accessory Components + +| Available Components | +|--------------------------------------------------------------| +| [Button](/developers/docs/components/reference#button) | +| [Thumbnail](/developers/docs/components/reference#thumbnail) | + +###### Examples + + + + + ![Example of a Section showing a fake game changelog and a thumbnail](/images/components/section.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 9, // ComponentType.SECTION + "components": [ + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "# Real Game v7.3" + }, + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "Hope you're excited, the update is finally here! Here are some of the changes:\n- Fixed a bug where certain treasure chests wouldn't open properly\n- Improved server stability during peak hours\n- Added a new type of gravity that will randomly apply when the moon is visible in-game\n- Every third thursday the furniture will scream your darkest secrets to nearby npcs" + }, + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "-# That last one wasn't real, but don't use voice chat near furniture just in case..." + } + ], + "accessory": { + "type": 11, // ComponentType.THUMBNAIL + "media": { + "url": "https://websitewithopensourceimages/gamepreview.webp" + } + } + } + ] + } + ``` + + +--------------- +## Text Display + +A Text Display is a content component that allows you to add markdown formatted text, including mentions (users, roles, etc) and emojis. +The behavior of this component is extremely similar to the [`content` field of a message](/developers/docs/resources/message#message-object), but allows you to add multiple text components, controlling the layout of your message. + +When sent in a message, pingable mentions (@user, @role, etc) present in this component will ping and send notifications based on the +value of the [allowed mention object](/developers/docs/resources/message#allowed-mentions-object) set in [`message.allowed_mentions`](/developers/docs/resources/message#message-object). + + + To use this component in messages you must send the [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (IS_COMPONENTS_V2) which can be activated on a per-message basis. + + +###### Text Display Structure + +| Field | Type | Description | +|---------|---------|--------------------------------------------------| +| type | integer | `10` for text display | +| id? | integer | Optional identifier for component | +| content | string | Text that will be displayed similar to a message | + +###### Text Display Interaction Response Structure + +| Field | Type | Description | +|-------|---------|-------------------------------------| +| type | integer | `10` for a Text Display | +| id | integer | Unique identifier for the component | + +###### Examples + + + + + ![Example of a Text Display with markdown](/images/components/text-display.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "# This is a Text Display\nAll the regular markdown rules apply\n- You can make lists\n- You can use `code blocks`\n- You can use [links](http://watchanimeattheoffice.com/)\n- Even :blush: :star_struck: :exploding_head:\n- Spoiler alert: ||these too!||" + } + ] + } + ``` + + + + + + ![Example of a modal with a Text Display](/images/components/modal-text-display.webp) + + + ```json + { + "type": 9, + "data": { + "custom_id": "jail_modal", + "title": "Jail", + "components": [ + { + "type": 10, + "content": "This action will move the selected user to the selected voice channel and take away all their permissions **for 1 hour**." + }, + { + "type": 18, + "label": "Choose a user", + "component": { + "type": 5, + "custom_id": "user_selected", + "required": true + } + }, + { + "type": 18, + "label": "Where should they be sent?", + "component": { + "type": 8, + "custom_id": "channel_selected", + "channel_types": [ + 2 + ], + "required": true + } + } + ] + } + } + ``` + + +------------ +## Thumbnail +A Thumbnail is a content component that displays visual media in a small form-factor. It is intended as an accessory for +to other content, and is primarily usable with [sections](/developers/docs/components/reference#section). The media displayed is +defined by the [unfurled media item](/developers/docs/components/reference#unfurled-media-item) structure, which supports +both uploaded media and externally hosted media. + +Thumbnails are currently only available in messages as an accessory in a [section](/developers/docs/components/reference#section). + +Thumbnails currently only support images, including animated formats like GIF and WEBP. Videos are not supported at this time. + + + To use this component, you need to send the [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (IS_COMPONENTS_V2), which can be activated on a per-message basis. + + +###### Thumbnail Structure + +| Field | Type | Description | +|--------------|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| type | integer | `11` for thumbnail component | +| id? | integer | Optional identifier for component | +| media | [unfurled media item](/developers/docs/components/reference#unfurled-media-item) | A url or attachment provided as an [unfurled media item](/developers/docs/components/reference#unfurled-media-item) | +| description? | string | Alt text for the media, max 1024 characters | +| spoiler? | boolean | Whether the thumbnail should be a spoiler (or blurred out). Defaults to `false` | + +###### Examples + + + + + ![Example of a Section showing a fake game changelog and a thumbnail](/images/components/section.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 9, // ComponentType.SECTION + "components": [ + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "# Real Game v7.3" + }, + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "Hope you're excited, the update is finally here! Here are some of the changes:\n- Fixed a bug where certain treasure chests wouldn't open properly\n- Improved server stability during peak hours\n- Added a new type of gravity that will randomly apply when the moon is visible in-game\n- Every third thursday the furniture will scream your darkest secrets to nearby npcs" + }, + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "-# That last one wasn't real, but don't use voice chat near furniture just in case..." + } + ], + "accessory": { + "type": 11, // ComponentType.THUMBNAIL + "media": { + "url": "https://websitewithopensourceimages/gamepreview.webp" + } + } + } + ] + } + ``` + + +---------------- +## Media Gallery + +A Media Gallery is a top-level content component that allows you to display 1-10 media attachments in an organized gallery format. +Each item can have optional descriptions and can be marked as spoilers. + +Media Galleries are currently only available in messages. + + + To use this component in messages you must send the [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (IS_COMPONENTS_V2) which can be activated on a per-message basis. + + +###### Media Gallery Structure + +| Field | Type | Description | +|-------|------------------------------------------------------------------------------------------------------------------|-----------------------------------| +| type | integer | `12` for media gallery component | +| id? | integer | Optional identifier for component | +| items | array of [media gallery items](/developers/docs/components/reference#media-gallery-media-gallery-item-structure) | 1 to 10 media gallery items | + +###### Media Gallery Item Structure + +| Field | Type | Description | +|--------------|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| media | [unfurled media item](/developers/docs/components/reference#unfurled-media-item) | A url or attachment provided as an [unfurled media item](/developers/docs/components/reference#unfurled-media-item) | +| description? | string | Alt text for the media, max 1024 characters | +| spoiler? | boolean | Whether the media should be a spoiler (or blurred out). Defaults to `false` | + +###### Examples + + + + + ![Example of a Media Gallery showing screenshots from live webcam feeds](/images/components/media-gallery.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "Live webcam shots as of 18-04-2025 at 12:00 UTC" + }, + { + "type": 12, // ComponentType.MEDIA_GALLERY + "items": [ + { + "media": {"url": "https://livevideofeedconvertedtoimage/webcam1.webp"}, + "description": "An aerial view looking down on older industrial complex buildings. The main building is white with many windows and pipes running up the walls." + }, + { + "media": {"url": "https://livevideofeedconvertedtoimage/webcam2.webp"}, + "description": "An aerial view of old broken buildings. Nature has begun to take root in the rooftops. A portion of the middle building's roof has collapsed inward. In the distant haze you can make out a far away city." + }, + { + "media": {"url": "https://livevideofeedconvertedtoimage/webcam3.webp"}, + "description": "A street view of a downtown city. Prominently in photo are skyscrapers and a domed building" + } + ] + } + ] + } + ``` + + +------- +## File + +A File is a top-level content component that allows you to display an [uploaded file](/developers/docs/components/reference#uploading-a-file) as +an attachment to the message and reference it in the component. Each file component can only display 1 attached file, but you can upload +multiple files and add them to different file components within your payload. + +Files are currently only available in messages. + + + The File component only supports using the `attachment://` protocol in [unfurled media item](/developers/docs/components/reference#unfurled-media-item) + + + + To use this component in messages you must send the [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (IS_COMPONENTS_V2) which can be activated on a per-message basis. + + +###### File Structure + +| Field | Type | Description | +|----------|----------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------| +| type | integer | `13` for a file component | +| id? | integer | Optional identifier for component | +| file | [unfurled media item](/developers/docs/components/reference#unfurled-media-item) | This unfurled media item is unique in that it **only** supports attachment references using the `attachment://` syntax | +| spoiler? | boolean | Whether the media should be a spoiler (or blurred out). Defaults to `false` | +| name | string | The name of the file. This field is ignored and provided by the API as part of the response | +| size | integer | The size of the file in bytes. This field is ignored and provided by the API as part of the response | + +###### Examples + + + + + ![Example of a File showing a download for a game and manual](/images/components/file.webp) + + + + This example makes use of the `attachment://` protocol functionality in [unfurled media item](/developers/docs/components/reference#unfurled-media-item). + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "# New game version released for testing!\nGrab the game here:" + }, + { + "type": 13, // ComponentType.FILE + "file": { + "url": "attachment://game.zip" + } + }, + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "Latest manual artwork here:" + }, + { + "type": 13, // ComponentType.FILE + "file": { + "url": "attachment://manual.pdf" + } + } + ] + } + ``` + + +------------ +## Separator + +A Separator is a top-level layout component that adds vertical padding and visual division between other components. + +Separators are currently only available in messages. + + + To use this component in messages you must send the [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (IS_COMPONENTS_V2) which can be activated on a per-message basis. + + +###### Separator Structure + +| Field | Type | Description | +|----------|---------|-----------------------------------------------------------------------------------------| +| type | integer | `14` for separator component | +| id? | integer | Optional identifier for component | +| divider? | boolean | Whether a visual divider should be displayed in the component. Defaults to `true` | +| spacing? | integer | Size of separator padding—`1` for small padding, `2` for large padding. Defaults to `1` | + +###### Examples + + + + ![Example of a separator with large spacing dividing content](/images/components/separator.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "It's dangerous to go alone!" + }, + { + "type": 14, // ComponentType.SEPARATOR + "divider": true, + "spacing": 1 + }, + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "Take this." + } + ] + } + ``` + + +------------ +## Container + +A Container is a top-level layout component. Containers offer the ability to visually encapsulate a collection of components +and have an optional customizable accent color bar. + +Containers are currently only available in messages. + + + To use this component in messages you must send the [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (IS_COMPONENTS_V2) which can be activated on a per-message basis. + + +###### Container Structure + +| Field | Type | Description | +|---------------|-------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| +| type | integer | `17` for container component | +| id? | integer | Optional identifier for component | +| components | array of [container child components](/developers/docs/components/reference#container-container-child-components) | Child components that are encapsulated within the Container | +| accent_color? | ?integer | Color for the accent on the container as RGB from `0x000000` to `0xFFFFFF` | +| spoiler? | boolean | Whether the container should be a spoiler (or blurred out). Defaults to `false`. | + +###### Container Child Components + +| Available Components | +|----------------------------------------------------------------------| +| [Action Row](/developers/docs/components/reference#action-row) | +| [Text Display](/developers/docs/components/reference#text-display) | +| [Section](/developers/docs/components/reference#section) | +| [Media Gallery](/developers/docs/components/reference#media-gallery) | +| [Separator](/developers/docs/components/reference#separator) | +| [File](/developers/docs/components/reference#file) | + +###### Examples + + + + + ![Example of a container showing text, image, and buttons for a wild enemy encounter](/images/components/container.webp) + + + ```json + { + "flags": 32768, + "components": [ + { + "type": 17, // ComponentType.CONTAINER + "accent_color": 703487, + "components": [ + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "# You have encountered a wild coyote!" + }, + { + "type": 12, // ComponentType.MEDIA_GALLERY + "items": [ + { + "media": {"url": "https://websitewithopensourceimages/coyote.webp"}, + } + ] + }, + { + "type": 10, // ComponentType.TEXT_DISPLAY + "content": "What would you like to do?" + }, + { + "type": 1, // ComponentType.ACTION_ROW + "components": [ + { + "type": 2, // ComponentType.BUTTON + "custom_id": "pet_coyote", + "label": "Pet it!", + "style": 1 + }, + { + "type": 2, // ComponentType.BUTTON + "custom_id": "feed_coyote", + "label": "Attempt to feed it", + "style": 2 + }, + { + "type": 2, // ComponentType.BUTTON + "custom_id": "run_away", + "label": "Run away!", + "style": 4 + } + ] + } + ] + } + ] + } + ``` + + +-------- +## Label + +A Label is a top-level layout component. Labels wrap modal components with text as a label and optional description. + + + The `description` may display above or below the `component` depending on the platform. + + +###### Label Structure + +| Field | Type | Description | +|--------------|---------------------------------------------------------------------------------------------|----------------------------------------------------------------| +| type | integer | `18` for a label | +| id? | integer | Optional identifier for component | +| label | string | The label text; max 45 characters | +| description? | string | An optional description text for the label; max 100 characters | +| component | [label child component](/developers/docs/components/reference#label-label-child-components) | The component within the label | + +###### Label Child Components + +| Available Components | +|--------------------------------------------------------------------------------| +| [Text Input](/developers/docs/components/reference#text-input) | +| [String Select](/developers/docs/components/reference#string-select) | +| [User Select](/developers/docs/components/reference#user-select) | +| [Role Select](/developers/docs/components/reference#role-select) | +| [Mentionable Select](/developers/docs/components/reference#mentionable-select) | +| [Channel Select](/developers/docs/components/reference#channel-select) | +| [File Upload](/developers/docs/components/reference#file-upload) | + +###### Label Interaction Response Structure + +| Field | Type | Description | +|-----------|---------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------| +| type | integer | `18` for a Label | +| id | integer | Unique identifier for the component | +| component | [label interaction response child component](/developers/docs/components/reference#label-label-interaction-response-child-components) | The component within the label | + +###### Label Interaction Response Child Components + +| Available Components | +|----------------------------------------------------------------------------------------------------------------------------------| +| [Text Input](/developers/docs/components/reference#text-input-text-input-interaction-response-structure) | +| [String Select](/developers/docs/components/reference#string-select-string-select-interaction-response-structure) | +| [User Select](/developers/docs/components/reference#user-select-user-select-interaction-response-structure) | +| [Role Select](/developers/docs/components/reference#role-select-role-select-interaction-response-structure) | +| [Mentionable Select](/developers/docs/components/reference#mentionable-select-mentionable-select-interaction-response-structure) | +| [Channel Select](/developers/docs/components/reference#channel-select-channel-select-interaction-response-structure) | +| [File Upload](/developers/docs/components/reference#file-upload-file-upload-interaction-response-structure) | + +###### Examples + + + + + ![A modal with Text Input in a Label](/images/components/modal-label.webp) + + + ```json + { + "type": 9, // InteractionCallbackType.MODAL + "data": { + "custom_id": "game_feedback_modal", + "title": "Game Feedback", + "components": [ + { + "type": 18, // ComponentType.LABEL + "label": "What did you find interesting about the game?", + "description": "Please give us as much detail as possible so we can improve the game!", + "component": { + "type": 4, // ComponentType.TEXT_INPUT + "custom_id": "game_feedback", + "style": 2, + "min_length": 100, + "max_length": 4000, + "placeholder": "Write your feedback here...", + "required": true + } + } + ] + } + } + ``` + + +-------------- +## File Upload + +File Upload is an interactive component that allows users to upload files in modals. File Uploads can be configured to have a minimum and maximum number of files between 0 and 10, along with `required` for if the upload is required to submit the modal. The max file size a user can upload is based on the user's upload limit in that channel. + +File Uploads are available on modals. They must be placed inside a [Label](/docs/components/reference#label). + +###### File Upload Structure + +| Field | Type | Description | +|-------------|---------|--------------------------------------------------------------------------------------------------------| +| type | integer | `19` for file upload | +| id? | integer | Optional identifier for component | +| custom_id | string | ID for the file upload; max 100 characters | +| min_values? | integer | Minimum number of items that must be uploaded (defaults to 1); min 0, max 10 | +| max_values? | integer | Maximum number of items that can be uploaded (defaults to 1); max 10 | +| required? | boolean | Whether the file upload requires files to be uploaded before submitting the modal (defaults to `true`) | + +###### File Upload Interaction Response Structure + +| Field | Type | Description | +|-----------|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------| +| type | integer | `19` for a File Upload | +| id | integer | Unique identifier for the component | +| custom_id | string | Developer-defined identifier for the input; max 100 characters | +| values | array of snowflakes | IDs of the uploaded files found in the [resolved data](/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | + +###### Examples + + + + + ![Example of a modal with a File Upload](images/components/file-upload-modal-example.webp) + + + ```json + { + "type": 9, + "data": { + "custom_id": "bug_submit_modal", + "title": "Bug Submission", + "components": [ + { + "type": 18, // ComponentType.LABEL + "label": "File Upload", + "description": "Please upload a screenshot or other image that shows the bug you encountered.", + "component": { + "type": 19, // ComponentType.FILE_UPLOAD + "custom_id": "file_upload", + "min_values": 1, + "max_values": 10, + "required": true + } + } + ] + } + } + ``` + + + + + When a user submits a modal that contains a File Upload, this is the basic form of the interaction data payload you will receive. The full payload + is available in the [interaction](/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) reference. + + ```json + { + "type": 5, // InteractionType.MODAL_SUBMIT + ...additionalInteractionFields, // See the Interaction documentation for all fields + + "data": { + "components": [ + { + "component": { + "custom_id": "file_upload", + "id": 2, + "type": 19, + "values": [ + "111111111111111111111" + ] + }, + "id": 1, + "type": 18 + } + ], + "custom_id": "bug_submit_modal", + "resolved": { + "attachments": { + "111111111111111111111": { + "content_type": "image/png", + "ephemeral": true, + "filename": "bug.png", + "height": 604, + "id": "111111111111111111111", + "placeholder": "/PcBAoBQydvKesabEIoMsdg=", + "placeholder_version": 1, + "proxy_url": "https://media.discordapp.net/ephemeral-attachments/2222222222222222222/111111111111111111111/bug.png?ex=68dc7ce1&is=68db2b61&hm=5954f90117ccf8716ffa6c7f97a778a0d039810c9584045f400d8a9fff590768&", + "size": 241394, + "url": "https://cdn.discordapp.com/ephemeral-attachments/2222222222222222222/111111111111111111111/bug.png?ex=68dc7ce1&is=68db2b61&hm=5954f90117ccf8716ffa6c7f97a778a0d039810c9584045f400d8a9fff590768&", + "width": 2482 + } + } + } + } + } + ``` + + +---------------------- +## Unfurled Media Item + +An Unfurled Media Item is a piece of media, represented by a URL, that is used within a component. It can be +constructed via either uploading media to Discord, or by referencing external media via **a direct link** to the asset. + + + While the structure below is the full representation of an Unfurled Media Item, + **only the `url` field is settable by developers** when making requests that utilize this structure. + + All other fields will be automatically populated by Discord. + + +###### Unfurled Media Item Structure + +| Field | Type | Description | +|------------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| url | string | Supports arbitrary urls and `attachment://` references | +| proxy_url? | string | The proxied url of the media item. This field is ignored and provided by the API as part of the response | +| height? | ?integer | The height of the media item. This field is ignored and provided by the API as part of the response | +| width? | ?integer | The width of the media item. This field is ignored and provided by the API as part of the response | +| content_type? | string | The [media type](https://en.wikipedia.org/wiki/Media_type) of the content. This field is ignored and provided by the API as part of the response | +| attachment_id?\* | snowflake | The id of the uploaded attachment. This field is ignored and provided by the API as part of the response | + +\* Only present if the media item was uploaded as an attachment. + +### Uploading a file + +To upload a file with your message, you'll need to send your payload as `multipart/form-data` (rather than `application/json`) and include your file with a valid filename in your payload. Details and examples for uploading files can be found in the [API Reference](/developers/docs/reference#uploading-files). + +## Legacy Message Component Behavior + +Before the introduction of the `IS_COMPONENTS_V2` flag ([see changelog](/developers/docs/change-log#introducing-new-components-for-messages)), message components were sent in conjunction with message content. This means that you could send a message using a subset of the available components without setting the `IS_COMPONENTS_V2` flag, and the components would be included in the message content along with `content` and `embeds`. + +Additionally, components of messages preceding components V2 will contain an `id` of `0`. + +Apps using this Legacy Message Component behavior will continue to work as expected, but it is recommended to use the new `IS_COMPONENTS_V2` flag for new apps or features as they offer more options for layout and customization. + + + Legacy messages allow up to 5 action rows as top-level components + + +Legacy Message Component Example + +```json +{ + "content": "This is a message with legacy components", + "components": [ + { + "type": 1, + "components": [ + { + "type": 2, + "style": 1, + "label": "Click Me", + "custom_id": "click_me_1" + } + ] + } + ] +} +``` \ No newline at end of file diff --git a/discord/developers/docs/components/using-message-components.mdx b/discord/developers/docs/components/using-message-components.mdx new file mode 100644 index 0000000000..c852eda0d5 --- /dev/null +++ b/discord/developers/docs/components/using-message-components.mdx @@ -0,0 +1,110 @@ +--- +title: Using Message Components +sidebarTitle: Using Message Components +description: Learn how to send interactive message components. +--- + +## Overview + +Message components are a powerful way to add interactivity to your messages. They allow you to create rich, interactive experiences for your users, making it easier for them to engage with your content. + + +If you are sending components as part of a [webhook](/developers/docs/resources/webhook) you'll need to use the [`?with_components=true`](/developers/docs/resources/webhook#execute-webhook-query-string-params) query param otherwise they'll be ignored. + + +### Prerequisites + +- You must have a Discord account and be a member of the Discord Developer Portal. +- You must have a Discord application created in the Discord Developer Portal. +- You must have the necessary permissions to send messages in the channel where you want to use components. + +--- + +## Sending a Message with a Component + +To send a message with a component, you need to set the `IS_COMPONENTS_V2` flag (`1<<15`) in your message's `flags` field. This can be done when using [Message Create](/developers/docs/resources/message#create-message), [Execute Webhook](/developers/docs/resources/webhook#execute-webhook), or [responding to an interaction](/developers/docs/interactions/receiving-and-responding#create-followup-message). + + +Setting the `IS_COMPONENTS_V2` message flag cannot be reverted: once the message has been sent, the flag cannot be removed from the message when editing the message. + + +This flag indicates that the message contains components and disables traditional content and embeds. + +All content must be sent as components instead of using the standard message format. + +```json +{ + "flags": 32768, + "components": [ + { + "type": 10, + "content": "This is a message using the Text Display component" + } + ] +} +``` + +## Sending a Message with Multiple Components + +To send a message with multiple components, you can include multiple component objects in your message's `components` field. This field allows you to specify an array of components that will be included in the message. + +```json +{ + "flags": 32768, + "components": [ + { + "type": 10, + "content": "This is a Text Display component." + }, + { + "type": 10, + "content": "This is another Text Display component!" + } + ] +} +``` + +## Nesting Components with Layout Components + +You can also nest components within layout components. This gives you more flexibility in displaying information, images, and interactive components to your users. Check out the [list of components](/developers/docs/components/reference#component-object-component-types) for a complete list of available layout components. + +For example, you can create a message with an Action Row component that contains multiple Button components. + +```json +{ + "flags": 32768, + "components": [ + { + "type": 10, + "content": "This is a message with v2 components" + }, + { + "type": 1, + "components": [ + { + "type": 2, + "style": 1, + "label": "Click Me", + "custom_id": "click_me_1" + }, + { + "type": 2, + "style": 2, + "label": "Click Me Too", + "custom_id": "click_me_2" + } + ] + } + ] +} +``` + +## Using Message Components with Interactions + +When a user interacts with an interactive message component, your app will [receive an interaction event](/developers/docs/interactions/overview). This event contains information about the interaction, including the type of interaction and the component that was interacted with. + +See the [list of supported component types](/developers/docs/components/reference#component-object-component-types) for a list of interactive message components and their interaction event payloads. + +You can use this information to respond to the interaction, update the message, or perform other actions, such as displaying a modal based on the user's input. + +Check out the [Interactions documentation](/developers/docs/interactions/overview) for more information on handling interactions and responding to user input from interactive components. diff --git a/discord/developers/docs/components/using-modal-components.mdx b/discord/developers/docs/components/using-modal-components.mdx new file mode 100644 index 0000000000..fe93b17e2c --- /dev/null +++ b/discord/developers/docs/components/using-modal-components.mdx @@ -0,0 +1,84 @@ +--- +title: Using Modal Components +sidebarTitle: Using Modal Components +mode: wide +description: Guide to creating and displaying modal dialogs with text input fields. +--- + +## Overview + +Modal components are a great way to collect freeform information from your users. + +### Prerequisites + +- You must have a Discord account and be a member of the Discord Developer Portal. +- You must have a Discord application created in the Discord Developer Portal. + +--- + +## Displaying a Modal + +Displaying a modal can be done in response to an [interaction](/developers/docs/interactions/receiving-and-responding). When displaying a modal you'll use an [interaction response](/developers/docs/interactions/receiving-and-responding#interaction-response-object) with the [`MODAL`](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) interaction callback type and [modal fields](/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal). + +An example of a modal with a [String Select](/developers/docs/components/reference#string-select) and [Text Input](/developers/docs/components/reference#text-input) both wrapped in [Labels](/developers/docs/components/reference#label): + +```json +{ + "type": 9, + "data": { + "custom_id": "bug_modal", + "title": "Bug Report", + "components": [ + { + "type": 18, + "label": "What's your favorite bug?", + "component": { + "type": 3, + "custom_id": "bug_string_select", + "placeholder": "Choose...", + "options": [ + { + "label": "Ant", + "value": "ant", + "description": "(best option)", + "emoji": { + "name": "🐜" + } + }, + { + "label": "Butterfly", + "value": "butterfly", + "emoji": { + "name": "🦋" + } + }, + { + "label": "Caterpillar", + "value": "caterpillar", + "emoji": { + "name": "🐛" + } + } + ] + } + }, + { + "type": 18, + "label": "Why is it your favorite?", + "description": "Please provide as much detail as possible!", + "component": { + "type": 4, + "custom_id": "bug_explanation", + "style": 2, + "min_length": 1000, + "max_length": 4000, + "placeholder": "Write your explanation here...", + "required": true + } + } + ] + } +} +``` + +![Example of a modal with a String Select and a Text Input both wrapped in Labels](/images/components/modal.png) diff --git a/docs/topics/Community_Resources.md b/discord/developers/docs/developer-tools/community-resources.mdx similarity index 61% rename from docs/topics/Community_Resources.md rename to discord/developers/docs/developer-tools/community-resources.mdx index e87741f215..6a04dddc16 100644 --- a/docs/topics/Community_Resources.md +++ b/discord/developers/docs/developer-tools/community-resources.mdx @@ -1,6 +1,11 @@ -# Community Resources +--- +title: Community Resources +description: Discover community-built libraries, tools, and resources for Discord development. +--- -Discord has the best online community. At least, we like to think so, and this is our website, so our word is law, deal with it. Therefore it's a fact that our community is the best, and they make really awesome things that we want to share with developers to make their lives easier. From permissions calculators to embed visualizers to full libraries to interface with our API, there are so many great things that have come out of our community. +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +Our community members create amazing tools and resources that help developers build and maintain their apps. From permissions calculators and embed visualizers to comprehensive libraries for interfacing with our API, our community has built a wealth of resources to make your development process smoother and more efficient. ## Discord Developers @@ -10,18 +15,22 @@ The [Official Discord Developers server](https://discord.gg/discord-developers) Discord does not maintain official SDKs. The following table is an inexhaustive list of third-party libraries that have valid rate limit implementations, are recently maintained, and have large communities of active bots. + ###### Discord Libraries | Name | Language | -| ------------------------------------------------------------- | ---------- | +|---------------------------------------------------------------|------------| +| [Concord](https://github.com/Cogmasters/concord) | C | | [Discord.Net](https://github.com/discord-net/Discord.Net) | C# | | [DSharpPlus](https://github.com/DSharpPlus/DSharpPlus) | C# | +| [D++](https://github.com/brainboxdotcc/DPP) | C++ | +| [discljord](https://github.com/discljord/discljord) | Clojure | | [DiscordGo](https://github.com/bwmarrin/discordgo) | Go | -| [Discord4J](https://discord4j.com/) | Java | -| [Javacord](https://github.com/Javacord/Javacord) | Java | +| [Discord4J](https://github.com/Discord4J/Discord4J) | Java | | [JDA](https://github.com/DV8FromTheWorld/JDA) | Java | | [discord.js](https://github.com/discordjs/discord.js) | JavaScript | | [Eris](https://github.com/abalabahaha/eris) | JavaScript | +| [Oceanic](https://github.com/OceanicJS/Oceanic) | JavaScript | | [Discordia](https://github.com/SinisterRectus/Discordia) | Lua | | [DiscordPHP](https://github.com/discord-php/DiscordPHP) | PHP | | [discord.py](https://github.com/Rapptz/discord.py) | Python | @@ -35,16 +44,17 @@ Discord does not maintain official SDKs. The following table is an inexhaustive ## Interactions -[Interactions](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/) are the great, new way of making a Discord bot. The following open-source libraries provide help for the security and authentication checks that are mandatory if you are receiving Interactions via outgoing webhook. They also include some types for the Interactions data models. +[Interactions](/developers/docs/interactions/receiving-and-responding) are the great, new way of making a Discord bot. The following open-source libraries provide help for the security and authentication checks that are mandatory if you are receiving Interactions via outgoing webhook. They also include some types for the Interactions data models. - C# - [Discord.Net.Rest](https://github.com/discord-net/Discord.Net) + - [DSharpPlus.Http.AspNetCore](https://github.com/DSharpPlus/DSharpPlus) - Clojure - [ring-discord-auth](https://github.com/JohnnyJayJay/ring-discord-auth) - Dart - [nyxx_interactions](https://github.com/l7ssha/Nyxx) - Go - - [Tempest](https://github.com/Amatsagu/Tempest) + - [tempest](https://github.com/amatsagu/tempest) - Javascript - [discord-interactions-js](https://github.com/discord/discord-interactions-js) - [discord-slash-commands](https://github.com/MeguminSama/discord-slash-commands) and its [Deno fork](https://deno.land/x/discord_slash_commands) @@ -58,32 +68,28 @@ Discord does not maintain official SDKs. The following table is an inexhaustive - [discord-interactions-php](https://github.com/discord/discord-interactions-php) - Other - [caddy-discord-interactions-verifier](https://github.com/CarsonHoffman/caddy-discord-interactions-verifier) - - [Rauf's Slash Command Generator](https://rauf.wtf/slash) - - [Autocode Slash Command Builder](https://autocode.com/tools/discord/command-builder/) + - [BotForge's Application Commands Builder & Previewer](https://tools.botforge.org/appbuilder) - [Bsati's Slash Command Builder](https://bsati.github.io/dc-app-command-builder/) -## Game SDK Tools - -Discord Game SDK's lobby and networking layer shares similarities with other gaming platforms (i.e. Valve's Steamworks SDK). The following open source library provides developers a uniform interface for these shared features and can simplify developing for multiple platforms. Note: this library is tailored for Unity3D development. - -- [HouraiNetworking](https://github.com/HouraiTeahouse/HouraiNetworking) - -## Dispatch Tools +## OpenAPI Specification -Using Discord's [Dispatch](#DOCS_DISPATCH_DISPATCH_AND_YOU) tool for game developers publishing on Discord can sometimes involve using the same long commands multiple times. The following open-source tool helps shorten these commands for you. It will also provide webhook support for when you're pushing an update. + +The OpenAPI spec is currently in public preview and **is subject to breaking changes** + -- [JohnyTheCarrot's Dispatch CLI](https://github.com/JohnyTheCarrot/droops-dispatch) +The public preview of the [Discord HTTP API specification](https://github.com/discord/discord-api-spec) provides a standard [OpenAPI 3.1 spec](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md) for the HTTP API. ## Permission Calculators -[Permissions](#DOCS_TOPICS_PERMISSIONS/permissions) in Discord are tricky. Luckily, we've got really smart people who love us and have made some great permissions calculators. If you're making a bot for others, and you're not sure how to properly calculate permissions or generate your [authorization URL](#DOCS_TOPICS_OAUTH2/bot-authorization-flow), these are great tools: +[Permissions](/developers/docs/topics/permissions) in Discord are tricky. Luckily, we've got really smart people who love us and have made some great permissions calculators. If you're making a bot for others, and you're not sure how to properly calculate permissions or generate your [authorization URL](/developers/docs/topics/oauth2#bot-authorization-flow), these are great tools: +- [BotForge's Permissions Calculator](https://tools.botforge.org/permissions) - [FiniteReality's Permissions Calculator](https://finitereality.github.io/permissions-calculator/?v=0) - [abalabahaha's Permissions Calculator](https://discordapi.com/permissions.html#0) ## Intent Calculators -[Gateway Intents](#DOCS_TOPICS_GATEWAY/gateway-intents) are pretty confusing at first. If you're not sure what to send in your [Identify payload](#DOCS_TOPICS_GATEWAY_EVENTS/identify), then these tools may be of help: +[Gateway Intents](/developers/docs/events/gateway#gateway-intents) are pretty confusing at first. If you're not sure what to send in your [Identify payload](/developers/docs/events/gateway-events#identify), then these tools may be of help: - [ziad87's Intent Calculator](https://ziad87.net/intents/) - [Larko's Intent Calculator](https://discord-intents-calculator.vercel.app/) @@ -92,14 +98,19 @@ Using Discord's [Dispatch](#DOCS_DISPATCH_DISPATCH_AND_YOU) tool for game develo Webhooks and embeds might seem like black magic. That's because they are, but let us help you demystify them a bit. These tools can help you test how embeds will appear inside of Discord: -- [Autocode Embed Builder](https://autocode.com/tools/discord/embed-builder/) - [JohnyTheCarrot's Embed Previewer](https://github.com/JohnyTheCarrot/discord-embed-previewer) (Browser Extension) ## API Types If you're working on a project that interacts with our API, you might find an API types module useful as it provides type inspection/completion for the Discord API. -| Name | Language | -| ---------------------------------------------------------------------- | ---------- | -| [dasgo](https://github.com/switchupcb/dasgo) | Go | -| [discord-api-types](https://github.com/discordjs/discord-api-types) | JavaScript | +| Name | Language | +|---------------------------------------------------------------------|------------| +| [dasgo](https://github.com/switchupcb/dasgo) | Go | +| [discord-api-types](https://github.com/discordjs/discord-api-types) | JavaScript | + +## Game SDK Tools + +Discord Game SDK's lobby and networking layer shares similarities with other gaming platforms (i.e. Valve's Steamworks SDK). The following open source library provides developers a uniform interface for these shared features and can simplify developing for multiple platforms. Note: this library is tailored for Unity3D development. + +- [HouraiNetworking](https://github.com/HouraiTeahouse/HouraiNetworking) diff --git a/discord/developers/docs/developer-tools/embedded-app-sdk.mdx b/discord/developers/docs/developer-tools/embedded-app-sdk.mdx new file mode 100644 index 0000000000..edd112c4b7 --- /dev/null +++ b/discord/developers/docs/developer-tools/embedded-app-sdk.mdx @@ -0,0 +1,1806 @@ +--- +title: Embedded App SDK Reference +sidebarTitle: Embedded App SDK +description: Complete reference for the Embedded App SDK. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +The Embedded App SDK handles making RPC calls between your application and Discord. It is designed to assist developers in developing interactive Activities like games. + +To learn more about building Activities, check out our [Building an Activity](/developers/docs/activities/building-an-activity) tutorial or explore our [Sample Projects](/developers/docs/activities/overview#sample-projects). + +--- + +import {Monospace} from '/snippets/monospace.jsx' + +## Install the SDK + +The Embedded App SDK is available via **[npm](https://www.npmjs.com/package/@discord/embedded-app-sdk)** and **[GitHub](https://github.com/discord/embedded-app-sdk)**. + +In your frontend JavaScript project directory, install using your package manager of choice. + +``` +npm install @discord/embedded-app-sdk +``` + +After installing, you can import and instantiate the SDK in your project. + +```javascript +import { DiscordSDK } from "@discord/embedded-app-sdk"; + +const discordSdk = new DiscordSDK(DISCORD_CLIENT_ID); +``` + +--- + +## SDK Methods + +| Name | Description | +|------------------------------------------------------------------------------|-------------------------------------------------------------------------| +| [ready](/developers/docs/developer-tools/embedded-app-sdk#ready) | Resolves when your app has successfully connected to the Discord client | +| [subscribe](/developers/docs/developer-tools/embedded-app-sdk#subscribe) | Subscribe to an Embedded App SDK Event | +| [unsubscribe](/developers/docs/developer-tools/embedded-app-sdk#unsubscribe) | Unsubscribe to an Embedded App SDK Event | +| [close](/developers/docs/developer-tools/embedded-app-sdk#close) | Close an Embedded App | + +### ready() + +Resolves when your app has successfully connected to the Discord client. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +ready(): Promise\ + + +#### SDK Usage + +```js +async function setup() { + await discordSdk.ready(); + // The rest of your app logic +} +``` + +--- + +### subscribe() + +Used to subscribe to a specific event from the list of [SDK Events](/developers/docs/developer-tools/embedded-app-sdk#sdk-events). + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +Depends on the event. Refer to the Required Scopes for the specific event you are subscribing to. + +#### Signature + + +subscribe\(event: [Event](/developers/docs/developer-tools/embedded-app-sdk#sdk-events), listener: (data: EventPayloadData\) => void, ...subscribeArgs: Partial\\>): Promise\<[EventEmitter](https://nodejs.org/docs/latest/api/events.html)\> + + +#### Usage + +```js +await discordSdk.subscribe("SDK_EVENT_NAME", eventHandler, args); +``` + +--- + +### unsubscribe() + +Used to unsubscribe to [SDK Events](/developers/docs/developer-tools/embedded-app-sdk#sdk-events) that your app has already subscribed to. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + +_The `EventPayloadData` will vary based on the event you are unsubscribing from. See the specific [event](/developers/docs/developer-tools/embedded-app-sdk#sdk-events) for details._ + + +unsubscribe\(event: [Event](/developers/docs/developer-tools/embedded-app-sdk#sdk-events), listener: (data: EventPayloadData\) => void, ...subscribeArgs: Partial\\>): Promise\<[EventEmitter](https://nodejs.org/docs/latest/api/events.html)\> + + +#### Usage + +```js +await discordSdk.unsubscribe("SDK_EVENT_NAME"); +``` + +--- + +### close() + +Used to close your app with a specified code and reason. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +close(code: [RPCCloseCodes](/developers/docs/developer-tools/embedded-app-sdk#rpcclosecodes), message: string): void + + +#### SDK Usage + +```js +discordSdk.close(RPCCloseCodes.CLOSE_NORMAL, "You exited from app"); +``` + +--- + +## SDK Commands + +Developers can use these commands to interact with the Discord client. The following SDK commands are prefixed with `.commands`, such as, `discordSDK.commands.authenticate`. + +| Name | Description | +|------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| +| [authenticate](/developers/docs/developer-tools/embedded-app-sdk#authenticate) | Authenticate an existing client with your app | +| [authorize](/developers/docs/developer-tools/embedded-app-sdk#authorize) | Authorize a new client with your app | +| [captureLog](/developers/docs/developer-tools/embedded-app-sdk#capturelog) | Forward logs to your own logger | +| [encourageHardwareAcceleration](/developers/docs/developer-tools/embedded-app-sdk#encouragehardwareacceleration) | Presents a modal dialog to allow enabling of hardware acceleration | +| [getChannel](/developers/docs/developer-tools/embedded-app-sdk#getchannel) | Returns information about the channel, per the channel_id | +| [getChannelPermissions](/developers/docs/developer-tools/embedded-app-sdk#getchannelpermissions) | Returns permissions for the current user in the currently connected channel | +| [getEntitlements](/developers/docs/developer-tools/embedded-app-sdk#getentitlements) | Returns a list of entitlements for the current user | +| [getInstanceConnectedParticipants](/developers/docs/developer-tools/embedded-app-sdk#getinstanceconnectedparticipants) | Returns all participants connected to the instance | +| [getPlatformBehaviors](/developers/docs/developer-tools/embedded-app-sdk#getplatformbehaviors) | Returns information about supported platform behaviors | +| [getRelationships](/developers/docs/developer-tools/embedded-app-sdk#getrelationships) | Returns the current user's relationships | +| [getSkus](/developers/docs/developer-tools/embedded-app-sdk#getskus) | Returns a list of your app's SKUs | +| [initiateImageUpload](/developers/docs/developer-tools/embedded-app-sdk#initiateimageupload) | Presents the file upload flow in the Discord client | +| [openExternalLink](/developers/docs/developer-tools/embedded-app-sdk#openexternallink) | Allows for opening an external link from within the Discord client | +| [openInviteDialog](/developers/docs/developer-tools/embedded-app-sdk#openinvitedialog) | Presents a modal dialog with Channel Invite UI without requiring additional OAuth scopes | +| [openShareMomentDialog](/developers/docs/developer-tools/embedded-app-sdk#opensharemomentdialog) | Presents a modal dialog to share media to a channel or DM | +| [setActivity](/developers/docs/developer-tools/embedded-app-sdk#setactivity) | Modifies how your activity's rich presence is displayed in the Discord client | +| [setConfig](/developers/docs/developer-tools/embedded-app-sdk#setconfig) | Set whether or not the PIP (picture-in-picture) is interactive | +| [setOrientationLockState](/developers/docs/developer-tools/embedded-app-sdk#setorientationlockstate) | Set options for orientation and picture-in-picture (PIP) modes | +| [shareLink](/developers/docs/developer-tools/embedded-app-sdk#sharelink) | Presents a modal for the user to share a link to your activity with custom query params | +| [startPurchase](/developers/docs/developer-tools/embedded-app-sdk#startpurchase) | Launches the purchase flow for a specific SKU, per the sku_id | +| [userSettingsGetLocale](/developers/docs/developer-tools/embedded-app-sdk#usersettingsgetlocale) | Returns the current user's locale | + +### authenticate() + +Authenticate an existing client with your app. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +authenticate(args: [AuthenticateRequest](/developers/docs/developer-tools/embedded-app-sdk#authenticaterequest)): Promise\<[AuthenticateResponse](/developers/docs/developer-tools/embedded-app-sdk#authenticateresponse)\> + + +#### Usage + +```js +await discordSdk.commands.authenticate({ + access_token: 'ACCESS_TOKEN_STRING' +}); +``` + +--- + +### authorize() + +Authorize a new client with your app. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +authorize(args: [AuthorizeRequest](/developers/docs/developer-tools/embedded-app-sdk#authorizerequest)): Promise\<[AuthorizeResponse](/developers/docs/developer-tools/embedded-app-sdk#authorizeresponse)\> + + +#### Usage + +```js +await discordSdk.commands.authorize({ + client_id: DISCORD_CLIENT_ID, + response_type: "code", + state: "", + prompt: "none", + scope: [ + // "applications.builds.upload", + // "applications.builds.read", + // "applications.store.update", + // "applications.entitlements", + // "bot", + "identify", + // "connections", + // "email", + // "gdm.join", + "guilds", + // "guilds.join", + // "guilds.members.read", + // "messages.read", + // "relationships.read", + // 'rpc.activities.write', + // "rpc.notifications.read", + // "rpc.voice.write", + // "rpc.voice.read", + // "webhook.incoming", + ], +}); +``` + +--- + +### captureLog() + +Forward logs to your own logger. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +captureLog(args: [CaptureLogRequest](/developers/docs/developer-tools/embedded-app-sdk#capturelogrequest)): Promise\ + + +#### Usage + +```js +await discordSdk.commands.captureLog({ + level: 'log', + message: 'This is my log message!' +}); +``` + +--- + +### encourageHardwareAcceleration() + +Presents a modal dialog to allow enabling of hardware acceleration. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ⛔️ | ⛔️ | + +#### Required Scopes + +No scopes required + +#### Signature + + +encourageHardwareAcceleration(): Promise\<[EncourageHardwareAccelerationResponse](/developers/docs/developer-tools/embedded-app-sdk#encouragehardwareaccelerationresponse)\> + + +#### Usage + +```js +await discordSdk.commands.encourageHardwareAcceleration(); +``` + +--- + +### getChannel() + +Returns information about the channel for a provided channel ID. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +- [guilds] for guild channels +- [guilds, dm_channels.read] for GDM channels. dm_channels.read requires approval from Discord. + +#### Signature + + +getChannel(args: [GetChannelRequest](/developers/docs/developer-tools/embedded-app-sdk#getchannelrequest)): Promise\<[GetChannelResponse](/developers/docs/developer-tools/embedded-app-sdk#getchannelresponse)\> + + +#### Usage +```js +await discordSdk.commands.getChannel({ + channel_id: discordSdk.channelId, +}); +``` + +--- + +### getChannelPermissions() + +Returns permissions for the current user in the currently connected channel. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +- guilds.members.read + +#### Signature + + +getChannelPermissions(): Promise\<[GetChannelPermissionsResponse](/developers/docs/developer-tools/embedded-app-sdk#getchannelpermissionsresponse)\> + + +#### Usage + +```js +await discordSdk.commands.getChannelPermissions(); +``` + +--- + +### getEntitlements() + +Returns a list of entitlements for the current user. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +getEntitlements(): Promise\<[GetEntitlementsResponse](/developers/docs/developer-tools/embedded-app-sdk#getentitlementsresponse)\> + + +#### Usage + +```js +await discordSdk.commands.getEntitlements(); +``` + +--- + +### getInstanceConnectedParticipants() + +Returns all participants connected to the instance. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +getInstanceConnectedParticipants(): Promise\<[GetInstanceConnectedParticipantsResponse](/developers/docs/developer-tools/embedded-app-sdk#getinstanceconnectedparticipantsresponse)\> + + +#### Usage + +```js +await discordSdk.commands.getInstanceConnectedParticipants(); +``` + +--- + +### getPlatformBehaviors() + +Returns information about supported platform behaviors. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +getPlatformBehaviors(): Promise\<[GetPlatformBehaviorsResponse](/developers/docs/developer-tools/embedded-app-sdk#getplatformbehaviorsresponse)\> + + +#### Usage + +```js +await discordSdk.commands.getPlatformBehaviors(); +``` + +--- + + +### getRelationships() + +Returns the current user's relationships. + + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +- relationships.read + + +Requires Discord approval + + +#### Signature + + +getRelationships(): Promise\<[GetRelationshipsResponse](/developers/docs/developer-tools/embedded-app-sdk#getrelationshipsresponse)\> + + +#### Usage + +```js +await discordSdk.commands.getRelationships(); +``` + +--- + +### getSkus() + +Returns a list of SKU objects. SKUs without prices are automatically filtered out. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +getSkus(): Promise\<[GetSkusResponse](/developers/docs/developer-tools/embedded-app-sdk#getskusresponse)\> + + +#### Usage + +```js +await discordSdk.commands.getSkus(); +``` + +--- + +### initiateImageUpload() + +Presents the file upload flow in the Discord client. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +initiateImageUpload(): Promise\<[InitiateImageUploadResponse](/developers/docs/developer-tools/embedded-app-sdk#initiateimageuploadresponse)\> + + +#### Usage + +```js +await discordSdk.commands.initiateImageUpload(); +``` + +--- + +### openExternalLink() + +Allows for opening an external link from within the Discord client. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +openExternalLink(args: [OpenExternalLinkRequest](/developers/docs/developer-tools/embedded-app-sdk#openexternallinkrequest)): Promise\<[OpenExternalLinkResponse](/developers/docs/developer-tools/embedded-app-sdk#openexternallinkresponse)\> + + +#### Usage + +```js +await discordSdk.commands.openExternalLink({ + url: 'string url' +}); +``` + +--- + +### openInviteDialog() + +Presents a modal dialog with Channel Invite UI without requiring additional OAuth scopes. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +openInviteDialog(): Promise\ + + +#### Usage + +```js +await discordSdk.commands.openInviteDialog(); +``` + +--- + +### openShareMomentDialog() + +Presents a modal dialog to share media to a channel or direct message. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ⛔️ | ⛔️ | + +#### Required Scopes + +No scopes required + +#### Signature + + +openShareMomentDialog(args: [OpenShareMomentDialogRequest](/developers/docs/developer-tools/embedded-app-sdk#opensharemomentdialogrequest)) Promise\ + + +#### Usage + +```js +await discordSdk.commands.openShareMomentDialog({ + mediaUrl: 'DISCORD_CDN_URL' +}); +``` + +--- + +### setActivity() + +Modifies how your Activity's Rich Presence data is displayed in the Discord client. The inner `activity` field is a partial [Activity object](/developers/docs/events/gateway-events#activity-object-activity-structure). + +Read the guide on [Using Rich Presence with the Embedded App SDK](/developers/docs/rich-presence/using-with-the-embedded-app-sdk) for more usage details. + +#### Supported Platforms + +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +- rpc.activities.write + +#### Signature + + +setActivity(args: [SetActivityRequest](/developers/docs/developer-tools/embedded-app-sdk#setactivityrequest)): Promise\<[Activity](/developers/docs/developer-tools/embedded-app-sdk#activity)\> + + +#### Usage + +```js +await discordSdk.commands.setActivity({ + activity: { + type: 0, + details: 'Details', + state: 'Playing' + } +}); +``` + +--- + +### setConfig() + +Set whether or not the PIP (picture-in-picture) is interactive. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ⛔️ | ⛔️ | + +#### Required Scopes + +No scopes required + +#### Signature + + +setConfig(args: [SetConfigRequest](/developers/docs/developer-tools/embedded-app-sdk#setconfigrequest)): Promise\<[SetConfigResponse](/developers/docs/developer-tools/embedded-app-sdk#setconfigresponse)\> + + +#### Usage + +```js +await discordSdk.commands.setConfig({ + use_interactive_pip: true +}) +``` + +--- + +### setOrientationLockState() + +Locks the application to specific orientations in each of the supported layout modes. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ⛔️ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +setOrientationLockState(args: [SetOrientationLockStateRequest](/developers/docs/developer-tools/embedded-app-sdk#setorientationlockstaterequest)): Promise\ + + +#### Usage + +```js +import {Common} from '@discord/embedded-app-sdk'; + +await discordSdk.commands.setOrientationLockState({ + lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE, + picture_in_picture_lock_state: Common.OrientationLockStateTypeObject.LANDSCAPE, + grid_lock_state: Common.OrientationLockStateTypeObject.UNLOCKED +}); +``` + +--- + + +### shareLink() + +Presents the user with a modal to share a link + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +No scopes required + +#### Signature + + +shareLink(args: [ShareLinkRequest](/developers/docs/developer-tools/embedded-app-sdk#sharelinkrequest)): Promise\<[ShareLinkResponse](/developers/docs/developer-tools/embedded-app-sdk#sharelinkresponse)\>\ + + +#### Usage + +```js +const { success } = await discordSdk.commands.shareLink({ + message: 'This message is shared alongside the link!', + custom_id: 'some_custom_id', +}); +success ? console.log('User shared link!') : console.log('User did not share link!'); +``` + +--- + +### startPurchase() + +Launches the purchase flow for a specific SKU ID. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ⛔️ | ⛔️ | + +#### Required Scopes + +No scopes required + +#### Signature + + +startPurchase(args: [StartPurchaseRequest](/developers/docs/developer-tools/embedded-app-sdk#startpurchaserequest)): Promise\<[StartPurchaseResponse](/developers/docs/developer-tools/embedded-app-sdk#startpurchaseresponse)\> + + +#### Usage + +```js +await discordSdk.commands.startPurchase({sku_id: skuId}); +``` + +--- + +### userSettingsGetLocale() + +Returns the current user's locale. + +#### Supported Platforms +| Web | iOS | Android | +|-----|-----|---------| +| ✅ | ✅ | ✅ | + +#### Required Scopes + +- identify + +#### Signature + + +userSettingsGetLocale(): Promise\<[UserSettingsGetLocaleResponse](/developers/docs/developer-tools/embedded-app-sdk#usersettingsgetlocaleresponse)\> + + +#### Usage + +```js +await discordSdk.commands.userSettingsGetLocale(); +``` + +--- + +## SDK Events + +Developers may use the following events alongside the `subscribe()` SDK method to subscribe to events from Discord and supported devices. + +| Name | Description | +|-------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------| +| [READY](/developers/docs/developer-tools/embedded-app-sdk#ready) | non-subscription event sent immediately after connecting, contains server information | +| [ERROR](/developers/docs/developer-tools/embedded-app-sdk#error) | non-subscription event sent when there is an error, including command responses | +| [VOICE_STATE_UPDATE](/developers/docs/developer-tools/embedded-app-sdk#voicestateupdate) | sent when a user's voice state changes in a subscribed voice channel (mute, volume, etc.) | +| [SPEAKING_START](/developers/docs/developer-tools/embedded-app-sdk#speakingstart) | sent when a user in a subscribed voice channel speaks | +| [SPEAKING_STOP](/developers/docs/developer-tools/embedded-app-sdk#speakingstop) | sent when a user in a subscribed voice channel stops speaking | +| [ACTIVITY_LAYOUT_MODE_UPDATE](/developers/docs/developer-tools/embedded-app-sdk#activitylayoutmodeupdate) | Received when a user changes the layout mode in the Discord client | +| [ORIENTATION_UPDATE](/developers/docs/developer-tools/embedded-app-sdk#orientationupdate) | Received when screen orientation changes | +| [CURRENT_USER_UPDATE](/developers/docs/developer-tools/embedded-app-sdk#currentuserupdate) | Received when the current user object changes | +| [CURRENT_GUILD_MEMBER_UPDATE](/developers/docs/developer-tools/embedded-app-sdk#currentguildmemberupdate) | Received when the current guild member object changes | +| [THERMAL_STATE_UPDATE](/developers/docs/developer-tools/embedded-app-sdk#thermalstateupdate) | Received when Android or iOS thermal states are surfaced to the Discord app | +| [ACTIVITY_INSTANCE_PARTICIPANTS_UPDATE](/developers/docs/developer-tools/embedded-app-sdk#activityinstanceparticipantsupdate) | Received when the number of instance participants changes | +| [RELATIONSHIP_UPDATE](/developers/docs/developer-tools/embedded-app-sdk#relationshipupdate) | Received when a relationship of the current user is updated | +| [ENTITLEMENT_CREATE](/developers/docs/developer-tools/embedded-app-sdk#entitlementcreate) | Received when an entitlement is created for a SKU | + + +### READY + +Non-subscription event sent immediately after connecting, contains server information. + +#### Required Scopes + +No scopes required + +#### Sample Event Payload + +```javascript +{ + "v": 1, + "config": { + "cdn_host": "cdn.discordapp.com", + "api_endpoint": "//discord.com/api", + "environment": "production" + } +} +``` + +### ERROR + +Non-subscription event sent when there is an error, including command responses. + +#### Required Scopes + +No scopes required + +#### Sample Event Payload + +```javascript +{ + "code": 4006, + "message": "Not authenticated or invalid scope" +} +``` +--- + + +### VOICE_STATE_UPDATE + +Received when a user's voice state changes in a subscribed voice channel (mute, volume, etc). + +#### Required Scopes + +- rpc.voice.read + +#### Sample Event Payload + +```javascript +{ + "voice_state": { + "mute": false, + "deaf": false, + "self_mute": false, + "self_deaf": false, + "suppress": false + }, + "user": { + "id": "190320984123768832", + "username": "test 2", + "discriminator": "7479", + "avatar": "b004ec1740a63ca06ae2e14c5cee11f3", + "bot": false + }, + "nick": "test user 2", + "volume": 110, + "mute": false, + "pan": { + "left": 1.0, + "right": 1.0 + } +} +``` + +--- + +### SPEAKING_START + +Received when a user in a subscribed voice channel speaks. + +#### Required Scopes + +- rpc.voice.read + +#### Sample Event Payload + +```javascript +{ + "channel_id": "7173758092142710784", + "user_id": "7173758143913005056" +} +``` + +--- + + +### SPEAKING_STOP + +Received when a user in a subscribed voice channel stops speaking. + +#### Required Scopes + +- rpc.voice.read + +#### Sample Event Payload + +```javascript +{ + "channel_id": "7173758211307081728", + "user_id": "7173758261412237312" +} +``` + +--- + + +### ACTIVITY_LAYOUT_MODE_UPDATE + +Received when a user changes the layout mode in the Discord client. + +#### Required Scopes + +No scopes required + +#### Sample Event Payload + +```javascript +{ + "layout_mode": 1 +} +``` + +--- + + +### ORIENTATION_UPDATE + +Received when screen orientation changes. + +#### Required Scopes + +No scopes required + +#### Sample Event Payload + +```javascript +{ + "screen_orientation": 1 +} +``` + +--- + + +### CURRENT_USER_UPDATE + +Received when the current user object changes. + +#### Required Scopes + +- identify + +#### Sample Event Payload + +```javascript +{ + "id": "7173771622812225536", + "username": "beef_supreme", + "discriminator": "0", + "global_name": "Dis Cord", + "avatar": "abcdefg", + "avatar_decoration_data": { + "asset": "abcdefg", + "sku_id": "123456789" + }, + "bot": false, + "flags": 1, + "premium_type": 2 +} +``` + +--- + + +### CURRENT_GUILD_MEMBER_UPDATE + +Received when the current guild member object changes. + +#### Required Scopes + +- identify +- guilds.members.read + +#### Sample Event Payload + +```javascript +{ + "user_id": "7173771622812225536", + "nick": "beef_supreme", + "guild_id": "613425648685547541" + "avatar": "abcdefg", + "avatar_decoration_data": { + "asset": "abcdefg", + "sku_id": "123456789" + }, + "color_string": "#ffff00" +} +``` + +--- + + +### THERMAL_STATE_UPDATE + +Received when Android or iOS thermal states are surfaced to the Discord mobile app. + +#### Required Scopes + +No scopes required + +#### Sample Event Payload +```javascript +{ + thermal_state: 0 +} +``` + +--- + + +### ACTIVITY_INSTANCE_PARTICIPANTS_UPDATE + +Received when the number of instance participants changes. + +#### Required Scopes + +No scopes required + +#### Sample Event Payload + +```javascript +{ + "participants": [ + { + "id": "7173771622812225536", + "username": "beef_supreme", + "discriminator": "0", + "global_name": "Dis Cord", + "avatar": "abcdefg", + "avatar_decoration_data": { + "asset": "abcdefg", + "sku_id": "123456789" + }, + "bot": false, + "flags": 1, + "premium_type": 2 + } + ] +} +``` + +--- + + +### RELATIONSHIP_UPDATE + +Received when a relationship of the current user is updated. + +#### Required Scopes + +- relationships.read + + +Requires Discord approval + + +#### Sample Event Payload +```javascript +{ + "type": 1, + "user": { + "id": "7173771622812225536", + "username": "beef_supreme", + "discriminator": "0", + "global_name": "Dis Cord", + "avatar": "abcdefg", + "avatar_decoration_data": { + "asset": "abcdefg", + "sku_id": "123456789" + }, + "bot": false, + "flags": 1, + "premium_type": 2 + } +} +``` + +--- + + +### ENTITLEMENT_CREATE + + +Coming soon! Not available during Developer Preview + + +## SDK Interfaces + +#### Activity + +| Property | Type | +|-----------------|----------------------------------------------------------------------------------| +| name | string | +| type | number | +| url? | string \| null | +| created_at? | number \| null | +| timestamps? | [Timestamp](/developers/docs/developer-tools/embedded-app-sdk#timestamp) \| null | +| application_id? | string \| null | +| details? | string \| null | +| details_url? | string \| null | +| state? | string \| null | +| state_url? | string \| null | +| emoji? | [Emoji](/developers/docs/developer-tools/embedded-app-sdk#emoji) \| null | +| party? | [Party](/developers/docs/developer-tools/embedded-app-sdk#party) \| null | +| assets? | [Assets](/developers/docs/developer-tools/embedded-app-sdk#assets) \| null | +| secrets? | [Secrets](/developers/docs/developer-tools/embedded-app-sdk#secrets) \| null | +| instance? | boolean \| null | +| flags? | number \| null | + +#### Assets + +| Property | Type | +|--------------|----------------| +| large_image? | string \| null | +| large_text? | string \| null | +| large_url? | string \| null | +| small_image? | string \| null | +| small_text? | string \| null | +| small_url? | string \| null | + +#### Application + +| Property | Type | +|--------------|----------------| +| description | string | +| icon? | string \| null | +| id | string | +| rpc_origins? | string[] | +| name | string | + +#### Attachment + +| Property | Type | +|-----------|----------------| +| id | string | +| filename | string | +| size | number | +| url | string | +| proxy_url | string | +| height? | number \| null | +| width? | number \| null | + +#### AuthenticateRequest + +| Property | Type | +|---------------|----------------| +| access_token? | string \| null | + +#### AuthenticateResponse + +| Property | Type | +|--------------|------------------------------------------------------------------------------| +| access_token | string | +| user | [User](/developers/docs/developer-tools/embedded-app-sdk#user) | +| scopes | string[] | +| expires | string | +| application | [Application](/developers/docs/developer-tools/embedded-app-sdk#application) | + +#### AuthorizeRequest + +| Property | Type | +|------------------------|--------------------------------------------------------------------------------| +| client_id | string | +| scope | [OAuthScopes](/developers/docs/developer-tools/embedded-app-sdk#oauthscopes)[] | +| response_type? | 'code' | +| code_challenge? | string | +| state? | string | +| prompt? | 'none' | +| code_challenge_method? | 'S256' | + +#### AuthorizeResponse + +| Property | Type | +|----------|--------| +| code | string | + +#### AvatarDecorationData + +| Property | Type | +|----------|----------------| +| asset | string | +| sku_id? | string \| null | + +#### CaptureLogRequest + +| Property | Type | +|----------|--------------------------------------------------------------------------------| +| level | [ConsoleLevel](/developers/docs/developer-tools/embedded-app-sdk#consolelevel) | +| message | string | + +#### ChannelMention + +| Property | Type | +|----------|--------| +| id | string | +| guild_id | string | +| type | number | +| name | string | + +#### Embed + +| Property | Type | +|--------------|------------------------------------------------------------------------------------------| +| title? | string \| null | +| type? | string \| null | +| description? | string \| null | +| url? | string \| null | +| timestamp? | string \| null | +| color? | number \| null | +| footer? | [EmbedFooter](/developers/docs/developer-tools/embedded-app-sdk#embedfooter) \| null | +| image? | Image \| null | +| thumbnail? | Image \| null | +| video? | Video \| null | +| provider? | [EmbedProvider](/developers/docs/developer-tools/embedded-app-sdk#embedprovider) \| null | +| author? | [EmbedAuthor](/developers/docs/developer-tools/embedded-app-sdk#embedauthor) \| null | +| fields? | [EmbedField](/developers/docs/developer-tools/embedded-app-sdk#embedfield)[] \| null | + +#### EmbedAuthor + +| Property | Type | +|-----------------|----------------| +| name? | string \| null | +| url? | string \| null | +| icon_url? | string \| null | +| proxy_icon_url? | string \| null | + +#### EmbedField + +| Property | Type | +|----------|---------| +| name | string | +| value | string | +| inline | boolean | + +#### EmbedFooter + +| Property | Type | +|-----------------|----------------| +| text | string | +| icon_url? | string \| null | +| proxy_icon_url? | string \| null | + +#### EmbedProvider + +| Property | Type | +|----------|----------------| +| name? | string \| null | +| url? | string \| null | + +#### Emoji + +| Property | Type | +|-----------------|------------------------------------------------------------------------| +| id | string | +| name? | string \| null | +| roles? | string[] \| null | +| user? | [User](/developers/docs/developer-tools/embedded-app-sdk#user) \| null | +| require_colons? | boolean \| null | +| managed? | boolean \| null | +| animated? | boolean \| null | +| available? | boolean \| null | + +#### EncourageHardwareAccelerationResponse + +| Property | Type | +|----------|---------| +| enabled | boolean | + +#### Entitlement + +| Property | Type | +|---------------------|------------------| +| id | string | +| sku_id | string | +| application_id | string | +| user_id | string | +| gift_code_flags | number | +| type | string \| number | +| gifter_user_id? | string \| null | +| branches? | string[] \| null | +| starts_at? | string \| null | +| ends_at? | string \| null | +| parent_id? | string \| null | +| consumed? | boolean \| null | +| deleted? | boolean \| null | +| gift_code_batch_id? | string \| null | + +#### GetChannelPermissionsResponse + +| Property | Type | +|-------------|------------------| +| permissions | bigint \| string | + +#### GetChannelRequest + +| Property | Type | +|------------|--------| +| channel_id | string | + +#### GetChannelResponse + +| Property | Type | +|--------------|--------------------------------------------------------------------------------------------| +| id | string | +| type | [ChannelTypesObject](/developers/docs/developer-tools/embedded-app-sdk#channeltypesobject) | +| guild_id? | string \| null | +| name? | string \| null | +| topic? | string \| null | +| bitrate? | number \| null | +| user_limit? | number \| null | +| position? | number \| null | +| voice_states | [UserVoiceState](/developers/docs/developer-tools/embedded-app-sdk#uservoicestate)[] | +| messages | [Message](/developers/docs/developer-tools/embedded-app-sdk#message)[] | + +#### GetEntitlementsResponse + +| Property | Type | +|--------------|--------------------------------------------------------------------------------| +| entitlements | [Entitlement](/developers/docs/developer-tools/embedded-app-sdk#entitlement)[] | + +#### GetInstanceConnectedParticipantsResponse + +| Property | Type | +|--------------|------------------------------------------------------------------| +| participants | [User](/developers/docs/developer-tools/embedded-app-sdk#user)[] | + +#### GetPlatformBehaviorsResponse + +| Property | Type | +|-------------------------|---------| +| iosKeyboardResizesView? | boolean | + +#### GetRelationshipsResponse + +| Property | Type | +|---------------|----------------------------------------------------------------------------------| +| relationships | [Relationship](/developers/docs/developer-tools/embedded-app-sdk#relationship)[] | + +#### GetSkusResponse + +| Property | Type | +|----------|----------------------------------------------------------------| +| skus | [Sku](/developers/docs/developer-tools/embedded-app-sdk#sku)[] | + +#### GuildMember + +| Property | Type | +|-----------|----------------------------------------------------------------| +| user | [User](/developers/docs/developer-tools/embedded-app-sdk#user) | +| nick? | string \| null | +| roles | string[] | +| joined_at | string | +| deaf | boolean | +| mute | boolean | + +#### GuildMemberRPC + +| Property | Type | +|-------------------------|--------------------------------------------------------------------------------------------------------| +| user_id | string | +| nick? | string \| null | +| guild_id | string | +| avatar? | string \| null | +| avatar_decoration_data? | [AvatarDecorationData](/developers/docs/developer-tools/embedded-app-sdk#avatardecorationdata) \| null | +| color_string? | string \| null | + +#### Image + +| Property | Type | +|------------|----------------| +| url? | string \| null | +| proxy_url? | string \| null | +| height? | number \| null | +| width? | number \| null | + +#### InitiateImageUploadResponse + +| Property | Type | +|-----------|--------| +| image_url | string | + +#### Message + +| Property | Type | +|-------------------|--------------------------------------------------------------------------------------| +| id | string | +| channel_id | string | +| guild_id? | string \| null | +| author? | [User](/developers/docs/developer-tools/embedded-app-sdk#user) \| null | +| member? | [GuildMember](/developers/docs/developer-tools/embedded-app-sdk#guildmember) \| null | +| content | string | +| timestamp | string | +| edited_timestamp? | string \| null | +| tts | boolean | +| mention_everyone | boolean | +| mentions | [User](/developers/docs/developer-tools/embedded-app-sdk#user)[] | +| mention_roles | string[] | +| mention_channels | [ChannelMention](/developers/docs/developer-tools/embedded-app-sdk#channelmention)[] | +| attachments | [Attachment](/developers/docs/developer-tools/embedded-app-sdk#attachment)[] | +| embeds | [Embed](/developers/docs/developer-tools/embedded-app-sdk#embed)[] | +| reactions? | [Reaction](/developers/docs/developer-tools/embedded-app-sdk#reaction)[] \| null | +| nonce? | string | number \| null | +| pinned | boolean | +| webhook_id? | string \| null | +| type | number | +| activity? | [MessageActivity](/developers/docs/developer-tools/embedded-app-sdk#messageactivity) \| null | +| application? | [MessageApplication](/developers/docs/developer-tools/embedded-app-sdk#messageapplication) \| null | +| message_reference? | [MessageReference](/developers/docs/developer-tools/embedded-app-sdk#messagereference) \| null | +| flags? | number | +| stickers? | Sticker[] \| null | +| referenced_message? | [Message](/developers/docs/developer-tools/embedded-app-sdk#message) \| null | + +#### MessageActivity + +| Property | Type | +|-----------|----------------| +| type | number | +| party_id? | string \| null | + +#### MessageApplication + +| Property | Type | +|--------------|----------------| +| id | string | +| cover_image? | string \| null | +| description | string | +| icon? | string \| null | +| name | string | + +#### MessageReference + +| Property | Type | +|-------------|----------------| +| message_id? | string \| null | +| channel_id? | string \| null | +| guild_id? | string \| null | + +#### OpenExternalLinkRequest + +| Property | Type | +|----------|--------| +| url | string | + +#### OpenExternalLinkResponse + + +`{ opened: null }` is returned on Discord clients before December 2024 that do not report the open link result. + + +| Property | Type | +|----------|-----------------| +| opened | boolean \| null | + +#### OpenShareMomentDialogRequest + +| Property | Type | +|----------|--------| +| mediaUrl | string | + +#### Party + +| Property | Type | +|----------|------------------| +| id? | string \| null | +| size? | number[] \| null | + +#### Reaction + +| Property | Type | +|----------|------------------------------------------------------------------| +| count | number | +| me | boolean | +| emoji | [Emoji](/developers/docs/developer-tools/embedded-app-sdk#emoji) | + +#### Relationship + +| Property | Type | +|----------|--------------------------------------------------------------------------------| +| type | [number](/developers/docs/developer-tools/embedded-app-sdk#relationship-types) | +| user | [User](/developers/docs/developer-tools/embedded-app-sdk#user) | + +#### Secrets + +| Property | Type | +|----------|--------| +| join? | string | +| match? | string | + +#### SetActivityRequest + +| Property | Type | +|----------|------------------------------------------------------------------------| +| activity | [Activity](/developers/docs/developer-tools/embedded-app-sdk#activity) | + +#### SetConfigRequest + +| Property | Type | +|---------------------|---------| +| use_interactive_pip | boolean | + +#### SetConfigResponse + +| Property | Type | +|---------------------|---------| +| use_interactive_pip | boolean | + +#### SetOrientationLockStateRequest + +| Property | Type | +|-------------------------------|----------------------------------------------------------------------------------------------------------| +| lock_state | [OrientationLockState](/developers/docs/developer-tools/embedded-app-sdk#orientationlockstatetypeobject) | +| picture_in_picture_lock_state | [OrientationLockState](/developers/docs/developer-tools/embedded-app-sdk#orientationlockstatetypeobject) | +| grid_lock_state | [OrientationLockState](/developers/docs/developer-tools/embedded-app-sdk#orientationlockstatetypeobject) | + +#### ShareLinkRequest + +| Property | Type | +|------------|--------| +| custom_id? | string | +| message | string | + +#### ShareLinkResponse + +| Property | Type | +|----------|---------| +| success | boolean | + +#### Sku + +| Property | Type | +|----------------|----------------------------------------------------------------------------------| +| id | string | +| name | string | +| type | [SkuTypeObject](/developers/docs/developer-tools/embedded-app-sdk#skutypeobject) | +| price | [SkuPrice](/developers/docs/developer-tools/embedded-app-sdk#skuprice) | +| application_id | string | +| flags | number | +| release_date | string \| null | + +#### SkuPrice + +| Property | Type | +|----------|--------| +| amount | number | +| currency | string | + +#### StartPurchaseRequest + +| Property | Type | +|----------|--------| +| sku_id | string | + +#### StartPurchaseResponse + +| Value | +|----------------------------------------------------------------------------------------| +| [Entitlement](/developers/docs/developer-tools/embedded-app-sdk#entitlement)[] \| null | + +#### Timestamp + +| Property | Type | +|----------|--------| +| start? | number | +| end? | number | + +#### User + +| Property | Type | +|------------------------|--------------------------------------------------------------------------------------------------------| +| id | string | +| username | string | +| discriminator | string | +| global_name? | string \| null | +| avatar? | string \| null | +| avatar_decoration_data | [AvatarDecorationData](/developers/docs/developer-tools/embedded-app-sdk#avatardecorationdata) \| null | +| bot | boolean | +| flags? | number \| null | +| premium_type? | number \| null | + +#### UserSettingsGetLocaleResponse + +| Property | Type | +|----------|--------| +| locale | string | + +#### UserVoiceState + +| Property | Type | +|-------------|----------------------------------------------------------------------------| +| mute | boolean | +| nick | string | +| user | [User](/developers/docs/developer-tools/embedded-app-sdk#user) | +| voice_state | [VoiceState](/developers/docs/developer-tools/embedded-app-sdk#voicestate) | +| volume | number | + +#### Video + +| Property | Type | +|----------|----------------| +| url? | string \| null | +| height? | number \| null | +| width? | number \| null | + +#### VoiceState + +| Property | Type | +|-----------|---------| +| mute | boolean | +| deaf | boolean | +| self_mute | boolean | +| self_deaf | boolean | +| suppress | boolean | + +## SDK Enums + +#### ChannelTypesObject + +| Name | Value | +|---------------------|-------| +| UNHANDLED | -1 | +| DM | 1 | +| GROUP_DM | 3 | +| GUILD_TEXT | 0 | +| GUILD_VOICE | 2 | +| GUILD_CATEGORY | 4 | +| GUILD_ANNOUNCEMENT | 5 | +| GUILD_STORE | 6 | +| ANNOUNCEMENT_THREAD | 10 | +| PUBLIC_THREAD | 11 | +| PRIVATE_THREAD | 12 | +| GUILD_STAGE_VOICE | 13 | +| GUILD_DIRECTORY | 14 | +| GUILD_FORUM | 15 | + +#### ConsoleLevel + +| Value | +|---------| +| 'error' | +| 'log' | +| 'warn' | +| 'debug' | +| 'info' | + +#### OrientationLockStateTypeObject + +| Name | Value | +|-----------|-------| +| UNHANDLED | -1 | +| UNLOCKED | 1 | +| PORTRAIT | 2 | +| LANDSCAPE | 3 | + +#### ThermalStateTypeObject + +| Name | Value | +|-----------|-------| +| UNHANDLED | -1 | +| NOMINAL | 0 | +| FAIR | 1 | +| SERIOUS | 2 | +| CRITICAL | 3 | + +#### OrientationTypeObject + +| Name | Value | +|-----------|-------| +| UNHANDLED | -1 | +| PORTRAIT | 0 | +| LANDSCAPE | 1 | + +#### LayoutModeTypeObject + +| Name | Value | +|-----------|-------| +| UNHANDLED | -1 | +| FOCUSED | 0 | +| PIP | 1 | +| GRID | 2 | + +#### OAuthScopes + +| Value | +|------------------------------| +| 'bot' | +| 'rpc' | +| 'identify' | +| 'connections' | +| 'email' | +| 'guilds' | +| 'guilds.join' | +| 'guilds.members.read' | +| 'gdm.join' | +| 'messages.read' | +| 'rpc.notifications.read' | +| 'rpc.voice.write' | +| 'rpc.voice.read' | +| 'rpc.activities.write' | +| 'webhook.incoming' | +| 'applications.commands' | +| 'applications.builds.upload' | +| 'applications.builds.read' | +| 'applications.store.update' | +| 'applications.entitlements' | +| 'relationships.read' | +| 'activities.read' | +| 'activities.write' | +| 'dm_channels.read' | + +#### RPCCloseCodes + +| Name | Code | +|-------------------|------| +| CLOSE_NORMAL | 1000 | +| CLOSE_UNSUPPORTED | 1003 | +| CLOSE_ABNORMAL | 1006 | +| INVALID_CLIENTID | 4000 | +| INVALID_ORIGIN | 4001 | +| RATELIMITED | 4002 | +| TOKEN_REVOKED | 4003 | +| INVALID_VERSION | 4004 | +| INVALID_ENCODING | 4005 | + +#### SkuTypeObject + +| Name | Value | +|--------------|-------| +| UNHANDLED | -1 | +| APPLICATION | 1 | +| DLC | 2 | +| CONSUMABLE | 3 | +| BUNDLE | 4 | +| SUBSCRIPTION | 5 | + +#### Relationship Types + +| Value | Name | Description | +|-------|------------------|--------------------------------------------------------------------------------------------------| +| 0 | None | The user has no relationship with the other user. | +| 1 | Friend | The user is friends with the other user. | +| 2 | Blocked | The current user has blocked the target user. | +| 3 | Pending Incoming | The current user has received a friend request from the target user, but it is not yet accepted. | +| 4 | Pending Outgoing | The current user has sent a friend request to the target user, but it is not yet accepted. | +| 5 | Implicit | The Implicit type is documented for visibility, but should be unused in the SDK. | +| 6 | Suggestion | The Suggestion type is documented for visibility, but should be unused in the SDK. | diff --git a/discord/developers/docs/developer-tools/game-sdk.mdx b/discord/developers/docs/developer-tools/game-sdk.mdx new file mode 100644 index 0000000000..196cd5703f --- /dev/null +++ b/discord/developers/docs/developer-tools/game-sdk.mdx @@ -0,0 +1,1538 @@ +--- +title: Game SDK +tag: "LEGACY" +description: Legacy Game SDK documentation for integrating games with Discord. +--- + + +**The Game SDK has been archived.** + +We recommend using the [Discord Social SDK](/developers/docs/discord-social-sdk/overview) for new projects. + +Existing projects using the Game SDK will continue to work, but we encourage you to migrate to the [Discord Social SDK](/developers/docs/discord-social-sdk/overview) for new features and updates. + + +Welcome to the documentation for the Discord Game SDK! We're glad you made it. The Game SDK helps you develop your 3rd party game or app, and integrate it with Discord. + +--- + +## Getting Started + +This section will walk you through the first few steps you need to take to get up-and-running with the Game SDK. After you download the SDK and configure your app, you can find more details in the [Using the SDK section](/developers/docs/developer-tools/game-sdk#using-the-sdk). + +### Step 1: Get the SDK + +Now, let's get started. First, get the SDK. Here it is: + +- [Discord Game SDK v3.2.1](https://dl-game-sdk.discordapp.net/3.2.1/discord_game_sdk.zip) - Latest version, includes Apple silicon (aarch64) support +- [Discord Game SDK v2.5.6](https://dl-game-sdk.discordapp.net/2.5.6/discord_game_sdk.zip) - Try this version if you encounter bugs on the latest version + +There's a few things in there, but let's quickly talk about what the SDK actually _is_. Inside the `lib/` folder, you'll see `x86/` and `x86_64/` that have some `.lib`, `.bundle`, and `.dll` files. These are the things you want to distribute with your game. + +These files are comprised of two parts: a "stub", and fallback modules. What that means is that when everything is running smoothly, the DLLs will just call back to the local running Discord client to do the heavy lifting. If, however, something is wrong, like a breaking change, the files also include "fallback" modules that reflect the native SDK modules in Discord at the time that version of the SDK was published. TLDR - you don't need to worry about breaking changes. + +### Step 2: Create your App + +Next, we need to set up the application for your game. An [app](/developers/docs/resources/application) is the base "entity" in Discord for your game. + +Head over to our [developer site](https://discord.com/developers/) and create an account/log in if you haven't yet. The first thing we're going to do is create a Team. Teams are groups of developers working together on applications; you should create a team for your organization at [https://discord.com/developers/teams](https://discord.com/developers/teams). You can invite other users to join your team and work on applications together with you. + +Now that your team is created, you'll want to make an application. To do so, click on "Applications" at the top of the page and create an application. Make sure you pick your newly-created team in the `Team` dropdown. You want your team to own the application! Now that your app is made, let's dive into some more setup. + + +If you're integrating our SDK into an already-released game, there's a good chance that we may _already have_ an application in our database for your game! Reach out to our [Dev Support](https://dis.gd/devsupport) to learn more + + +First, we'll need to set an OAuth2 redirect URL. You can add `http://127.0.0.1` in there for now; this powers some behind-the-scenes stuff you don't need to worry about. + +Next, copy the **Client ID** at the top of the page. This id, also referred to as an "application id", is your game's unique identifier across Discord. Keep it handy! + +While you're here, head to the "OAuth2" section of your application and add `http://127.0.0.1` as a redirect URI for your application. This will allow us to do the OAuth2 token exchange within the Discord client. + +### Step 3: Start Coding + +Before you start developing, there are a couple of notes to keep in mind about the SDK: + +- All strings in the SDK are UTF8 strings. Make sure you've converted properly if necessary! +- The SDK is **_NOT_** threadsafe! + +With that out of the way, let's start coding. Didn't think we'd get there so fast, did ya? _Think again!_ The dropdowns are code primers for the main languages of the SDK: C#, C, and C++. They'll get you up and running with the most basic examples, and then you're off to the races. + + +- Open up that SDK zip that you downloaded. +- Copy the contents of the `lib/` folder to `Assets/Plugins` in your Unity project +- Copy the contents of the `csharp/` folder to `Assets/Plugins/DiscordGame SDK` + +From there, you'll be able to reference functions in the DLL within your scripts. A basic example of a script can be found [in this example repo](https://github.com/msciotti/discord-game-sdk-test-apps/tree/master/cs-examples/unity-examples/Assets). In this example, we attach our `DiscordController.cs` script to the Main Camera object of the default created scene. We then instantiate the SDK with: + +```cs +/* + Grab that Client ID from earlier + Discord.CreateFlags.Default will require Discord to be running for the game to work + If Discord is not running, it will: + 1. Close your game + 2. Open Discord + 3. Attempt to re-open your game + Step 3 will fail when running directly from the Unity editor + Therefore, always keep Discord running during tests, or use Discord.CreateFlags.NoRequireDiscord +*/ +var discord = new Discord.Discord(CLIENT_ID, (UInt64)Discord.CreateFlags.Default); +``` + +You're now free to use other functionality in the SDK! Make sure to call `discord.RunCallbacks()` in your main game loop; that's your `Update()` function. + +You're ready to go! Check out the rest of the documentation for more info on how to use the other pieces of the SDK. See an example of everything it can do in `examples/Program.cs` in the SDK zip file. + + + +- Open up that SDK zip that you downloaded. +- Create a folder in your project directory called `DiscordGame SDK` and copy the contents of the `csharp/` folder to it +- Build your solution then place the `.dll` in the directory of the `.exe` (either x86 or x86_64 version depending on your compile platform). If you compile for Any CPU you may need to perform additional wrapping around DLL importing (like setting the DLL directory dynamically) to make sure you load the correct DLL. + +From there, you'll be able to reference functions in the DLL within your scripts. We then instantiate the SDK with: + +```cs +/* + Grab that Client ID from earlier + Discord.CreateFlags.Default will require Discord to be running for the game to work + If Discord is not running, it will: + 1. Close your game + 2. Open Discord + 3. Attempt to re-open your game + Step 3 may fail when running directly from your editor + Therefore, always keep Discord running during tests, or use Discord.CreateFlags.NoRequireDiscord +*/ +var discord = new Discord.Discord(CLIENT_ID, (UInt64)Discord.CreateFlags.Default); +``` + +You're now free to use other functionality in the SDK! Make sure to call `discord.RunCallbacks()` in your main game loop; that's your `Update()` function. + +You're ready to go! Check out the rest of the documentation for more info on how to use the other pieces of the SDK. See an example of everything it can do in `examples/Program.cs` in the SDK zip file. + + + +Before jumping into the C binding, a word of caution. If you are using Unreal Engine 3, or need to support an older version of Visual Studio, you may at first see some unexpected crashes due to compile configurations. The way to fix this is to wrap the include statement for the Discord Game SDK header file like so: + +```c +#pragma pack(push, 8) +#include "discord_game_sdk.h" +#pragma pack(pop) +``` + +This should let you use the SDK without any further crashes. Now, on with the show! + +- Open up that SDK zip that you downloaded. +- Copy the contents of the `lib/` folder to the best location within your project for DLLs. +- Copy the contents of the `c/` folder to your source directory +- It's dangerous to go alone—take this small code block with you (to start)! + +```c +struct Application { + struct IDiscordCore* core; + struct IDiscordUsers* users; +}; + +struct Application app; +// Don't forget to memset or otherwise initialize your classes! +memset(&app, 0, sizeof(app)); + +struct IDiscordCoreEvents events; +memset(&events, 0, sizeof(events)); + +struct DiscordCreateParams params; +params.client_id = CLIENT_ID; +params.flags = DiscordCreateFlags_Default; +params.events = &events; +params.event_data = &app; + +DiscordCreate(DISCORD_VERSION, ¶ms, &app.core); +``` + +- Make sure to call `core->run_callbacks(core, 0)` in your game loop. + +You're ready to go! Check out the rest of the documentation for more info on how to use the other pieces of the SDK. See an example of everything it can do in `examples/c/main.c` in the SDK zip file. + + + +First, you'll want to copy the header files and `.cpp` files to a folder somewhere in your project directory. For ease of a quickstart example, you can put them right inside your `Source/your-project-name` folder; I'd put them in a containing folder called something like `discord-files/`. + +Second, you'll want to copy the `.dll` and `.lib` files from the `lib/x86_64` folder of the downloaded zip. These files should be put in `your-project-name/Binaries/Win64/`. For win32, take the files from `x86/` and put them, in `your-project-name/Binaries/Win32`. + +Next, we need to link these files within our project so that we can reference them. If you open up your project's `.sln` file in Visual Studio, you'll find a file called `your-project-name.Build.cs`. We're going to add the following lines of code to that file: + +```cpp +/* + ABSOLUTE_PATH_TO_DISCORD_FILES_DIRECTORY will look something like: + + "H:\\Unreal Projects\\Game SDKtest\\Source\\Game SDKtest\\discord-files\\" + + You should get this value programmatically +*/ +PublicIncludePaths.Add(ABSOLUTE_PATH_TO_DISCORD_FILES_DIRECTORY) + +/* + ABSOLUTE_PATH_TO_LIB_FILE will look something like: + + "H:\\Unreal Projects\\Game SDKtest\\Binaries\\Win64\\discord_game_sdk.dll.lib" + + You should get this value programmatically +*/ +PublicAdditionalLibraries.Add(ABSOLUTE_PATH_TO_LIB_FILE) +``` + +Now that we've got our new dependencies properly linked, we can reference them in our code. In this example, we're going to make a new `Pawn` class called `MyPawn`. It will look something like this: + +```cpp +#include "MyPawn.h" +#include "discord-files/discord.h" + +discord::Core* core{}; + +AMyPawn::AMyPawn() +{ + // Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it. + PrimaryActorTick.bCanEverTick = true; +} + +// Called when the game starts or when spawned +void AMyPawn::BeginPlay() +{ + Super::BeginPlay(); + /* + Grab that Client ID from earlier + Discord.CreateFlags.Default will require Discord to be running for the game to work + If Discord is not running, it will: + 1. Close your game + 2. Open Discord + 3. Attempt to re-open your game + Step 3 will fail when running directly from the Unreal Engine editor + Therefore, always keep Discord running during tests, or use Discord.CreateFlags.NoRequireDiscord + */ + auto result = discord::Core::Create(461618159171141643, DiscordCreateFlags_Default, &core); + discord::Activity activity{}; + activity.SetState("Testing"); + activity.SetDetails("Fruit Loops"); + core->ActivityManager().UpdateActivity(activity, [](discord::Result result) { + + }); +} + +// Called every frame +void AMyPawn::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + ::core->RunCallbacks(); +} + +// Called to bind functionality to input +void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) +{ + Super::SetupPlayerInputComponent(PlayerInputComponent); +} +``` + +Make sure you've got `core->RunCallbacks()` going every frame! + +You're ready to go! Check out the rest of the documentation for more info on how to use the other pieces of the SDK. See an example of everything it can do in `examples/cpp/main.cpp` in the SDK zip file. + + + +In your project folder, you'll want to make something like a "discord-files" folder, for organization. In that folder, copy all the `.h` and `.cpp` files from the zip. +You want to include all the header and source files respectively in your project + +![Correct Files](/images/cpp-files-sdk.png) + +In your project settings, you'll want to include the relevant library (e.g. `discord_game_sdk.dll.lib`) as an additional dependency. + +![Linked Library](/images/lib-linked-sdk.png) + +- From there, you should be able to `#include "discord-files/discord.h"`, or whatever the path to that header file is, and have access to the code. + + +## Using the SDK + +At a high level, the Discord Game SDK has a parent class, `Discord`. + +This class is in charge of the creation of a few "manager" sub-classes to interact with Discord. + +### Managers + +Each manager class contains its own functions and events used to interact with Discord in the context of the manager: + +| Name | Description | +|---------------------------------------------------------------------------|------------------------------------------------------------| +| [`ActivityManager`](/developers/docs/developer-tools/game-sdk#activities) | for Rich Presence and game invites | +| [`OverlayManager`](/developers/docs/developer-tools/game-sdk#overlay) | for interacting with Discord's built-in overlay | +| [`UserManager`](/developers/docs/developer-tools/game-sdk#users) | for fetching user data for a given id and the current user | + +### Using Functions in the SDK + +Most functions in the Discord Game SDK, uh, _function_ in a similar way. They take whatever parameters are required for the function to do its job—a user id, the requested size for an image, etc.—and a callback by means of a function pointer. That callback is fired when the function completes its work, letting you handle events without worrying about piping asynchronously-returned data to the right context. + +Some functions behave with a normal return behavior; e.g. `RelationshipManager.Count()` just returns the number directly. Don't worry, it's outlined in the docs. + + + +```c# +var userManager = discord.GetUserManager(); + +// Return via callback +userManager.GetUser(290926444748734465, (Discord.Result result, ref Discord.User otherUser) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine(otherUser.Username); + Console.WriteLine(otherUser.Id); + } +}); + + +// Return normally +userManager.OnCurrentUserUpdate += () => +{ + var currentUser = userManager.GetCurrentUser(); + Console.WriteLine(currentUser.Username); + Console.WriteLine(currentUser.Discriminator); + Console.WriteLine(currentUser.Id); +}; +``` + + + +### Environment Variables + +Discord passes a number of environment variables down to the SDK. These are accessed by various functions in the SDK and can be changed for local testing by changing the value in your local environment. + +| name | method | description | +|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| +| DISCORD_INSTANCE_ID | [Local Testing](/developers/docs/developer-tools/game-sdk#testing-locally) | the locally running instance of Discord to connect to; allows you to choose between multiple running clients | +| DISCORD_ACCESS_TOKEN | [ApplicationManager.GetOAuth2Token()](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Applications.md#getoauth2token) | the connected user's bearer token | +| DISCORD_CURRENT_LOCALE | [ApplicationManager.GetCurrentLocale()](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Applications.md#getcurrentlocale) | the language that Discord is in for the connected user | +| DISCORD_CURRENT_BRANCH | [ApplicationManager.GetCurrentBranch()](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Applications.md#getcurrentbranch) | the branch of the running application that the user has launched | + +### Error Handling + +Debugging is a pain, so before we get into the meat of the SDK, we want to make sure you're prepared for when things go awry. Within the Discord core is a function called `SetLogHook()`. It takes a `level`, which is minimum level of log message you want to listen to, and a callback function: + + + +```cs +public void LogProblemsFunction(Discord.LogLevel level, string message) +{ + Console.WriteLine("Discord:{0} - {1}", level, message); +} + +discord.SetLogHook(Discord.LogLevel.Debug, LogProblemsFunction); +``` + +You should begin your integration by setting up this callback to help you debug. Helpfully, if you put a breakpoint inside the callback function you register here, you'll be able to see the stack trace for errors you run into (as long as they fail synchronously). Take the guess work out of debugging, or hey, ignore any and all logging by setting a callback that does nothing. We're not here to judge. + + +### Testing Locally + +While integrating the Discord Game SDK, you will probably find yourself wanting to test functionality between two game clients locally, be it for networking, Rich Presence, etc. + +We know that getting a test build of a game on two separate machines can be both difficult and cumbersome. So, we've got a solution for you! + + + + +Value from environment variable `DISCORD_INSTANCE_ID` + + +By using system environment variables, you can tell the SDK in a certain game client to connect to a specific Discord client. Here's how it works: + +1. Download Discord Canary. This is our most updated build, and is good to develop against: [Windows](https://discord.com/api/download/canary?platform=win) - [Mac](https://discord.com/api/download/canary?platform=osx) +2. Download a second Discord Build. Here's our Public Test Build: [Windows](https://discord.com/api/download/ptb?platform=win) - [Mac](https://discord.com/api/download/ptb?platform=osx) +3. Open up two Discord clients. We recommend you develop against Discord Canary, so you can use PTB or Stable for your test account +4. Log in with two separate users. Make sure any test account is added to the application's App Whitelist in the portal! + +Now, in your game code, you can tell the SDK which client to connect to via the environment variable `DISCORD_INSTANCE_ID` **before initializing the SDK**. The value of the variable corresponds to the order in which you opened the clients, so `0` would connect to the first opened client, `1` the second, etc. + +###### Environment Variable Example + +```cs +// This machine opened Discord Canary first, and Discord PTB second + +// This makes the SDK connect to Canary +System.Environment.SetEnvironmentVariable("DISCORD_INSTANCE_ID", "0"); +var discord = new Discord(applicationId, Discord.CreateFlags.Default); + +// This makes the SDK connect to PTB +System.Environment.SetEnvironmentVariable("DISCORD_INSTANCE_ID", "1"); +var discord = new Discord(applicationId, Discord.CreateFlags.Default); +``` + +This will set the environment variable only within the context of the running process, so don't worry about messing up global stuff. + + +If you test with this, make sure to remove this code before pushing a production build. It will interfere with the way that Discord launches games for users. + + + + +### Data Models + +#### Result Enum + + + +| Code | value | description | +|------|---------------------------------|-------------------------------------------------------------------------------------------------| +| 0 | Ok | everything is good | +| 1 | ServiceUnavailable | Discord isn't working | +| 2 | InvalidVersion | the SDK version may be outdated | +| 3 | LockFailed | an internal error on transactional operations | +| 4 | InternalError | something on our side went wrong | +| 5 | InvalidPayload | the data you sent didn't match what we expect | +| 6 | InvalidCommand | that's not a thing you can do | +| 7 | InvalidPermissions | you aren't authorized to do that | +| 8 | NotFetched | couldn't fetch what you wanted | +| 9 | NotFound | what you're looking for doesn't exist | +| 10 | Conflict | user already has a network connection open on that channel | +| 11 | InvalidSecret | activity secrets must be unique and not match party id | +| 12 | InvalidJoinSecret | join request for that user does not exist | +| 13 | NoEligibleActivity | you accidentally set an `ApplicationId` in your `UpdateActivity()` payload | +| 14 | InvalidInvite | your game invite is no longer valid | +| 15 | NotAuthenticated | the internal auth call failed for the user, and you can't do this | +| 16 | InvalidAccessToken | the user's bearer token is invalid | +| 17 | ApplicationMismatch | access token belongs to another application | +| 18 | InvalidDataUrl | something internally went wrong fetching image data | +| 19 | InvalidBase64 | not valid Base64 data | +| 20 | NotFiltered | you're trying to access the list before creating a stable list with `Filter()` | +| 21 | LobbyFull | the lobby is full | +| 22 | InvalidLobbySecret | the secret you're using to connect is wrong | +| 23 | InvalidFilename | file name is too long | +| 24 | InvalidFileSize | file is too large | +| 25 | InvalidEntitlement | the user does not have the right entitlement for this game | +| 26 | NotInstalled | Discord is not installed | +| 27 | NotRunning | Discord is not running | +| 28 | InsufficientBuffer | insufficient buffer space when trying to write | +| 29 | PurchaseCancelled | user cancelled the purchase flow | +| 30 | InvalidGuild | Discord guild does not exist | +| 31 | InvalidEvent | the event you're trying to subscribe to does not exist | +| 32 | InvalidChannel | Discord channel does not exist | +| 33 | InvalidOrigin | the origin header on the socket does not match what you've registered (you should not see this) | +| 34 | RateLimited | you are calling that method too quickly | +| 35 | OAuth2Error | the OAuth2 process failed at some point | +| 36 | SelectChannelTimeout | the user took too long selecting a channel for an invite | +| 37 | GetGuildTimeout | took too long trying to fetch the guild | +| 38 | SelectVoiceForceRequired | push to talk is required for this channel | +| 39 | CaptureShortcutAlreadyListening | that push to talk shortcut is already registered | +| 40 | UnauthorizedForAchievement | your application cannot update this achievement | +| 41 | InvalidGiftCode | the gift code is not valid | +| 42 | PurchaseError | something went wrong during the purchase flow | +| 43 | TransactionAborted | purchase flow aborted because the SDK is being torn down | + + + +#### LogLevel Enum + + + +| value | description | +|---------|--------------------------------| +| Error | Log only errors | +| Warning | Log warnings and errors | +| Info | Log info, warnings, and errors | +| Debug | Log _all_ the things! | + + + +#### CreateFlags Enum + + + +| value | description | +|------------------|---------------------------------------------------------------------| +| Default | Requires Discord to be running to play the game | +| NoRequireDiscord | Does not require Discord to be running, use this on other platforms | + + + +### Functions + +#### Create + +Creates an instance of Discord to initialize the SDK. This is the overlord of all things Discord. We like to call her Nelly. + +Returns a new `Discord`. + +###### Parameters + +| name | type | description | +|----------|-------------|-----------------------------------------------------| +| clientId | Int64 | your application's client id | +| flags | CreateFlags | the creation parameters for the SDK, outlined above | + +###### Example + +```cpp +// c++ land +discord::Core* core{}; +discord::Core::Create(53908232506183680, DiscordCreateFlags_Default, &core); + +// c# land +var discord = new Discord(53908232506183680, (UInt64)Discord.CreateFlags.Default); +``` + +#### Destroy + +Destroys the instance. Wave goodbye, Nelly! You monster. In C# land, this is `Dispose()`. + + +The C++ binding does not include a `destroy()` method, as the destructor for the Core does the work for you. + + +Returns `void`. + +###### Example + +```cs +discord.Dispose(); +``` + +#### SetLogHook + +Registers a logging callback function with the minimum level of message to receive. The callback function should have a signature of: + +```cs +MyCallbackFunction(LogLevel level, string message); +``` + +Returns `void`. + +###### Parameters + +| name | type | description | +|----------|----------|---------------------------------------------| +| level | LogLevel | the minimum level of event to log | +| callback | function | the callback function to catch the messages | + +###### Example + +```cs +public void LogProblemsFunction(Discord.LogLevel level, string message) +{ + Console.WriteLine("Discord:{0} - {1}", level, message); +} + +discord.SetLogHook(Discord.LogLevel.Debug, LogProblemFunctions); +``` + +#### RunCallbacks + +Runs all pending SDK callbacks. Put this in your game's main event loop, like `Update()` in Unity. That way, the first thing your game does is check for any new info from Discord. + +This function also serves as a way to know that the local Discord client is still connected. If the user closes Discord while playing your game, `RunCallbacks()` will return/throw `Discord.Result.NotRunning`. + +In C and C++, this function returns `Discord.Result`. In C#, it returns `void` and will throw `Discord.Result` error if something went wrong. + +###### Example + +```cs +void Update() +{ + discord.RunCallbacks(); +} +``` + +#### GetActivityManager + +Fetches an instance of the manager for interfacing with activities in the SDK. + +Returns an `ActivityManager`. + +###### Example + +```cs +var activityManager = discord.GetActivityManager(); +``` + +#### GetUserManager + +Fetches an instance of the manager for interfacing with users in the SDK. + +Returns an `UserManager`. + +###### Example + +```cs +var userManager = discord.GetUserManager(); +``` + +#### GetOverlayManager + +Fetches an instance of the manager for interfacing with the overlay in the SDK. + +Returns an `OverlayManager`. + +###### Example + +```cs +var overlayManager = discord.GetOverlayManager(); +``` + +## Activities + + +Looking to build a game inside of Discord? Check out the (other) [Activities](/developers/docs/activities/overview) and the [Embedded SDK](/developers/docs/developer-tools/embedded-app-sdk) documentation. + + +Looking to integrate Rich Presence into your game? No need to manage multiple SDKs—this one does all that awesome stuff, too! Delight your players with the ability to post game invites in chat and party up directly from Discord. Surface interesting live game data in their profile for their friends, encouraging them to group up and play together. + +For more detailed information and documentation around the Rich Presence feature set and integration tips, check out our [Rich Presence Documentation](/developers/docs/rich-presence/overview). + +### Data Models + +#### User Struct + +| name | type | description | +|---------------|--------|-------------------------------| +| id | Int64 | the user's id | +| username | string | their name | +| discriminator | string | the user's unique discrim | +| avatar | string | the hash of the user's avatar | +| bot | bool | if the user is a bot user | + +#### Activity Struct + +| name | type | description | +|---------------|--------------------------------------------------------------------------------------------|-----------------------------------------------------------------| +| applicationId | Int64 | your application id - this is a read-only field | +| name | string | name of the application - this is a read-only field | +| state | string | the player's current party status | +| details? | ?string | what the player is currently doing | +| timestamps? | ?[ActivityTimestamps](/developers/docs/developer-tools/game-sdk#activitytimestamps-struct) | helps create elapsed/remaining timestamps on a player's profile | +| assets? | ?[ActivityAssets](/developers/docs/developer-tools/game-sdk#activityassets-struct) | assets to display on the player's profile | +| party? | ?[ActivityParty](/developers/docs/developer-tools/game-sdk#activityparty-struct) | information about the player's party | +| secrets? | ?[ActivitySecrets](/developers/docs/developer-tools/game-sdk#activitysecrets-struct) | secret passwords for joining and spectating the player's game | +| instance? | ?bool | whether this activity is an instanced context, like a match | + +#### ActivityTimestamps Struct + +| name | type | description | +|--------|--------|--------------------------------------------------------| +| start? | ?Int64 | unix timestamp - send this to have an "elapsed" timer | +| end? | ?Int64 | unix timestamp - send this to have a "remaining" timer | + +#### ActivityAssets Struct + +| name | type | description | +|-------------|---------|---------------------------------------------------------------------------------------------------------| +| largeImage? | ?string | see [Activity Asset Image](/developers/docs/events/gateway-events#activity-object-activity-asset-image) | +| largeText? | ?string | hover text for the large image | +| smallImage? | ?string | see [Activity Asset Image](/developers/docs/events/gateway-events#activity-object-activity-asset-image) | +| smallText? | ?string | hover text for the small image | + +#### ActivityParty Struct + +| name | type | description | +|------|-----------|------------------------------------| +| id | string | a unique identifier for this party | +| size | PartySize | info about the size of the party | + +#### PartySize Struct + +| name | type | description | +|-------------|-------|------------------------------------| +| currentSize | Int32 | the current size of the party | +| maxSize | Int32 | the max possible size of the party | + +#### ActivitySecrets Struct + +| name | type | description | +|-----------|---------|----------------------------------------------| +| match? | ?string | unique hash for the given match context | +| join? | ?string | unique hash for chat invites and Ask to Join | +| spectate? | ?string | unique hash for Spectate button | + +#### ActivityType Enum + +| name | Value | +|-----------|-------| +| Playing | 0 | +| Streaming | 1 | +| Listening | 2 | +| Watching | 3 | +| Custom | 4 | +| Competing | 5 | + +For more details about the activity types, [see Gateway documentation](/developers/docs/events/gateway-events#activity-object-activity-types). + +`ActivityType` is strictly for the purpose of handling events that you receive from Discord; though the SDK will not reject a payload with an `ActivityType` sent, it will be discarded and will not change anything in the client. + +#### ActivityJoinRequestReply Enum + +| name | value | +|--------|-------| +| No | 0 | +| Yes | 1 | +| Ignore | 2 | + +#### ActivityActionType Enum + +| name | value | +|----------|-------| +| Join | 1 | +| Spectate | 2 | + +### Activity Action Field Requirements + +If you want to hook up joining and spectating for your games, there are certain fields in the activity payload that need to be sent. Refer to the following handy table for what needs to be set for certain actions. + + + + +| Field | Custom Artwork | Spectate | Join | Ask to Join | +|--------------------------------|:--------------:|:--------:|:----:|:-----------:| +| State | | | | | +| Details | | | | | +| ActivityTimestamps.start | | | | | +| ActivityTimestamps.end | | | | | +| ActivityAssets.largeImage | x | | | | +| ActivityAssets.smallImage | x | | | | +| ActivityAssets.largeText | x | | | | +| ActivityAssets.smallText | x | | | | +| ActivityParty.id | | | x | x | +| ActivityParty.size.currentSize | | | x | x | +| ActivityParty.size.maxSize | | | x | x | +| ActivitySecrets.join | | | x | x | +| ActivitySecrets.spectate | | x | | | + + + +### Functions + +#### RegisterCommand + +Registers a command by which Discord can launch your game. This might be a custom protocol, like `my-awesome-game://`, or a path to an executable. It also supports any launch parameters that may be needed, like `game.exe --full-screen --no-hax`. + +On macOS, due to the way Discord registers executables, your game needs to be bundled for this command to work. That means it should be a `.app`. + +Returns `void`. + +###### Parameters + +| name | type | description | +|---------|--------|-------------------------| +| command | string | the command to register | + +###### Example + +```cs +activityManager.RegisterCommand("my-awesome-game://run --full-screen"); +``` + +#### RegisterSteam + +Used if you are distributing this SDK on Steam. Registers your game's Steam app id for the protocol `steam://run-game-id/`. + +Returns `void`. + +###### Parameters + +| name | type | description | +|---------|--------|--------------------------| +| steamId | UInt32 | your game's Steam app id | + +###### Example + +```cs +activityManager.RegisterSteam(1938123); +``` + +#### UpdateActivity + +Sets a user's presence in Discord to a new activity. This has a rate limit of 5 updates per 20 seconds. + + +It is possible for users to hide their presence on Discord (User Settings -> Game Activity). Presence set through this SDK may not be visible when this setting is toggled off. + + +Returns a `Discord.Result` via callback. + +###### Parameters + +| name | type | description | +|----------|----------|-------------------------------| +| activity | Activity | the new activity for the user | + +###### Example + + + +```cs +var activity = new Discord.Activity +{ + State = "In Play Mode", + Details = "Playing the Trumpet!", + Timestamps = + { + Start = 5, + }, + Assets = + { + LargeImage = "foo largeImageKey", // Larger Image Asset Value + LargeText = "foo largeImageText", // Large Image Tooltip + SmallImage = "foo smallImageKey", // Small Image Asset Value + SmallText = "foo smallImageText", // Small Image Tooltip + }, + Party = + { + Id = "foo partyID", + Size = { + CurrentSize = 1, + MaxSize = 4, + }, + }, + Secrets = + { + Match = "foo matchSecret", + Join = "foo joinSecret", + Spectate = "foo spectateSecret", + }, + Instance = true, +}; + +activityManager.UpdateActivity(activity, (result) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("Success!"); + } + else + { + Console.WriteLine("Failed"); + } +}); +``` + + + +#### ClearActivity + +Clear's a user's presence in Discord to make it show nothing. + +Results a `Discord.Result` via callback. + +###### Example + + + +```cs +activityManager.ClearActivity((result) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("Success!"); + } + else + { + Console.WriteLine("Failed"); + } +}); +``` + + + +#### SendRequestReply + +Sends a reply to an Ask to Join request. + +Returns a `Discord.Result` via callback. + +###### Parameters + +| name | type | description | +|--------|--------------------------|---------------------------------------------| +| userId | Int64 | the user id of the person who asked to join | +| reply | ActivityJoinRequestReply | No, Yes, or Ignore | + +###### Example + + + +```cs +activityManager.OnActivityJoinRequest += user => +{ + Console.WriteLine("Join request from: {0}", user.Id); + activityManager.SendRequestReply(user.Id, Discord.ActivityJoinRequestReply.Yes, (res) => + { + if (res == Discord.Result.Ok) + { + Console.WriteLine("Responded successfully"); + } + }); +} +``` + + +#### SendInvite + +Sends a game invite to a given user. If you do not have a valid activity with all the required fields, this call will error. See [Activity Action Field Requirements](/developers/docs/developer-tools/game-sdk#activity-action-field-requirements) for the fields required to have join and spectate invites function properly. + +Returns a `Discord.Result` via callback. + +###### Parameters + +| name | type | description | +|---------|--------------------|-------------------------------------------------------| +| userId | Int64 | the id of the user to invite | +| type | ActivityActionType | marks the invite as an invitation to join or spectate | +| content | string | a message to send along with the invite | + +###### Example + + + +```cs +var userId = 53908232506183680; +activityManager.SendInvite(userId, Discord.ActivityActionType.Join, "Come play!", (result) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("Success!"); + } + else + { + Console.WriteLine("Failed"); + } +}); +``` + + +#### AcceptInvite + +Accepts a game invitation from a given userId. + +Returns a `Discord.Result` via callback. + +###### Parameters + +| name | type | description | +|--------|-------|------------------------------------| +| userId | Int64 | the id of the user who invited you | + +###### Example + + + +```cs +activityManager.AcceptInvite(290926444748734466, (result) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("Success!"); + } + else + { + Console.WriteLine("Failed"); + } +}); +``` + + +### Events + +#### OnActivityJoin + +Fires when a user accepts a game chat invite or receives confirmation from Asking to Join. + +###### Parameters + +| name | type | description | +|------------|--------|------------------------------------| +| joinSecret | string | the secret to join the user's game | + +###### Example + + + +```cs +// Received when someone accepts a request to join or invite. +// Use secrets to receive back the information needed to add the user to the group/party/match +activityManager.OnActivityJoin += secret => { + Console.WriteLine("OnJoin {0}", secret); + // Now update your internal systems to reflect the user as joined + + // Sends this off to a Activity callback named here as 'UpdateActivity' passing in the discord instance details + UpdateActivity(discord); +}; + +void UpdateActivity(Discord.Discord discord) + { + //Creates a Static String for Spectate Secret. + string discordSpectateSecret = "wdn3kvj320r8vk3"; + spectateActivitySecret = discordSpectateSecret; + var activity = new Discord.Activity + { + State = "Playing Co-Op", + Details = "In a Multiplayer Match!", + Timestamps = + { + Start = startTimeStamp, + }, + Assets = + { + LargeImage = "matchimage1", + LargeText = "Inside the Arena!", + }, + Party = { + Id = , + Size = { + CurrentSize = ..., + MaxSize = ..., + }, + }, + Secrets = { + Spectate = spectateActivitySecret, + Join = joinActivitySecret, + }, + Instance = true, + }; + + activityManager.UpdateActivity(activity, result => + { + Debug.LogFormat("Updated to Multiplayer Activity: {0}", result); + + // Send an invite to another user for this activity. + // Receiver should see an invite in their DM. + // Use a relationship user's ID for this. + // activityManager + // .SendInvite( + // 364843917537050624, + // Discord.ActivityActionType.Join, + // "", + // inviteResult => + // { + // Console.WriteLine("Invite {0}", inviteResult); + // } + // ); + }); + } +``` + + +#### OnActivitySpectate + +Fires when a user accepts a spectate chat invite or clicks the Spectate button on a user's profile. + +###### Parameters + +| name | type | description | +|----------------|--------|---------------------------------------------------| +| spectateSecret | string | the secret to join the user's game as a spectator | + +###### Example + + + +```cs +// Received when someone accepts a request to spectate +activityManager.OnActivitySpectate += secret => +{ + Console.WriteLine("OnSpectate {0}", secret); +}; +``` + + +#### OnActivityJoinRequest + +Fires when a user asks to join the current user's game. + +###### Parameters + +| name | type | description | +|------|------|-------------------------| +| user | User | the user asking to join | + +###### Example + + + +```cs +// A join request has been received. Render the request on the UI. +activityManager.OnActivityJoinRequest += (ref Discord.User user) => +{ + Console.WriteLine("OnJoinRequest {0} {1}", user.Username, user.Id); +}; +``` + + +#### OnActivityInvite + +Fires when the user receives a join or spectate invite. + +###### Parameters + +| name | type | description | +|----------|--------------------|--------------------------------------------| +| type | ActivityActiontype | whether this invite is to join or spectate | +| user | User | the user sending the invite | +| activity | Activity | the inviting user's current activity | + +###### Example + + + +```cs +// An invite has been received. Consider rendering the user / activity on the UI. +activityManager.OnActivityInvite += (Discord.ActivityActionType Type, ref Discord.User user, ref Discord.Activity activity2) => +{ + Console.WriteLine("Received Invite Type: {0}, from User: {1}, with Activity Name: {2}", Type, user.Username, activity2.Name); + // activityManager.AcceptInvite(user.Id, result => + // { + // Console.WriteLine("AcceptInvite {0}", result); + // }); +}; +``` + + +### Inviting a User to a Game + + + +```cs +var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); + +// Update user's activity for your game. +// Party and secrets are vital. +var activity = new Discord.Activity +{ + State = "olleh", + Details = "foo details", + Timestamps = + { + Start = 5, + End = 6, + }, + Assets = + { + LargeImage = "foo largeImageKey", + LargeText = "foo largeImageText", + SmallImage = "foo smallImageKey", + SmallText = "foo smallImageText", + }, + Party = + { + Id = "foo partyID", + Size = { + CurrentSize = 1, + MaxSize = 4, + }, + }, + Secrets = + { + Match = "foo matchSecret", + Join = "foo joinSecret", + Spectate = "foo spectateSecret", + }, + Instance = true, +}; + +activityManager.UpdateActivity(activity, (result) => +{ + Console.WriteLine("Update Activity {0}", result); + + // Send an invite to another user for this activity. + // Receiver should see an invite in their DM. + // Use a relationship user's ID for this. + activityManager.SendInvite(364843917537050999, Discord.ActivityActionType.Join, "", (inviteUserResult) => + { + Console.WriteLine("Invite User {0}", inviteUserResult); + }); +}); +``` + + +## Overlay + + +The overlay is only supported on Windows for DirectX or OpenGL games. Linux, Mac, and games using Vulkan are not supported. [Click here for more info.](https://support.discord.com/hc/en-us/articles/217659737-Games-Overlay-101) + + +Discord comes with an awesome built-in overlay, and you may want to make use of it for your game. This manager will help you do just that! It gives you the current state of the overlay for the user, and allows you to update that state. + +### Data Models + +#### ActivityActionType Enum + +| name | value | +|----------|-------| +| Join | 1 | +| Spectate | 2 | + +### Functions + +#### IsEnabled + +Check whether the user has the overlay enabled or disabled. If the overlay is disabled, all the functionality in this manager will still work. The calls will instead focus the Discord client and show the modal there instead. + +Returns a `bool`. + +###### Example + + + +```cs +if (!overlaymanager.IsEnabled()) +{ + Console.WriteLine("Overlay is not enabled. Modals will be shown in the Discord client instead"); +} +``` + + +#### IsLocked + +Check if the overlay is currently locked or unlocked + +###### Example + + + +```cs +if (overlayManager.IsLocked()) +{ + overlayManager.SetLocked(true, (res) => + { + Console.WriteLine("Input in the overlay is now accessible again"); + }); +} +``` + + +#### SetLocked + +Locks or unlocks input in the overlay. Calling `SetLocked(true);` will also close any modals in the overlay or in-app from things like IAP purchase flows and disallow input. + +Returns `Discord.Result` via callback. + +###### Parameters + +| name | type | description | +|--------|------|----------------------------| +| locked | bool | lock or unlock the overlay | + +###### Example + + + +```cs +overlayManager.SetLocked(true, (res) => +{ + Console.WriteLine("Overlay has been locked and modals have been closed"); +}); +``` + + + +#### OpenActivityInvite + +Opens the overlay modal for sending game invitations to users, channels, and servers. If you do not have a valid activity with all the required fields, this call will error. See [Activity Action Field Requirements](/developers/docs/developer-tools/game-sdk#activity-action-field-requirements) for the fields required to have join and spectate invites function properly. + +Returns a `Discord.Result` via callback. + +###### Parameters + +| name | type | description | +|------|--------------------|-----------------------------| +| type | ActivityActionType | what type of invite to send | + +###### Example + + + +```cs +overlayManager.OpenActivityInvite(Discord.ActivityActionType.Join, (result) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("User is now inviting others to play!"); + } +}); +``` + + +#### OpenGuildInvite + +Opens the overlay modal for joining a Discord guild, given its invite code. An invite code for a server may look something like `fortnite` for a verified server—the full invite being `discord.gg/fortnite`—or something like `rjEeUJq` for a non-verified server, the full invite being `discord.gg/rjEeUJq`. + +Returns a `Discord.Result` via callback. Note that a successful `Discord.Result` response does not necessarily mean that the user has joined the guild. If you want more granular control over and knowledge about users joining your guild, you may want to look into implementing the [`guilds.join` OAuth2 scope in an authorization code grant](/developers/docs/topics/oauth2#authorization-code-grant) in conjunction with the [Add Guild Members](/developers/docs/resources/guild#add-guild-member) endpoint. + +###### Parameters + +| name | type | description | +|------|--------|----------------------------| +| code | string | an invite code for a guild | + +###### Example + + + +```cs +overlayManager.OpenGuildInvite("rjEeUJq", (result) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("Invite was valid."); + } +}); +``` + + +#### OpenVoiceSettings + +Opens the overlay widget for voice settings for the currently connected application. These settings are unique to each user within the context of your application. That means that a user can have different favorite voice settings for each of their games! + + + +![Screenshot of the Voice Settings modal for an application](/images/game-overlay-sdk-voice-settings.webp) + + +Returns a `Discord.Result` via callback. + +###### Example + + + +```cs +overlayManager.OpenVoiceSettings((result) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("Overlay is open to the voice settings for your application/") + } +}) +``` + + +#### OnToggle + +Fires when the overlay is locked or unlocked (a.k.a. opened or closed) + +###### Parameters + +| name | type | description | +|--------|------|---------------------------------------| +| locked | bool | is the overlay now locked or unlocked | + +###### Example + + + +```cs +overlayManager.OnToggle += overlayLock => +{ + Console.WriteLine("Overlay Locked: {0}", overlayLock); +}; +``` + + +#### Activate Overlay Invite Modal + + + +```cs +var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); +var overlayManager = discord.GetOverlayManager(); + +// Invite users to join your game +overlayManager.OpenActivityInvite(ActivityActionType.Join, (result) => +{ + Console.WriteLine("Overlay is now open!"); +}) +``` + +And that invite modal looks like this! + +![Screenshot of an Invitation Modal in an application](/images/game-overlay-sdk-invite.webp) + + + +## Users + +This manager helps retrieve basic user information for any user on Discord. + +### Data Models + +#### User Struct + +| name | type | description | +|---------------|--------|-------------------------------| +| Id | Int64 | the user's id | +| Username | string | their name | +| Discriminator | string | the user's unique discrim | +| Avatar | string | the hash of the user's avatar | +| Bot | bool | if the user is a bot user | + +#### UserFlag Enum + +| name | value | description | +|-----------------|-------|------------------------------| +| Partner | 2 | Discord Partner | +| HypeSquadEvents | 4 | HypeSquad Events participant | +| HypeSquadHouse1 | 64 | House Bravery | +| HypeSquadHouse2 | 128 | House Brilliance | +| HypeSquadHouse3 | 256 | House Balance | + +#### PremiumType Enum + +| name | value | description | +|-------|-------|--------------------------| +| None | 0 | Not a Nitro subscriber | +| Tier1 | 1 | Nitro Classic subscriber | +| Tier2 | 2 | Nitro subscriber | +| Tier3 | 3 | Nitro Basic subscriber | + +### Functions + +#### GetCurrentUser + + +Before calling this function, you'll need to wait for the [OnCurrentUserUpdate](/developers/docs/developer-tools/game-sdk#oncurrentuserupdate) callback to fire after instantiating the User manager. + + +Fetch information about the currently connected user account. If you're interested in getting more detailed information about a user—for example, their email—check out our [GetCurrentUser](/developers/docs/resources/user#get-current-user) API endpoint. You'll want to call this with an authorization header of `Bearer `, where `` is the token retrieved from a standard [OAuth2 Authorization Code Grant](/developers/docs/topics/oauth2#authorization-code-grant) flow. + +Returns a `Discord.User`. + +###### Example + + + +```cs +var user = userManager.GetCurrentUser(); +Console.WriteLine("Connected to user {0}", user.Id); +``` + + +#### GetUser + +Get user information for a given id. + +Returns a `Discord.Result` and `ref Discord.User` via callback. + +###### Parameters + +| name | type | description | +|--------|-------|-----------------------------| +| userId | Int64 | the id of the user to fetch | + +###### Example + + + +```cs +userManager.GetUser(userId, (Discord.Result result, ref Discord.User user) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("User {0} is {1}", user.Id, user.Username); + } +}); +``` + + +#### GetCurrentUserPremiumType + +Get the [PremiumType](/developers/docs/developer-tools/game-sdk#premiumtype-enum) for the currently connected user. + +Returns `Discord.PremiumType`. + +###### Example + + + +```cs +var userManager = discord.GetUserManager(); +var premiumType = userManager.GetCurrentUserPremiumType(); +switch (premiumType) +{ + case PremiumType.None: + Console.WriteLine("User is not a Nitro subscriber"); + + case PremiumType.Tier1: + Console.WriteLine("User has Nitro Classic"); + + case PremiumType.Tier2: + Console.WriteLine("User has Nitro"); + + default: + return; +} +``` + + + +#### CurrentUserHasFlag + +See whether or not the current user has a certain [UserFlag](/developers/docs/developer-tools/game-sdk#userflag-enum) on their account. + +Returns `bool`. + +###### Parameters + +| name | type | description | +|------|---------------------------------------------------------------------|-----------------------------------------| +| flag | [UserFlag](/developers/docs/developer-tools/game-sdk#userflag-enum) | the flag to check on the user's account | + +###### Example + + + +```cs +var userManager = discord.GetUserManager(); +if (userManager.CurrentUserHasFlag(Discord.UserFlag.HypeSquadHouse1)) +{ + Console.WriteLine("User is a member of House Bravery!"); +} +``` + + + +### Events + +#### OnCurrentUserUpdate + +Fires when the `User` struct of the currently connected user changes. They may have changed their avatar, username, or something else. + +###### Example + + + +```cs +var userManager = discord.GetUserManager(); +// GetCurrentUser will error until this fires once. +userManager.OnCurrentUserUpdate += () => { + var currentUser = userManager.GetCurrentUser(); + + Console.WriteLine(currentUser.Username); + Console.WriteLine(currentUser.Id); + Console.WriteLine(currentUser.Discriminator); + Console.WriteLine(currentUser.Avatar); +}; +``` + + +### Fetching Data About a Discord User + + + +```cs +var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); + +var userManager = discord.GetUserManager(); +userManager.GetUser(450795363658366976, (Discord.Result result, ref Discord.User user) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("user fetched: {0}", user.Username); + } + else + { + Console.WriteLine("user fetch error: {0}", result); + } +}); +``` + diff --git a/discord/developers/docs/discord-social-sdk/core-concepts.mdx b/discord/developers/docs/discord-social-sdk/core-concepts.mdx new file mode 100644 index 0000000000..7a73b25358 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/core-concepts.mdx @@ -0,0 +1,56 @@ +--- +title: Discord Social SDK Core Concepts Glossary +sidebarTitle: Glossary +description: Definitions and explanations of key terms used in the Discord Social SDK. +--- + +The Discord Social SDK allows you to build social features into your game, including friend lists, messaging, voice chat, and rich presence. Unlike a traditional SDK with built-in UI components, the Discord Social SDK provides access to raw data, allowing developers to create a fully customized experience that aligns with their game's aesthetic. + +## Core Concepts Overview + +import {TrophyIcon} from '/snippets/icons/TrophyIcon.jsx' +import {PhoneCallIcon} from '/snippets/icons/PhoneCallIcon.jsx' +import {HammerIcon} from '/snippets/icons/HammerIcon.jsx' +import {UserPlatformIcon} from '/snippets/icons/UserPlatformIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' + +Select a topic below to learn more about the Discord Social SDK: + + + }> + Explore social features like account linking, friends, and rich presence. + + }> + Learn about messaging, voice chat, and in-game lobbies. + + }> + Learn about the integration overview and implementation steps. + + }> + See supported platforms and download information. + + }> + Understand OAuth2 scopes and client types for authentication. + + + +--- + +## Next Steps + +After exploring these core concepts, you can start implementing the Discord Social SDK in your game: + +- **Ready to integrate?** Follow our [Getting Started](/developers/docs/discord-social-sdk/getting-started) guide for step-by-step setup instructions. +- **Need implementation details?** Check out the [Development Guides](/developers/docs/discord-social-sdk/development-guides) for specific feature implementations. +- **Looking for UI guidance?** Review the [Design Guidelines](/developers/docs/discord-social-sdk/design-guidelines) for best practices. + +--- + +## Change Log + +| Date | Changes | +|----------------|---------------------------| +| July 21, 2025 | restructure core concepts | +| June 30, 2025 | restructure oauth scopes | +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/core-concepts/communication-features.mdx b/discord/developers/docs/discord-social-sdk/core-concepts/communication-features.mdx new file mode 100644 index 0000000000..cde41474a2 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/core-concepts/communication-features.mdx @@ -0,0 +1,410 @@ +--- +title: Communication Features +description: "Learn about Discord Social SDK's communication features including direct messages and voice chat." +--- + +import SonyCallout from '/snippets/discord-social-sdk/callouts/sony.mdx'; + +The Discord Social SDK provides powerful communication features to enhance player interaction in your game. +These features include messaging, voice chat, and in-game lobbies, allowing players to connect seamlessly. + + +The following communication features are available for development and testing, but their usage is capped with a rate limit. + + +For more information about current rate limits for communication features, please see +[Rate Limits](/developers/docs/discord-social-sdk/core-concepts/communication-features#rate-limits), and +see the +[Applying for Rate Limit Removal](/developers/docs/discord-social-sdk/core-concepts/communication-features#applying-for-rate-limit-removal) +section below for how to remove those rate limits, such that you can use these features for full release. + +## Messaging & Communication + +Users can communicate via direct messages (DMs) and voice calls: + +- DMs: One-on-one private chat ([`MessageHandle`]). +- Calls: Real-time voice communication inside a game lobby ([`Call`]). + +| Development Guides | +|-----------------------------------------------------------------------------------------------------------| +| [Sending Direct Messages](/developers/docs/discord-social-sdk/development-guides/sending-direct-messages) | +| [Managing Voice Chat](/developers/docs/discord-social-sdk/development-guides/managing-voice-chat) | + +| Design Guidelines | +|------------------------------------------------------------------------------------------| +| [Direct Messages](/developers/docs/discord-social-sdk/design-guidelines/direct-messages) | + +## Lobbies & In-Game Chat + +A lobby is a virtual space where players can interact through voice and text chat. + +- Your game controls lobbies, which can have different membership rules. +- Integrated voice chat allows real-time communication among players in a session. + +| Development Guides | +|--------------------------------------------------------------------------------------------| +| [Manging Lobbies](/developers/docs/discord-social-sdk/development-guides/managing-lobbies) | + +## Linked Channels + +Games can link in-game chat with Discord's server-based text channels in their UI, allowing players to chat in a Discord server without leaving the game. + +| Development Guides | +|-------------------------------------------------------------------------------------------| +| [Linked channels](/developers/docs/discord-social-sdk/development-guides/linked-channels) | + +| Design Guidelines | +|------------------------------------------------------------------------------------------| +| [Linked channels](/developers/docs/discord-social-sdk/design-guidelines/linked-channels) | + +--- + +## Rate Limits + +Prior to a successful application to remove rate limits, communication features have the following restrictions: + +* **Lobby Create/Join operations:** 20 requests every 2 hours. +* **Lobby Update operations:** 100 requests every 2 hours. + * This includes adding/removing members, updating metadata, and creating Discord server invites for linked channels. +* **Lobby Linking operations:** 20 requests every 2 hours. + * This includes creating or deleting a linked channel +* **Sending Messages to Lobby operations:** 100 requests every 2 hours +* **Sending Direct Message operations:** 100 requests every 2 hours. + + +These are per-application rate limits, not per-user. + + +These limitations are designed to provide sufficient capacity for development, testing, and small-scale demos while ensuring system stability. Once you gain full access through the rate limit removal application process, these restrictions are removed, allowing your game to scale to production levels. + +## Applying for Rate Limit Removal + +To apply for removal of rate limits on Discord Social SDK communication features, you must meet the requirements +outlined in the [Application Requirements for Rate Limit Removal](/developers/docs/discord-social-sdk/core-concepts/communication-features#application-requirements-for-rate-limit-removal) +section below. + +Once you have met those requirements, you can apply for rate limit removal by following these steps: + +1. Open the [Application page of the Developer Portal](https://discord.com/developers/applications) +2. Open the Application you want to apply for rate limit removal +3. Under the **Discord Social SDK** heading in the sidebar, click the **Comms Access** button +5. Fill out the application form with the required information and supporting materials + + +Video capture provided to the application should be in url format, hosted on a file or video-sharing service of your choice as an unlisted video + + +## Application Requirements for Rate Limit Removal + +This section outlines the specific requirements you must meet before submitting your game for approval to unlock +Discord Social SDK text and/or voice communications for full release. You'll find: + +1. **Minimum required feature set:** Your game must have integrated core Discord Social SDK features, such as Discord Account Linking, Rich Presence with Discord Joins, and Full Access to Discord Friends. +2. **Feature Functionality:** The Discord Social SDK features you choose to integrate in your game must have end-to-end functionality, meaning that all your user interactions can be completed, work as intended across both game and Discord clients, and resolve negative options (such as user denial of options and failure states) correctly. +3. **Feature Access and Conveyance:** Discord Social SDK integrations must include the option for users to link their Discord account, and make it accessible and easily understandable for players to activate. +4. **Supporting Materials:** You’ll be required to provide specific documentation of your game for review, such as a capture of integration and development timeline. +5. **Age-restricted user protection:** You’ll need to confirm that you have appropriate measures in place for players’ access to the SDK features in your game, depending on their age. + + + +These requirements detail the **minimum bar** to be eligible for Social SDK communications usage at scale. +Check out our [design guidelines](/developers/docs/discord-social-sdk/design-guidelines) for what **“great”** looks like. Great +integrations can drive higher player engagement and retention by creating seamless social experiences that keep players +connected and coming back to your game. And of course, remember to make sure you are complying with our +[Social SDK Terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms). +

If you believe you meet these requirements, you can +[apply for access to full release comms features](/developers/docs/discord-social-sdk/core-concepts/communication-features#applying-for-rate-limit-removal). +Discord may approve or deny your access after review of your application and additional terms may apply. +
+ + +### Minimum Required Feature Set + +**In order to unlock Discord text and/or voice communications for full release, integrations of the Social SDK must include the following features at a minimum**. Developers are of course welcome (and encouraged) to take advantage of any SDK features that will supercharge their games beyond these minimum requirements. + + + +Required features are… + +#### Option for Discord Account Linking + +**Players must have the opportunity to [link their Discord account in-game](/developers/docs/discord-social-sdk/design-guidelines/signing-in).** This ensures players can access the full Social SDK-powered experience, and connects your game to a player’s existing social network. + +#### Rich Presence and Discord Joins + +**Discord Users must be able to see [Rich Presence](/developers/docs/discord-social-sdk/design-guidelines/status-rich-presence) updates for Discord-linked player accounts and join their game via Ask to Join.** This feature drives organic discovery and social engagement by showing friends what players are doing and creating opportunities for spontaneous social interaction and game sessions. + +#### Full Access to Discord Friends + +**Account-linked players must have their Discord friends accessible to them in-game, with supporting UI to take actions like [messaging](/developers/docs/discord-social-sdk/design-guidelines/direct-messages), inviting, blocking, and ignoring**. This can be accomplished as a [Unified Friends List](/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list) which shows status across platforms, or as a simple in-game Discord friends list. Games should never copy and store Discord friend information (i.e. a player unlinking their Discord account should automatically remove Discord friend information in-game). + +### Feature Functionality + +**All integrated Social SDK features (whether required as a part of the minimum feature set or not) must have at a minimum base level functionality for their intended use.** This is a **separate requirement in addition to the minimum feature set** above. “Base level functionality” means that each integrated feature’s **end-to-end user flow is** **completable** and **resolves negative options** (user denial of options and failure states) correctly. Details for each feature are as follows: + +#### Discord Account Linking + +**Intended Use:** Connect a player's game account with their Discord account to enable social features and cross-platform functionality. + +**Example Complete User Flow:** + +1. Player encounters sign-in prompt (at game start, friends list access, or feature interaction) +2. Player clicks Discord sign-in button +3. Authorization flow launches (Overlay \> Discord Client \> Browser fallback) +4. Player completes OAuth authorization on Discord +5. Game receives authorization confirmation +6. Success state displays in-game confirming connection +7. Social features become available and populate with Discord data + +**Negative Options to Resolve:** + +* **User declines authorization:** Return to game normally with a provisional account; maintain access to sign-in option +* **Authorization fails/times out:** Show clear error message with retry option +* **User wants to disconnect:** Provide deauthorization option in-game and maintain game functionality post-disconnect + + +These requirements represent the minimum standards for functionality. +To create an even better player experience, see our [Design Guidelines for Signing In](/developers/docs/discord-social-sdk/design-guidelines/signing-in). + + +#### Rich Presence + +**Intended Use:** Display detailed, real-time information about a player's in-game activity on their Discord profile, allowing friends to see what they're doing and potentially join their session. + +**Example Complete User Flow:** + +1. Player launches game and performs actions (enters lobby, starts match, joins party, etc.) +2. Game feeds Rich Presence update to Discord via the SDK (e.g. “In lobby”) +3. Rich Presence appears on player's Discord profile with current activity details +4. Discord friends can view the presence information +5. If including Joins/Invites, Discord Friends can "Ask to Join" or receive direct invites +6. Game feeds additional Rich Presence updates to Discord via the SDK as the player's in-game status changes (e.g. “Looking for a match”, “In a match”, etc) +7. On game shutdown, Discord automatically handles clearing player's Rich Presence + +**Negative Options to Resolve:** + +* **Network connectivity issues:** Queue presence updates and retry when connection restored +* **Active and inactive clients**: Invites can complete even when one player doesn’t have the game client open on acceptance. + + +These requirements represent the minimum standards for functionality. +To create an even better player experience, see our [Design Guidelines for Rich Presence](/developers/docs/discord-social-sdk/design-guidelines/status-rich-presence). + + +#### Unified Friends List + +**Intended Use:** Combine Discord friends and in-game relationships into a single, organized friends list that shows comprehensive social connections. + +**Example Complete User Flow:** + +1. Player opens their in-game friends list +2. Game UI quickly populates friends in an organized list showing: + * In-game friends (always visible) + * "Online Elsewhere" section with Discord friends not currently in-game + * Appropriate status indicators and Discord badges +3. Player interacts with friends in list (message, invite, block, view profile) +4. Friends list updates dynamically in real time as statuses change +5. Player can add/remove friends through the interface + +**Negative Options to Resolve:** + +* **Friend removal:** Handle removal across both game and Discord systems appropriately +* **Loading failures:** Show loading states and error messages for failed friend data retrieval +* **Special characters**: Non-standard characters in Discord usernames populate as expected. +* **Unlinking Discord**: Player’s in-game friends list removes Discord friend entries when they unlink their Discord account + + +These requirements represent the minimum standards for functionality. +To create an even better player experience, see our [Design Guidelines for Unified Friends List](/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list). + + +#### Direct Messages through Discord + +**Intended Use:** Enable seamless messaging between players across game platforms through Discord, maintaining conversation continuity. + +**Complete User Flow:** + +1. Player selects friend to message from an in-game friends list or other interface +2. Message interface opens showing chat history (if available) +3. Player composes and sends message +4. Message appears in both the game interface and Discord (for linked users) +5. Messages from Discord appear in-game with Discord visual notation +6. Conversation continues seamlessly across platforms + +**Negative Options to Resolve:** + +* **Message delivery failure:** Show retry options and clear error states +* **Unsupported media:** Display placeholder with external link icon for rich media +* **Blocked users:** Respect Discord blocking relationships and show appropriate messaging +* **Network issues:** Queue messages locally and sync when connection restored + + +These requirements represent the minimum standards for functionality. +To create an even better player experience, see our [Design Guidelines for Direct Messages](/developers/docs/discord-social-sdk/design-guidelines/direct-messages). + + +#### Linked Channels + +**Intended Use:** Link in-game group chats to Discord text channels, enabling persistent conversations that flow between game platforms through Discord. + +**Complete User Flow:** + +1. Player with Discord server admin & game chat admin permissions accesses channel linking option in group chat interface +2. Channel selection interface opens showing available Discord servers/channels +3. Player selects appropriate text channel (with proper permissions) +4. System shows setup warning if linking to restricted channel +5. Link establishes successfully with confirmation +6. Messages flow bidirectionally between game and Discord +7. Persistent entrypoint shows linked channel info and management options + +**Negative Options to Resolve:** + +* **Insufficient permissions:** Show clear error about channel management requirements +* **No available channels:** Provide guidance on creating or accessing appropriate channels +* **Link establishment failure:** Show retry options and clear error messaging +* **User wants to unlink:** Provide confirmation dialog and clean removal process + + +These requirements represent the minimum standards for functionality. +To create an even better player experience, see our [Design Guidelines for Linked Channels](/developers/docs/discord-social-sdk/design-guidelines/linked-channels). + + +#### Voice Communications + +**Intended Use:** Provide high-quality voice chat integration that works seamlessly between game platforms through Discord. + +**Complete User Flow:** + +1. Player joins voice-enabled game session/party +2. Voice interface becomes available with Discord-powered audio +3. Player can mute/unmute, adjust volume, see speaking indicators +4. Voice streams work for individual users with clear audio quality +5. Player can leave voice + +**Negative Options to Resolve:** + +* **Voice server unavailable:** Provide clear error messaging and retry mechanisms +* **User leaves voice:** Clean up voice states and update interfaces appropriately + +### Feature Access and Conveyance + +#### Discord Account Linking + +**The option for players to link their Discord account must have top-level surfacing in-game.** Social SDK integrations must not only include account linking, but also make it accessible and easily understandable for players to activate and deactivate. That means… + +**Access Requirements:** + +* Linking a Discord account is surfaced in parity with other platform-linking options. +* The location for account linking is located in a **persistent and accessible** area in-game (e.g. Game Settings, Accounts menus). +* The entry point for account linking is visually persistent within its associated menu location (i.e. it can’t be a one time pop up that players can’t access again). +* The entry point for account linking is discoverable by players through normal expected play (i.e. not buried in a sub-menu or requiring right-click access). +* The option to unlink accounts is also surfaced in-game in a persistent and easily accessible manner. +* Users without linked accounts must have an associated token path for handling social features (see: [Provisional Accounts](/developers/docs/discord-social-sdk/design-guidelines/provisional-accounts)). + +**Conveyance Requirements:** + +* Developers must provide contextual education about Discord account-linking benefits before users encounter the authorization flow. +* Acceptable contextual education looks like: +* **Modal or informational screen** that explains specific benefits the player will receive (e.g., "Chat with friends here and on Discord," "See what your friends are playing," "Join friends' games directly") +* **Contextual tooltips or hints** near social features that explain how they improve with Discord linking +* **In-game messaging** that appears when players interact with limited social features, explaining how linking would enhance the experience + + +Discord Account Linking entry points are prohibited on Sony platforms. + + +#### Direct Messages through Discord + +If you’ve chosen to integrate Direct Messages through Discord in your game, **Direct Messages must be identifiable as coming from Discord,** and **players must know when their messages are showing up in Discord.** + +**Conveyance Requirements:** + +* Cross-platform DMs need to be identified as Discord-specific through visual indicators (e.g. Discord logo/badge next to sender name, contextual information on hover about the message source) +* Players must know when their messages are showing up in Discord through clear visual feedback when composing or sending messages +* The messaging interface must indicate the cross-platform nature of the conversation (e.g., "This conversation syncs with Discord" or similar messaging) + +### Supporting Materials + +To **unlock Discord text and/or voice communications for full release,** Social SDK integrations must have accompanying +supporting materials in your application. You may submit your application and supporting materials under your application +in the Developer Portal. + + +**For integrations available on multiple platforms including Sony platforms, submit materials that reflect your non-Sony platform user experience.** +

**For example:** Example Studio's *Wumpus Wars* is available to players on Sony PlayStation 5 and Xbox +Series S and Xbox Series X. Example Studio submits the feature list and *Wumpus Wars* capture for Xbox. +
+ +#### List of SDK Features Integrated + +Provide a comprehensive list of all Discord Social SDK features implemented in your game, such as: + +* Discord Sign-in (Account Linking) +* Rich Presence +* Game Invites and/or Discord Joins +* Unified Friends List +* Direct Messages through Discord +* Voice Communications +* Text Communications (Linked Channels) + +#### Release Timeline + +Indicate the anticipated release date, and the current development status of your Discord integration: + +* Concept / Prototype +* Production (Alpha) +* Post-product (Beta) +* Released (Already commercially available) + +#### Capture of Integrated Features + +Acceptable format: url link + +* **1-5 minute video capture** of the complete user flow covering each SDK feature integrated +* Videos must demonstrate end-to-end user flow as outlined in +[Feature Functionality](/developers/docs/discord-social-sdk/core-concepts/communication-features#feature-functionality) +* Show both successful flows and how negative options (errors, user denials) are handled +* Include clear narration or on-screen text explaining each step +* Ensure video quality allows reviewers to clearly see UI elements and user interactions + +### Age-restricted User Protection + +You must comply with all age requirements under our [Social SDK Terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms). This includes ensuring that only people who are at least 13 years old and meet the minimum age requirements in their country can access Social SDK features integrated in your game. + +### Confirmation of Standards Compliance + +With your application submission, **you confirm that you have met the requirements detailed above**. + +## Next Steps + +import {UserPlatformIcon} from '/snippets/icons/UserPlatformIcon.jsx' +import {HammerIcon} from '/snippets/icons/HammerIcon.jsx' +import {BookCheckIcon} from '/snippets/icons/BookCheckIcon.jsx' + +Learn about integration steps, check platform support, and explore implementation guides: + + + }> + Learn about the integration overview and implementation steps. + + }> + See supported platforms and download information. + + }> + Learn how to implement messaging, voice chat, and linked channels. + + + +--- + +## Change Log + +| Date | Changes | +|---------------|-----------------| +| July 21, 2025 | initial release | + + +{/* Autogenerated Reference Links */} +[`Call`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a1cc8a7f73c15a960bc409d734b5edbd1 +[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/core-concepts/core-features.mdx b/discord/developers/docs/discord-social-sdk/core-concepts/core-features.mdx new file mode 100644 index 0000000000..6be6fecbca --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/core-concepts/core-features.mdx @@ -0,0 +1,96 @@ +--- +title: Core Features +description: Overview of the Discord Social SDK's core features including user identity and relationships. +--- + +The Discord Social SDK offers a range of features to enhance social interactions within games. Developers can leverage these features to create a more engaging and connected experience for players in their game. + +## Account Linking + +Account linking allows a game to authenticate users with their Discord credentials, gaining access to social features like friends, chat, and presence. This process uses OAuth2 authentication. + +| Development Guides | +|---------------------------------------------------------------------------------------------------------------------| +| [Account Linking with Discord](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord) | +| [Account Linking on Consoles](/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles) | + +| Design Guidelines | +|---------------------------------------------------------------------------------------------| +| [Signing in with Discord](/developers/docs/discord-social-sdk/design-guidelines/signing-in) | +| [Consoles](/developers/docs/discord-social-sdk/design-guidelines/consoles) | + +## Provisional Accounts + +Provisional accounts let players use social features in your game without linking a Discord account so all players can have a consistent gameplay experience. + +| Development Guides | +|-----------------------------------------------------------------------------------------------------------------| +| [Using Provisional Accounts](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts) | + +| Design Guidelines | +|----------------------------------------------------------------------------------------------------| +| [Provisional Accounts](/developers/docs/discord-social-sdk/design-guidelines/provisional-accounts) | + +## Friend System & Relationships + +The SDK models friendships and relationships in two ways: + +- Discord Friends: Persistent across all games. +- Game-Specific Friends: Limited to the current game. + +| Development Guides | +|---------------------------------------------------------------------------------------------------------------------------| +| [Creating a Unified Friends List](/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list) | + +| Design Guidelines | +|----------------------------------------------------------------------------------------------------| +| [Unified Friends List](/developers/docs/discord-social-sdk/design-guidelines/provisional-accounts) | +| [Game Friends](/developers/docs/discord-social-sdk/design-guidelines/game-friends) | + +## Presence & Rich Presence + +Presence refers to a user's online status, while Rich Presence provides game-specific activity data: + +- Displays if a user is online, idle, or offline. +- Shows detailed game stats (e.g., what level they're playing and time played). +- Allows users to send game invites through Discord and in-game. + +| Development Guides | +|-------------------------------------------------------------------------------------------------------| +| [Setting Rich Presence](/developers/docs/discord-social-sdk/development-guides/setting-rich-presence) | + +| Design Guidelines | +|------------------------------------------------------------------------------------------------------| +| [Status & Rich Presence](/developers/docs/discord-social-sdk/design-guidelines/status-rich-presence) | + + +--- + +## Next Steps + +import {PhoneCallIcon} from '/snippets/icons/PhoneCallIcon.jsx' +import {HammerIcon} from '/snippets/icons/HammerIcon.jsx' +import {BookCheckIcon} from '/snippets/icons/BookCheckIcon.jsx' + +Learn the implementation steps, configure authentication, and explore development guides: + + + }> + Learn about messaging, voice chat, and in-game lobbies. + + }> + Learn about the integration overview and implementation steps. + + }> + Step-by-step guides for implementing each social feature. + + + +--- + +## Change Log + +| Date | Changes | +|---------------|-----------------| +| July 21, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/core-concepts/integration-overview.mdx b/discord/developers/docs/discord-social-sdk/core-concepts/integration-overview.mdx new file mode 100644 index 0000000000..fdb3f925eb --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/core-concepts/integration-overview.mdx @@ -0,0 +1,59 @@ +--- +title: Integration Overview +description: High-level overview of integrating the Discord Social SDK into your application. +--- + +To implement the Discord Social SDK, developers for all platforms will generally follow these steps: + +1. Import the SDK +2. Initialize the SDK: + - Create a [`Client`] instance. + - Set up event listeners to monitor SDK events and callbacks +3. Authenticate users with flexible account options + - Link an existing Discord account via OAuth ([`Client::Authorize`]). + - Create and manage provisional accounts for users who don't have or want a Discord account ([`Client::GetProvisionalToken`]). +4. Implement social features + - Implement [unified friends list](/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list) and [relationships](/developers/docs/discord-social-sdk/development-guides/managing-relationships). + - Use [rich presence](/developers/docs/discord-social-sdk/development-guides/setting-rich-presence) for game activity updates. + - Set up [lobbies](/developers/docs/discord-social-sdk/development-guides/managing-lobbies) for multiplayer interaction and [game invites](/developers/docs/discord-social-sdk/development-guides/managing-game-invites). + - Manage [direct message](/developers/docs/discord-social-sdk/development-guides/sending-direct-messages), [linked channels](/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list), and [voice communication](/developers/docs/discord-social-sdk/development-guides/managing-voice-chat). +5. Handle events & API calls: + - Listen for changes in friend lists, presence updates, and chat messages. + - Use Discord's APIs to update statuses, send messages, and manage connections. + +This guide is a conceptual overview. If you're ready to start building, [follow our step-by-step guide](/developers/docs/discord-social-sdk/getting-started) to set up the SDK in your game engine. + +--- + +## Next Steps + +import {UserPlatformIcon} from '/snippets/icons/UserPlatformIcon.jsx' +import {PlayIcon} from '/snippets/icons/PlayIcon.jsx' +import {PhoneCallIcon} from '/snippets/icons/PhoneCallIcon.jsx' + +Check platform compatibility, explore communication features, and start your integration: + + + }> + See supported platforms and download information. + + }> + Learn about messaging, voice chat, and in-game lobbies. + + }> + Choose your platform and follow step-by-step setup instructions. + + + +--- + +## Change Log + +| Date | Changes | +|---------------|-----------------| +| July 21, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Client`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91716140c699d8ef0bdf6bfd7ee0ae13 +[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468 +[`Client::GetProvisionalToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8003130b6c46e54ac68442483bf0480c \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes.mdx b/discord/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes.mdx new file mode 100644 index 0000000000..89e6f65bbf --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes.mdx @@ -0,0 +1,109 @@ +--- +title: OAuth2 Scopes +description: Learn about OAuth2 scopes required for Discord Social SDK features. +--- + +OAuth2 scopes define the level of access your app has to a user's Discord account + +What OAuth scopes are available to your integration are set via +[`AuthorizationArgs::SetScopes`] on [`AuthorizationArgs`] which is passed to [`Client::Authorize`] on Social SDK +authentication. + +## Default Presence Scopes + +At a minimum, the Social SDK uses the following scopes to use features like rich presence and friends list: + +- `openid` +- `sdk.social_layer_presence` + +The default presence features include: + +* [Account Linking](/developers/docs/discord-social-sdk/core-concepts/core-features#account-linking) +* [Provisional Accounts](/developers/docs/discord-social-sdk/core-concepts/core-features#provisional-accounts) +* [Friend System & Relationships](/developers/docs/discord-social-sdk/core-concepts/core-features#friend-system-relationships) +* [Presence & Rich Presence](/developers/docs/discord-social-sdk/core-concepts/core-features#presence-rich-presence) + +The Social SDK provides the helper method [`Client::GetDefaultPresenceScopes`], which returns `openid sdk.social_layer_presence`, +that you can use when setting up your OAuth2 flow, for integrations that only need the above functionality. + + +With only the default presence scopes, your game will not be able to use any of the limited access +communications features. + + +## Default Communication Scopes + +The communications features are currently available but have limited access. Those features **require** the scope +of `sdk.social_layer`, which includes the `sdk.social_layer_presence` scope but also allows your app to use those limited features on behalf of the user. + +- `openid` +- `sdk.social_layer` + +These communication features include: +* [Messaging & Communication](/developers/docs/discord-social-sdk/core-concepts/communication-features#messaging-communication) +* [Lobbies & In-Game Chat](/developers/docs/discord-social-sdk/core-concepts/communication-features#lobbies-ingame-chat) +* [Linked Channels](/developers/docs/discord-social-sdk/core-concepts/communication-features#linked-channels) + +The Social SDK provides the helper method [`Client::GetDefaultCommunicationScopes`], which returns `openid sdk.social_layer`, +that you can use when setting up your OAuth2 flow, for integrations that integrates both the default and limited communications features. + + +For more information about these features, please see [Core Concepts: Communication Features](/developers/docs/discord-social-sdk/core-concepts/communication-features). + + +If your game requires additional scopes, you can add them to the default scopes to authorize additional access from your users. + +You should only add scopes that are necessary for your game to function. Requesting unnecessary scopes can lead to user distrust and may result in users not linking their Discord account. + +See [available OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) available with the Discord API. + +## OAuth2 Client Types + +OAuth2 has two client types: **Confidential** and **Public**. Most games will not want to ship with **Public Client** enabled. + +Some Social SDK methods require your Discord application to be a **Public Client**. These methods also have server-side alternatives that you can use with a **Confidential Client**. + +- Using confidential clients with proper secret management for production applications is generally recommended. +- Public clients cannot securely store client secrets. +- Your security team should review this setting and authentication flows before releasing your game. + +[Learn more about OAuth2 client types](https://oauth.net/2/client-types) + +--- + +## Next Steps + +import {PlayIcon} from '/snippets/icons/PlayIcon.jsx' +import {LinkIcon} from '/snippets/icons/LinkIcon.jsx' +import {PaintPaletteIcon} from '/snippets/icons/PaintPaletteIcon.jsx' + + + +Start your integration, implement user authentication, and learn about UI design: + + + }> + Choose your platform and follow step-by-step setup instructions. + + }> + Implement user authentication with Discord OAuth2. + + }> + Learn how to design your game's UI to integrate social features. + + + +--- + +## Change Log + +| Date | Changes | +|---------------|-----------------| +| July 21, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`AuthorizationArgs`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#adb47ac55258db29d4cb8a2c506093eed +[`AuthorizationArgs::SetScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#aa3714d11a196e0d71c8c1cf38c506d92 +[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468 +[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1 +[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/core-concepts/platform-compatibility.mdx b/discord/developers/docs/discord-social-sdk/core-concepts/platform-compatibility.mdx new file mode 100644 index 0000000000..566d697225 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/core-concepts/platform-compatibility.mdx @@ -0,0 +1,66 @@ +--- +title: Platform Compatibility +description: Learn about Discord Social SDK platform support and compatibility. +--- +import ConsoleAccess from '/snippets/discord-social-sdk/callouts/console-access.mdx'; +import SonyCallout from '/snippets/discord-social-sdk/callouts/sony.mdx'; + + +You can find instructions on how to download the SDK for each platform in the [Getting Started](/developers/docs/discord-social-sdk/getting-started) guide. + + +The Discord Social SDK is available for the following platforms: + +| Platform | Support Level | Standalone C++ | Unreal Engine | Unity | +|------------------------|---------------------|----------------|---------------|-------| +| **Desktop** | | | | | +| Windows (x64) | Generally Available | ✅ | ✅ | ✅ | +| Windows (ARM64) | Generally Available | ✅ | ❌ | ❌ | +| macOS | Generally Available | ✅ | ❌ | ✅ | +| Linux* (glibc >= 2.31) | Experimental | ✅ | ✅ | ✅ | +| **Mobile** | | | | | +| Android | Experimental | ✅ | ❌ | ✅ | +| iOS | Experimental | ✅ | ❌ | ✅ | +| **Console** | | | | | +| Xbox One | Experimental | ✅ | ✅ | ❌ | +| Xbox Series X\|S | Experimental | ✅ | ✅ | ✅ | +| PlayStation 4 | Experimental | ✅ | ✅ | ❌ | +| PlayStation 5 | Experimental | ✅ | ✅ | ✅ | + + +\* There are too many Linux distributions to test, but most distros with glibc 2.31, e.g. Ubuntu 20.04, +or later should work. + + + + + +--- + +## Next Steps + +import {TrophyIcon} from '/snippets/icons/TrophyIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' +import {PlayIcon} from '/snippets/icons/PlayIcon.jsx' + +Configure authentication, explore features, and get started with setup: + + + }> + Understand OAuth2 scopes and client types for authentication. + + }> + Explore social features like account linking, friends, and rich presence. + + }> + Platform-specific setup instructions and download links. + + + +--- + +## Change Log + +| Date | Changes | +|---------------|-----------------| +| July 21, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines.mdx new file mode 100644 index 0000000000..f493d6467e --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines.mdx @@ -0,0 +1,72 @@ +--- +title: Discord Social SDK Design Guidelines +sidebarTitle: Glossary +description: Definitions and explanations of key terms used in Discord Social SDK design guidelines. +--- + +These design guidelines provide best practices and guidelines for designing social features in your game. Following these patterns can create a seamless and engaging user experience that leverages SDK social features to enhance your game. + +If you are looking for a place to get started, we recommend you start with [Principles](/developers/docs/discord-social-sdk/design-guidelines/principles) or the [Getting Started](/developers/docs/discord-social-sdk/getting-started) guide. + +import {CrownIcon} from '/snippets/icons/CrownIcon.jsx' +import {UserPlusIcon} from '/snippets/icons/UserPlusIcon.jsx' +import {DpadIcon} from '/snippets/icons/DpadIcon.jsx' +import {ClydeIcon} from '/snippets/icons/ClydeIcon.jsx' +import {ContactsIcon} from '/snippets/icons/ContactsIcon.jsx' +import {ChatIcon} from '/snippets/icons/ChatIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' +import {UserStatusIcon} from '/snippets/icons/UserStatusIcon.jsx' +import {GameControllerIcon} from '/snippets/icons/GameControllerIcon.jsx' +import {GroupIcon} from '/snippets/icons/GroupIcon.jsx' +import {TextControllerIcon} from '/snippets/icons/TextControllerIcon.jsx' + + + + }> + Fundamental best practices for integrating Discord's social layer into your game. These principles ensure a seamless and engaging user experience. + + }> + Strategies for implementing user authentication and account linking, allowing players to connect their Discord accounts with your game. + + }> + Key integration points where your game can interact with Discord's services, enhancing the overall player experience through seamless connectivity. + + }> + Guidelines for using Discord's brand assets within your game, ensuring consistent and respectful representation of the Discord brand. + + }> + Techniques for designing a unified friends list - allowing players to see and interact with their Discord friends directly within your game. + + }> + Best practices for enabling direct messaging between players, facilitating private communication, and enhancing social interactions. + + }> + Best practices for implementing chat history in Discord Social SDK integrations + + }> + Best practices for implementing and managing access to the Discord social settings in games using the Discord Social SDK + + }> + Designing around provisional accounts for users who prefer not to link their Discord accounts, allowing them to enjoy social features still. + + }> + Leveraging Discord's presence and rich presence features to display detailed player status and game activity for in-game social interactions. + + }> + Integrating Discord's social features on console platforms, ensuring a consistent experience across different devices. + + }> + Techniques for managing and displaying in-game friends, allowing players to connect and play with their Discord friends easily. + + }> + Guidelines for linking Discord channels to your game, enabling players to connect and communicate across different platforms. + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/branding-guidelines.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/branding-guidelines.mdx new file mode 100644 index 0000000000..fd25210bf9 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/branding-guidelines.mdx @@ -0,0 +1,44 @@ +--- +title: Branding Guidelines +description: Official branding guidelines for using Discord's brand assets in your integration. +--- + +## Discord Logo + +Download the Discord word-mark assets [discord.com/branding](https://discord.com/branding). + +Please do not edit, change, distort, recolor, or reconfigure the Discord logo. + +![Discord Logo](/images/social-sdk/design-guidelines/Brand-02.png) + +## Buttons + +There are four sets of button styles for this integration: Blurple, Light, Dark, and Flexible. + +#### Flexible + +The Flexible buttons are intended to be restyled to match the game's aesthetic (examples). Flexible buttons include the font of the text before the logo as well as the button colors. The Discord logo and its colors should not be altered. This set is for in-game connection points. + +#### Blurple / Light / Dark + +These pre-styled buttons leverage colors vetted by our Design Systems team to pass contrast ratios across different backgrounds. + +![Brand Buttons](/images/social-sdk/design-guidelines/Brand-03.png) + +## Spacing + +Ensure that there is adequate spacing between button text and the Discord logo as well as the left/right edges of the button. + +When using the Flexible button styling, ensure that your typeface's baseline and x-height are aligned to the Discord logo. + +![Brand Spacing](/images/social-sdk/design-guidelines/Brand-04.png) + +## Resources + +- [Discord branding guidelines](https://discord.com/branding) + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | Initial Release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/chat-history.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/chat-history.mdx new file mode 100644 index 0000000000..e3da97525b --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/chat-history.mdx @@ -0,0 +1,32 @@ +--- +title: Chat History +description: Design guidelines for displaying and managing chat history from Discord. +--- + +## Style recommendations + +Chat history is a highly requested **optional** feature that helps players pick up conversations where they left off—whether they're returning to a lobby or jumping back into a linked channel. + +We recommend showing **timestamps** to give players better context about when messages were sent and seen. + +![Badge when online elsewhere](/images/social-sdk/design-guidelines/ChatHistory-02.webp) + +## Display logic + +If one player is messaging from a game lobby or linked channel, then chat history is preserved, even if the other player is messaging from Discord. + +When players return to a lobby or open a linked channel, we recommend surfacing **10 to 15** of the most recent messages to provide meaningful context. This isn't a hard limit—older messages can be revealed through scroll or loading more content, depending on your UI constraints. + +![Message styling](/images/social-sdk/design-guidelines/ChatHistory-03.webp) + +--- + +## Resources + +- [Development Guide: Creating and Managing Lobbies](/developers/docs/discord-social-sdk/development-guides/managing-lobbies) + +## Change Log + +| Date | Changes | +|---------------|-----------------| +| July 02, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/connection-points.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/connection-points.mdx new file mode 100644 index 0000000000..63ea8dc960 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/connection-points.mdx @@ -0,0 +1,46 @@ +--- +sidebar_label: Connection Points +description: Design guidelines for creating effective connection points between your application and Discord. +--- + +[Home](/developers/docs/intro) > [Discord Social SDK](/developers/docs/discord-social-sdk/overview) > [Design Guidelines](/developers/docs/discord-social-sdk/design-guidelines) > Connection Points + +# Connection Points + +This guide provides best practices and design guidelines for integrating key connection points within your game. Connection points are crucial touchpoints where players can login to Discord, enhancing their overall gaming experience. + +## Signing in + +If the game has account management, then this connection point is **required**, otherwise it is not. + +Discord's sign-in button is presented as the **primary option** to log-in for the player amongst the list of **external identity-providers** due to providing deeper user-benefits than a standard OAuth login. + +Please use the [**Blurple Button**](/developers/docs/discord-social-sdk/design-guidelines/branding-guidelines#buttons) button styling for the sign-in connection point. + +![Signing In](/images/social-sdk/design-guidelines/ConnectionPoints-03.png) + +## Friends list + +If the player has not connected their Discord account yet, they will see a **persistent call-to-action** in their friend's list until they connect. + +Select your preferred _button-styling_ for the friends list connection point (see Brand guidelines section). + +After the player connects their account, the connection point is no longer visible. + +![Friends List](/images/social-sdk/design-guidelines/ConnectionPoints-04.png) + +## Content Guidelines + +Please use the strings shown here within the relevant contexts. + +These connection point strings should be consistent across all games that use the Discord Social SDK to help the user build recognition, trust, and understanding when taking this action. + +![Content Guidelines](/images/social-sdk/design-guidelines/ConnectionPoints-05.png) + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/consoles.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/consoles.mdx new file mode 100644 index 0000000000..036551f828 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/consoles.mdx @@ -0,0 +1,54 @@ +--- +title: Consoles +description: Design guidelines for implementing Discord Social SDK features on gaming consoles. +--- + +import ConsoleAccess from '/snippets/discord-social-sdk/callouts/console-access.mdx'; + + + +Players expect a consistent Discord social experience across devices—whether on PC, mobile, or console. While the console experience largely mirrors other devices, the following slides highlight adjustments tailored to console navigation. + +Console players **will need a secondary device** to experience the full benefits of the Discord Social SDK. + +![Connect on console](/images/social-sdk/design-guidelines/Consoles-02.jpg) + +## Connect to Discord on console + +The overall connection flow remains the same on console as other devices. After clicking an in-game CTA to connect to Discord, a screen with sign in options appears. + +Users will need to use a mobile device or computer in order to avoid the clunky experience of signing in on a web browser using a controller. + +A player without an existing Discord account can create one easily on the web. + +![Connect on console](/images/social-sdk/design-guidelines/Consoles-03.jpg) + +Users can skip the device code screen by scanning the QR code with their mobile camera. They will be prompted to authorize the game. If the Discord app is not detected upon scanning, users are prompted to sign in and connect via the web browser. + +If a player does not wish to use the QR scan, they will need to go to discord.com/activate to enter the 8-digit code. + +![connecting with your phone](/images/social-sdk/design-guidelines/Consoles-04.jpg) + +## Chatting on console +Game chat will be available for games who support the feature on consoles. However, unsupported rich media from Discord (i.e. attachments, polls, voice messages, etc.) should not include a clickable link icon. Similar to the auth flow, we do not want to encourage players to visit Discord in the console web browser—they should use secondary devices to view this content. + +![chat on console](/images/social-sdk/design-guidelines/Consoles-05.jpg) + + +## Friends list on console + +Please leverage the same [friends list guidelines](/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list) previously documented in the playbook. There are no notable differences in the friends list on console in comparison to other devices. However, consoles do not support hover interactions so there needs to be console-friendly way for players to access secondary information (such as alternate identities) about friends in the list. + +![chat on console](/images/social-sdk/design-guidelines/Consoles-06.jpg) + +--- + +## Resources + +- [Development Guide: Account Linking on Consoles](/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles) + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/direct-messages.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/direct-messages.mdx new file mode 100644 index 0000000000..860ff6da3e --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/direct-messages.mdx @@ -0,0 +1,37 @@ +--- +title: Direct Messages +description: Design guidelines for implementing Discord direct messaging features in your application. +--- + +## Badge when online elsewhere + +The badging logic in direct-messages is consistent with the logic detailed in the [Unified Friends List](/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list) section. + +If the user is **online elsewhere** and is not in the game client, their usernames will include a **Discord badge**. Once they're online in the game-client, they will not retain the Discord badge. + +![Badge when online elsewhere](/images/social-sdk/design-guidelines/animated/DMs-02.gif) + +## Message styling + +Style the Discord logo to match a player's username color. The logo should feel tied to a user's identity, not the message content. + +![Message styling](/images/social-sdk/design-guidelines/DMs-03.png) + +## Unsupported rich media +Regardless of whether a user has signed into Discord, unsupported rich media content should be paired with an **external link icon**. + +When content isn't clickable, don't show the icon. + +![Unsupported rich media](/images/social-sdk/design-guidelines/DMs-04.png) + +--- + +## Resources + +- [Development Guide: Direct Messages](/developers/docs/discord-social-sdk/development-guides/sending-direct-messages) + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/game-friends.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/game-friends.mdx new file mode 100644 index 0000000000..ba84ba3972 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/game-friends.mdx @@ -0,0 +1,86 @@ +--- +title: Game Friends +description: Design guidelines for displaying and managing game friends and Discord relationships. +--- + + +Any in-game imagery used in is purely fictional for concept purposes. + + +## User needs + +We've heard from players that they want more of a distinction between connections they make in-game, and friends they spend time with on Discord outside of and across games. Someone I party up with may not necessarily be my "friend". + +Users want more controls over how in-game relationships extend beyond the game client with communication, presence, and identity. + +![User needs](/images/social-sdk/design-guidelines/GameFriends-02.png) + +## Game Friends — A new tier + +Discord is introducing a new tier of friendship called **Game Friends**. + +Game Friends are a way to create an in-game relationship with another player that provides all of the in-game benefits, but limits how the relationship extends outside the game. + +![Game Friends](/images/social-sdk/design-guidelines/GameFriends-03.png) + +## Game vs Discord Friends + +### Communication + +A Game Friend **can reach Discord-connected friends outside the game** by DMing those in the game who are offline — these messages will be received on the Discord client. Discord provides players a setting to disable receiving DMs from Game Friends. + +### Presence + +A Game Friend **cannot see your rich presence** beyond the game your friendship originated in. The exception is if both friends have their Discord accounts connected and are in a shared server. + +![Game vs Discord Friends](/images/social-sdk/design-guidelines/GameFriends-04.png) + +## Equally-weighted + +When adding a new friend, **present two options to players with "Game Friends" listed first**, since it's the more localized/limited friend-tier. Please ensure that neither type of friend is pre-selected in the UI — present them equally to the user (ordering aside for above reasons). + +![Equally-weighted friend options](/images/social-sdk/design-guidelines/GameFriends-05.png) + +## Adding friends + +If the user is not yet a friend, show both options of adding as either a Game or Discord Friend. + +If a user is already your Game Friend, show the action to also add them on Discord. + +![Adding friends](/images/social-sdk/design-guidelines/GameFriends-06.png) + +## Removing friends + +When removing a Game Friend or Discord Friend, show players the standard "Remove Friend" action. The SDK will remove the friend across whatever friendships are applicable. + +![Removing friends](/images/social-sdk/design-guidelines/GameFriends-07.png) + +## Style guidelines + +When removing a Game Friend or Discord Friend, show players the standard "Remove Friend" action. The SDK will remove the friend across whatever friendships are applicable. + +![Removing friends](/images/social-sdk/design-guidelines/GameFriends-08.png) + +## UX, not UI + +The visual styling of the "Add Friend" menu-items is not intended to be prescriptive. So long as both options are equally-weighted, please use whichever UI fits your game's needs. + +![UX, not UI](/images/social-sdk/design-guidelines/GameFriends-09.png) + +## Setting on Discord + +This mock showcases how the corresponding DM looks within Discord's UI. + +![Removing friends](/images/social-sdk/design-guidelines/GameFriends-10.png) + +--- + +## Resources + +- [Development Guide: Managing Relationships](/developers/docs/discord-social-sdk/development-guides/managing-relationships) + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/linked-channels.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/linked-channels.mdx new file mode 100644 index 0000000000..aae8afa9e9 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/linked-channels.mdx @@ -0,0 +1,82 @@ +--- +title: Linked Channels +description: Design guidelines for integrating Discord channel functionality into your application. +--- + +## Link in-game chats to Discord + +Empower players to continue their conversations beyond the game. Now you can link an in-game chat to a text-channel on Discord. + +![Linking a channel via the game](/images/social-sdk/design-guidelines/animated/LinkedChannel-Setup.gif) + +## Style guidelines + +#### Feature Name & Language + +The official feature-name that will be reflected across the Discord client is "Linked Channels." Please use the following language when referring to this feature to promote player comprehension across clients. + +- "Linked to \{Discord/GameTitle\}" +- "Link a \{groupChat\} to Discord" +- "Edit/Remove Channel Link" + +#### Entrypoints + +Please use the string guidance from above: "Link \{groupChat\} to Discord." For interface, use whichever UI treatment that fits your game's needs. + +![Linked channel style guidelines](/images/social-sdk/design-guidelines/LinkedChannels-04.png) + +## Selecting a channel + +Players require a channel-selection flow where they can pick a text-channel to link their game-chat with. Only one text-channel can be linked at a time. Furthermore, the player must have the permissions to manage the channel. + +Please include the following in the selection UI: + +#### UI Elements + +- Description of what a channel link does and **who can read and write to the channel** after establishing the link +- List of **servers** and their nested **text-channels** with respective iconography (ie, private channel has a lock) +- **Search bar** for channel-names + +![Selecting a channel](/images/social-sdk/design-guidelines/LinkedChannels-05.png) + +## Setup warning + +Players can link channels with **limited access** on Discord. Doing so will allow players from their game-chat to also read and send messages to this channel, **regardless of whether or not they have permissions on Discord**. In short, linking a game-channel will bypass that channel's existing permissions on Discord. + +Show a **warning** when a user is about to link to a channel with limited access. We strongly recommend you use the copy shown to the right image. + +![Setup warning](/images/social-sdk/design-guidelines/LinkedChannels-06.png) + +## Success state & Entrypoint + +Once the channel link is established, show a **success state** to the user detailing which text-channel they've linked to on Discord. + +Separately, create a **persistent entrypoint** somewhere in the game UI. This persistent entrypoint should also show details such as the Discord server and channel name. It will also serve as the entrypoint for managing the linked channel (next slide). + +[You can download the official "Linked Channels" channel-icon in our GitHub repository.](https://github.com/discord/discord-api-docs/blob/main/resources/discord-social-sdk) + +![Success state & Entrypoint](/images/social-sdk/design-guidelines/LinkedChannels-07.png) + +## Removing Channel Link + +Players can navigate to the relevant channel settings on Discord from the "**Edit settings**" menu item. Please pair with an "open in new window" icon. + +Players can remove the channel link from either the game client or the Discord client. + +When "Remove channel-link" is interacted with, show the player a **confirmation** dialog to confirm this action. + +![Removing Channel Link](/images/social-sdk/design-guidelines/animated/LinkedChannels-08.gif) + +--- + +## Resources + +- [Development Guide: Linked Channels](/developers/docs/discord-social-sdk/development-guides/linked-channels) +- [Design Assets: Linked Channel Icon](https://github.com/discord/discord-api-docs/blob/main/resources/discord-social-sdk) + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | + diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/principles.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/principles.mdx new file mode 100644 index 0000000000..a129caaa7d --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/principles.mdx @@ -0,0 +1,47 @@ +--- +title: Principles +description: Core design principles for creating effective Discord Social SDK integrations. +--- + + +Any in-game imagery used in is purely fictional for concept purposes. + + +## 1. Add Value for the Player + +This integration aims to bring value to the player's gaming experience. Branded connection points should highlight said value-add based on the context. + +![Add Value for the Player](/images/social-sdk/design-guidelines/Principle-03.png) + +## 2. Foster trust with Discord's brand + +Lean into Discord's visual-styling to establish trust through brand recognition. The more players that connect, the more robust the social graph is for the game. + +![Foster trust with Discord's brand](/images/social-sdk/design-guidelines/Principle-04.png) + +## 3. Promote Understanding through consistency + +Use consistent branding and user-flows across games to build expected behavior. We care about the UX, not the UI. + +![Promote Understanding through consistency](/images/social-sdk/design-guidelines/Principle-05.png) + +## 4. Prioritize the player's immersion + +Our focus is on preserving the player's immersion in the game. There are no requirements to replicate Discord's UI. The visual-design should feel as seamlessly integrated into the game as our SDKs. + +![Prioritize the player's immersion](/images/social-sdk/design-guidelines/Principle-06.png) + +## 5. Uphold data privacy, transparency, and control + +Each platform is in control and responsible for the data on their respective platforms. Players are informed of where their data shows up. + +![Uphold data privacy, transparency, and control](/images/social-sdk/design-guidelines/Principle-07.png) + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | + diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/provisional-accounts.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/provisional-accounts.mdx new file mode 100644 index 0000000000..5211bfea48 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/provisional-accounts.mdx @@ -0,0 +1,51 @@ +--- +title: Provisional Accounts +description: Design guidelines for handling provisional Discord accounts and onboarding users. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +## For those who don't connect a Discord account + +Not all users are going to link their Discord account to their game. This creates the potential burden of having to maintain **two different friend systems** for each account type. + +The Discord Social SDK has built provisional accounts as a way for game-devs to create a lightweight, **limited Discord account** for unlinked users, so you can use the same APIs regardless of whether a user has connected their Discord account or not. + +![Badge when online elsewhere](/images/social-sdk/design-guidelines/Prov-02.png) + + +## What is a provisional account? + +To players, a provisional account is simply a **standard account** that has yet to be linked to Discord. They have access to standard communication features you'd expect any game user to have. + +The complexity around provisional accounts that game-developers have to handle are invisible to players in the game. + +![What is a provisional account?](/images/social-sdk/design-guidelines/Prov-03.png) + +## How provisional accounts look within the game + +Provisional accounts **won't have the Discord badge** by their username in the friends list and other contexts where their username may appear because they have not connected their Discord account. + +Only players who've connected their Discord accounts and have provisional account friends from other games using the Discord Social SDK will see this state. + +![How provisional accounts look within the game](/images/social-sdk/design-guidelines/Prov-04.png) + +## How provisional accounts look within Discord + +Provisional accounts will leverage **branded avatars** and **text-hints** on Discord to differentiate them from standard, Discord users. + +Because provisional accounts have limited capabilities, for example, you cannot start a voice or video call with them, it's important to visually distinguish them from Discord users. + +![How provisional accounts look within Discord](/images/social-sdk/design-guidelines/Prov-05.png) + +--- + +## Resources + +- [Development Guide: Using Provisional Accounts](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts) + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/signing-in.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/signing-in.mdx new file mode 100644 index 0000000000..a98e30ec05 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/signing-in.mdx @@ -0,0 +1,78 @@ +--- +title: Signing In +description: Design guidelines for implementing Discord sign-in flows and authentication. +--- + + +Any in-game imagery used in is purely fictional for concept purposes. + + +## Overall flow + +Sign-in or log-in flows all begin with the interaction of a [**Discord-styled button**](/developers/docs/discord-social-sdk/design-guidelines/branding-guidelines#buttons). + +The Discord Social SDK will support the user-journeys for those who already **have a Discord account** and those who **do not**. We will similarly support those who have Discord installed as well as those who do not (via browser). + +A player can also proceed **without** connecting their Discord and continue onwards to playing their game. + +![Overall Flow](/images/social-sdk/design-guidelines/SigningIn-02.png) + +### Connecting your Discord account via Browser (gif) + +![Connecting your Discord account via Browser](/images/social-sdk/design-guidelines/animated/Connecting-via-Browser.gif) + +## Overlay Authorization + +Discord's [Overlay](https://support.discord.com/hc/en-us/articles/217659737-Game-Overlay-101) feature can be used to enable authorization without ever leaving the game. This is available for games who support the feature. + +![Overlay Authorization](/images/social-sdk/design-guidelines/SigningIn-05.png) + +## Before the user connects + +We recommend adding a **one-time user education** modal based on feedback we've heard from developers and players. + +This modal should include some of the key value props that the Discord Social SDK provides. The modal should be placed contextually **in the game**. + +#### Suggested Moments + +- Trigger upon game-start for first time after the Discord Social SDK integration is launched to players +- Show when player interacts with the friends list for the first time +- Show in the authorization flow once a player taps the Discord connection point + +![Before the user connects](/images/social-sdk/design-guidelines/SigningIn-06.png) + +## After the user connects + +We recommend adding a **success state** in the game when the authorization completes — styled in a way that fits the game's aesthetic. + +#### Content + +Include the following points sequentially in the game' s writing-style. Remove any points that covers features that the game does not utilize. + +- You successfully connected your Discord account +- You can chat with your friends here and on Discord +- It might take some time for your game friends to show up in your Discord + +![After the user connects](/images/social-sdk/design-guidelines/SigningIn-07.png) + +## Error State + +The user will also need to be shown an **error state** if the authorization fails — also styled in a way that fits the game's aesthetic. + +Please include these key points sequentially in the game's writing-style: +- We were unable to connect your Discord account +- A call-to-action that redirects back to Discord with the initial authorization modal + +![Error State](/images/social-sdk/design-guidelines/SigningIn-08.png) + +--- + +## Resources + +- [Development Guide: Account Linking with Discord](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord) + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/social-settings.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/social-settings.mdx new file mode 100644 index 0000000000..0084236453 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/social-settings.mdx @@ -0,0 +1,42 @@ +--- +title: Social Settings +description: Design guidelines for implementing social settings and privacy controls. +--- + +## Evolving with users + +Discord is listening to feedback and releasing settings for players to tailor their social experience to their needs. These settings will continue to change and evolve with the SDK and Discord client. + +Discord Social SDK settings are global, meaning they will impact all games that use the Discord Social SDK. These settings live in the Discord client. + +![Evolving with users](/images/social-sdk/design-guidelines/SocialSettings-02.webp) + +## Main entrypoint + +An entrypoint is required in games to +[send players to the relevant Discord client settings page](/developers/docs/discord-social-sdk/development-guides/sending-direct-messages#ingame-direct-message-settings). +Only players who have their Discord account connected should see this. + +Exactly like the auth flow, players will be redirected to the Discord client to manage their social settings. If they do not have the client, the browser will be the fallback. + +![Main entrypoint](/images/social-sdk/design-guidelines/SocialSettings-03.webp) + +## Content guidelines + +The official settings title for players is "Discord Social Experience." Use this language when referring to Social SDK settings to players. + +The description is optional, but if your game settings has the space, we recommend describing what users should expect to find when they navigate to Discord. + +![Content guidelines](/images/social-sdk/design-guidelines/SocialSettings-04.webp) + +--- + +## Resources + +- [Development Guide: Direct Messages](/developers/docs/discord-social-sdk/development-guides/sending-direct-messages) + +## Change Log + +| Date | Changes | +|--------------|-----------------| +| May 08, 2025 | Initial Release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/status-rich-presence.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/status-rich-presence.mdx new file mode 100644 index 0000000000..3e0f2102a3 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/status-rich-presence.mdx @@ -0,0 +1,58 @@ +--- +title: Status & Rich Presence +description: Design guidelines for displaying user status and Rich Presence information. +--- + +This guide provides best practices and design guidelines for integrating status and rich presence features within your game. These features are essential for enhancing player engagement and providing real-time information about player activities. + +By following this guide, you will learn how to: + +- Implement a status matrix to display player statuses in your friends list. +- Apply core style guidelines to ensure consistency and player comprehension. +- Customize the appearance to fit your game while maintaining the essential elements of status indicators. + +## Core statuses, plus game-specific + +![Core statuses, plus game-specific](/images/social-sdk/design-guidelines/StatusPresence-02.png) + +## Status matrix + +![Status matrix](/images/social-sdk/design-guidelines/StatusPresence-03.png) + +## Side-by-side + +Any of the **core statuses** (online, idle, DND, offline) will always be the same between the game and the Discord client. + +You can customize the status icon, but custom icons will only render within the game. + +![Side-by-side](/images/social-sdk/design-guidelines/StatusPresence-04.png) + +## Core style guidelines + +To ensure player comprehension, keep the **core colors, symbols, and information architecture consistent** across Discord-powered games — with choice of styling of applied. + +In other words, as long as the status colors read as yellow/green/red, and the symbols as moon/circle/minus/offline, **all other styling is the game's choice**. + +Avatars are not required. + +![Core style guidelines](/images/social-sdk/design-guidelines/StatusPresence-05.png) + +## Rich Presence + +The game developer should set rich presence — this will show in both the Discord and game client. + +Please refer to [**Setting Rich Presence**](/developers/docs/discord-social-sdk/development-guides/setting-rich-presence) for more information on Rich Presence, such as other types of traits that can be included. + +![Rich Presence](/images/social-sdk/design-guidelines/StatusPresence-06.png) + +--- + +## Resources + +- [Development Guide: Setting Rich Presence](/developers/docs/discord-social-sdk/development-guides/setting-rich-presence) + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | Initial Release | diff --git a/discord/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list.mdx b/discord/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list.mdx new file mode 100644 index 0000000000..3bbf030966 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list.mdx @@ -0,0 +1,110 @@ +--- +title: Unified Friends List +description: Design guidelines for creating unified friends lists that combine Discord relationships. +--- + +This guide provides best practices and design guidelines for integrating a unified friends list within your game. The unified friends list is a crucial feature that allows players to see and interact with their friends across different platforms and games, enhancing their overall social gaming experience. + +By following this guide, you will learn how to: + +- Implement sectioning to organize friends by availability and game status. +- Display the Discord badge to indicate universal communication capabilities. +- Prioritize and display different identities based on the player's relationship with the game and Discord. +- Ensure consistency and familiarity in naming conventions to reduce confusion and enhance user experience. + +These design guidelines are intended to help you create a seamless and intuitive friends list that leverages Discord's social features to their fullest potential. Let's get started! + +--- +## Figma File - The Social SDK: Friend List Starter Pack + +We’ve created a Figma resource with a templated design-system of building out your game's friends list. Change +various components to match your game’s visual-language, and see it update in realtime. We’ve also tossed in a +few common use-cases, as well as an example of one of our partnered games, SUPERVIVE. + +We'd love your feedback! Join [Discord Developers Server](https://discord.gg/discord-developers) and create a post in +the `#social-sdk-dev-help` channel if you would like to see more resources like this, or if you have suggestions +for improvement. + +[![starter pack](/images/social-sdk/design-guidelines/starter_pack.webp)](https://www.figma.com/community/file/1512487996808869592/the-social-sdk-friend-list-starter-pack) + +[See Figma Resource↗](https://www.figma.com/community/file/1512487996808869592/the-social-sdk-friend-list-starter-pack) + +--- + +## Sectioning + +Sections are ordered by descending availability to help users find friends they can play with. + +The **Online — GameTitle** section shows friends who are online in the same game and are compatible to play with. + +The **Online — Elsewhere** section shows friends who are online but not in the same game. However, they can be messaged or invited to play the game. + +![Sectioning Friends List](/images/social-sdk/design-guidelines/UFL-02.png) + +## Discord = Communication + +Show the Discord badge next to a player's name when they are **online elsewhere** and have **connected their Discord** account. Provisional accounts will not have a badge. + +The Discord logo represents **universal communication**. It aims to convey to users: “As a player, I know I can message or invite this friend to play a game anywhere Discord is present.” This means via phone, computer, and even console. + +![Discord logo represents universal communication](/images/social-sdk/design-guidelines/UFL-03.png) + +## Identities + +If the friend **owns the game**, display their in-game identity (username). Prioritize what users are already familiar with. + +If the friend does not own the game, use their **Discord Display Name**. + +If the friend does not own the game nor have a linked Discord account, fall back to their **Provisional identity**. + +![Displaying different relationships](/images/social-sdk/design-guidelines/UFL-04.png) + +## Examples of friends lists + +A player's **console ID** is effectively their **game ID**, as consoles require this. As such, it's likely the set of names players will be most familiar with. + +The examples to the right show how the hierarchy translates to consoles. + +![Examples of friends lists](/images/social-sdk/design-guidelines/UFL-05.png) + +## How did we land here? + +It reduces sudden **name changes** within the game, which can be confusing to the player, and reduce erratic UI-width shifts. + +It's also **scalable** and **inclusive** of provisional accounts who do not have Discord accounts connected. + +![How did we land here?](/images/social-sdk/design-guidelines/animated/UFL-06.gif) + +## Discord Display Names + +When referring to a player's Discord Identity, please use their **Discord Display Name**, and not their username. + + +See [How to Manage Special Characters in Discord Display Names](/developers/docs/discord-social-sdk/how-to/handle-special-characters-display-names) for guidance on handling special characters in text chat and friend lists. + + +![Discord display names](/images/social-sdk/design-guidelines/UFL-07.png) + +## Scaling to Multiple Platforms + +If your game uses more than one platform's friend graph, combine them into "Online Elsewhere." + +Prioritize player needs by elevating available players to the top of the friends list, regardless of platform. + +Avoid friend-lists organized by company. This hierarchy dilutes the value-prop of putting online players first, in favor of platform delineation. + +![Scaling to Multiple Platforms](/images/social-sdk/design-guidelines/UFL-08.png) + +--- + +## Resources + +- [Development Guide: Creating a Unified Friends List](/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list) +- [Figma File - The Social SDK: Friend List Starter Pack ↗](https://www.figma.com/community/file/1512487996808869592/the-social-sdk-friend-list-starter-pack) + +## Change Log + +| Date | Changes | +|----------------|------------------------------| +| June 25, 2025 | Added Figma UFL starter pack | +| March 17, 2025 | initial release | diff --git a/discord/developers/docs/discord-social-sdk/development-guides.mdx b/discord/developers/docs/discord-social-sdk/development-guides.mdx new file mode 100644 index 0000000000..e95f6749b8 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides.mdx @@ -0,0 +1,65 @@ +--- +title: Discord Social SDK Development Guides Glossary +sidebarTitle: Glossary +description: Definitions and explanations of key terms used in Discord Social SDK development guides. +--- + +These guides include suggested development practices, and user flows for you to consider while integrating the Discord Social SDK into your game. These will help provide your users with a consistent and clear experience while interacting with your game. + +If you are new to the Discord Social SDK, we recommend you start with the [Getting Started](/developers/docs/discord-social-sdk/getting-started) guide. + +import {UserPlusIcon} from '/snippets/icons/UserPlusIcon.jsx' +import {GameControllerIcon} from '/snippets/icons/GameControllerIcon.jsx' +import {MagicWandIcon} from '/snippets/icons/MagicWandIcon.jsx' +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserStatusIcon} from '/snippets/icons/UserStatusIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' +import {ChatIcon} from '/snippets/icons/ChatIcon.jsx' +import {DoorEnterIcon} from '/snippets/icons/DoorEnterIcon.jsx' +import {TextControllerIcon} from '/snippets/icons/TextControllerIcon.jsx' +import {VoiceNormalIcon} from '/snippets/icons/VoiceNormalIcon.jsx' + + +## Authentication & Account Linking + + + }> + Learn how to authenticate users with their Discord accounts using OAuth2. + + }> + Implement Discord authentication flows for console platforms. + + }> + Give your users a seamless account experience with provisional accounts. + + + +## Game Relationships, Presence & Game Invites + + }> + Combine Discord and game-specific friends in one view. + + }> + Display detailed game status in Discord profiles. + + }> + Allow players to invite friends to join their game session or party. + + + +## Text & Voice Chat + + + }> + Enable private messaging between players. + + }> + Bring players together in a shared lobby with invites, text chat, and voice comms. + + }> + Connect game lobbies to Discord text channels. + + }> + Add in-game voice communication. + + diff --git a/discord/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles.mdx b/discord/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles.mdx new file mode 100644 index 0000000000..3486241326 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles.mdx @@ -0,0 +1,321 @@ +--- +title: Account Linking on Consoles +description: Implement Discord account linking on gaming consoles. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; +import ConsoleAccess from '/snippets/discord-social-sdk/callouts/console-access.mdx'; + +## Overview + +This guide will walk you through implementing OAuth2 device authorization for console users, allowing them to log in to your game using their Discord account. + +By following this guide, you will learn how to: + +- Initiate OAuth2 device authorization +- Handle user authentication via QR codes or manual entry +- Exchange the device code for an access token +- Implement automatic token handling with the SDK + +### Prerequisites + +Before you begin, make sure you have: + +- Read the [Core Concepts](/developers/docs/discord-social-sdk/core-concepts) guide to understand: + - OAuth2 authentication flow + - Discord application setup + - SDK initialization +- Set up your development environment with: + - Discord application created in the [Developer Portal](https://discord.com/developers/applications) + - Discord Social SDK downloaded and configured + - Basic SDK integration working (initialization and connection) + + + +If you haven't completed these prerequisites, we recommend following the [Getting Started](/developers/docs/discord-social-sdk/getting-started) guide. + +--- + +### OAuth2 Device Authorization Flow + +Console users cannot authenticate via a web browser. Instead, they follow these steps: + +1. The game requests a device code from Discord +2. To approve the authorization request, The user scans a QR code or enters a user code at `discord.com/activate`. +3. Your game polls the Discord API to exchange the device code for an access token. + +The SDK can manage this process automatically or allow manual token handling. + + +The OAuth2 flow requires a user's account to be verified + + +![Authorization screen from using OpenAuthorizeDeviceScreen and GetTokenFromDevice](/images/social-sdk/development-guides/authorize_device.png) + +--- + +## Automatic Token Handling + + + +The SDK automates all of the OAuth2 device authorization flow steps with a single call to [`Client::GetTokenFromDevice`]. This method will handle the entire authorization process, including polling for the access token. + +Once you have the access token, you can authenticate the user with [`Client::UpdateToken`] and call [`Client::Connect`] to establish a connection with Discord. + +```cpp +discordpp::DeviceAuthorizationArgs args{}; +args.SetClientId(APPLICATION_ID); +args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + +client->GetTokenFromDevice(args, [client]( + discordpp::ClientResult result, + std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + std::string scope) { + if (result.Successful()) { + std::cout << "🔓 Access token received! Establishing connection...\n"; + client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) { + client->Connect(); + }); + } else { + std::cerr << "❌ Failed to get token from device \n"; + } + }); +``` + +--- + +## Manual Flow for Console Authorization + +If you plan to handle console authorization manually, you can follow these steps to authorize a user. + +We'll be following the same OAuth2 device authorization flow as the automatic method, but you'll need to manually handle the polling and token exchange. + +1. [Request a device code from Discord](/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles#step-1-request-a-device-code-from-discord) +2. [Display the user verification information](/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles#step-2-display-authorize-screen-with-qr-code-and-user-code) or open the authorization screen (optional) +3. [Poll for the user's authorization](/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles#step-3-poll-for-users-authorization) +4. [Exchange the device code for an access token](/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles#step-4-exchange-device-code-for-access-token) +5. [Handle the token response](/developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles#step-5-handle-token-response) and close authorization screen (optional) + +### Step 1: Request a Device Code from Discord + +Your game must request a device and user code from the Discord API. + +```python +import requests + +API_ENDPOINT = "https://discord.com/api/v10" +CLIENT_ID = "YOUR_CLIENT_ID" +CLIENT_SECRET = "YOUR_CLIENT_SECRET" +SCOPE = "sdk.social_layer" + +def authorize_device(): + data = {"scope": SCOPE} + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + response = requests.post(f"{API_ENDPOINT}/oauth2/device/authorize", data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + response.raise_for_status() + return response.json() +``` + +#### Example Response +```json +{ + "device_code": "", + "user_code": "", + "verification_uri": "https://discord.com/activate", + "verification_uri_complete": "https://discord.com/activate?user_code=", + "expires_in": 300, + "interval": 5 +} +``` + +### Step 2: Display Authorize Screen with QR Code and User Code + +To open an authorization screen after requesting the user code, use [`Client::OpenAuthorizeDeviceScreen`]. + +```cpp +client->OpenAuthorizeDeviceScreen(user_code); +``` + + +You can also display the `verification_uri_complete` or `verification_uri` with `user_code` in your game's interface to allow the user to enter the code manually. + + +![Authorization screen from using OpenAuthorizeDeviceScreen and GetTokenFromDevice](/images/social-sdk/development-guides/authorize_device.png) + +Once the user approves the authorization request from a web browser or their mobile device, the `device_code` from Step 1 is ready to be exchanged for an access token. + +### Step 3: Poll for User's Authorization + +While the user completes the authorization request, your game must poll the Discord Oauth2 token endpoint to exchange the device code for a valid access token. + +You should poll this endpoint using the provided `interval` until the code expires after `expires_in` or succeeds. These values are available from the Authorize response above. If the code expires, you'll want to start over with a new authorization request or cancel the authorization. + +### Step 4: Exchange Device Code for Access Token +Once the user completes authentication on Discord, your game must exchange the device code for an access token. + +```python +def exchange_device_code(device_code): + data = { + "grant_type": "urn:ietf:params:oauth:grant-type:device_code", + "device_code": device_code + } + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + response = requests.post(f"{API_ENDPOINT}/oauth2/token", data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + response.raise_for_status() + return response.json() +``` + +### Step 5: Handle Token Response + +Once exchanged, the API returns an access token to be used with [`Client::UpdateToken`] and [`Client::Connect`] to start using Discord Social SDK features. + +#### Example Response +```json +{ + "access_token": "", + "token_type": "Bearer", + "expires_in": 604800, + "refresh_token": "", + "scope": "sdk.social_layer" +} +``` + +If you followed the manual flow, you'll need to call [`Client::CloseAuthorizeDeviceScreen`] to close the authorization screen. + + +### Example for Manual Token Authorization + +Here is an example of implementing the manual authorization flow in Python. + +```python +import requests +import json +import time + +API_ENDPOINT = 'https://discord.com/api/v10' +CLIENT_ID = 'YOUR_CLIENT_ID' +CLIENT_SECRET = 'YOUR_CLIENT_SECRET' +SCOPE = 'sdk.social_layer' + +def authorize_device(): + data = { + 'scope': SCOPE + } + headers = { + 'Content-Type': 'application/x-www-form-urlencoded' + } + r = requests.post('%s/oauth2/device/authorize' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + r.raise_for_status() + return r.json() + +def exchange_device_code(device_code): + print("Attempting to exchange device code for access token...") + data = { + "grant_type": "urn:ietf:params:oauth:grant-type:device_code", + "device_code": device_code + } + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + r.raise_for_status() + response = r.json() + return r.json() + +def poll_token_exchange(device_code, interval, expires_in): + start_time = time.time() + + while time.time() - start_time < expires_in: + try: + print("Checking for authorization...") + result = exchange_device_code(device_code) + return result + except requests.exceptions.HTTPError as e: + if e.response.status_code == 400: + # Authorization is pending, wait and try again + print(f"Waiting {interval} seconds for user to authorize...") + time.sleep(interval) + else: + # Some other error occurred + raise e + + raise Exception(f"Authorization timed out after {expires_in} seconds") + +# Step 1: Get device authorization details +device_auth = authorize_device() + +# Step 2: Display authorization details to user +print("\nPlease visit:", device_auth["verification_uri_complete"]) + +# Step 3: Poll for token exchange +try: + access_token = poll_token_exchange( + device_auth["device_code"], + device_auth["interval"], + device_auth["expires_in"] + ) + print("\nSuccess! Access Token:", access_token["access_token"]) +except Exception as e: + print("\nError:", str(e)) + +# Step 4: Use the access token with discordpp::Client::UpdateToken and discordpp::Client::Connect +``` + +--- + +## Working with Tokens + +Once you receive the player's access and refresh tokens, you can set the access token in the SDK with [`Client::UpdateToken`]. At this point, you're authorized and ready to use [`Client::Connect`]. + +```cpp +client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) { + client->Connect(); +}); +``` + +You'll want to store the access and refresh tokens for the player to use in future sessions. + +Please note that `access_token` values do expire. You'll need to make use of the `refresh_token` to refresh the player's token, which is covered under [Refreshing Access Tokens](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord#refreshing-access-tokens). + +--- + +## Next Steps + +Now that you've successfully implemented account linking with Discord on a console, you can integrate more social features into your game. + +import {PaintPaletteIcon} from '/snippets/icons/PaintPaletteIcon.jsx' +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' + + + }> + Combine Discord and game friends into a single list for easy management. + + }> + Display game status and information to Discord friends. + + }> + Follow our design guidelines when integrating Discord features into a console game. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | Initial release | + +{/* Autogenerated Reference Links */} +[`Client::CloseAuthorizeDeviceScreen`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a4fcdd697702d086a8170a2d60a69acb8 +[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa +[`Client::GetTokenFromDevice`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a4a9c666b2d30bae0a16f5afd7ccee60d +[`Client::OpenAuthorizeDeviceScreen`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af29a13acc992a75fc0870051ff68575b +[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord.mdx b/discord/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord.mdx new file mode 100644 index 0000000000..be8fb7459b --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord.mdx @@ -0,0 +1,326 @@ +--- +title: Account Linking with Discord +description: Implement account linking to connect user accounts between your application and Discord. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + +## Overview +This guide explains how to authenticate users with their existing Discord accounts via OAuth2, enabling seamless login and access to Discord features. + +### Flexible Account Options +If a player does not have a Discord account, you can use the SDK to [create a provisional account](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts) instead so that they can still access your game's features. + +### Prerequisites + +Before you begin, make sure you have: + +- Read the [Core Concepts](/developers/docs/discord-social-sdk/core-concepts) guide to understand: + - OAuth2 authentication flow + - Discord application setup + - SDK initialization +- Set up your development environment with: + - Discord application created in the [Developer Portal](https://discord.com/developers/applications) + - Discord Social SDK downloaded and configured + - Basic SDK integration working (initialization and connection) + +If you haven't completed these prerequisites, we recommend first following the [Getting Started](/developers/docs/discord-social-sdk/getting-started) guide. + +--- + + +## Our Authentication Flow + +OAuth2 is the standard authentication flow that allows users to sign in using their Discord account. The process follows these steps: + +1. [Request authorization](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord#step-1-request-authorization): Your game sends an authentication request to Discord. +2. [User Approval](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord#step-2-user-approval): The user approves the request, granting access to your application. +3. [Receive Authorization Code](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord#step-3-receiving-the-authorization-code): After approval, Discord redirects the user to your app with an authorization code. +4. [Exchange for Tokens](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord#step-4-exchanging-the-authorization-code-for-an-access-token): The authorization code is exchanged for: + - Access Token, which is valid for ~7 days + - Refresh Token, used to obtain a new access token + + +The OAuth2 flow requires a user's account to be verified + + +### OAuth2 using the Discord Social SDK + +- If the Discord client has [overlay support](https://support.discord.com/hc/en-us/articles/217659737-Game-Overlay-101) (Windows only), the OAuth2 login modal appears in your game instead of opening a browser. +- The SDK automatically handles redirects, simplifying the authentication flow. +- Some security measures, such as CSRF protection, are built-in, but you should always follow best practices to secure your app. + +--- + +## Requesting Access Tokens + +### Step 0: Configure OAuth2 Redirects +For OAuth2 to work correctly, you must **register the correct redirect URIs** for your app in the **Discord Developer Portal**. + +| Platform | Redirect URI | +|-------------|--------------------------------------------------------------------------------------------| +| **Desktop** | `http://127.0.0.1/callback` | +| **Mobile** | `discord-APP_ID:/authorize/callback` _(replace `APP_ID` with your Discord application ID)_ | + +### Step 1: Request Authorization + +The SDK provides helper methods to simplify OAuth2 login. + +Use the [`Client::Authorize`] method to initiate authorization and allow the user to approve access. + +#### Authorization Scopes + +One of the required arguments to [`Client::Authorize`] is scopes, which is the set of permissions that you are requesting. We recommend using [`Client::GetDefaultPresenceScopes`], but you can choose whatever scopes you need. + +#### Authorization Code Verifier + +If you are using [`Client::GetToken`] in [Step 4](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord#step-4-exchanging-the-authorization-code-for-an-access-token), you will need to specify a "code challenge" and "code verifier" in your requests. We'll spare you the boring details of how that works (woo… crypto), as we've made a simple function to create these for you, [`Client::CreateAuthorizationCodeVerifier`], which you can use to generate the code challenge and verifier. + +```cpp +// Create a code verifier and challenge if using GetToken +auto codeVerifier = client->CreateAuthorizationCodeVerifier(); +discordpp::AuthorizationArgs args{}; +args.SetClientId(YOUR_DISCORD_APPLICATION_ID); +args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); +args.SetCodeChallenge(codeVerifier.Challenge()); + +client->Authorize(args, [client, codeVerifier](discordpp::ClientResult result, std::string code, std::string redirectUri) { + if (!result.Successful()) { + std::cerr << "❌ Authorization Error: " << result.Error() << std::endl; + } else { + std::cout << "✅ Authorization successful! Next step: exchange code for an access token \n"; + } +}); +``` + +### Step 2: User Approval + +After calling [`Client::Authorize`], the SDK will open a browser window, Discord client, or an in-game overlay to prompt the user to approve the request. + +### Step 3: Receiving the Authorization Code + +Once the user approves the request from Step 2, Discord will redirect the user back to your app with an authorization code that you can use to exchange for an access token. + +### Step 4: Exchanging the Authorization Code for an Access Token + +#### Server-to-Server Get Token Exchange + +If your application uses a backend server and does **not** have `Public Client` enabled, you can manually exchange the authorization code for an access token using the Discord API. + +``` python +import requests + +API_ENDPOINT = 'https://discord.com/api/v10' +CLIENT_ID = 'YOUR_CLIENT_ID' +CLIENT_SECRET = 'YOUR_CLIENT_SECRET' + +def exchange_code(code, redirect_uri): + data = { + 'grant_type': 'authorization_code', + 'code': code, + 'redirect_uri': redirect_uri + } + headers = {'Content-Type': 'application/x-www-form-urlencoded'} + r = requests.post(f'{API_ENDPOINT}/oauth2/token', data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + r.raise_for_status() + return r.json() +``` + +#### Example Response + +```json +{ + "access_token": "", + "token_type": "Bearer", + "expires_in": 604800, + "refresh_token": "", + "scope": "sdk.social_layer" +} +``` + +#### Token Exchange for Public Clients + + + +If your app does not have a backend server, enable `Public Client` in the Discord Developer Portal and use [`Client::GetToken`] to automatically exchange the authorization code for a token. + +We will also need the code verifier used to generate the code challenge in Step 1. + +```cpp +client->GetToken(YOUR_DISCORD_APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, + [client](discordpp::ClientResult result, + std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + std::string scope) { + std::cout << "🔓 Access token received! Establishing connection...\n"; + // Next step: Update the token in the client and connect to Discord + }); +``` + +--- + +## Working with Tokens + +Once you've received your access token, you'll want to set the token in the SDK. You can use [`Client::UpdateToken`] to do that. At this point, you're authorized and ready to go! You'll want to store the player's access token and refresh tokens somewhere. + +Please note that the `access_token` values do expire. You'll need to use the `refresh_token` to refresh the player's access token. + +```cpp +client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, ACCESS_TOKEN_VALUE, [client](discordpp::ClientResult result) { + client->Connect(); +); +``` + +--- + +## Refreshing Access Tokens + +Access tokens expire after 7 days, requiring refresh tokens to get a new one. + +### Server-to-Server Token Refresh + +If you're handling authentication on your server, send an API request to refresh the token. + +```python +import requests + +API_ENDPOINT = 'https://discord.com/api/v10' +CLIENT_ID = 'YOUR_CLIENT_ID' +CLIENT_SECRET = 'YOUR_CLIENT_SECRET' + +def refresh_token(refresh_token): + data = { + 'grant_type': 'refresh_token', + 'refresh_token': refresh_token + } + headers = {'Content-Type': 'application/x-www-form-urlencoded'} + r = requests.post(f'{API_ENDPOINT}/oauth2/token', data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + r.raise_for_status() + return r.json() +``` + +### Refreshing Access Tokens for Public Clients + + + +The easiest way to refresh tokens is using the SDK's [`Client::RefreshToken`] method. + +``` cpp +client->RefreshToken( + YOUR_DISCORD_APPLICATION_ID, GetRefreshToken(), + [client](discordpp::ClientResult result, std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, int32_t expiresIn, + std::string scope) { + if (!result.Successful()) { + std::cout << "❌ Error refreshing token: " << result.Error() + << std::endl; + return; + } + + // Update token and connect + UpdateToken(client, refreshToken, accessToken); + }); +``` + +--- + +## Revoking Access Tokens + +If a user wants to disconnect their Discord account or if a token is compromised, you can revoke access and refresh tokens. + + +When any valid access or refresh token is revoked, all of your application's access and refresh tokens for that user are immediately invalidated. + + +### Server-to-Server Token Revocation + +If your application uses a backend server, you can revoke tokens by making an API request to Discord's token revocation endpoint. + +```python +import requests + +API_ENDPOINT = 'https://discord.com/api/v10' +CLIENT_ID = 'YOUR_CLIENT_ID' +CLIENT_SECRET = 'YOUR_CLIENT_SECRET' + +def revoke_token(access_or_refresh_token): + data = {'token': access_or_refresh_token} + headers = {'Content-Type': 'application/x-www-form-urlencoded'} + r = requests.post(f'{API_ENDPOINT}/oauth2/token', data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + r.raise_for_status() +``` + +### Revoking Access Tokens for Public Clients + + + +The easiest way to revoke tokens is using the SDK's `Client::RevokeToken` method. This will invalidate all access and refresh tokens for the user and they cannot be used again. + +```cpp +client->RevokeToken(YOUR_DISCORD_APPLICATION_ID, + accessToken, // Can also use refresh token + [](const discordpp::ClientResult &result) { + if (!result.Successful()) { + std::cout + << "? Error revoking token: " << result.Error() + << std::endl; + return; + } + + std::cout + << "? Token successfully revoked! User logged out." + << std::endl; + // Handle successful logout (clear stored tokens, + // redirect to login, etc.) + }); +``` + +### Handling User Initiated Revocation + +Users can unlink their account by removing access to your application on their Discord `User Settings -> Authorized Apps` page. + +If you would like to be notified when a user unlinks this way, you can [configure you application to listen for the `APPLICATION_DEAUTHORIZED` webhook event](/developers/docs/events/webhook-events#application-deauthorized). +Otherwise, you will know that the user has unlinked because their access token and refresh token (if you have one) will be invalidated. + +--- + +## Next Steps + +Now that you've successfully implemented account linking with Discord, you can integrate more social features into your game. + +import {PaintPaletteIcon} from '/snippets/icons/PaintPaletteIcon.jsx' +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' + + + }> + Design guidelines for account linking and user authentication + + }> + Combine Discord and game friends into a single list for easy management. + + }> + Display game status and information to Discord friends. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468 +[`Client::CreateAuthorizationCodeVerifier`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aba6adc1f978e7bf4c5433c560e1ad704 +[`Client::GetDefaultPresenceScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c +[`Client::GetToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aaee636f91fb1ea3465157c20313b702c +[`Client::RefreshToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a187af0f99f94b3b9a4ad4302f6a443e7 +[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list.mdx b/discord/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list.mdx new file mode 100644 index 0000000000..0d6084d489 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list.mdx @@ -0,0 +1,455 @@ +--- +title: Creating a Unified Friends List +description: Build a unified friends list that combines Discord relationships with your application. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + + +## Overview + +A unified friends list combines both Discord and game-specific relationships in one view. This guide will show you how to: +- Fetch all relationship data +- Filter and organize relationships +- Display online status +- Handle different relationship types + +### Prerequisites + +Before you begin, make sure you have: +- Set up the Discord Social SDK with [Getting Started Guide](/developers/docs/discord-social-sdk/getting-started) +- Authenticated your users with [Development Guide: Account Linking](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord) +- Understanding of relationship types with [Development Guide: Managing Relationships in Your Game](/developers/docs/discord-social-sdk/development-guides/managing-relationships) + +### Implementing a Unified Friends List + +The Discord friend list is ultimately constructed from two entities: Relationships, and Users. You can query +Relationships API to find everyone a user is a friend with, and the Users API to find the necessary extra information +for rendering the list, such as whether they are online or not. + +### Relationships + +[Relationships](/developers/docs/discord-social-sdk/development-guides/managing-relationships) are how Discord models friends, +friend requests, and more. All relationships for the current user are loaded when the Client connects. Each +relationship has a target user id, and a type, such as `Friend`, `PendingOutgoing`, or `Blocked`. + +To allow users to manage their relationships in your game, you should provide a way to accept or reject friend +requests, block users, and manage pending requests. See [Development Guide: Managing Relationships in Your Game](/developers/docs/discord-social-sdk/development-guides/managing-relationships) for implementation details. + +### Users + +Users are the Discord users that are part of the relationships. The SDK provides a way to fetch a user by their ID, and the user object contains information such as their username, display name, avatar, and more. + +### User Status & Rich Presence + +Presence is how Discord stores whether a user is currently online or not, as well as what activities they are currently doing (such as playing a game). The SDK gives you access to two types of status: + +- Online Status: Online, Offline, Idle, etc. +- Rich Presence: Any activities associated with the current game (or application in Discord parlance). + + +The SDK will only display activities associated with the current game, meaning you will not be able to see other game activities in Rich Presence, even if you can see them in the Discord client. + + +See our [Design Guidelines: Status & Rich Presence](/developers/docs/discord-social-sdk/design-guidelines/status-rich-presence) for best practices on displaying presence information. + +There are two ways in which you can create a unified friends list in your game: + +1. Using the SDK Unified Friends List helper functions, which automatically group and sort +relationships and users for you. +2. Directly retrieving relationships and users from the SDK, and sorting manually. + +## Approach 1: Using SDK Unified Friends List Helper Functions + + +This approach is recommended as it significantly reduces the amount of code you need to write and maintain compared to +manually fetching and organizing relationships, while ensuring your friends list follows Discord's best practices. + + +The Discord Social SDK provides built-in helper functions that automatically group and sort your friends list according +to Discord's [recommended design guidelines](/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list). +This approach is generally simpler and more maintainable than manually fetching and organizing relationships. + +The SDK automatically organizes friends into the three groups we find via [`RelationshipGroupType`]: +- `OnlinePlayingGame`: Friends who are online and currently playing your game +- `OnlineElsewhere`: Friends who are online but not playing your game +- `Offline`: Friends who are offline + +### Step 1: Display the Unified Friends List + +The [`Client::GetRelationshipsByGroup`] method returns a pre-sorted list of relationships for a specific group type. +This eliminates the need to manually filter, categorize, and sort friends yourself. The SDK handles all the logic +for determining which group each friend belongs to based on their online status and game activity, and +automatically sorts users within each group (for example, users who have played your game are moved to the top of +the OnlineElsewhere group). + +Let's create a function that uses the SDK helper functions to display a properly organized friends list: + +```cpp +void DisplayUnifiedFriendsList(const std::shared_ptr &client) { + // Get friends playing the game + const auto onlineInGame = client->GetRelationshipsByGroup( + discordpp::RelationshipGroupType::OnlinePlayingGame + ); + + // Get friends online elsewhere + const auto onlineElsewhere = client->GetRelationshipsByGroup( + discordpp::RelationshipGroupType::OnlineElsewhere + ); + + // Get offline friends + const auto offline = client->GetRelationshipsByGroup( + discordpp::RelationshipGroupType::Offline + ); + + // Display "Online - GameTitle" Friends + std::cout << "\n=== Online - GameTitle (" << onlineInGame.size() << ") ===\n"; + for (const auto& relationship : onlineInGame) { + auto user = relationship.User(); + if (user) { + std::string displayStr = "🟣 " + user->DisplayName(); + + // Add Discord friend indicator + if (relationship.DiscordRelationshipType() == discordpp::RelationshipType::Friend) { + displayStr += " 👾"; + } + + // Add game friend indicator + if (relationship.GameRelationshipType() == discordpp::RelationshipType::Friend) { + displayStr += " 🎮"; + } + + std::cout << displayStr << "\n"; + } + } + + // Display "Online - Elsewhere" Friends + std::cout << "\n=== Online - Elsewhere (" << onlineElsewhere.size() << ") ===\n"; + for (const auto& relationship : onlineElsewhere) { + auto user = relationship.User(); + if (user) { + std::string displayStr = "🟢 " + user->DisplayName(); + + // Add Discord friend indicator + if (relationship.DiscordRelationshipType() == discordpp::RelationshipType::Friend) { + displayStr += " 👾"; + } + + // Add game friend indicator + if (relationship.GameRelationshipType() == discordpp::RelationshipType::Friend) { + displayStr += " 🎮"; + } + + std::cout << displayStr << "\n"; + } + } + + // Display "Offline" Friends + std::cout << "\n=== Offline (" << offline.size() << ") ===\n"; + for (const auto& relationship : offline) { + auto user = relationship.User(); + if (user) { + std::string displayStr = "⚫ " + user->DisplayName(); + + // Add Discord friend indicator + if (relationship.DiscordRelationshipType() == discordpp::RelationshipType::Friend) { + displayStr += " 👾"; + } + + // Add game friend indicator + if (relationship.GameRelationshipType() == discordpp::RelationshipType::Friend) { + displayStr += " 🎮"; + } + + std::cout << displayStr << "\n"; + } + } +} +``` + +### Step 2: Set Up Automatic Updates + +To keep your friends list up-to-date automatically, use the [`Client::SetRelationshipGroupsUpdatedCallback`]. This +callback is triggered whenever any change occurs that might affect the friends list grouping, such as a friend going +online or offline, or when a relationship changes, such as when you accept a friend request, or block a user. + +```cpp +// Set up the unified friends list update callback +client->SetRelationshipGroupsUpdatedCallback([&client](const uint64_t userId) { + std::cout << "👥 Friends list updated for user: " << userId << std::endl; + DisplayUnifiedFriendsList(client); +}); +``` + +--- + +## Approach 2: Manually Fetching Relationships and Users + +In this section we'll show a more manual method which gives you more control over how the friends list is displayed in your game. + +### Step 1: Fetch Relationships + +First, let's create a function that utilises [`Client::GetRelationships`] to query all the relationships and +user information we for our account: + +```cpp +void DisplayFriendsList(discordpp::Client& client) { + std::vector relationships{}; + for (auto& relationship: client->GetRelationships()) { + auto user = relationship.User(); + if (!user) { + continue; + } + + std::string str{}; + // Identifying information about the user: + str += " DiscordName: " + user->DisplayName(); + str += " DiscordId: " + std::to_string(user->Id()); + // Provisional users don't have a Discord icon shown next to them: + str += " IsProvisional: " + std::to_string(user->IsProvisional()); + // Whether the relationship is for a friend, a friend request, or because the user is blocked: + // For a friends list you'll want to filter out blocked users + // And likely display friend requests in a different section + str += " DiscordRelationshipType: " + std::string(discordpp::EnumToString(relationship.DiscordRelationshipType())); + str += " GameRelationshipType: " + std::string(discordpp::EnumToString(relationship.GameRelationshipType())); + // Whether the user is online/offline/etc: + str += " IsOnlineAnywhere: " + std::to_string(user->Status() != discordpp::StatusType::Offline); + str += " IsOnlineInGame: " + std::to_string(user->GameActivity() != std::nullopt); + relationships.push_back(str); + } + + std::sort(relationships.begin(), relationships.end()); + for (auto str : relationships) { + printf("%s\n", str.c_str()); + } +} +``` + + +The `relationship.User()` function returns a [`UserHandle`] object that represents a user that the Discord Social SDK +knows about. +Handle objects maintain references to both the underlying data and the SDK instance, which means that when their +data changes, existing handle objects automatically reflect these changes without needing to be recreated. + + +We will want to call this function when the client is ready, so let's add it to our ready callback: + +```cpp +// Set up status callback to monitor client connection +client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) { + std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl; + + if (status == discordpp::Client::Status::Ready) { + std::cout << "✅ Client is ready! You can now call SDK functions.\n"; + std::cout << "👥 Friends Count: " << client->GetRelationships().size() << std::endl; + + SetRichPresence(client); + DisplayFriendsList(client); + + } else if (error != discordpp::Client::Error::None) { + std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl; + } +}); +``` + +This will output the raw relationship data to the console. You can use this information to filter, organize and build a friends list that fits your game's design aesthetic. + +--- + +### Step 2: Organize Relationships + +Based on our design guidelines for a [Unified Friends List](/developers/docs/discord-social-sdk/design-guidelines/unified-friends-list), you should separate the player's friends list into three sections: `Online - GameTitle`, `Online - Elsewhere`, and `Offline`. + +Because we are building a text console application, we will use emojis to represent the status of each friend but you can use your own design elements to convey status and presence in your game. + +Let's update our `DisplayFriendsList` function to reflect our three sections and categorize friends based on their status: + +- We will create three vectors to store the friends in each category. +- We will filter out pending friends and blocked users. +- We will add indicators for Discord friends, game friends, and provisional users. +- We will categorize friends based on their game and presence status. +- We will sort each category alphabetically. +- We will display each category separately. + + +This example is for reference only. Please make sure you use efficient data structures and cache data when appropriate to avoid performance issues in your game. + + +```cpp +void DisplayFriendsList(std::shared_ptr client) { + // Create vectors for each section + std::vector inGame; + std::vector online; + std::vector offline; + + for (auto& relationship : client->GetRelationships()) { + auto user = relationship.User(); + if (!user) { + continue; + } + + // Filter out pending friends and blocked users + // You can display friend requests and blocked users in a different view to allow players to manage them in your game + if (relationship.DiscordRelationshipType() != discordpp::RelationshipType::Friend) { + continue; + } + + std::string str; + str += user->DisplayName(); + + // Add Discord friend indicator + // In a real game, please use the official Discord logo available in our design guidelines + if (relationship.DiscordRelationshipType() == discordpp::RelationshipType::Friend) { + str += " 👾"; + } + + // Add game friend indicator + if (relationship.GameRelationshipType() == discordpp::RelationshipType::Friend) { + str += " 🎮"; + } + + // Add provisional indicator + if (user->IsProvisional()) { + str += " (Provisional)"; + } + + // Categorize based on status + if (user->GameActivity()) { + // in game + inGame.push_back("🟣 " + str); + } else if (user->Status() != discordpp::StatusType::Offline) { + // online + online.push_back("🟢 " + str); + } else { + // offline + offline.push_back("⚫ " + str); + } + } + + // Sort each category + std::sort(inGame.begin(), inGame.end()); + std::sort(online.begin(), online.end()); + std::sort(offline.begin(), offline.end()); + + // Display "Online - GameTitle" Friends + std::cout << "\n=== Online - GameTitle (" << inGame.size() << ") ===\n"; + for (const auto& str : inGame) { + std::cout << str << "\n"; + } + + // Display "Online - Elsewhere" Friends + std::cout << "\n=== Online - Elsewhere (" << online.size() << ") ===\n"; + for (const auto& str : online) { + std::cout << str << "\n"; + } + + // Display "Offline" Friends + std::cout << "\n=== Offline (" << offline.size() << ") ===\n"; + for (const auto& str : offline) { + std::cout << str << "\n"; + } +} +``` + +If we build and run our application, we should now see a list of friends separated into three categories: `Online - GameTitle`, `Online - Elsewhere`, and `Offline`. + +### Step 3: Monitor Changes to Users + +To monitor for user changes, we're going using the [`Client::SetUserUpdatedCallback`] function. + +This callback will be triggered whenever a user's info is updated, such as name or presence changes (when they go online, offline, or start playing your game). + +```cpp +client->SetUserUpdatedCallback([&client](uint64_t userId) { + std::cout << "👤 User updated: " << userId << std::endl; + DisplayFriendsList(*client); +}); +``` + +Now your friends list will automatically update when the presence of a friend changes, such as when they go online, offline or start playing your game. + + +The automatic updates of the [`UserHandle`] object to the latest the user information should be sufficient for +retrieving the most up-to-date user information. +[`Client::SetUserUpdatedCallback`] may be more useful to identify times when you wish to re-sort your +user list, or similar operations. + +--- + +### Step 4: Monitor Changes in Relationships + +Let us setup two callbacks to handle relationship updates. + + +These examples rebuild the friends list from scratch every time a relationship changes. For performance reasons, we +recommend maintaining a collection of [`UserHandle`] objects and adding and removing them appropriately. + + +#### Relationship Created Callback + +This can happen when a user sends or accepts a friend invite, or blocks a user. + +```cpp +client->SetRelationshipCreatedCallback([&client](uint64_t userId, bool isDiscordRelationshipUpdate) { + std::optional user = client->GetUser(userId); + // if the userid is valid (which it should be), we can display the user's display name + if(user) { + std::cout << "🤝 Relationship created: " << user->DisplayName() << std::endl; + DisplayFriendsList(*client); + } +}); +``` + +#### Relationship Deleted Callback + +This can happen when a user rejects a friend request or removes a friend. + +```cpp +client->SetRelationshipDeletedCallback([&client](uint64_t userId, bool isDiscordRelationshipUpdate) { + std::cout << "🔥 Relationship deleted: " << userId << std::endl; + DisplayFriendsList(*client); +}); +``` + +Now your friends list will automatically update when relationships change, such as when you add a new friend, accept a friend request, or block a user. + +--- + +## Next Steps + +Now that you have a unified friends list, you can build on your social features by allowing players to manage relationships, send game invites, and more. Check out our other guides for more information: + +import {PaintPaletteIcon} from '/snippets/icons/PaintPaletteIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Best practices for designing your Unified Friends List. + + }> + Allow players to manage their friends, friend requests, and blocked users. + + }> + Allow players to invite friends to join their game session or party. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|------------------------| +| March 17, 2025 | Initial release | +| July 17, 2025 | Add UFL helper methods | + +{/* Autogenerated Reference Links */} +[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d +[`Client::GetRelationshipsByGroup`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9f7898d3f3d1ec92b06c662df70746d5 +[`Client::SetRelationshipGroupsUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af12441ef091298f968075b7190851098 +[`Client::SetUserUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3559f375165acedc6d6677ef599b3a4a +[`RelationshipGroupType`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a503ed2f7b0bfbd435321a0e8b1dfba35 +[`UserHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a587bcc838e42dc5c56f840a350070707 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/linked-channels.mdx b/discord/developers/docs/discord-social-sdk/development-guides/linked-channels.mdx new file mode 100644 index 0000000000..1cac5dde73 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/linked-channels.mdx @@ -0,0 +1,391 @@ +--- +title: Linked Channels +description: Connect Discord channels with your application for seamless communication. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; +import LimitedAccessCallout from '/snippets/discord-social-sdk/callouts/limited-access.mdx'; +import CommsScopeWarning from '/snippets/discord-social-sdk/callouts/oauth-comms-scopes.mdx'; + + + +## Overview + +Linked Channels let players connect in-game lobbies with Discord text channels, enabling chat between your game and Discord servers. + +When linked: +- In-game messages appear in the Discord channel +- Discord messages appear in your game +- Messages from your game show your game's name and icon in Discord + +### Prerequisites + +Before implementing Linked Channels, make sure you have: + +- [Set up the Discord Social SDK](/developers/docs/discord-social-sdk/getting-started) +- An understanding of [Creating and Managing Lobbies](/developers/docs/discord-social-sdk/development-guides/managing-lobbies) + + + +--- + +## Linked Channel Requirements + +### Channel Requirements + +For a Discord channel to be linkable with your game lobby, it must meet these requirements: + +#### Channel Type and Settings + +| Requirement | Description | +|--------------|---------------------------------------------| +| Channel Type | Must be a Discord text channel in a server | +| NSFW Status | Cannot be marked as NSFW | +| Link Status | Cannot be currently linked to another lobby | + +#### Required Permissions + +The user linking the channel must have these Discord permissions: + +| Permission | Description | +|-----------------|----------------------------------| +| Manage Channels | Ability to edit channel settings | +| View Channel | Can see and read the channel | +| Send Messages | Can post messages in the channel | + + +### Private Channels + +Discord allows all channels the user can access in a server to be linked in game, even if that channel is private to other server members. This means a user could choose to link a private "admins chat" channel (assuming they are an admin) in the game if they wanted. + +It's not possible for the game to know which users should have access to that channel. Every lobby member can view and reply to all messages sent in the linked channel. + + +If you are going to allow private channels to be linked in-game, you must make sure that users are aware that their private channel will be viewable by everyone in the lobby. + + +To help you identify which channels are public or private, we have added a `isViewableAndWriteableByAllMembers` boolean. You can use that boolean to prevent private channels from being linked or to know when to show a clear warning; it's up to you! + +### User Requirements + +#### Linking A Lobby + +The lobby member must have the `CanLinkLobby` flag set to link a channel to a lobby. This flag is disabled by default and must be explicitly set using the [Lobby API](/developers/docs/resources/lobby) for users you want elevated permissions. We recommend only toggling this on for the equivalent of the administrator/owner of a lobby. + +This allows you, as the game developer, to say, "Only the admins of this guild are allowed to configure the linked channel." + +#### Unlinking a Lobby + +To unlink a channel from a lobby, lobby members only require the `CanLinkLobby` flag to be set for them. They **do not** need to have any permissions on the Discord side. + +### Lobby Requirements + +We recommend only using channel linking for **persistent** lobbies. Ephemeral lobbies such as match-chat are not good candidates for linking. + +Lobbies created on the client side with secrets are also not eligible for channel linking. + +--- + +## Creating a Linked Channel + +Channel linking happens entirely in the game. The user is never kicked out of the game to go to Discord to set it up. So, at a high level, you will need to fetch the list of servers (also known as guilds) and channels the user can access, show them some UI to pick a channel, and then save that selection back to Discord. + +### Fetch Available Servers (Guilds) and Channels + +We provide a few helper methods in the SDK to fetch the guilds and channels to populate the lists. + +[`Client::GetUserGuilds`] will fetch the user's Discord servers from our API. A list of `GuildMinimal` structs, including the guild ID and name, is returned on success. + +[`Client::GetGuildChannels`] takes a guildId and fetches all the channels in a single guild that the user can access. On success, returns a list of `GuildChannel` objects, which are shown below: + +```cpp +struct GuildChannel { + struct LinkedLobby { + uint64_t applicationId; + uint64_t lobbyId; + }; + uint64_t id; + std::string name; + bool isLinkable; + bool isViewableAndWriteableByAllMembers; + std::optional linkedLobby; +}; +``` + +- `isLinkable` indicates whether the user can link the given guild channel. This includes checking for channel validity (Is it a text channel? Is it marked safe for work? Is it already linked to another lobby?) and validating the user's channel permissions. (Check the **Edit Lobby/Channel Link** endpoint docs for a full list of requirements) +- `isViewableAndWriteableByAllMembers` indicates if the channel in Discord has restrictions on any members' or roles' ability to read or write to it (e.g. the channel may be considered private in some way). These read/write permissions are only enforced in the Discord client. A player who may not be able to view the channel in-Discord can read/write to it in-game as long as they are a lobby member. **It is important to notify the player performing the link that the channel contents may become exposed to players in-game who do not have access to the channel in Discord.** + +### Saving a channel link selection + +Once the user has chosen a channel to link to, you can call [`Client::LinkChannelToLobby`] to set or change the channel a lobby is linked to. You can also use [`Client::UnlinkChannelFromLobby`] to remove the link. The conditions specified in [Linked Channel Requirements](/developers/docs/discord-social-sdk/development-guides/linked-channels#channel-requirements) must be met for the given lobby, channel, and user. + +### Try It Out + +To test Linked Channels before building out a player interface, here are some steps you can follow to get things working in a prototype using the Discord API: + +1. Create a lobby using the **[Create Lobby](/developers/docs/resources/lobby#create-lobby)** endpoint. +2. Enable the `CanLinkLobby` flag (`1 << 0`) on your lobby member by either sending a request to the `/lobbies//members/` endpoint or by including the member data in the body of the [Create Lobby](/developers/docs/resources/lobby#create-lobby) request. +3. Identify a Discord server text channel that your `lobby_member` has the specified permissions enabled for (again, read/write and manage channels) and grab the channel's id. +4. Send a request to the `/lobbies//channel-linking` endpoint described above with the channel id. + +Success! Your lobby and channel should now be linked, and if you send a message in the lobby you should see it appear in the channel and vice versa. + +--- + +## Example Implementation + +### Step 1: Set Up Message Handling + +```cpp +// filepath: your_game/channel_linking.cpp +void HandleDiscordMessage(const std::string& message) { + std::cout << "📱 Game received: " << message << "\n"; + // Check if message is from the lobby + // Display in your game UI +} + +void SetupMessageHandlers(std::shared_ptr client) { + client->SetMessageCreatedCallback([](uint64_t messageId) { + discordpp::MessageHandle message = client->GetMessageHandle(messageId); + HandleDiscordMessage(message); + }); +} +``` + +### Step 2: Fetch Available Servers + +```cpp +void FetchDiscordServers(std::shared_ptr client) { + client->GetUserGuilds([](auto result, const auto& guilds) { + if (!result.Successful()) { + std::cout << "❌ Failed to fetch guilds\n"; + return; + } + + for (const auto& guild : guilds) { + std::cout << "📁 Guild: " << guild.name << "\n"; + // Store guild IDs for later use + SaveGuildId(guild.id); + } + }); +} + +void FetchChannelsForGuild( + std::shared_ptr client, + uint64_t guildId +) { + client->GetGuildChannels(guildId, [](auto result, const auto& channels) { + if (!result.Successful()) return; + + for (const auto& channel : channels) { + if (channel.isLinkable) { + std::cout << " 📝 Channel: " << channel.name; + if (!channel.isViewableAndWriteableByAllMembers) { + std::cout << " (Private)"; + } + std::cout << "\n"; + } + } + }); +} +``` + +### Step 3: Link Channel to Lobby + +```cpp +void ShowPrivateChannelWarning() { + std::cout << "⚠️ Warning: This is a private channel.\n"; + std::cout << "All lobby members will be able to see messages.\n"; + std::cout << "Do you want to continue? (y/n)\n"; + + char response; + std::cin >> response; + return (response == 'y' || response == 'Y'); +} + +void LinkChannelToLobby( + std::shared_ptr client, + uint64_t lobbyId, + uint64_t channelId, + bool isPrivate +) { + if (isPrivate && !ShowPrivateChannelWarning()) { + std::cout << "❌ Channel linking cancelled\n"; + return; + } + + client->LinkChannelToLobby( + lobbyId, + channelId, + [](auto result) { + if (result.Successful()) { + std::cout << "✅ Channel linked successfully!\n"; + } else { + std::cout << "❌ Failed to link channel\n"; + } + } + ); +} +``` + +### Step 4: Send and Receive Messages + +```cpp +void SendLobbyMessage( + std::shared_ptr client, + uint64_t lobbyId, + const std::string& message +) { + client->SendLobbyMessage(lobbyId, message, [](auto result) { + if (!result.Successful()) { + std::cout << "❌ Failed to send message\n"; + } + }); +} + +void UnlinkChannel( + std::shared_ptr client, + uint64_t lobbyId +) { + client->UnlinkChannelFromLobby(lobbyId, [](auto result) { + if (result.Successful()) { + std::cout << "✅ Channel unlinked\n"; + } + }); +} +``` + +### Step 5: Putting It All Together + +Here's how you might use these functions together: + +```cpp +// filepath: your_game/main.cpp +int main() { + auto client = std::make_shared(); + + // Set up handlers + SetupMessageHandlers(client); + + // When user wants to link a channel: + FetchDiscordServers(client); + + // After user selects a guild: + FetchChannelsForGuild(client, selectedGuildId); + + // After user selects a channel: + LinkChannelToLobby( + client, + currentLobbyId, + selectedChannelId, + isPrivateChannel + ); + + // When sending a message: + SendLobbyMessage(client, currentLobbyId, "Hello from the game!"); + + // When done with the channel: + UnlinkChannel(client, currentLobbyId); + + return 0; +} +``` + +## Joining Discord Servers via Linked Lobbies + +Once a lobby is linked to a Discord channel, players can join the associated Discord server directly from your game. This feature simplifies the process of getting players into your Discord community by generating invites on-demand, eliminating the need to manually share invite links. + +The [`Client::JoinLinkedLobbyGuild`] function generates a one-time-use invite for the current user and on supported +platforms, automatically navigates them to Discord to accept it. + +For example, an in-game player flow could look like: + +1. A Discord server admin links a channel to your game's lobby +2. Players in the lobby see an option to "Join Discord Server" in your game +3. When clicked, the SDK generates a unique invite and opens Discord +4. The player accepts the invite and becomes a Discord server member + + +Only players with linked Discord accounts can join the server. If a player is using a provisional account, you should prompt them to link their Discord account first. + + +```cpp +const uint64_t lobbyId = 1234567890; + +// Invite the user to join the Discord guild associated with the linked lobby +client->JoinLinkedLobbyGuild( + lobbyId, + // This is triggered when the user is using a provisional account, since + // the user needs a real Discord account to join the Discord server, so you don't need + // to implement a provisional user check on implementation. + [] { + // Show a message in your UI explaining that they need to link their Discord account + // and/or step them through the process + std::cout << "📝 User needs to link their Discord account\n"; + }, + // Called after the invite generation attempt completes, providing either a + // successful result with the invite URL or an error if the operation failed. + [](const discordpp::ClientResult &result, const std::string& inviteUrl) { + if(result.Successful()) { + std::cout << "✅ Discord invite generated successfully!\n"; + + // On console platforms, you'll need to display the invite URL + // for users to manually navigate to + #ifdef CONSOLE_PLATFORM + std::cout << "Join the Discord server at: " << inviteUrl << "\n"; + // Display this URL in your game's UI for the player to use + #endif + } else { + std::cerr << "❌ Failed to generate Discord invite\n"; + } + } +); +``` + +### Platform Considerations + +- **Desktop**: The SDK automatically opens the Discord client or web browser with the invite +- **Console**: Since console platforms cannot navigate to Discord directly, you should display the invite URL in your game's UI so players can use it on another device + + +Discord server admins cannot restrict who can join the server via this method. Any player in a linked lobby can generate an invitation to the server, regardless of their lobby permissions. +
Make sure to make your players aware to only link channels in servers you trust your players to join, and/or provide in-game options to disable this feature for certain lobbies. +
+ +--- + +## Next Steps + +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' + + + }> + Combine Discord and game friends into a single list for easy management. + + }> + Display game status and information to Discord friends. + + }> + Allow players to invite friends to join their game session or party. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|----------------------------------| +| June 30, 2025 | Add communications scope warning | +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Client::GetGuildChannels`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adba1e5a83c219a9c4f6dab1657778017 +[`Client::GetUserGuilds`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aac1ec02df6074ed9213ce230e6a42fe1 +[`Client::JoinLinkedLobbyGuild`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a54ec764e72e168de419ac14e24e8fc60 +[`Client::LinkChannelToLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3114d58d50d4d2cb5752d95e121315d4 +[`Client::UnlinkChannelFromLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28f78a6fe46eb11eb54ee9b53fa94ffe \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/managing-game-invites.mdx b/discord/developers/docs/discord-social-sdk/development-guides/managing-game-invites.mdx new file mode 100644 index 0000000000..0197a76a2d --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/managing-game-invites.mdx @@ -0,0 +1,504 @@ +--- +title: Managing Game Invites +description: Create and manage game invitations through Discord to bring friends into your application. +--- + +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + +## Overview + +Game Invites allow users to invite others to join their game session or party. This feature is available on the +Discord client and the Social SDK. + + +**Game Invites are not a standalone feature** - they are **powered entirely by Rich Presence**. When you configure Rich +Presence with party information, a join secret, and/or supported platforms, Discord automatically enables invite +functionality. This guide shows you how to configure Rich Presence to unlock game invites. + + +### Prerequisites + +Before you begin, make sure you have: + +- Completed the [Setting Rich Presence](/developers/docs/discord-social-sdk/development-guides/setting-rich-presence) guide +- Understanding that without an active Rich Presence with party data, invites will not work +- Set up the Discord Social SDK with our [Getting Started guide](/developers/docs/discord-social-sdk/getting-started) + +--- + +## Configuring Rich Presence to Enable Game Invites + +Game invites, or activity invites, are **powered by rich presence**. + +We covered the basics of [Setting Rich Presence](/developers/docs/discord-social-sdk/development-guides/setting-rich-presence) in a previous guide but let's go over the key points again. + + +Let's talk about the naming of some Discord primitives first. Rich Presence, aka "Activity", can be thought of as the "current activity of a user" and is represented by the [`Activity`] class in the SDK and [in our gateway events](/developers/docs/events/gateway-events#activity-object). This is not to be confused with [Discord Activities](/developers/docs/activities/overview), which are embedded games that can also set and display rich presence. + + +### Setting Up Rich Presence + +Below is an example of setting up rich presence in your game to be used with game invites. + +```cpp +// Create discordpp::Activity - This Rich Presence Activity is your invite configuration +discordpp::Activity activity; +activity.SetType(discordpp::ActivityTypes::Playing); + +// Set the game state and details +activity.SetState("In Competitive Match"); +activity.SetDetails("Valhalla"); + +// Update Rich Presence presence +client.UpdateRichPresence(activity, [](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🎮 Rich Presence updated successfully!\n"; + // Note: Invites are NOT yet enabled - we need party info and join secret + } else { + std::cerr << "❌ Rich Presence update failed"; + } +}); +``` + +If we run our game, the Discord client will show we are "In Competitive Match" on "Valhalla". + +You must set up your rich presence [`Activity`] with party information and a join secret to send game invites. Let's do that next. + +### Adding an Activity Party + +```cpp +// rest of the code + +// Create discordpp::ActivityParty +discordpp::ActivityParty party; +party.SetId("party1234"); +// current party size +party.SetCurrentSize(1); +// max party size +party.SetMaxSize(5); +// Set the party information in the Activity, to inform the invite about the party size and how many players can join +activity.SetParty(party); + +// Update Rich Presence +// Still not enough for invites - we need the join secret! +``` + +If we run our game, the Discord client will show that we are "In Competitive Match" on "Valhalla" with more information about the party. + +We're almost there! Let's add the join secret next so we can send and receive game invites. + +### Adding Join Secret & Supported Platforms + +The last step is to add a join secret to your rich presence activity. + +The `Join Secret` is a generic secret that you can use to [join a Discord lobby](/developers/docs/discord-social-sdk/development-guides/managing-lobbies), a game session, or something else. The game invite system is a way for players to share this secret value with other players - how you use it is up to you. + +You will also need to set the supported platforms for joining the game so that the Discord client can display the correct invite button for the user's platform. + +```cpp +// Create discordpp::Activity + +// Create ActivitySecrets +discordpp::ActivitySecrets secrets; +secrets.SetJoin("joinsecret1234"); // Rich Presence secret will be in the invite payload +activity.SetSecrets(secrets); + +// Set supported platforms that can join the game +// See discordpp::ActivityGamePlatforms for available platforms +activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Desktop); + +// Update Rich Presence +// ✅ NOW invites are enabled through Rich Presence! +``` + +### Putting It All Together + +```cpp +// Create discordpp::Activity - This Rich Presence Activity is your invite configuration +discordpp::Activity activity; +activity.SetType(discordpp::ActivityTypes::Playing); + +// Set the game state and details +activity.SetState("In Competitive Match"); +activity.SetDetails("Valhalla"); + +// Set the party information +discordpp::ActivityParty party; +party.SetId("party1234"); +party.SetCurrentSize(1); // current party size +party.SetMaxSize(5); // max party size +// Set the party information in the Activity, to inform the invite about the party size and how many players can join +activity.SetParty(party); + +// Create ActivitySecrets +discordpp::ActivitySecrets secrets; +secrets.SetJoin("joinsecret1234"); // Rich Presence secret will be in the invite payload +activity.SetSecrets(secrets); + +// Set supported platforms that can join the game +// See discordpp::ActivityGamePlatforms for available platforms +activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Desktop); + +// Update Rich Presence presence +client.UpdateRichPresence(activity, [](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🎮 Rich Presence updated successfully!\n"; + // ✅ Rich Presence updated = Game invites now available! + } else { + std::cerr << "❌ Rich Presence update failed"; + // ❌ No Rich Presence = No invites possible + } +}); +``` + +Now your game supports rich presence with game invites! The Discord client will show an invite button to your friends when they see your rich presence. + +--- + +## Registering a Launch Command + +Before we send a game invite, let's make sure that the Discord client knows about your game and how to launch it. + +When a user accepts a game invite for your game within Discord, the Discord client needs to know how to launch the game for that user. We have two ways to do this: + +- Register a launch command for your game +- Register a Steam Game ID + +For desktop games, you should run one of these commands when the SDK starts up so that if the user tries to join from Discord, the game can be launched for them. + +### Registering a Launch Command + +[`Client::RegisterLaunchCommand`] allows you to register a command that Discord will run to launch your game. + +```cpp +client->RegisterLaunchCommand(YOUR_APP_ID, "yourgame://"); +``` + +### Registering a Steam Game + +For Steam games, [`Client::RegisterLaunchCommand`] allows you to register what the Steam game ID. You should run this when the SDK starts up so that if the user tries to join from Discord the game will be able to be launched for them. + +```cpp +client->RegisterLaunchSteamApplication(YOUR_APP_ID, STEAM_GAME_ID); +``` + +--- + +## Sending Game Invites + +Game invites can be sent in two ways: + +1. Users can send game invites directly through the Discord client. +2. You can programmatically send game invites on a user's behalf through the SDK. + +### Sending Game Invites in the Discord Client + +Users can send game invites directly through the Discord client. This feature is described in detail in the [Game Invites help center article](https://support.discord.com/hc/en-us/articles/115001557452-Game-Invites). + +### Sending Game Invites in the SDK + +If a player has the required party, join secret, and supported platforms set in their rich presence, your game can send game invites programmatically through the SDK using [`Client::SendActivityInvite`]. + + +[`Client::SendActivityInvite`] only works if Rich Presence is active with proper configuration + + +```cpp +uint64_t targetUserId = 1111785262289277050; +std::string inviteMessage = "Join my game!"; +client->SendActivityInvite(targetUserId, inviteMessage, [](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "Activity Invite sent to user" << std::endl; + } else { + std::cerr << "Failed - check if Rich Presence has party, secret, and platforms set" << std::endl; + } +}); +``` + +![Example of a sent game invite in the Discord client](/images/social-sdk/development-guides/game-invite.webp) + +--- + +## Receiving Game Invites + +Game invites can also be received in two ways: + +1. Users can receive game invites directly through the Discord client. +2. Your game can receive game invites for a user programmatically through the SDK. + +### Receiving Game Invites in the Discord Client + +Users can receive game invites directly in their DMs. This feature is described in detail in the [Game Invites help center article](https://support.discord.com/hc/en-us/articles/115001557452-Game-Invites). + +### Receiving Game Invites in the SDK + +Use [`Client::SetActivityInviteCreatedCallback`] to detect new invites and [`Client::AcceptActivityInvite`] to accept them. The callback you specify for [`Client::AcceptActivityInvite`] will be invoked with the join secret you set in Rich Presence. + +```cpp +client->SetActivityInviteCreatedCallback([&client](discordpp::ActivityInvite invite) { + std::cout << "Activity Invite received from user: " << invite.SenderId() << std::endl; + if(auto message = client->GetMessageHandle(invite.MessageId())){ + std::cout << "Invite Message: " << message->Content() << std::endl; + } + client->AcceptActivityInvite(invite, [](discordpp::ClientResult result, std::string joinSecret) { + if(result.Successful()) { + std::cout << "Activity Invite accepted successfully!\n"; + // joinSecret comes from the sender's Rich Presence configuration + // Use the joinSecret to connect the two players in your game + } else { + std::cerr << "❌ Activity Invite accept failed"; + } + }); +}); +``` +--- + +## Accepting Game Invites + +Use [`Client::SetActivityJoinCallback`] to monitor for a user accepting a game invite, either in-game or in Discord. Use the join secret to connect the players in your game. + +```cpp +// This fires when a user clicks "Join" on someone's Rich Presence +client->SetActivityJoinCallback([&client](std::string joinSecret) { + // joinSecret is pulled from the host's Rich Presence ActivitySecrets + // Use the joinSecret to connect the players in your game +}); +``` + +--- + +## Using Game Invites with Lobbies + +Game invites can be used in conjunction with [Lobbies](/developers/docs/discord-social-sdk/development-guides/managing-lobbies) to create a seamless experience for players to join a game session or party. + +When a player accepts a game invite, you can use the join secret to connect the two players in your game. + +An example flow might look like this: + +- When a user starts playing the game, they create a lobby with a random secret string, using [`Client::CreateOrJoinLobby`] +- That user publishes their Rich Presence with the join secret set to the lobby secret, along with party size information +- Another user can then see that Rich Presence on Discord and request to join +- Once accepted, the new user receives the join secret, and their client can call CreateOrJoinLobby(joinSecret) to join the lobby +- Finally, the original user can notice that the lobby membership has changed, so they publish a new Rich Presence update containing the updated party size information + + +These examples use client-side lobby management but can also be adapted for lobbies created on the server side. + + +### Game Invite with Lobby Example + +Here's a code example of how you might implement this flow: + +```cpp +// User A +// 1. Create a lobby with secret +std::string lobbySecret = "foo" +uint64_t USER_B_ID = 01234567890; +client->CreateOrJoinLobby(lobbySecret, [&client](discordpp::ClientResult result, uint64_t lobbyId) { + // 2. Update Rich Presence with a party and join secret to enable invites + discordpp::Activity activity{}; + activity.SetType(discordpp::ActivityTypes::Playing); + activity.SetState("In Lobby"); + + // Rich Presence party configuration for how many players can join + discordpp::ActivityParty party{}; + party.SetId("party1234"); + party.SetCurrentSize(1); + party.SetMaxSize(4); + activity.SetParty(party); + + // Rich Presence secret is what is shared via invites + discordpp::ActivitySecrets secrets{}; + secrets.SetJoin(lobbySecret); // This connects Rich Presence to your lobby + activity.SetSecrets(secrets); + + // Don't forget to set this Rich Presence update, otherwise SendActivityInvite won't work! + client->UpdateRichPresence(std::move(activity), [&client](discordpp::ClientResult result) { + // 3. NOW we can send invites because Rich Presence is configured + client->SendActivityInvite(USER_B_ID, "come play with me", [](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "💌 Invite sent successfully!\n"; + } else { + std::cerr << "❌ Invite failed - check Rich Presence configuration\n"; + } + }); + }); +}); + +// User B +// 4. Monitor for new invites +client->SetActivityInviteCreatedCallback([&client](discordpp::ActivityInvite invite) { + std::cout << "💌 New invite received: " << invite.SenderId() << "\n"; + // 5. When an invite is received, ask the user if they want to accept it. + + // If they choose to do so then go ahead and invoke AcceptActivityInvite + client->AcceptActivityInvite(invite, [&client](discordpp::ClientResult result, std::string joinSecret) { + if (result.Successful()) { + std::cout << "🎮 Invite accepted! Joining lobby...\n"; + // joinSecret came from User A's Rich Presence configuration + + // 6. Join the lobby using the joinSecret + client->CreateOrJoinLobby(joinSecret, [=](discordpp::ClientResult result, uint64_t lobbyId) { + // Successfully joined lobby! + if (result.Successful()) { + std::cout << "🎮 Lobby joined successfully! " << lobbyId << std::endl; + } else { + std::cerr << "❌ Lobby join failed\n"; + } + }); + } + }); +}); +``` + +### Lobby Join Request Example + +Users can also request to join each other's parties. This code example shows how that flow might look: + +```cpp +// User A +// 1. Create a lobby with secret +std::string lobbySecret = "foo"; +uint64_t USER_A_ID = 286438705638408203; +client->CreateOrJoinLobby(lobbySecret, [&client](discordpp::ClientResult result, uint64_t lobbyId) { + // 2. Update Rich Presence with a party and join secret to enable invites + discordpp::Activity activity{}; + activity.SetType(discordpp::ActivityTypes::Playing); + activity.SetState("In Lobby"); + + // Rich Presence party configuration for how many players can join + discordpp::ActivityParty party{}; + party.SetId("party1234"); + party.SetCurrentSize(1); + party.SetMaxSize(4); + activity.SetParty(party); + + // Rich Presence secret is what is shared via invites + discordpp::ActivitySecrets secrets{}; + secrets.SetJoin(lobbySecret); + activity.SetSecrets(secrets); + + // This Rich Presence update is what enables the "Ask to Join" button in Discord + client->UpdateRichPresence(std::move(activity), [&client](discordpp::ClientResult result) {}); +}); + +// User B +// 3. Request to join User A's party +client->SendActivityJoinRequest(USER_A_ID, [](discordpp::ClientResult result) {}); + +// User A +// 4. Monitor for new invites: +client->SetActivityInviteCreatedCallback([&client](discordpp::ActivityInvite invite) { + // 5. The game can now show that User A has received a request to join their party + // If User A is ok with that, they can reply back: + // Note: invite.type will be ActivityActionTypes::JoinRequest in this example + client->SendActivityJoinRequestReply(invite, [](discordpp::ClientResult result) {}); +}); + +// User B +// 6. Same as before, user B can monitor for invites +client->SetActivityInviteCreatedCallback([&client](discordpp::ActivityInvite invite) { + std::cout << "💌 New invite received: " << invite.SenderId() << "\n"; + // 7. When an invite is received, ask the user if they want to accept it. + // If they choose to do so then go ahead and invoke AcceptActivityInvite + client->AcceptActivityInvite(invite, [&client](discordpp::ClientResult result, std::string joinSecret) { + if (result.Successful()) { + std::cout << "🎮 Invite accepted! Joining lobby...\n"; + // joinSecret came from User A's Rich Presence + // 5. Join the lobby using the joinSecret + client->CreateOrJoinLobby(joinSecret, [=](discordpp::ClientResult result, uint64_t lobbyId) { + // Successfully joined lobby! + if (result.Successful()) { + std::cout << "🎮 Lobby joined successfully! " << lobbyId << std::endl; + } else { + std::cerr << "❌ Lobby join failed\n"; + } + }); + } + }); +}); +``` + +--- + +## Supporting Mobile Game Invites + +When a player receives a game invite on mobile, Discord must know how to launch your game. Game launching is handled through deep linking, which allows Discord to pass the join information to your game. + +### Setting Up Mobile Deep Links + +1. Configure your deep link URL in the Discord Developer Portal: + - Go to your application's `General` tab + - Enter your game's URL scheme (e.g., `yourgame://`) + - Discord will append `/_discord/join?secret=SECRETHERE` to your URL + +2. Tell Discord which platforms can accept invites: +```cpp +activity.SetSupportedPlatforms( + ActivityGamePlatforms.Desktop | // Enable PC/Mac invites + ActivityGamePlatforms.IOS | // Enable iOS invites + ActivityGamePlatforms.Android // Enable Android invites +); +``` + +### How Mobile Deep Links Work + +1. The user receives and accepts an invite in Discord +2. Discord launches your game using your URL scheme: + ``` + yourgame://_discord/join?secret=the_join_secret_you_set + ``` +3. Your game receives the URL and extracts the join secret +4. Use the secret to connect the player to the session + +--- + +## Setting a Cover Image for the Invite (Optional) + +You can set a cover image for an invite via rich presence activity. This image displays as the banner on the invite and helps draw attention to it. Setting a cover image via rich presence allows you to dynamically customize invites per rich presence activity; for example, showing different images based on game mode, map, etc. rather than using the same static image for all invites. If not set, the banner falls back to the "Rich Presence Invite Image" configured in the Developer Portal under the "Rich Presence" tab. Setting an invite cover image is entirely optional. + +```cpp +discordpp::ActivityAssets assets; +assets.SetInviteCoverImage("invite-cover-image"); // This example uses an asset key, but you can use an external URL asset for more dynamic cover images. +activity.SetAssets(assets); +``` + +--- + +## Next Steps + +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {MagicDoorIcon} from '/snippets/icons/MagicDoorIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Combine Discord and game friends into a single list for easy management. + + }> + Bring players together in a shared lobby with invites, text chat, and voice comms. + + }> + Enable private messaging between players. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682 +[`Client::AcceptActivityInvite`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad12cf35065e4d2b303ee470af7c6ef37 +[`Client::CreateOrJoinLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512 +[`Client::RegisterLaunchCommand`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a024d7222931fdcb7d09c2b107642ecab +[`Client::SendActivityInvite`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afc14e98fc070399895739da6d53efa60 +[`Client::SetActivityInviteCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3b4e37a222a8662506d763514774bedc +[`Client::SetActivityJoinCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a587d1c6d0352eba397c888987aa58418 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/managing-lobbies.mdx b/discord/developers/docs/discord-social-sdk/development-guides/managing-lobbies.mdx new file mode 100644 index 0000000000..1c1c24a42e --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/managing-lobbies.mdx @@ -0,0 +1,261 @@ +--- +title: Creating and Managing Lobbies +sidebarTitle: Managing Lobbies +description: Create and manage game lobbies with Discord Social SDK for matchmaking. +--- + +import RateLimitCallout from '/snippets/discord-social-sdk/callouts/rate-limit.mdx'; +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; +import LimitedAccessCallout from '/snippets/discord-social-sdk/callouts/limited-access.mdx'; +import CommsScopeWarning from '/snippets/discord-social-sdk/callouts/oauth-comms-scopes.mdx'; + + +# Creating and Managing Lobbies + + + +## Overview + +Lobbies are groups of users that can communicate via text and voice. + +This guide will show you how to: + +- Create and manage lobbies +- Handle lobby membership +- Send messages to lobbies + +### Prerequisites + +Before you begin, make sure you have: + +- Set up the Discord Social SDK with our [Getting Started guide](/developers/docs/discord-social-sdk/getting-started) + + + +--- + +## Lobby Features + +Lobbies are groups of users that can communicate with each other via text and voice. Users can be in multiple lobbies at once. A lobby can also have metadata (an arbitrary JSON blob) associated with the lobby and each user. + +### Lobby Members + +Lobbies may have a maximum of 1,000 members, and each user may have a maximum of 100 lobbies per game. If your game needs more than 1,000 members in a lobby, please get in touch with us to discuss your use case. + +### Text Chat + +Lobbies have a text chat channel that all members can use to communicate. Messages are sent to all members of the lobby. + +### Voice Chat + +Lobbies support voice calls. Although a lobby is allowed to have 1,000 members, you should not start voice calls in lobbies that large. + +We recommend sticking to around 25 members or fewer for voice calls, but please do contact Developer Support if you need more than 25 member calls so we can discuss your use case. + +--- + +## Managing Lobbies + +There are two ways to manage lobbies: + +1. From your server using the Discord API. +2. From the client using the [`Client::CreateOrJoinLobby`] function. + +### Server-Side Lobby Management + +You can use the Discord HTTP API to create, update, and delete lobbies and manage lobby membership. + +See the [Lobby](/developers/docs/resources/lobby) API resource for available endpoints and [Use with Discord APIs](/developers/docs/discord-social-sdk/how-to/use-with-discord-apis) for information on how to authenticate your requests. + + +Clients will not be able to use [`Client::CreateOrJoinLobby`] or [`Client::LeaveLobby`] with lobbies created using the API. + + +### Client-Side Lobby Management + +The SDK client can also create and join lobbies. This works by associating a secret value with the lobby. You can distribute this secret as necessary to folks, and they can then join the lobby using that secret. If the lobby does not exist, it will be created on demand. + +- The relevant SDK functions are [`Client::CreateOrJoinLobby`] and [`Client::LeaveLobby`]. +- Lobby secrets are unique per game (ie: application). For example, use a new secret if you want to generate a new lobby at the end of the match. Calling [`Client::LeaveLobby`] and then [`Client::CreateOrJoinLobby`] with the same secret value will just re-add you to the same lobby. +- Calling [`Client::CreateOrJoinLobby`] while the user is already in the lobby will update their metadata (if included) instead. +- Discord's Rich Presence system supports syncing this secret, too. Using this flow, clients can request to join another user's activity. When approved, the SDK will be given the secret, which you can access and join the associated lobby if you choose to do so. See the [Game Invites for Lobbies](/developers/docs/discord-social-sdk/development-guides/managing-lobbies#creating-lobby-invites) example below. +- If you want to keep track of metadata for the lobby or the lobby members, you can use the [`Client::CreateOrJoinLobbyWithMetadata`] function. This function takes a JSON object as an argument, which will be stored with the lobby and the lobby members. This metadata can be retrieved using the discordpp::Client::GetLobbyHandle function. + +```cpp +// Create or join a lobby from the client +client->CreateOrJoinLobby("your-unique-lobby-secret",[client](discordpp::ClientResult result, uint64_t lobbyId) { + if(result.Successful()) { + std::cout << "🎮 Lobby created or joined successfully! Lobby Id: " << lobbyId << std::endl; + } else { + std::cerr << "❌ Lobby creation/join failed\n"; + } +}); +``` + +#### Leaving a Lobby + +To remove a user from a lobby, use the [`Client::LeaveLobby`] function. Only lobbies created with [`Client::CreateOrJoinLobby`] can be left using [`Client::LeaveLobby`]. + +```cpp +uint64_t lobbyId = 01234567890; + +// Leaving a lobby from the client +client->LeaveLobby(lobbyId, [&](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🎮 Left lobby successfully! Lobby Id: " << lobbyId << std::endl; + } else { + std::cerr << "❌ Leaving lobby failed\n"; + } +} +``` + +### Lobby Lifecycle + +Lobbies are intended to be ephemeral and should be cleaned up when the game/match/area/world is no longer needed. To support this, you can set a "max idle time". This means that if a lobby sits idle, with no one connected to it at all, for more than that time, we will automatically delete the lobby. But as long as one person is connected, the lobby won't be deleted (and the timer resets too). + +This value defaults to 5 minutes, and we expect that for most use cases, that will be ok. But we understand there are use cases such as "world chat" that might want to exist longer. The maximum value for this "idle time" will likely be 7 days, so that would mean that the lobby only gets deleted if no one connects to it for an entire week. This should give a good amount of permanence to lobbies when needed, but there may be rare cases where a lobby does need to be "rebuilt" if everyone is offline for an extended period. + + +Lobbies created by the SDK client using the [`Client::CreateOrJoinLobby`] function currently have an additional limitation. The "secret" value used in this call expires after 30 days, so the lobby will still exist, but new users won't be able to join the lobby after that. + + +--- + +## Sending Messages to a Lobby + +Once you have a lobby created, lobby members can send messages to it using the [`Client::SendLobbyMessage`] function. + +Clients can send messages to lobbies they are members of regardless of whether they joined the lobby using the client or server-side method. + +```cpp +uint64_t lobbyId = 01234567890; + +client->SendLobbyMessage(lobbyId, "Hello", [](discordpp::ClientResult result, uint64_t messageId) { + if(result.Successful()) { + std::cout << "📨 Message sent successfully!\n"; + } else { + std::cerr << "❌ Message sending failed\n"; + } +}); +``` + +--- + +## Receiving Lobby Messages + +You can receive messages sent to a lobby using the [`Client::SetMessageCreatedCallback`] function. + +This callback will fire whenever a new message is received in a lobby or a DM. From the `messageId`, you can fetch the [`MessageHandle`] and then the [`ChannelHandle`] to determine the location to which the message was sent. + +```cpp +client->SetMessageCreatedCallback([&client](uint64_t messageId) { + discordpp::MessageHandle message = client->GetMessageHandle(messageId); + std::cout << "📨 New message received: " << message->Content() << "\n"; +}); +``` + +--- +## Getting Lobby Chat History + +You can retrieve previous messages from a lobby using the [`Client::GetLobbyMessagesWithLimit`] function. This allows you to fetch chat history for display when users join a lobby or need to see previous conversations. + +The function takes a lobby ID and a limit parameter to specify how many recent messages to retrieve. + +**Important limitations:** +- Only a maximum of 200 messages and up to 72 hours of history can be retrieved +- Only messages from lobbies the user is currently a member of can be retrieved + +```cpp +const uint64_t lobbyId = 01234567890; +const uint32_t messageLimit = 50; // Number of recent messages to retrieve (max 200) + +client->GetLobbyMessagesWithLimit( + lobbyId, messageLimit, + [](const discordpp::ClientResult &result, const std::vector &messages) { + if (result.Successful()) { + std::cout << "? Retrieved " << messages.size() + << " messages from lobby chat history!\n"; + + // Process the messages (they are returned in chronological order) + for (const auto &message : messages) { + std::cout << "Message: " << message.Content() << std::endl; + } + } else { + std::cerr << "? Failed to retrieve lobby chat history\n"; + } + }); +``` + +The messages are returned as a list of [`MessageHandle`] objects, ordered chronologically from oldest to newest. +Each [`MessageHandle`] contains the message content, author information, and timestamp. + +This is particularly useful for: +- Displaying recent chat when a user joins a lobby +- Implementing chat history scrollback features +- Preserving conversation context across game sessions + +--- + +## Linking a Channel to Lobby + +You can connect a lobby to a Discord text channel with Linked Channels. Linked Channels allows users to chat with each other in the lobby using Discord, even if they are not in the game. + +See our guide on [Linked Channels](/developers/docs/discord-social-sdk/development-guides/linked-channels) for more information on how to link a channel to a lobby. + +--- + +## Managing Voice Chat + +See our guide on [Managing Voice Chat](/developers/docs/discord-social-sdk/development-guides/managing-voice-chat) for more information on how to start a voice call in a lobby. + +--- + +## Creating Lobby Invites + +Your game can use lobbies and game invites to allow users to invite friends to join an existing lobby. + +Check out the [Using Game Invites with Lobbies](/developers/docs/discord-social-sdk/development-guides/managing-game-invites#using-game-invites-with-lobbies) example to try it out in your game. + +--- + +## Next Steps + +With your game able to create and manage lobbies, you can now implement additional features: + +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' +import {VoiceNormalIcon} from '/snippets/icons/VoiceNormalIcon.jsx' +import {HashmarkIcon} from '/snippets/icons/HashmarkIcon.jsx' + + + }> + Allow players to invite friends to join their game session or party. + + }> + Add voice communication to your lobbies. + + }> + Connect lobbies to Discord text channels. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|----------------------------------| +| June 30, 2025 | Add communications scope warning | +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`ChannelHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#ac32096b2ef15c5c220e9b7b92253cc46 +[`Client::CreateOrJoinLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512 +[`Client::CreateOrJoinLobbyWithMetadata`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5c84fa76c73cf3c0bfd68794ca5595c1 +[`Client::GetLobbyMessagesWithLimit`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a0586192e85caf548b8b321f1cb21301f +[`Client::LeaveLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8c78f797240b35d721383461a2e62926 +[`Client::SendLobbyMessage`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a779e0483f51dc99f0db3dd761d22ab6f +[`Client::SetMessageCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28325a8e8c688a84ac851da4bc86e148 +[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/managing-relationships.mdx b/discord/developers/docs/discord-social-sdk/development-guides/managing-relationships.mdx new file mode 100644 index 0000000000..37580ede65 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/managing-relationships.mdx @@ -0,0 +1,262 @@ +--- +title: Managing Relationships in Your Game +sidebarTitle: Managing Relationships +description: Manage Discord user relationships including friends and blocked users in your application. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + +## Overview + +The Discord Social SDK lets you manage relationships between players in your game. This guide will show you how to: + +- Send and accept friend requests +- Handle different types of relationships +- Block and unblock users +- Work with both Discord-wide and game-specific friendships + +### Prerequisites + +Before you begin, make sure you have: + +- Completed the [Getting Started](/developers/docs/discord-social-sdk/getting-started) guide +- Completed the [Creating a Unified Friends List](/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list) guide + +--- + +## Understanding Relationship Types + +Discord models the relationship between two users using the Relationship entity in the SDK. Relationships are not just for friends. They are also used to send and receive friend requests and block other users. + +While the SDK allows you to manage a user's relationships, you should never act without their explicit consent. You should not automatically send or accept friend requests. Only invoke APIs to manage relationships in response to a user action such as clicking a "Send Friend Request" button. + +### Relationship Types + +We know that sometimes users will want to be friends with each other across all their games. If they start playing a new game, they can see all of their previous friends and don't start from scratch. But sometimes, they don't want to give out that access and only want to be friends in the current game they are playing. + +To support this, the Discord Social SDK supports two types of relationships between users: + +- **Discord relationships**: These relationships persist across games and on the Discord client. Both users can see whether each other is online, regardless of whether they are in the same game. Discord Relationships are the same as becoming a friend in the Discord client. +- **Game relationships**: These are per-game relationships and do not carry over to other games. The two users can only see if the other is online if they are playing a game in which they are friends. Game friends can DM each other, and those DMs will show up in Discord, but they can disable that behavior and keep their game conversations restricted to just the game. That option is located at the bottom of the "Content & Social" settings. + +[`RelationshipHandle`] can be used to determine the type of friendship between the player and another user. It has two fields: + +- [`RelationshipHandle::DiscordRelationshipType`] for the **Discord friendship** +- [`RelationshipHandle::GameRelationshipType`] for the **game friendship** + +Having both of these friend types is important because a pair of users might start out as game friends but later choose to "upgrade" to being full Discord friends. In this case, their [`RelationshipHandle::DiscordRelationshipType`] would be set to `RelationshipType::PendingIncoming` or `RelationshipType::PendingOutgoing` (based on whether they are receiving or sending the request respectively), and their [`RelationshipHandle::GameRelationshipType`] would remain as `RelationshipType::Friend`. + +While our API technically supports users being both types of friends, you don't have to ensure that every Discord friend is a game friend or vice versa. When adding friends, offer users a choice of friend type and explain the difference. See our design guidelines for more. + +### Discord Friend Relationships +- Persist across all games and Discord +- Limited to 1,000 friends +- Online status visible everywhere +- Full Discord chat functionality + +### Game Friend Relationships +- Only exist within your game +- No current friend limit +- Online status is only visible in-game + +--- + +## Relationship Actions + +Once you've [created a unified friends list](/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list), you can start managing relationships between players in your game. + +Here are some common actions you might want to take: + +### Sending Game Friend Requests + +Sends (or accepts) a game friend request to the target user. + +You can send game friend requests to users using their Discord unique username or user ID. + +After the friend request is sent, each user will have a new game relationship created. For the current user, the [`RelationshipHandle::GameRelationshipType`] will be `RelationshipType::PendingOutgoing`, and for the target user, it will be `RelationshipType::PendingIncoming`. + +If the current user has already received a game friend request from the target user (meaning [`RelationshipHandle::GameRelationshipType`] is `RelationshipType::PendingIncoming`), the two users will become game friends. + +```cpp +client->SendGameFriendRequest("username", [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Game friend request sent successfully!\n"; + } +}); + +client->SendGameFriendRequestById(123456789, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Game friend request sent successfully!\n"; + } +}); +``` + +### Sending Discord Friend Requests + +Sends (or accepts) a Discord friend request to the target user. + +You can send Discord friend requests to users by using their Discord unique username or user ID. + +After the friend request is sent, each user will have a new Discord relationship created. For the current user, the [`RelationshipHandle::DiscordRelationshipType`] will be `RelationshipType::PendingOutgoing`, and for the target user, it will be `RelationshipType::PendingIncoming`. + +If the current user has already received a Discord friend request from the target user (meaning [`RelationshipHandle::DiscordRelationshipType`] is `RelationshipType::PendingIncoming`), the two users will become Discord friends. + +```cpp +client->SendDiscordFriendRequest("username", [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Discord friend request sent successfully!\n"; + } +}); + +client->SendDiscordFriendRequestById(123456789, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Discord friend request sent successfully!\n"; + } +}); +``` + +### Accept Incoming Friend Requests + +Allow your players to accept incoming friend requests, which `RelationshipType::PendingIncoming` represents. + +```cpp +// RelationshipHandle::DiscordRelationshipType == RelationshipType::PendingIncoming +client->AcceptDiscordFriendRequest(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Discord friend request accepted!\n"; + } +}); + +// RelationshipHandle::GameRelationshipType == RelationshipType::PendingIncoming +client->AcceptGameFriendRequest(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Game friend request accepted!\n"; + } +}); +``` + +### Reject Incoming Friend Requests + +Allow your players to reject incoming friend requests, which `RelationshipType::PendingIncoming` represents. + +```cpp +// Reject Incoming Friend Requests +// RelationshipHandle::DiscordRelationshipType == RelationshipType::PendingIncoming +client->RejectDiscordFriendRequest(userId,[](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Discord friend request rejected!\n"; + } +}); + +// RelationshipHandle::GameRelationshipType == RelationshipType::PendingIncoming +client->RejectGameFriendRequest(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Game friend request rejected!\n"; + } +}); +``` + +### Cancel Outgoing Friend Requests + +Allow your players to cancel outgoing friend requests, which `RelationshipType::PendingOutgoing` represents. + +```cpp +// Cancel Outgoing Friend Requests +// RelationshipHandle::DiscordRelationshipType == RelationshipType::PendingIncoming +client->CancelDiscordFriendRequest(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Discord friend request canceled!\n"; + } +}); + +// RelationshipHandle::GameRelationshipType == RelationshipType::PendingOutgoing +client->CancelGameFriendRequest(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Game friend request canceled!\n"; + } +}); +``` + +### Managing Existing Relationships + +Allow your players to remove existing relationships with other users. This will remove the relationship from both users, and they will no longer be able to see each other's online status or send messages. + +```cpp +// Removes any friendship between the current user and the target user. +// This function will remove BOTH any Discord friendship and any game friendship between the users. +client->RemoveDiscordAndGameFriend(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Discord and Game friendships removed!\n"; + } +}); + +// Removes any game friendship between the current user and the target user. +client->RemoveGameFriend(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 Game friendship removed!\n"; + } +}); +``` + +### Blocking Users + +Allow your players to block another user so they cannot send friend or activity invites and cannot message them anymore. + +Blocking a user will also remove any existing relationship between the two users and persist across games, so blocking a user in one game or on Discord will block them in all other games and on Discord. + +```cpp +client->BlockUser(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 User blocked successfully!\n"; + } +}); +``` + +### Unblocking Users + +Allow your players to unblock another user if they have been blocked. Unblocking a user does not restore any previous relationships between the users. + +```cpp +client->UnblockUser(userId, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "🎮 User unblocked successfully!\n"; + } +}); +``` + +--- + +## Next Steps + +Continue learning about the Discord Social SDK with these guides: + +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Combine Discord and game friends into a single list for easy management. + + }> + Allow players to invite friends to join their game session or party. + + }> + Bring players together in a shared lobby with invites, text chat, and voice comms. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | Initial release | + +{/* Autogenerated Reference Links */} +[`RelationshipHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a7da36b15ad0b7d38ba658a622e9ded77 +[`RelationshipHandle::DiscordRelationshipType`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a5fecfb79a4a2b6f3dc5f73b09d0c3881 +[`RelationshipHandle::GameRelationshipType`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#aa60146eb72ede07e3e615565f61f97eb \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/managing-voice-chat.mdx b/discord/developers/docs/discord-social-sdk/development-guides/managing-voice-chat.mdx new file mode 100644 index 0000000000..e7250ff191 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/managing-voice-chat.mdx @@ -0,0 +1,324 @@ +--- +title: Managing Voice Chat +description: Integrate Discord voice chat features into your application. +--- +import RateLimitCallout from '/snippets/discord-social-sdk/callouts/rate-limit.mdx'; +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; +import CommsScopeWarning from '/snippets/discord-social-sdk/callouts/oauth-comms-scopes.mdx'; + + + +## Overview + +Voice calls are a core feature of the Discord Social SDK that enable real-time voice communication between players in your game within lobbies. + +This guide will show you how to: + +- Start and join voice calls in lobbies +- Control voice settings like mute, deafen, and volume +- Process audio data with custom callbacks +- Integrate with external audio systems +- Check voice call status and participant states + +## Prerequisites + +Before you can start a voice call, you must complete these essential steps: + + + +### 1. Lobby Management + +**Voice calls require an active lobby with participants.** You must: + +1. **Create or join a lobby** using the Discord Social SDK +2. **Add players to the lobby** - voice calls only work with lobby members + + +Essential Reading: For detailed instructions on creating lobbies, joining lobbies, and managing lobby members, see +the complete [Managing Lobbies Guide](/developers/docs/discord-social-sdk/development-guides/managing-lobbies). + + +### 2. Lobby Size Limitations + +While Discord lobbies technically support up to 1,000 members, **voice calls should be limited to much smaller groups**. We **strongly recommend keeping voice calls to 25 members or fewer** for optimal performance and user experience. + +--- + +## Starting and Joining Voice Calls + +The Discord Social SDK makes it simple to start and join voice calls - both operations use the same functions whether you're creating something new or joining something that already exists. + +### Creating/Joining a Lobby and Starting/Joining a Call + +Here's a complete example of joining a lobby and starting a voice call: + +```cpp +// First, create or join a lobby using a shared secret +const std::string lobbySecret = "my-game-lobby-secret"; + +client->CreateOrJoinLobby(lobbySecret, [client](const discordpp::ClientResult& result, uint64_t lobbyId) { + if (result.Successful()) { + std::cout << "🎮 Successfully joined lobby!" << std::endl; + + // Now start or join the voice call in this lobby + // StartCall returns a Call object but has no callback + const auto call = client->StartCall(lobbyId); + + // StartCall returns null if user is already in this voice channel + if (call) { + std::cout << "🎤 Voice call operation initiated..." << std::endl; + } else { + std::cout << "ℹ️ Already in this voice channel" << std::endl; + } + + } else { + std::cerr << "❌ Failed to join lobby: " << result.Error() << std::endl; + } +}); +``` + +### How It Works + +Both [`Client::CreateOrJoinLobby`] and [`Client::StartCall`] are designed to handle existing and new scenarios automatically: + +- [`Client::CreateOrJoinLobby`]: If a lobby with the given secret already exists, you'll join it. If not, a new +lobby is created with that secret. +- [`Client::StartCall`]: If a voice call is already active in the lobby, you'll join it. If not, a new voice call is +started. + + +You don't need to check if a lobby exists or if a call is already ongoing. The SDK handles both scenarios seamlessly, making your code simpler and more reliable. + + +## Controlling Voice Features + +The Discord Social SDK provides comprehensive voice control options at both the individual call level and globally across all calls. + +### Global Voice Controls + +These methods control voice settings across all active calls using the [`Client`] object: + +- [`Client::SetSelfMuteAll`] - Mutes your microphone across all active calls +- [`Client::SetSelfDeafAll`] - Deafens you across all active calls +- [`Client::SetInputVolume`] - Sets microphone volume +- [`Client::SetOutputVolume`] - Sets speaker volume + +### Per-Call Voice Controls + +These methods control voice settings for a specific call using the [`Call`] object: + +- [`Call::SetSelfMute`] - Mutes your microphone so other participants in this call cannot hear you +- [`Call::SetSelfDeaf`] - Mutes all audio from this call so you cannot hear other participants, and they cannot hear you either +- [`Call::SetParticipantVolume`] - Adjusts the volume of a specific participant + +### Voice Activity Detection + +Use [`Call::SetVADThreshold`] to control voice activation detection sensitivity. This allows optional fine-tuning of +when the system considers someone to be speaking. + +```cpp +// Per-call controls (assuming you have a Call object) +uint64_t lobbyId = 123456789; // Your lobby ID +auto call = client->GetCall(lobbyId); +if (call) { + call.SetSelfMute(true); // Mute in this call only + call.SetSelfDeaf(false); // Unmute audio in this call + call.SetParticipantVolume(userId, 150.0f); // Increase participant volume + call.SetVADThreshold(false, -30.0f); // Set custom voice detection threshold +} + +// Global controls +client->SetSelfMuteAll(true); // Mute across all calls +client->SetInputVolume(75.0f); // Set microphone to 75% +client->SetOutputVolume(120.0f); // Increase speaker volume to 120% +``` + +## Advanced Audio Processing + +### Manipulating Voice Data with Callbacks + +For advanced audio processing needs, use [`Client::StartCallWithAudioCallbacks`] to access raw audio data. This +enables real-time audio manipulation and integration with external audio processing systems. + +### In-Place Audio Modification + +To directly modify incoming audio samples (e.g., volume dampening): + +```cpp +const auto call = client->StartCallWithAudioCallbacks( + lobbyId, + [](uint64_t userId, int16_t *data, const size_t samplesPerChannel, + int sampleRate, const size_t channels, + bool &outShouldMuteData) { + // Dampen volume of incoming audio by modifying data's samples + // in-place + for (int i = 0; i < samplesPerChannel * channels; i++) { + data[i] *= 0.5; // Reduce volume by 50% + } + }, + [](int16_t *data, uint64_t samplesPerChannel, int32_t sampleRate, + uint64_t channels) {}); + +``` + +### External Audio Pipeline Integration + +To route audio to external processing systems such as [FMOD](https://www.fmod.com/) or +[Wwise](https://www.audiokinetic.com/en/wwise/overview/): + +```cpp +const auto call = client->StartCallWithAudioCallbacks(lobbyId, + [](uint64_t userId, int16_t* data, size_t samplesPerChannel, + int sampleRate, size_t channels, bool& outShouldMuteData) { + // Prevent Discord from playing the audio directly + outShouldMuteData = true; + + const int totalNumSamples = samplesPerChannel * channels; + // Send audio data to your external audio system + SendAudioToExternalAudioSystem(data, totalNumSamples); + }, + [](int16_t *data, uint64_t samplesPerChannel, int32_t sampleRate, + uint64_t channels) {}); +``` + +### Key Audio Processing Points + +1. **Direct Manipulation**: The `data` parameter in `Client::UserAudioReceivedCallback` can be modified in-place to alter incoming audio samples +2. **External Processing**: Set `outShouldMuteData = true` to prevent Discord from playing audio directly, allowing you to handle it through your own audio pipeline +3. **No Encoding Required**: The SDK handles all voice encoding/decoding automatically - you work with raw audio samples + +## Ending Voice Calls + +When you need to terminate voice calls, you have two options: + +### End a Call For a Specific Lobby + +```cpp +uint64_t lobbyId = 123456789; // Your lobby ID +client->EndCall(lobbyId, []() { + std::cout << "🔇 Call ended successfully" << std::endl; +}); +``` + +### End All Calls + +```cpp +client->EndCalls([]() { + std::cout << "🔇 All calls ended successfully" << std::endl; +}); +``` + +## Checking Lobby Voice Call Status + +You may want to check the voice call status for your lobby to display UI indicators, monitor participant activity, or provide information to players. The Discord Social SDK provides several ways to inspect active voice calls and participant states. + +### Checking if a Call is Active + +Use `LobbyHandle::GetCallInfoHandle()` to determine if there's an active voice call in your lobby: + +```cpp +// Check if there's an active call in the lobby +const auto callInfoHandle = lobby->GetCallInfoHandle(); +if (callInfoHandle) { + // There's an active call - you can join it or get participant + // info + const auto participants = callInfoHandle->GetParticipants(); + std::cout << "Active call with " << participants.size() + << " participants" << std::endl; +} else { + // No active call in this lobby + std::cout << "No active voice call in this lobby" << std::endl; +} + +``` + +### Checking Individual Participant Status + +For each participant in a voice call, you can check their voice state using `VoiceStateHandle`: + +```cpp +// Get voice state information for participants +const auto callInfo = lobby->GetCallInfoHandle(); +if (callInfo) { + const auto participants = callInfo->GetParticipants(); + + for (const auto &participantId : participants) { + const auto voiceState = callInfo->GetVoiceStateHandle(participantId); + if (voiceState) { + const bool isMuted = voiceState->SelfMute(); + const bool isDeafened = voiceState->SelfDeaf(); + + std::cout << "Participant " << participantId + << " - Muted: " << (isMuted ? "Yes" : "No") + << ", Deafened: " << (isDeafened ? "Yes" : "No") + << std::endl; + } + } +} + +``` + +### Voice State Information Available + +The [`VoiceStateHandle`] provides these key details about each participant: + +- [`VoiceStateHandle::SelfMute`]: Returns `true` if the user has muted themselves (others cannot hear them) +- [`VoiceStateHandle::SelfDeaf`]: Returns `true` if the user has deafened themselves (they cannot hear others and others cannot hear them) + +This information is particularly useful for: + +- Displaying voice indicators in your UI +- Implementing voice-related features or debugging audio issues + +--- + +## Next Steps + +import {MagicWandIcon} from '/snippets/icons/MagicWandIcon.jsx' +import {ClydeIcon} from '/snippets/icons/ClydeIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Integrating and managing content moderation for your game when using the Discord Social SDK. + + }> + Make requests to Discord's HTTP APIs from your game. + + }> + Allow players to invite friends to join their game session or party. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|----------------------------------| +| June 30, 2025 | Add communications scope warning | +| June 19, 2025 | released guide | +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Call`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a1cc8a7f73c15a960bc409d734b5edbd1 +[`Call::SetParticipantVolume`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ad974fadbe89c453e4d8a3f9824e21ceb +[`Call::SetSelfDeaf`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a07d67c210f2a4655c6f1d2899c6d32d6 +[`Call::SetSelfMute`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#afa35a5d6a4564df97452df58bb74f617 +[`Call::SetVADThreshold`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a7c3fd83c5dfe37d796e30c5e28c93b6e +[`Client`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91716140c699d8ef0bdf6bfd7ee0ae13 +[`Client::CreateOrJoinLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512 +[`Client::SetInputVolume`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad4358f5baffd9a5f2a6fa74d62459313 +[`Client::SetOutputVolume`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a61a9321a79479c8b1be1559e2bbdd934 +[`Client::SetSelfDeafAll`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a59be56ae5752e9f2f0f299bc552282b2 +[`Client::SetSelfMuteAll`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9c6ef96590533d103a866cb8a99d2669 +[`Client::StartCall`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aef4f25d761fe198fbe9bc721fc24d83f +[`Client::StartCallWithAudioCallbacks`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#abcaa891769f9e912bfa0e06ff7221b05 +[`VoiceStateHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#aad2d4454b6677d82721128b0cd98a2d8 +[`VoiceStateHandle::SelfDeaf`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#a9fd4ac5fb813b926d1336fc65b440f42 +[`VoiceStateHandle::SelfMute`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#a5476a6e8d5e9092a153b4646371a9f3f \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/sending-direct-messages.mdx b/discord/developers/docs/discord-social-sdk/development-guides/sending-direct-messages.mdx new file mode 100644 index 0000000000..a64c40abc0 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/sending-direct-messages.mdx @@ -0,0 +1,253 @@ +--- +title: Sending Direct Messages +description: Send direct messages to Discord users from your application. +--- +import RateLimitCallout from '/snippets/discord-social-sdk/callouts/rate-limit.mdx'; +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; +import CommsScopeWarning from '/snippets/discord-social-sdk/callouts/oauth-comms-scopes.mdx'; + + + +## Overview + +Direct Messages (DMs) allow players to communicate privately. This guide will show you how to: + +- Send text messages between users +- Handle displaying messages in your game +- Retrieve conversation history and summaries + +### Prerequisites + +Before you begin, make sure you have: + +- Set up the Discord Social SDK with our [Getting Started guide](/developers/docs/discord-social-sdk/getting-started) + + + +### Types of Chat Messages + +The Discord Social SDK supports two types of chat: + +- [Direct messages (DMs) between two users](/developers/docs/discord-social-sdk/development-guides/sending-direct-messages#sending-a-direct-message-to-a-user) +- [Chat messages within a lobby](/developers/docs/discord-social-sdk/development-guides/managing-lobbies#sending-messages-to-a-lobby) + +The SDK receives messages in the following channel types: + +- DM +- Ephemeral DM +- Lobby + +Let's get started with sending a direct message to another user. + +--- + +## Sending a Direct Message to a User + + +While the SDK allows you to send messages on behalf of a user, you must only do so in response to a user action. You should never automatically send messages. + + +Here's how to send a direct message. You'll need the recipient's Discord ID and the message you want to send. + +```cpp +std::string message = "ready to queue?"; +uint64_t recipientId = 1234567890; // The recipient's Discord ID + +client->SendUserMessage(recipientId, message, [](auto result, uint64_t messageId) { + if (result.Successful()) { + std::cout << "✅ Message sent successfully\n"; + } else { + std::cout << "❌ Failed to send message: " << result.Error() << "\n"; + } +}); +``` + +### Syncing Messages with Discord + +In some situations, messages from your game with the Social SDK will also appear in Discord. This will happen for: + +- 1 on 1 chat when at least one of the users is a full Discord user +- Lobby chat when the lobby is [linked to a Discord channel](/developers/docs/discord-social-sdk/development-guides/linked-channels) +- The message must have been sent by a user who is not banned on Discord. + +When messaging between provisional accounts or non-friends, channel ID and recipient ID is set to the other user's ID. These messages are sent ephemerally and do not persist within a channel. Because of that, you will not be able to resolve a [`ChannelHandle`] for them. + +--- + +## Receiving and Rendering Messages + +When a user sends a message, the SDK can respond to new messages by registering [`Client::SetMessageCreatedCallback`]. You can access the message content and sender's ID in your callback from the [`MessageHandle`] object. + +The [`MessageHandle`] represents a single message received by the SDK. + +```cpp +client->SetMessageCreatedCallback([&client](uint64_t messageId) { + if (auto message = client->GetMessageHandle(messageId)) { + std::cout << "New message from " << message->AuthorId() << ": " << message->Content() << "\n"; + } +}); +``` + +There are also callbacks for when a message is updated or deleted. Register these callbacks with [`Client::SetMessageUpdatedCallback`] and [`Client::SetMessageDeletedCallback`]. + +### Suppressing Double Notifications + +Suppose the user has the Discord desktop application open on the same machine as the game. In that case, they will hear notifications from the Discord application, even though they can see those messages in-game. So to avoid double-notifying users, you should call [`Client::SetShowingChat`] whenever the chat is shown or hidden to suppress those duplicate notifications. + +### Legal Disclosure Message Type + +As a convenience for game developers, the first time a user sends a message in the game, that message shows up on the Discord client. The SDK will inject a "fake" message into the chat that contains a basic English explanation of what is happening to the user. You can identify these messages with the [`MessageHandle::DisclosureType`] method. We encourage you to customize the rendering of these messages, possibly changing the wording, translating them, and making them look more "official". You can choose to avoid rendering these as well. + +### Message History + +The SDK keeps the 25 most recent messages in each channel in memory, including direct messages. For older messages, +see the [Getting Direct Message History](/developers/docs/discord-social-sdk/development-guides/sending-direct-messages#getting-direct-message-history) +section below to retrieve additional history from Discord's servers. + +A [`MessageHandle`] will keep working even after the SDK has discarded the message for being too old. +You just won't be able to create new [`MessageHandle`] objects for that message via [`Client::GetMessageHandle`]. + +### Working with Unrenderable Content + +Messages sent on Discord can contain content that may not be renderable in-game, such as images, videos, embeds, polls, and more. The game isn't expected to render these. Instead, it should show a notice so the user knows there is more content and a way to view it on Discord. The [`MessageHandle::AdditionalContent`] method will contain data about the non-text content in this message. + +You can use this metadata to render a placeholder message for players and can link out to Discord using [`Client::CanOpenMessageInDiscord`] and [`Client::OpenMessageInDiscord`]. + +There is also more information about [messages in the Discord API](/developers/docs/resources/message) documentation. + +## Getting Direct Message History + +The SDK provides two methods to retrieve direct message conversation history, allowing you to display past conversations when users open a DM or browse their message list. + +### Retrieving All Conversation Summaries + +Use [`Client::GetUserMessageSummaries`] to get a list of all users the current user has DM conversations with, along +with the most recent message ID for each conversation: + +```cpp +client->GetUserMessageSummaries([](const discordpp::ClientResult &result, + const std::vector &summaries) { + if (result.Successful()) { + std::cout << "📋 Retrieved " << summaries.size() << " conversations\n"; + + for (const auto &summary : summaries) { + std::cout << "User ID: " << summary.UserId() + << ", Last Message ID: " << summary.LastMessageId() << "\n"; + } + } else { + std::cerr << "❌ Failed to retrieve conversation summaries\n"; + } +}); +``` + +This is particularly useful for: +- Building a conversation list UI +- Determining which users have active conversations +- Finding the most recent activity in each conversation + +### Retrieving Messages from a Specific Conversation + +Use [`Client::GetUserMessagesWithLimit`] to fetch message history from a specific DM conversation: + +```cpp +const uint64_t recipientId = 1234567890; // The other user's Discord ID +const int32_t messageLimit = 50; // Number of recent messages to retrieve + +client->GetUserMessagesWithLimit( + recipientId, messageLimit, + [](const discordpp::ClientResult &result, + const std::vector &messages) { + if (result.Successful()) { + std::cout << "💬 Retrieved " << messages.size() + << " messages from conversation\n"; + + // Messages are returned in reverse chronological order (newest first) + for (const auto &message : messages) { + std::cout << "Message: " << message.Content() + << " from " << message.AuthorId() << std::endl; + } + } else { + std::cerr << "❌ Failed to retrieve message history\n"; + } + } +); +``` + +Key points about [`Client::GetUserMessagesWithLimit`] that are important to note: + +- Messages are returned in reverse chronological order (newest first) +- The function checks the local cache first and only makes an HTTP request if needed +- Pass 0 or a negative value for `limit` to retrieve all available messages + +This functionality is useful for: +- Displaying conversation history when a user opens a DM +- Implementing message scrollback features +- Preserving conversation context across game sessions +- Building a full-featured in-game messaging interface + +## In-Game Direct Message Settings + +The Discord client provides a settings screen for users to be able to control who can DM them in-game via the +Social SDK. + +![Discord content and social - Connected game settings](/images/social-sdk/development-guides/discord-game-dm-settings.png) + +You cannot control these settings directly with the Social SDK. However, you can call +[`Client::OpenConnectedGamesSettingsInDiscord`], which opens the Connected Games settings in the Discord client, +where users can manage their direct messaging settings related to games using the Discord Social SDK. + +If the client isn't connected or the user is a provisional account, this function does nothing. + +It is always a no-op for console platforms. + +--- + +## Next Steps + +Now that you know how to send and receive messages, check out these other Discord Social SDK features: + +import {UserIcon} from '/snippets/icons/UserIcon.jsx' +import {MagicDoorIcon} from '/snippets/icons/MagicDoorIcon.jsx' +import {HashmarkIcon} from '/snippets/icons/HashmarkIcon.jsx' + + + }> + Display in-game status and activity to friends. + + }> + Bring players together in a shared lobby with invites, text chat, and voice comms. + + }> + Enable text chat between a Discord channel and your game. + + + + + +--- + +## Change Log + +| Date | Changes | +|-----------------|----------------------------------| +| August 29, 2025 | Add DM history | +| June 30, 2025 | Add communications scope warning | +| May 06, 2025 | link to DM Settings | +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`ChannelHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#ac32096b2ef15c5c220e9b7b92253cc46 +[`Client::CanOpenMessageInDiscord`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae2aac143a691091691c5cc75aa07dace +[`Client::GetMessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7825220b28952a2156bd0e46db40ea5c +[`Client::GetUserMessageSummaries`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a32dafc20ff1f99b019e40bdc81f46dde +[`Client::GetUserMessagesWithLimit`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a054a758a76c5873b38a4d79651a5f26c +[`Client::OpenConnectedGamesSettingsInDiscord`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a24f268f5eebe9919a3f774354eb577e0 +[`Client::OpenMessageInDiscord`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a66b8f85b14403a5d5ea125f39aa6e1b1 +[`Client::SetMessageCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28325a8e8c688a84ac851da4bc86e148 +[`Client::SetMessageDeletedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2b6079eded10bea29abbb9695702637b +[`Client::SetMessageUpdatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa01cf3c15403f29780dabfcfaf3b1dcd +[`Client::SetShowingChat`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#acdf400ecb926392d1a110da73152b594 +[`MessageHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f +[`MessageHandle::AdditionalContent`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#af4497491a95fda65402b6acf7a8b42e5 +[`MessageHandle::DisclosureType`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#abefb9be7836951a6acf78a4bb1676638 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/setting-rich-presence.mdx b/discord/developers/docs/discord-social-sdk/development-guides/setting-rich-presence.mdx new file mode 100644 index 0000000000..3579fcd455 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/setting-rich-presence.mdx @@ -0,0 +1,325 @@ +--- +title: Setting Rich Presence +description: Update Discord Rich Presence from your application to display activity information. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + +## Overview + +Rich Presence allows you to display detailed information about what players are doing in your game. Users can see this information in their Discord profile and friends list and use it to join their friends' games with Game Invites. + +### Prerequisites + +Before you begin, make sure you have: +- [Set up the Discord Social SDK](/developers/docs/discord-social-sdk/getting-started) +- Connected to Discord with a valid client instance + +--- + +## Understanding Rich Presence + +Rich Presence allows you to display detailed information about players' actions in your game. Users can see this information in various places in Discord, including: + +- User profiles +- Friend lists +- Server member lists + + +Let's talk about the naming of some Discord primitives first. Rich Presence, aka "Activity", can be thought of as the "current activity of a user" and is represented by the [`Activity`] class in the SDK and [in our gateway events](/developers/docs/events/gateway-events#activity-object). This is not to be confused with [Discord Activities](/developers/docs/activities/overview), which are embedded games that can also set and display rich presence. + + +Each [`Activity`] contains fields that describe the following: + +| Field | Description | Purpose | +|----------------------|--------------------------|------------------------------------------------------------------------------------------------------------------| +| `name` | Game or app name | Displayed in the user's profile | +| `type` | Activity type | What the player is doing (e.g., "Playing", "Watching", "Listening") | +| `details` | What the player is doing | Main activity description (e.g., "Playing Capture the Flag") | +| `state` | Their current status | Secondary status (e.g., "In Queue", "In Match, "In a group") | +| `party` | Party information | Shows party size and capacity (e.g., "2 of 4") | +| `timestamps` | Activity duration | Shows elapsed or remaining time | +| `assets` | Custom artwork | Game/map thumbnails and character icons | +| `secrets` | Join/spectate tokens | Enable [Game Invite](/developers/docs/discord-social-sdk/development-guides/managing-game-invites) functionality | +| `supportedPlatforms` | Platform flags | Control where join buttons appear | + +See below for examples of how to set these fields in your code. + + +While we support multiple [`ActivityTypes`], games should use `ActivityTypes::Playing` for `type`. The SDK automatically associates activity with your game, so fields like `name` will always show your game's name. + + +### Customizing Rich Presence + +When displayed in Discord, Rich Presence has three main components: + +``` +Playing "Your Game Name" <- Line 1: Game name (automatic) +Capture the Flag | 2 - 1 <- Line 2: Details field +In a group (2 of 3) <- Line 3: State + Party info +``` + +You can control how lines 2 and 3 are rendered in Discord, here's the breakdown: + +- Line 1, `Playing "game name"` is powered by the name of your game (or application) on Discord. +- Line 2, `Capture the flag | 2 - 1` is powered by the `details` field in the activity, and this should generally try to describe what the _player_ is currently doing. You can even include dynamic data such as a match score here. +- Line 3, `In a group (2 of 3)` describes the _party_ the player is in. "Party" is used to refer to a group of players in a shared context, such as a lobby, server, team, etc. The first half, `In a group` is powered by the state field in the activity, and the second half, `(2 of 3)` is powered by the party field in the activity and describes how many people are in the current party and how big the party can get. + +This diagram visually shows the field mapping: + +![Graphical representation of the legend for rich presence details](/images/rich-presence/legend.png) + + +For tips on designing Rich Presence, take a look at the [Rich Presence best practices guide](/developers/docs/rich-presence/best-practices). + + +--- + +## Setting an Invite Image + +The Rich Presence invite image appears when invites are sent for a 3rd party game or app using the Discord Social SDK. After uploading an invite image for your app, you can see a preview of it to the right (under "IRL Invite Image Example"). + +![Rich Presence invite image in app settings](/images/rich-presence/invite-image.png) + +--- + +## Uploading Assets + +While integrating Rich Presence, you'll likely want to upload custom art assets for your app. For all Rich Presence assets, it's highly recommended to make them 1024 x 1024. + +To add custom assets for Rich Presence, navigate to your app's settings and click Rich Presence on the left-hand sidebar. On the Art Assets page, you can upload two different types of assets. + +![Rich Presence invite image in app settings](/images/rich-presence-invite-image.webp) + +Up to 300 custom assets can be added to your app for later use when setting Rich Presence for a Discord user. These assets can be anything that help orient others to what a user is doing inside of your Activity or 3rd party game. + +If you need more than 300 custom assets or want to use images stored somewhere else, you can also [specify an external URL](/developers/docs/events/gateway-events#activity-object-activity-asset-image) as long it still has the proper dimensions and size. + + +For tips on choosing assets, take a look at the [Rich Presence best practices guide](/developers/docs/rich-presence/best-practices#have-interesting-expressive-art). + + +When uploading Rich Presence assets, **the asset keys will automatically be changed to lowercase**. You can see this reflected in your app's settings after saving a newly uploaded asset, and you should keep it in mind when referencing any asset keys in your code. + +Once you've uploaded these assets, you can use the asset key to reference them in your code when [Setting Assets in Rich Presence](/developers/docs/discord-social-sdk/development-guides/setting-rich-presence#setting-assets). + +![Rich Presence assets in app settings](/images/rich-presence/asset-images.png) + +--- + +## Setting Details and State + +Here's a simple example setting the details and state of a Rich Presence activity: + +```cpp +// Create a new activity +discordpp::Activity activity; +activity.SetType(discordpp::ActivityTypes::Playing); +activity.SetDetails("Battle Creek"); +activity.SetState("In Competitive Match"); + +// Update the presence +client->UpdateRichPresence(activity, [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "✅ Rich presence updated!\n"; + } +}); +``` + +--- + +## Setting Timestamps + +You can also include timestamps in your Rich Presence to show how long the player has been in their current activity or how long until their current match ends if it is a timed activity. Here's an example: + +```cpp +// Setting Timestamps +discordpp::ActivityTimestamps timestamps; +timestamps.SetStart(time(nullptr)); +// timestamps.SetEnd(time(nullptr) + 3600); +activity.SetTimestamps(timestamps); +``` + +--- + +## Setting Assets + +Once you've uploaded assets to your app, you can reference them using their **asset key** in your code to set custom artwork in Rich Presence. Here's an example of an asset with the key of "map-mainframe", "tank-avatar", and "invite-cover-image": + +```cpp +// Setting Activity Assets +discordpp::ActivityAssets assets; +assets.SetLargeImage("map-mainframe"); +assets.SetLargeText("Mainframe"); +assets.SetSmallImage("tank-avatar"); +assets.SetSmallText("Tank"); +assets.SetInviteCoverImage("invite-cover-image"); // Used for Game Invites +activity.SetAssets(assets); +``` + + +If you need more than 300 custom assets or want to use images stored somewhere else, you can also [specify an external URL](/developers/docs/events/gateway-events#activity-object-activity-asset-image) as long it still has the proper dimensions and size. + + +--- + +## Setting Field URLs + +You can set URLs for `details`, `state`, `assets.large_image` and `assets.small_image` in Rich Presence. When present, these URLs will make the corresponding image/text into clickable links. + +```cpp +activity.SetState("Playing on Mainframe"); +activity.SetStateUrl("https://example.com/maps/mainframe"); +activity.SetDetails("Rank #1337 in global leaderboard"); +activity.SetDetailsUrl("https://example.com/leaderboard/global"); + +discordpp::ActivityAssets assets; +assets.SetLargeImage("map-mainframe"); +assets.SetLargeText("Mainframe"); +assets.SetLargeUrl("https://example.com/maps/mainframe"); +assets.SetSmallImage("tank-avatar"); +assets.SetSmallText("Tank"); +assets.SetSmallUrl("https://example.com/classes/tank"); + +activity.SetAssets(assets); +``` + +--- + +## Configuring Status Text + +By default, Rich Presence will display the game's name in the user's status text. You can override this behavior by setting a status display type. + +```cpp +// uses the game's name in the status text (default) +activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::Name); + +// uses the activity's state field in the status text +activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::State); + +// uses the activity's details field in the status text +activity.SetStatusDisplayType(discordpp::StatusDisplayTypes::Details); +``` + +--- + +## Setting Party and Join Secret + +You can also include party details and a join secret in your Rich Presence to power Game Invites. Check out the [Game Invites guide](/developers/docs/discord-social-sdk/development-guides/managing-game-invites) for more information. + +```cpp + +// Setting Party Details +discordpp::ActivityParty party; +party.SetId("party1234"); +party.SetCurrentSize(1); +party.SetMaxSize(5); +activity.SetParty(party); + +// Setting Join Secret details +discordpp::ActivitySecrets secrets; +secrets.SetJoin("your-join-secret"); +activity.SetSecrets(secrets); +``` + +--- + +## Setting Supported Platforms + +You can set the supported platforms for your game in Rich Presence. This will control where the join buttons appear in Discord. + +If you only want the join button to appear on desktop, you can set the supported platforms like this: + +```cpp +activity.SetSupportedPlatforms(discordpp::ActivityGamePlatforms::Desktop); +``` + +See the `ActivityGamePlatforms` enum for all supported platforms. + +## Rich Presence Without Authentication + + +Rich Presence via RPC (Remote Procedure Call) will only work with a running Discord desktop client. It does not support mobile, console or web clients. + + +Unlike most other features of the Discord Social SDK, **Rich Presence can be set without authentication**. Instead of +using [`Client::Connect`] to authenticate with Discord, you can use Rich Presence functionality by directly communicating +with a running Discord desktop client through RPC (Remote Procedure Call). + +### Requirements + +* Discord desktop client must be running on the user's machine +* Your application must be registered with Discord and have a valid Application ID + +This direct approach makes Rich Presence integration much simpler for developers who only need basic presence +functionality while Discord desktop clients are running. + +### Setting Up Direct Rich Presence + +To use Rich Presence without authentication, simply: + +1. Set your application ID using [`Client::SetApplicationId`] +2. Configure your activity details +3. Call [`Client::UpdateRichPresence`] + +```cpp +auto client = std::make_shared(); + +// Set the application ID (no Connect() call needed) +client->SetApplicationId(APPLICATION_ID); + +// Configure rich presence details +discordpp::Activity activity; +activity.SetType(discordpp::ActivityTypes::Playing); +activity.SetState("In Competitive Match"); +activity.SetDetails("Rank: Diamond II"); + +// Update rich presence +client->UpdateRichPresence( + activity, [](const discordpp::ClientResult &result) { + if (result.Successful()) { + std::cout << "🎮 Rich Presence updated successfully!\n"; + } else { + std::cerr << "❌ Rich Presence update failed"; + } + }); +``` + +--- + +## Next Steps + +Now that you've set up Rich Presence, you might want to explore: + +import {UserIcon} from '/snippets/icons/UserIcon.jsx' +import {MagicDoorIcon} from '/snippets/icons/MagicDoorIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Allow players to invite friends to join their game session or party. + + }> + Bring players together in a shared lobby with invites, text chat, and voice comms. + + }> + Best practices for Rich Presence UI/UX. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | Initial release | + +{/* Autogenerated Reference Links */} +[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682 +[`ActivityTypes`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a6c76a8cbbc9270f025fd6854d5558660 +[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa +[`Client::SetApplicationId`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad452335c06b28be0406dab824acccc49 +[`Client::UpdateRichPresence`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af0a85e30f2b3d8a0b502fd23744ee58e \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts.mdx b/discord/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts.mdx new file mode 100644 index 0000000000..a8b347c441 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts.mdx @@ -0,0 +1,650 @@ +--- +title: Using Provisional Accounts +description: Implement provisional Discord accounts for users who don't have Discord accounts yet. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + +## Overview + +Provisional accounts let players use Social SDK features in your game without linking a Discord account so all players can have a consistent gameplay experience. + +With provisional accounts, players can: + +- Add friends and communicate with other players +- Join voice chats in game lobbies +- Send direct messages to other players +- Appear in friends lists and game lobbies + +All of this works seamlessly whether your players have Discord accounts or not. + +This guide will show you how to: + +1. Set up provisional accounts for your game +2. Create and manage provisional accounts +3. Handle account merging when users want to upgrade to full Discord + +## Prerequisites + +Before you begin, make sure you have: + +- A basic understanding of how the SDK works from the [Getting Started Guide](/developers/docs/discord-social-sdk/getting-started) +- An external authentication provider set up for your game + +--- + + +## What Are Provisional Accounts? + +Think of provisional accounts as temporary Discord accounts that: + +- Work only with your game +- Can be upgraded to full Discord accounts later +- Persist between game sessions +- Use your game's authentication system + +With provisional accounts, players can use Discord features like chat and voice and interact with game friends without creating a full Discord account. They are "placeholder" Discord accounts for the user that your game owns and manages. + +For existing Discord users who have added a provisional account as a game friend, the provisional account will appear in their friend list, allowing you to send direct messages and interact with them for text and voice in lobbies. + +### How It Works + +1. Your game authenticates players using your existing system (Steam, Epic, etc.) +2. Discord creates temporary accounts linked to those identities +3. Players can use Discord features immediately +4. Players can optionally upgrade to full Discord accounts later +5. All friends and chat history transfer when upgrading + +### Benefits + +- Instant Access: Players can use social features immediately +- Seamless Experience: Works the same for all players +- Easy Upgrade Path: Simple conversion to full Discord accounts +- Data Persistence: Friends and history are preserved +- Cross-Platform: Works on all supported platforms + +--- + +## Getting Set Up +### Choosing an Authentication Method + +Discord offers a number of authentication methods, the one you use depends on how you game and account system is set up: + +1. Use the [Bot Token Endpoint](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#server-authentication-with-bot-token-endpoint) if your game has an account system which uniquely identifies users. This is the recommended approach when possible. +2. Use the [Server Authentication with External Credentials Exchange](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#server-authentication-with-external-credentials-exchange) if you have an existing OIDC provider, or do not have an account system. +3. Use the [Client Side Token Exchange Method](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#authentication-for-public-clients) if you are using a Public Client. + +If you are using (2) or (3), you must configure you identity provider before being able to create provisional accounts. + +### Configuring Your Identity Provider + + +If you are using the bot token endpoint, no Identity Provider configuration is required. + + +Open the Discord app for your game in the [Developer Portal](https://discord.com/developers/applications). Find the `External Auth` page under the `Discord Social SDK` section in the sidebar. + +Click on `Add Auth Provider` and choose the type of provider you're using (Steam, OIDC, etc.). Fill in the required details for your provider. + +We currently support the following provider types: + +- OpenID Connect (OIDC) +- Steam Session Tickets +- Epic Online Services (EOS) +- Unity + +Providers are represented in Discord's systems by the following types: + +#### External Auth Types + +| Type | Description | +|-----------------------------------|-------------------------------------------------------------------------------| +| OIDC | OpenID Connect ID token | +| STEAM_SESSION_TICKET | A Steam auth ticket for web generated with discord as the identity | +| EPIC_ONLINE_SERVICES_ACCESS_TOKEN | Access token for Epic Online Services. Supports EOS Auth access tokens | +| EPIC_ONLINE_SERVICES_ID_TOKEN | ID token for Epic Online Services. Supports both EOS Auth + Connect ID tokens | +| UNITY_SERVICES_ID_TOKEN | Unity Services authentication ID token | +| DISCORD_BOT_ISSUED_ACCESS_TOKEN | An access token for a user authenticated via the Bot Token Endpoint | + +--- + +## Implementing Provisional Accounts + +Creating a Provisional Account and requesting an Access Token for the account always happens in a single step. + +You provide external authentication and uniquely identifies the user, and Discord finds a user associated with that identifier. + +- If there is no account associated with the identity, a new provisional account is created along with a new access token for the user. +- If there is a provisional account associated with the identity, an access token is returned. +- If there is an existing _full Discord account_ associated with the identity, the request is aborted (See [Error Handling](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#error-handling)). + +Once authentication is complete, you can use the access token as you would a full Discord user's access token. + +### Server Authentication with Bot Token Endpoint + + +This is the preferred method of authentication. It ends up being the simplest choice for most provisional account integrations. + + +```python +# filepath: your_game/server/auth.py +import requests +from models import GameAccount + +def get_provisional_token(game_account: GameAccount): + response = requests.post( + 'https://discord.com/api/v10/partner-sdk/token/bot', + headers={ + 'Content-Type': 'application/json', + 'Authorization': 'Bot ' # your application's bot token + }, + json={ + 'external_user_id': game_account.id, # your account system's unique id + 'preferred_global_name': game_account.display_name, # your account system's display name for the user + } + ) + return response.json() +``` + +#### Bot Token Endpoint Response + +```python +{ + "access_token": "", + "id_token": "", + "token_type": "Bearer", + "expires_in": 3600, + "scope": "sdk.social_layer" +} +``` + +### Server Authentication with External Credentials Exchange +```python +# filepath: your_game/server/auth.py +import requests + +def get_provisional_token(external_token: str): + response = requests.post( + 'https://discord.com/api/v10/partner-sdk/token', + json={ + 'client_id': CLIENT_ID, + 'client_secret': CLIENT_SECRET, + 'external_auth_type': EXTERNAL_AUTH_TYPE, # See External Auth Types above + 'external_auth_token': external_token + } + ) + return response.json() +``` + +#### External Credentials Exchange Response + +```python +{ + "access_token": "", + "id_token": "", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "", # only provided for OIDC when *not* a public client + "scope": "sdk.social_layer" +} +``` + +### Authentication for Public Clients + + + +If you have `Public Client` enabled on your Discord app, you can use the following code to authenticate your players with the external provider. + +```cpp +// filepath: your_game/auth_manager.cpp +void AuthenticateUser(std::shared_ptr client) { + // Get your external auth token (Steam, OIDC, etc.) + std::string externalToken = GetExternalAuthToken(); + + // Get provisional token from Discord + client->GetProvisionalToken(DISCORD_APPLICATION_ID, + discordpp::AuthenticationExternalAuthType::OIDC, + externalToken, + [client](discordpp::ClientResult result, std::string accessToken, std::string refreshToken, discordpp::AuthorizationTokenType tokenType, int32_t expiresIn, std::string scope) { + if (result.Successful()) { + std::cout << "🔓 Provisional token received! Establishing connection...\n"; + client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) { + client->Connect(); + }); + } else { + std::cerr << "❌ Provisional token request failed: " << result.Error() << std::endl; + } + }); +} +``` + +### Provisional Account Access Tokens + +These methods generate a Discord access token. You pass in the user's identity, and it generates a new Discord account tied to that identity. There are multiple ways of specifying that identity, including using Steam/Epic services or your own identity system. + +The callback function will be invoked with an access token that expires in 1 hour. Refresh tokens are not supported for provisional accounts, so that will be an empty string. When the old one expires, you must call this function again to get a new access token. + +You can use [`Client::SetTokenExpirationCallback`] to receive a callback when the current token is about to expire or expires. + + +When the token expires, the SDK will still receive updates, such as new messages sent in a lobby, and any voice calls will continue to be active. However, any new actions, such as sending a message or adding a friend, will fail. You can get a new token and pass it to UpdateToken without interrupting the user's experience. + + +#### Provisional Account Access Token Storage + +It is suggested that these provisional tokens are not stored and instead invoke this function each time the game is launched and when these tokens are about to expire. However, should you choose to store it, it is recommended that these provisional account tokens be differentiated from "full" Discord account tokens. + +### Error Handling + +Common error codes and solutions for the server token exchange methods: + +| Code | Meaning | Solution | +|--------|------------------------------|-----------------------------------------------------------------------------| +| 530000 | Application not configured | Contact Discord support to enable provisional accounts for your application | +| 530001 | Expired ID token | Request a new token from your identity provider | +| 530004 | Token too old | Request a new token (tokens over 1 week old are rejected) | +| 530006 | Username generation failed | Retry the operation (temporary error) | +| 530007 | Invalid client secret | Verify or regenerate your client secret in the Developer Portal | +| 530010 | User account non-provisional | User already linked to Discord account - use standard OAuth2 flow | + +If you are using OIDC, you may encounter more specific errors: + +| Code | Meaning | Solution | +|--------|------------------------------|--------------------------------------------------------------------------| +| 530002 | Invalid issuer | Verify the issuer in your ID token matches your configuration | +| 530003 | Invalid audience | Check that the audience in your ID token matches your OIDC configuration | +| 530008 | OIDC configuration not found | Verify your OIDC issuer URL is configured and accessible | +| 530009 | OIDC JWKS not found | Check that your OIDC provider's JWKS endpoint is accessible | +| 530020 | Invalid OIDC JWT token | Ensure your OIDC ID token is properly signed and formatted | + + You can find you OIDC configuration by visiting the [Developer Portal](https://discord.com/developers/applications), selecting your application, and opening the `External Auth` page under the `Discord Social SDK` section in the sidebar. + +--- + +## Setting Display Names + +Using these credentials, we'll create a limited Discord account just for your game and try to set the username for you according to the following: + +- For Bot issued tokens, the `preferred_global_name` you specified will be used. +- For OIDC, a provisional account's display name will be the value of the `preferred_username` claim, if specified in + the ID token. This field is optional and should be between 1 and 32 characters. If not specified, the user's display + name will default to the user's unique username, which Discord generates on creation. +- For [Steam session tickets](https://partner.steamgames.com/doc/features/auth), the display name of the user's Steam + account is used as the provisional account's display name. +- For [EOS Auth](https://dev.epicgames.com/docs/epic-account-services/auth/auth-interface) Access Tokens or ID Tokens, + the name of the user's Epic account is used as the provisional account's display name. EOS Connect ID Tokens do + not expose any username, and thus the game will need to configure the display name with + [`Client::UpdateProvisionalAccountDisplayName`]. +- For [Unity Services ID Tokens](https://services.docs.unity.com/docs/client-auth/), + the display name of the user's Unity Player Account is used as the provisional account's display name. + +If you'd like to set the display name for a provisional account, you can use the [`Client::UpdateProvisionalAccountDisplayName`] method. + +```cpp +client->UpdateProvisionalAccountDisplayName("CoolPlayer123", [](discordpp::ClientResult result) { + if (result.Successful()) { + std::cout << "✅ Display name updated\n"; + } + } +); +``` + +---- + +## Merging Provisional Accounts + +When a player wants to convert their provisional account to a full Discord account, we start a special version of the [access token request flow](/developers/docs/discord-social-sdk/development-guides/account-linking-with-discord#requesting-access-tokens) where the provisional users external identity is included. + +- If you have a backend, follow [Merging Provisional Accounts for Servers](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#merging-provisional-accounts-for-servers) +- If you do not have a backend, follow [Merging Provisional Accounts for Public Clients ](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#merging-provisional-accounts-for-public-clients) + +### Merging Provisional Accounts for Servers + +To merge provisional accounts, include `external_auth_type` and `external_auth_token` values with a request to `/oauth2/token`. Discord will look up the Provisional User associated with the provided identity and attempt to merge it in to the full Discord account that generated the provided `code`. + +#### Desktop & Mobile + +```python +import requests + +API_ENDPOINT = 'https://discord.com/api/v10' +CLIENT_ID = '332269999912132097' +CLIENT_SECRET = '937it3ow87i4ery69876wqire' +EXTERNAL_AUTH_TYPE = 'OIDC' # See External Auth Types above + +def exchange_code_with_merge(code, redirect_uri, external_auth_token): + data = { + 'grant_type': 'authorization_code', + 'code': code, + 'redirect_uri': redirect_uri, + 'external_auth_type': EXTERNAL_AUTH_TYPE, + 'external_auth_token': external_auth_token + } + headers = { + 'Content-Type': 'application/x-www-form-urlencoded' + } + r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + r.raise_for_status() + return r.json() +``` + +#### Console + +```python +import requests +API_ENDPOINT = 'https://discord.com/api/v10' +CLIENT_ID = '332269999912132097' +CLIENT_SECRET = '937it3ow87i4ery69876wqire' +EXTERNAL_AUTH_TYPE = 'OIDC' +def exchange_device_code_with_merge(device_code): + data = { + 'grant_type': 'urn:ietf:params:oauth:grant-type:device_code', + 'device_code': device_code, + 'external_auth_type': EXTERNAL_AUTH_TYPE, + 'external_auth_token': external_auth_token + } + headers = { + 'Content-Type': 'application/x-www-form-urlencoded' + } + r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) + r.raise_for_status() + return r.json() +``` + +#### Merge Request Response + +```python +{ + "access_token": "", + "token_type": "Bearer", + "expires_in": 604800, + "refresh_token": "", + "scope": "sdk.social_layer" +} +``` + +### Merging Provisional Accounts for Public Clients + + + +If you do not have a backend, leverage the [`Client::GetTokenFromProvisionalMerge`] (Desktop & Mobile) or [`Client::GetTokenFromDeviceProvisionalMerge`] (Console) method, which will handle the entire process for you. You'll want to first enable Public Client on your Discord application's OAuth2 tab on the Discord developer portal. You can then leverage the [`Client::GetTokenFromProvisionalMerge`] or [`Client::GetTokenFromDeviceProvisionalMerge`] method using just the client. + +This function should be used with the [`Client::Authorize`] function whenever a user with a provisional account wants to link to an existing Discord account or "upgrade" their provisional account into a "full" Discord account. + +In this case, data from the provisional account should be "migrated" to the Discord account, a process we call "account merging". Specifically, relationships, DMs, and lobby memberships are transferred to the Discord account. + +The provisional account will be deleted once this merging process is completed. If the user unlinks later, a new provisional account with a new unique ID is created. + +The account merging process starts like the normal login flow, invoking the [`Client::Authorize`] method to get an authorization code back. Instead of calling `GetToken`, call this function and pass on the provisional user's identity. + +Discord can then find the provisional account with that identity and the new Discord account and merge any data as necessary. + +See the documentation for [`Client::GetToken`] for more details on the callback. Note that the callback will be invoked when the token exchange is complete, but merging accounts happens asynchronously and will not be complete yet. + +```cpp +// Create a code verifier and challenge if using GetToken +auto codeVerifier = client->CreateAuthorizationCodeVerifier(); +discordpp::AuthorizationArgs args{}; +args.SetClientId(YOUR_DISCORD_APPLICATION_ID); +args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); +args.SetCodeChallenge(codeVerifier.Challenge()); + +client->Authorize(args, [client, codeVerifier](discordpp::ClientResult result, std::string code, std::string redirectUri) { + if (!result.Successful()) { + std::cerr << "❌ Authorization Error: " << result.Error() << std::endl; + } else { + std::cout << "✅ Authorization successful! Next step: GetTokenFromProvisionalMerge \n"; + + // Retrieve your external auth token + std::string externalAuthToken = GetExternalAuthToken(); + + client->GetTokenFromProvisionalMerge(YOUR_DISCORD_APPLICATION_ID, code, codeVerifier, redirectUri, discordpp::AuthenticationExternalAuthType::OIDC, externalAuthToken,[client]( + discordpp::ClientResult result, + std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + std::string scope) { + if (result.Successful()) { + std::cout << "🔓 Token received! Establishing connection...\n"; + client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) { + client->Connect(); + }); + } else { + std::cerr << "❌ Token request failed: " << result.Error() << std::endl; + } + }); + + } +}); +``` + +### Data Migration During Merging + +When a user merges their provisional account with a Discord account, the following data is automatically transferred: + +* **✅ Friends**: All In-game and Discord friendships made through the provisional account +* **✅ Lobby Memberships**: Active and historical lobby participation +* **✅ DM Messages**: Direct messages and history + +This migration ensures users don't lose their social connections built while using the provisional account. + + +### Merge Request Failures + +You may receive a merge specific error code while attempting this operation: + +| Code | HTTP Status | Meaning | Solution | +|--------|-------------|---------------------------------|-----------------------------------------------------------------------| +| 530014 | 400 | Invalid merge source | The source account is not provisional | +| 530016 | 400 | Invalid merge destination | The destination account is provisional | +| 530017 | 400 | Merge source user banned | The provisional account being merged is banned from platform | +| 530023 | 400 | Too many application identities | User already has an associated external identity for this application | +| - | 423 | Resource locked | Transient error, wait and retry | + +--- + +## Unmerging Provisional Accounts + +When a player wants to unlink their Discord account from their provisional account, there are three options: + +1. The user can unmerge their account from the Discord client +2. A developer can unmerge the account using the unmerge endpoint on the Discord API +3. A developer can use the SDK helper method for public clients + + +Unmerging invalidates all access/refresh tokens for the user. They cannot be used again after the unmerge operation completes. Any connected game sessions will be disconnected. + + +### Discord Users + +Users can unmerge their account by removing access to your application on their Discord `User Settings -> Authorized Apps` page. + +This method doesn't require any code changes from developers, but we recommend providing unmerging functionality through +one of the options below for a better user experience. + +If you would like to be notified when a user unlinks this way, you can [configure you application to listen for the `APPLICATION_DEAUTHORIZED` webhook event](/developers/docs/events/webhook-events#application-deauthorized). +Otherwise, you will know that the user has unlinked because their access token and refresh token (if you have one) will be invalidated. + +### Unmerging Provisional Accounts for Servers + +A developer can unmerge a user's account by sending a request to the unmerge endpoint on the Discord API. + + +```python +import requests + +API_ENDPOINT = 'https://discord.com/api/v10' +CLIENT_ID = '332269999912132097' +CLIENT_SECRET = '937it3ow87i4ery69876wqire' +EXTERNAL_AUTH_TYPE = 'OIDC' + +def unmerge_provisional_account(external_auth_token): + data = { + 'client_id': CLIENT_ID, + 'client_secret': CLIENT_SECRET, + 'external_auth_type': EXTERNAL_AUTH_TYPE, + 'external_auth_token': external_auth_token + } + r = requests.post('%s/partner-sdk/provisional-accounts/unmerge' % API_ENDPOINT, json=data, headers=headers) + r.raise_for_status() +``` + + +If you have a server backend, you'll want to use the server-to-server unmerge endpoint rather than the SDK helper method to maintain better security and control over the unmerge process. + + +#### Unmerging with Bot Token Endpoint + +If you're using the [Bot Token Endpoint](/docs/discord-social-sdk/development-guides/using-provisional-accounts#server-authentication-with-bot-token-endpoint) for authentication, you can unmerge accounts without an external auth token. + +```python +import requests + +API_ENDPOINT = 'https://discord.com/api/v10' +BOT_TOKEN = 'YOUR_BOT_TOKEN' + +def unmerge_provisional_account(external_user_id): + data = { + 'external_user_id': external_user_id # identifier used in the /token/bot endpoint + } + headers = { + 'Content-Type': 'application/json', + 'Authorization': f'Bot {BOT_TOKEN}' + } + r = requests.post('%s/provisional-accounts/unmerge/bot' % API_ENDPOINT, json=data, headers=headers) + r.raise_for_status() +``` + + +This endpoint can also be useful in cases where the Discord Auth token has been lost to to error or data loss, and an unmerge operation is required to migrate to a provisional account before re-linking a Discord account. + + +### Unmerging Provisional Accounts for Public Clients + + + +The quickest way to unmerge accounts is to leverage the [`Client::UnmergeIntoProvisionalAccount`] method, +which will handle the entire process for you. This method is designed for public clients that don't have a backend server. + +**Important Notes:** +- This function only works for **public clients** (applications without backend servers) +- You'll need to enable "Public Client" on your Discord application's OAuth2 tab in the Discord developer portal +- After unmerging, you should use [`Client::GetProvisionalToken`] to get a new provisional token for the newly created provisional account + +```cpp +// unmerge a user account +void UnmergeUserAccount(const std::shared_ptr& client) { + // Get your external auth token (Steam, OIDC, etc.) + std::string externalToken = GetExternalAuthToken(); + + // Unmerge the Discord account from the external identity + client->UnmergeIntoProvisionalAccount( + YOUR_DISCORD_APPLICATION_ID, + discordpp::AuthenticationExternalAuthType::OIDC, // or STEAM, EOS, etc. + externalToken, + [client, externalToken](const discordpp::ClientResult &result) { + if (result.Successful()) { + std::cout << "✅ Account unmerged successfully! Creating new provisional account...\n"; + + // Now get a new provisional token for the unlinked identity + client->GetProvisionalToken( + YOUR_DISCORD_APPLICATION_ID, + discordpp::AuthenticationExternalAuthType::OIDC, + externalToken, + [client](const discordpp::ClientResult &result, + const std::string &accessToken, + const std::string& refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + const std::string& scopes) { + if (result.Successful()) { + std::cout << "🔓 New provisional account created! Establishing connection...\n"; + client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, + [client](const discordpp::ClientResult &updateResult) { + if (updateResult.Successful()) { + client->Connect(); + } else { + std::cerr << "❌ Failed to update token: " << updateResult.Error() << std::endl; + } + } + ); + } else { + std::cerr << "❌ Failed to create new provisional account: " << result.Error() << std::endl; + } + } + ); + } else { + std::cerr << "❌ Unmerge failed: " << result.Error() << std::endl; + } + } + ); +} +``` + +### Data Migration During Unmerging + +When a user unmerges their account, a new provisional account is created with a new user ID. The relationship transfer follows these rules: + +* **✅ In-game friends**: All copied to the new provisional account +* **✅ Discord friends who use this application**: Copied to the provisional account +* **❌ Discord friends who don't use this application**: Not transferred +* **❌ DM message history**: Not moved to provisional accounts + + +Provisional accounts can have Discord friends, but can only message these friends when actively playing the game. + + +### Unmerge Request Failures + +You may receive an unmerge specific error code while attempting this operation: + +| Code | HTTP Status | Meaning | Solution | +|-------|-------------|-------------------|-----------------------------------------------------------| +| 50229 | 400 | Invalid user type | User account is provisional and cannot be unmerged | +| - | 404 | Unknown user | No user identity found for the provided external identity | + + +--- + +## Next Steps + +Now that you've set up provisional accounts for your game, you can explore more features of the Discord Social SDK: + +import {PaintPaletteIcon} from '/snippets/icons/PaintPaletteIcon.jsx' +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserStatusIcon} from '/snippets/icons/UserStatusIcon.jsx' + + + }> + Design guidelines for implementing provisional accounts in your game. + + }> + Combine Discord and game friends into a single list for easy management. + + }> + Display game status and information to Discord friends. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | Initial release | + +{/* Autogenerated Reference Links */} +[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468 +[`Client::GetProvisionalToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8003130b6c46e54ac68442483bf0480c +[`Client::GetToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aaee636f91fb1ea3465157c20313b702c +[`Client::GetTokenFromDeviceProvisionalMerge`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afd2207590ae7d6f60ee7bbb4fc7c21c8 +[`Client::GetTokenFromProvisionalMerge`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a41062b7dafa331ddd2320daf1b4b273b +[`Client::SetTokenExpirationCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aab5bfc76809ea22e79f2f7a067ac4519 +[`Client::UnmergeIntoProvisionalAccount`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2da21ae8a3015e0e5e42c1a7226b256f +[`Client::UpdateProvisionalAccountDisplayName`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7485979ab2d4c533b75f8efd5e50bc60 diff --git a/discord/developers/docs/discord-social-sdk/getting-started.mdx b/discord/developers/docs/discord-social-sdk/getting-started.mdx new file mode 100644 index 0000000000..33c418e0fb --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/getting-started.mdx @@ -0,0 +1,37 @@ +--- +title: Getting Started with the Discord Social SDK +sidebarTitle: Glossary +description: Quick start guide for integrating the Discord Social SDK. +--- + +The Discord Social SDK allows you to integrate Discord social features into your game. With the Discord Social SDK, you can offer a seamless social experience for your players, allowing them to connect with their friends and communities on Discord without leaving your game. + +If this is your first time learning about the Discord Social SDK, check out the Discord Social SDK [Overview](/developers/docs/discord-social-sdk/overview) and [Core Concepts](/developers/docs/discord-social-sdk/core-concepts) for more information on what the SDK can do and how it can benefit your game. + +## Let's Get Started + +Select a platform to get started. + +If you are unsure which platform to choose, we recommend starting with the C++ guide to familiarize yourself with the SDK's core concepts. + + + + For use with custom engines or standalone applications. + + + For use with Unity. + + + For use with Unreal Engine. + + + + +To see a complete list of currently compatible platforms, check out our [Platform Compatibility](/developers/docs/discord-social-sdk/core-concepts/platform-compatibility) section. + + +--- + +## Next Steps + +After you've run through a guide for your preferred platform, you can implement features such as a unified friend list, rich presence, and more. Check out the [Development Guides](/developers/docs/discord-social-sdk/development-guides) for detailed information on using the SDK for each feature. \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/getting-started/partials/dylib-mac-error.mdx b/discord/developers/docs/discord-social-sdk/getting-started/partials/dylib-mac-error.mdx new file mode 100644 index 0000000000..901a5e4a77 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/getting-started/partials/dylib-mac-error.mdx @@ -0,0 +1,14 @@ + +On Mac you may get the error "libdiscord_partner_sdk.dylib" Not Opened because Apple couldn't verify it. If this happens press **Done** on the popup. + +![Error](/images/social-sdk/getting-started/partials/error.webp) + +You'll need to open your **System Settings > Privacy & Security** and scroll down to the **Security** section. It will tell you "libdiscord_partner_sdk.dylib" was blocked to protect your Mac. Press **Open Anyway** and try running again. + +![Settings](/images/social-sdk/getting-started/partials/settings-security.webp) + +Now when you get the pop up you'll have the option to select **Open Anyway** and it will be able to use it successfully. + +![Open](/images/social-sdk/getting-started/partials/open-anyway.webp) + + \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/getting-started/partials/getting-started.mdx b/discord/developers/docs/discord-social-sdk/getting-started/partials/getting-started.mdx new file mode 100644 index 0000000000..a047bb55c6 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/getting-started/partials/getting-started.mdx @@ -0,0 +1,35 @@ +## Step 1: Create a Discord Developer Team + +Before you start, you'll need to create a developer team on the Discord Developer Portal. This team will be used to manage your Discord applications and SDK integrations. + +If you already have a team configured, you can skip this step. + +1. Create a developer team on the [Discord Developer Portal](https://discord.com/developers/teams). + +Later, you can invite your team members to your new team to collaborate on your integration. + +--- + +## Step 2: Create a Discord Application + +1. Create a new application on the [Discord Developer Portal](https://discord.com/developers/applications) and assign it to your team. +3. Add a redirect URL in the `OAuth2` tab: + - For desktop applications: `http://127.0.0.1/callback` (this can be changed later). + - See [`discordpp::Client::Authorize`](https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468) for more details on setting up more advanced redirect URIs. +4. Enable the `Public Client` toggle in the `OAuth2` tab. + + +**Note:** This guide requires enabling **Public Client** to allow you to get started with the SDK quickly. Most games will not want to ship as a public client. This setting should be reviewed by your team prior to releasing your game. [Learn more about public clients](/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types). + + +--- + +## Step 3: Enable Discord Social SDK for Your App + +Once you've created your Discord application, you'll need to enable the Discord Social SDK for it. + +1. From the [Discord Developer Portal](https://discord.com/developers/applications), select your newly created application from Step 2. +2. In the left sidebar for your app, locate and click the `Getting Started` link under `Discord Social SDK`. +3. Fill out the form to share details about your game. +4. Click `Submit` and the Social SDK will be enabled for your application. +5. Once enabled, you'll find binaries for the Social SDK under `Downloads`. \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/getting-started/using-c++.mdx b/discord/developers/docs/discord-social-sdk/getting-started/using-c++.mdx new file mode 100644 index 0000000000..84919719a1 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/getting-started/using-c++.mdx @@ -0,0 +1,978 @@ +--- +sidebarTitle: Using Standalone C++ +title: Getting Started with C++ and the Discord Social SDK +description: Get started with the Discord Social SDK in C++ applications. +--- +import AppCreationSteps from '/snippets/discord-social-sdk/partials/getting-started.mdx' +import ConsoleAccess from '/snippets/discord-social-sdk/callouts/console-access.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; +import DylibMacError from '/snippets/discord-social-sdk/partials/dylib-mac-error.mdx'; + +## Overview + +This guide will walk you through integrating the Discord Social SDK into a new standalone C++ project. By the end, you'll have an application that can: + +- Authenticate users with Discord +- Set up logging and status monitoring +- Start the SDK and establish a connection +- Request the number of Discord friends the player has +- Set the player's rich presence for your game + +### Prerequisites + +We are going to make a simple C++ console application for this guide. Make sure you have the following prerequisites: + +- Basic understanding of C++ and your platform's build system +- C++ Compiler and Build System that supports [C++17](https://en.cppreference.com/w/cpp/17) or greater. +- A network connection that can access Discord's API endpoints. + +Let's walk through the steps in detail. + +--- + + + +--- + +## Step 4: Download the Discord SDK for C++ + +1. Click on the `Downloads` link under the Discord Social SDK section of the sidebar. +2. Select the latest version from the version dropdown and download the SDK for C++. + +### Runtime Dependencies + +When shipping with the Social SDK, ensure you bundle the runtime dependencies: +- **Windows:** Ensure `discord_partner_sdk.dll` is in your executable directory. +- **Linux/macOS:** Make sure the `libdiscord_partner_sdk.so/libdiscord_partner_sdk.dylib` files are accessible via `LD_LIBRARY_PATH` or placed next to your binary. + +--- + +## Step 5: Project Setup + +To utilize the Discord Social SDK with C++, the following requirements must be met: + +- `discordpp.h` is included in your C++ source code. +- The appropriate SDK libraries for your platform are linked in your build system: + - **Windows:** `discord_partner_sdk.dll` + - **Linux**: `discord_partner_sdk.so` + - **macOS:** `discord_partner_sdk.dylib` + +All of which can be found in the SDK download archive. + + +💡 **Troubleshooting Tip:** If you encounter unresolved external symbols, ensure the SDK library is correctly linked in your build system. + + +Let's see this in action by starting with a folder for our project and the Social SDK dependency: + +```bash +mkdir MyGame +cd MyGame +mkdir lib +``` + +Unzip the Social SDK archive into the `lib` directory. You should end up with a `discord_social_sdk` folder under `lib` when complete. + +Within the `MyGame` directory, create a `CMakeLists.txt` file: + +```bash +touch CMakeLists.txt +``` + +Add the following contents to the `CMakeLists.txt` file to set up the Social SDK dependency appropriately for your operating system: + +```cmake +cmake_minimum_required(VERSION 3.10) +project(DiscordSDKExample) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_executable(DiscordSDKExample main.cpp) + +# Define some handy Social SDK variables +set(DISCORD_SDK_ROOT "${CMAKE_SOURCE_DIR}/lib/discord_social_sdk") +set(DISCORD_SDK_LIB_DIR "${DISCORD_SDK_ROOT}/lib/release") +set(DISCORD_SDK_BIN_DIR "${DISCORD_SDK_ROOT}/bin/release") +set(DISCORD_SDK_INCLUDE_DIR "${DISCORD_SDK_ROOT}/include") + +# Include for Social SDK headers +target_include_directories(DiscordSDKExample PRIVATE ${DISCORD_SDK_INCLUDE_DIR}) + +# Platform-specific Social SDK library paths +if(WIN32) + set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/discord_partner_sdk.lib") + set(DISCORD_SHARED_LIB "${DISCORD_SDK_BIN_DIR}/discord_partner_sdk.dll") +elseif(APPLE) + set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.dylib") + set(DISCORD_SHARED_LIB "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.dylib") +else() # Linux + set(DISCORD_LIB_PATH "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.so") + set(DISCORD_SHARED_LIB "${DISCORD_SDK_LIB_DIR}/libdiscord_partner_sdk.so") +endif() + +# Link the Social SDK library +target_link_libraries(DiscordSDKExample PRIVATE ${DISCORD_LIB_PATH}) + +# Set the runtime search path (RPATH) for Linux and macOS +if(UNIX) + # Use RPATH when building + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + # Set the RPATH to use the lib directory relative to the executable + set(CMAKE_INSTALL_RPATH "$ORIGIN") + if(APPLE) + set(CMAKE_INSTALL_RPATH "@executable_path") + endif() +endif() + +# Copy Social SDK shared library to output directory, so it's available at runtime. +add_custom_command(TARGET DiscordSDKExample POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${DISCORD_SHARED_LIB}" + $<TARGET_FILE_DIR:DiscordSDKExample> +) +``` + +Create a main.cpp file: + +```bash +touch main.cpp +``` + +Let's add the following code to `main.cpp`: + +```cpp +#define DISCORDPP_IMPLEMENTATION +#include "discordpp.h" +#include +#include +#include +#include +#include +#include + +// Replace with your Discord Application ID +const uint64_t APPLICATION_ID = 123456789012345678; + +// Create a flag to stop the application +std::atomic running = true; + +// Signal handler to stop the application +void signalHandler(int signum) { + running.store(false); +} + +int main() { + std::signal(SIGINT, signalHandler); + std::cout << "🚀 Initializing Discord SDK...\n"; + + // Create our Discord Client + auto client = std::make_shared(); + + // Keep application running to allow SDK to receive events and callbacks + while (running) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + return 0; +} +``` + +Compile and run the project: + +```bash +mkdir build && cd build +cmake .. +cmake --build . +./DiscordSDKExample +``` + +Although we've not done much yet other than create an instance of the client, you should see the following output printed to the console: +``` +🚀 Initializing Discord SDK... +``` + +### Troubleshooting + + +--- + +## Step 6: Setting Up SDK Event Handling + +Let's add some event handlers to monitor what's happening with our Discord connection. We'll set up two important callbacks: + +1. A logging callback to see what the SDK is doing +2. A status callback to know when we can start using Discord features + +### Adding Logging Support +First, let's add logging so we can see what's happening: + +```cpp +client->AddLogCallback([](auto message, auto severity) { + std::cout << "[" << EnumToString(severity) << "] " << message << std::endl; +}, discordpp::LoggingSeverity::Info); +``` + +### Monitoring Connection Status +Next, let's add a status callback that tells us when we're ready to use Discord features: + +```cpp +client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) { + std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl; + + if (status == discordpp::Client::Status::Ready) { + std::cout << "✅ Client is ready! You can now call SDK functions.\n"; + } else if (error != discordpp::Client::Error::None) { + std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl; + } +}); +``` + + + +```cpp +#define DISCORDPP_IMPLEMENTATION +#include "discordpp.h" +#include +#include +#include +#include +#include +#include + +// Replace with your Discord Application ID +const uint64_t APPLICATION_ID = 123456789012345678; + +// Create a flag to stop the application +std::atomic running = true; + +// Signal handler to stop the application +void signalHandler(int signum) { + running.store(false); +} + +int main() { + std::signal(SIGINT, signalHandler); + std::cout << "🚀 Initializing Discord SDK...\n"; + + // Create Discord Client + auto client = std::make_shared(); + + // Set up logging callback + client->AddLogCallback([](auto message, auto severity) { + std::cout << "[" << EnumToString(severity) << "] " << message << std::endl; + }, discordpp::LoggingSeverity::Info); + + // Set up status callback to monitor client connection + client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) { + std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl; + + if (status == discordpp::Client::Status::Ready) { + std::cout << "✅ Client is ready! You can now call SDK functions.\n"; + } else if (error != discordpp::Client::Error::None) { + std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl; + } + }); + + // Keep application running to allow SDK to receive events and callbacks + while (running) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + return 0; +} +``` + + + +Most Discord features won't work until the status is [`Client::Status::Ready`]. The status callback lets you know exactly when you can start using them. + + +### What These Callbacks Do + +- The **logging callback** shows you what's happening behind the scenes +- The **status callback** tells you when you're connected and ready to use Discord features + + +At this point, these callbacks **won't get called** since the client setup is not yet complete. However, very soon we will be using them to view debug information and see what our connection status is! + + +To get to a [`Client::Status::Ready`] state, we need to authenticate with Discord. We'll do that shortly. + +--- + +## Step 7: Run Callbacks + +Once you've registered callbacks with the SDK, you'll need to execute them in the event loop of your program. Add something like this to your game's main event loop or tick function. + +Let's add [`RunCallbacks`] to our main loop: + +```cpp + // Keep application running to allow SDK to receive events and callbacks + while (running) { + discordpp::RunCallbacks(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +``` + +--- + +## Step 8: Account Linking with Discord + +In this step, we'll implement OAuth2 authentication to support account-linking with Discord. This process will: +1. Open the Discord app or a browser window for Discord login +2. Get an authorization code +3. Exchange it for an access token +4. Connect to Discord + +### Add the Authentication Code + +Add this code to your `main.cpp` after setting up the status callback: + +```cpp +// Generate OAuth2 code verifier for authentication +auto codeVerifier = client->CreateAuthorizationCodeVerifier(); + +// Set up authentication arguments +discordpp::AuthorizationArgs args{}; +args.SetClientId(APPLICATION_ID); +args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); +args.SetCodeChallenge(codeVerifier.Challenge()); + +// Begin authentication process +client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) { + if (!result.Successful()) { + std::cerr << "❌ Authentication Error: " << result.Error() << std::endl; + return; + } else { + std::cout << "✅ Authorization successful! Getting access token...\n"; + + // Exchange auth code for access token + client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, + [client](discordpp::ClientResult result, + std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + std::string scope) { + std::cout << "🔓 Access token received! Establishing connection...\n"; + // Next Step: Update the token and connect + }); + } +}); +``` + +### What's Happening Here? + +1. We create a code verifier for OAuth2 PKCE security +2. Set up authorization arguments with your app ID and required scopes +3. Start the auth flow with [`Client::Authorize`], which opens a browser +4. When authorized, we exchange the code for an access token + + +Never log or store access tokens insecurely! They should be treated as sensitive credentials. + + +### Testing It Out + +1. Build and run your program +2. A browser window should open asking you to authorize your app +3. After authorizing, watch the console for the "🔓 Access token received!" message + +If you run into any issues: +- Double check your APPLICATION_ID is correct +- Ensure you've added the redirect URL in your Discord Developer Portal +- Check the console for specific error messages + + +```cpp +#define DISCORDPP_IMPLEMENTATION +#include "discordpp.h" +#include +#include +#include +#include +#include +#include + +// Replace with your Discord Application ID +const uint64_t APPLICATION_ID = 123456789012345678; + +// Create a flag to stop the application +std::atomic running = true; + +// Signal handler to stop the application +void signalHandler(int signum) { + running.store(false); +} + +int main() { + std::signal(SIGINT, signalHandler); + std::cout << "🚀 Initializing Discord SDK...\n"; + + // Create our Discord Client + auto client = std::make_shared(); + + // Set up logging callback + client->AddLogCallback([](auto message, auto severity) { + std::cout << "[" << EnumToString(severity) << "] " << message << std::endl; + }, discordpp::LoggingSeverity::Info); + + // Set up status callback to monitor client connection + client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) { + std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl; + + if (status == discordpp::Client::Status::Ready) { + std::cout << "✅ Client is ready! You can now call SDK functions.\n"; + } else if (error != discordpp::Client::Error::None) { + std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl; + } + }); + + // Generate OAuth2 code verifier for authentication + auto codeVerifier = client->CreateAuthorizationCodeVerifier(); + + // Set up authentication arguments + discordpp::AuthorizationArgs args{}; + args.SetClientId(APPLICATION_ID); + args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + args.SetCodeChallenge(codeVerifier.Challenge()); + + // Begin authentication process + client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) { + if (!result.Successful()) { + std::cerr << "❌ Authentication Error: " << result.Error() << std::endl; + return; + } else { + std::cout << "✅ Authorization successful! Getting access token...\n"; + + // Exchange auth code for access token + client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, + [client](discordpp::ClientResult result, + std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + std::string scope) { + std::cout << "🔓 Access token received! Establishing connection...\n"; + // Next Step: Update the token and connect + }); + } + }); + + // Keep application running to allow SDK to receive events and callbacks + while (running) { + discordpp::RunCallbacks(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + return 0; +} +``` + + +--- + +## Step 9: Connect the SDK to Discord +Now that we have our access token, let's connect to Discord! This involves two steps: +1. Updating the SDK with our access token with [`Client::UpdateToken`]. +2. Establishing the connection with [`Client::Connect`]. + +Find the comment ``// Next Step: Update the token and connect`` within `client->UpdateToken()`, and add the +following code after: + +```cpp +// Next Step: Update the token and connect +client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🔑 Token updated, connecting to Discord...\n"; + client->Connect(); + } +}); +``` + +### What's Happening Here? + +1. `client->UpdateToken()` tells the SDK to use our access token for Discord API calls +2. Once the token is updated, we call `client->Connect()` in the callback +3. The SDK will begin connecting asynchronously +4. Our status callback (from Step 6) will tell us when we're ready + + +Watch your console output! You should see status updates as the connection is established. + + +### Testing the Connection + +1. Run your program +2. Watch for these status messages in order: + - "🔑 Token updated, connecting to Discord..." + - "🔄 Status changed: Connecting" (also "Connected" and "Ready") + - "✅ Client is ready! You can now call SDK functions." + +### Troubleshooting + +If you don't see "Ready" status: +- Check that your access token is valid +- Ensure you have internet connectivity +- Look for error messages in the status callback +- Verify your `APPLICATION_ID` is correct + +Now that your client is in a ready state, we can start implementing Discord social features. + + +```cpp +#define DISCORDPP_IMPLEMENTATION +#include "discordpp.h" +#include +#include +#include +#include +#include +#include + +// Replace with your Discord Application ID +const uint64_t APPLICATION_ID = 1349146942634065960; + +// Create a flag to stop the application +std::atomic running = true; + +// Signal handler to stop the application +void signalHandler(int signum) { + running.store(false); +} + +int main() { + std::signal(SIGINT, signalHandler); + std::cout << "🚀 Initializing Discord SDK...\n"; + + // Create our Discord Client + auto client = std::make_shared(); + + // Set up logging callback + client->AddLogCallback([](auto message, auto severity) { + std::cout << "[" << EnumToString(severity) << "] " << message << std::endl; + }, discordpp::LoggingSeverity::Info); + + // Set up status callback to monitor client connection + client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) { + std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl; + + if (status == discordpp::Client::Status::Ready) { + std::cout << "✅ Client is ready! You can now call SDK functions.\n"; + } else if (error != discordpp::Client::Error::None) { + std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl; + } + }); + + // Generate OAuth2 code verifier for authentication + auto codeVerifier = client->CreateAuthorizationCodeVerifier(); + + // Set up authentication arguments + discordpp::AuthorizationArgs args{}; + args.SetClientId(APPLICATION_ID); + args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + args.SetCodeChallenge(codeVerifier.Challenge()); + + // Begin authentication process + client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) { + if (!result.Successful()) { + std::cerr << "❌ Authentication Error: " << result.Error() << std::endl; + return; + } else { + std::cout << "✅ Authorization successful! Getting access token...\n"; + + // Exchange auth code for access token + client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, + [client](discordpp::ClientResult result, + std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + std::string scope) { + std::cout << "🔓 Access token received! Establishing connection...\n"; + // Next Step: Update the token and connect + client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🔑 Token updated, connecting to Discord...\n"; + client->Connect(); + } + }); + }); + } + }); + + // Keep application running to allow SDK to receive events and callbacks + while (running) { + discordpp::RunCallbacks(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + return 0; +} +``` + + +--- + +## Step 10: Access Discord Relationships + +Let's access the user's Discord relationships (friends list) and display the count. This will help you understand how to access and use Discord data in your game. + +Within `client->SetStatusChangedCallback()`, add the following after `status == discordpp::Client::Status::Ready` +code to view how many friends you have in Discord: + +```cpp +// Access initial relationships data +std::cout << "👥 Friends Count: " << client->GetRelationships().size() << std::endl; +``` + +### What This Code Does +2. [`Client::GetRelationships`] gives you immediate access to the current friend list + +### Example Output + +``` +👥 Friends Count: 42 +``` + +### Testing It Out + +1. Run your program +2. Wait for the initial friend count + + +This relationship data forms the foundation for features like friend lists, activity feeds, and multiplayer invites! + + +### Troubleshooting + +If you're not seeing relationship data: +- Verify your OAuth2 scopes include relationships access +- Ensure you're connected (Status::Ready) +- Check that you have friends on Discord +- Look for errors in the logging callback + +Next, we'll learn how to show your game's activity on Discord with Rich Presence! + + +```cpp +#define DISCORDPP_IMPLEMENTATION +#include "discordpp.h" +#include +#include +#include +#include +#include +#include + +// Replace with your Discord Application ID +const uint64_t APPLICATION_ID = 1349146942634065960; + +// Create a flag to stop the application +std::atomic running = true; + +// Signal handler to stop the application +void signalHandler(int signum) { + running.store(false); +} + +int main() { + std::signal(SIGINT, signalHandler); + std::cout << "🚀 Initializing Discord SDK...\n"; + + // Create our Discord Client + auto client = std::make_shared(); + + // Set up logging callback + client->AddLogCallback([](auto message, auto severity) { + std::cout << "[" << EnumToString(severity) << "] " << message << std::endl; + }, discordpp::LoggingSeverity::Info); + + // Set up status callback to monitor client connection + client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) { + std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl; + + if (status == discordpp::Client::Status::Ready) { + std::cout << "✅ Client is ready! You can now call SDK functions.\n"; + + // Access initial relationships data + std::cout << "👥 Friends Count: " << client->GetRelationships().size() << std::endl; + + } else if (error != discordpp::Client::Error::None) { + std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl; + } + }); + + // Generate OAuth2 code verifier for authentication + auto codeVerifier = client->CreateAuthorizationCodeVerifier(); + + // Set up authentication arguments + discordpp::AuthorizationArgs args{}; + args.SetClientId(APPLICATION_ID); + args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + args.SetCodeChallenge(codeVerifier.Challenge()); + + // Begin authentication process + client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) { + if (!result.Successful()) { + std::cerr << "❌ Authentication Error: " << result.Error() << std::endl; + return; + } else { + std::cout << "✅ Authorization successful! Getting access token...\n"; + + // Exchange auth code for access token + client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, + [client](discordpp::ClientResult result, + std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + std::string scope) { + std::cout << "🔓 Access token received! Establishing connection...\n"; + // Next Step: Update the token and connect + client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🔑 Token updated, connecting to Discord...\n"; + client->Connect(); + } + }); + }); + } + }); + + // Keep application running to allow SDK to receive events and callbacks + while (running) { + discordpp::RunCallbacks(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + return 0; +} +``` + + +--- + +## Step 11: Set Rich Presence + +Let's show your game's activity on Discord using Rich Presence. This feature lets players see what others are doing in your game directly in their Discord friends list. + +### Add Rich Presence Code + +Right after the line where we called `client->GetRelationships()` let's add the following code to set the your rich +presence: + +```cpp +// Configure rich presence details +discordpp::Activity activity; +activity.SetType(discordpp::ActivityTypes::Playing); +activity.SetState("In Competitive Match"); +activity.SetDetails("Rank: Diamond II"); + +// Update rich presence +client->UpdateRichPresence(activity, [](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🎮 Rich Presence updated successfully!\n"; + } else { + std::cerr << "❌ Rich Presence update failed"; + } +}); +``` + +### What This Code Does +1. Creates an [`Activity`] object to represent what the player is doing +2. Sets basic information like: + - The activity type (Playing) + - Current state ("In Competitive Match") + - Additional details ("Rank: Diamond II") +3. Updates your rich presence on Discord + +### Testing It Out +1. Run your program +2. Watch for the console message "🎮 Rich Presence updated successfully!" +3. Check your Discord profile, you should see: + - "Playing [Your Game]" + - "In Competitive Match" + - "Rank: Diamond II" + +### Troubleshooting + +If you don't see your presence: + +- Ensure you're connected ([`Client::Status::Ready`]) +- Check the callback for error messages +- Verify your activity settings are valid +- Make sure you're not invisible on Discord + + +```cpp +#define DISCORDPP_IMPLEMENTATION +#include "discordpp.h" +#include +#include +#include +#include +#include +#include + +// Replace with your Discord Application ID +const uint64_t APPLICATION_ID = 1349146942634065960; + +// Create a flag to stop the application +std::atomic running = true; + +// Signal handler to stop the application +void signalHandler(int signum) { + running.store(false); +} + +int main() { + std::signal(SIGINT, signalHandler); + std::cout << "🚀 Initializing Discord SDK...\n"; + + // Create our Discord Client + auto client = std::make_shared(); + + // Set up logging callback + client->AddLogCallback([](auto message, auto severity) { + std::cout << "[" << EnumToString(severity) << "] " << message << std::endl; + }, discordpp::LoggingSeverity::Info); + + // Set up status callback to monitor client connection + client->SetStatusChangedCallback([client](discordpp::Client::Status status, discordpp::Client::Error error, int32_t errorDetail) { + std::cout << "🔄 Status changed: " << discordpp::Client::StatusToString(status) << std::endl; + + if (status == discordpp::Client::Status::Ready) { + std::cout << "✅ Client is ready! You can now call SDK functions.\n"; + + // Access initial relationships data + std::cout << "👥 Friends Count: " << client->GetRelationships().size() << std::endl; + + // Configure rich presence details + discordpp::Activity activity; + activity.SetType(discordpp::ActivityTypes::Playing); + activity.SetState("In Competitive Match"); + activity.SetDetails("Rank: Diamond II"); + + // Update rich presence + client->UpdateRichPresence(activity, [](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🎮 Rich Presence updated successfully!\n"; + } else { + std::cerr << "❌ Rich Presence update failed"; + } + }); + + } else if (error != discordpp::Client::Error::None) { + std::cerr << "❌ Connection Error: " << discordpp::Client::ErrorToString(error) << " - Details: " << errorDetail << std::endl; + } + }); + + // Generate OAuth2 code verifier for authentication + auto codeVerifier = client->CreateAuthorizationCodeVerifier(); + + // Set up authentication arguments + discordpp::AuthorizationArgs args{}; + args.SetClientId(APPLICATION_ID); + args.SetScopes(discordpp::Client::GetDefaultPresenceScopes()); + args.SetCodeChallenge(codeVerifier.Challenge()); + + // Begin authentication process + client->Authorize(args, [client, codeVerifier](auto result, auto code, auto redirectUri) { + if (!result.Successful()) { + std::cerr << "❌ Authentication Error: " << result.Error() << std::endl; + return; + } else { + std::cout << "✅ Authorization successful! Getting access token...\n"; + + // Exchange auth code for access token + client->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, + [client](discordpp::ClientResult result, + std::string accessToken, + std::string refreshToken, + discordpp::AuthorizationTokenType tokenType, + int32_t expiresIn, + std::string scope) { + std::cout << "🔓 Access token received! Establishing connection...\n"; + // Next Step: Update the token and connect + client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [client](discordpp::ClientResult result) { + if(result.Successful()) { + std::cout << "🔑 Token updated, connecting to Discord...\n"; + client->Connect(); + } + }); + }); + } + }); + + // Keep application running to allow SDK to receive events and callbacks + while (running) { + discordpp::RunCallbacks(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + return 0; +} +``` + + +--- + +## Conclusion + +Congratulations! You've successfully integrated the Discord Social SDK into your C++ application. Let's review what you've accomplished: + +### What You've Built +- ✅ Created a Discord application and configured OAuth2 +- ✅ Set up SDK logging and status monitoring +- ✅ Implemented user authentication flow +- ✅ Retrieved Discord relationships data +- ✅ Added Rich Presence support + +### Key Concepts Learned +- How to initialize and configure the Discord SDK +- Managing authentication and connections +- Working with Discord's social features +- Handling asynchronous callbacks +- Monitoring SDK status and events + +--- + +## Next Steps + +You have successfully set up the Discord Social SDK with C++ and authenticated with Discord! You can now use the SDK to add more social features in your project. + +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserStatusIcon} from '/snippets/icons/UserStatusIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Create a unified friends list combining Discord and game-specific friendships + + }> + Customize your game's rich presence to show more advanced information and game invites + + }> + Allow players to invite friends to join their game session or party. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682 +[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468 +[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa +[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d +[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4 +[`RunCallbacks`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ab5dd8cf274f581ee1885de5816be3c29 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/getting-started/using-unity.mdx b/discord/developers/docs/discord-social-sdk/getting-started/using-unity.mdx new file mode 100644 index 0000000000..82f05dc6c4 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/getting-started/using-unity.mdx @@ -0,0 +1,922 @@ +--- +title: Getting Started with Unity and the Discord Social SDK +sidebarTitle: Using Unity +description: Integrate the Discord Social SDK into Unity games. +--- +import AppCreationSteps from '/snippets/discord-social-sdk/partials/getting-started.mdx' +import ConsoleAccess from '/snippets/discord-social-sdk/callouts/console-access.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; +import DylibMacError from '/snippets/discord-social-sdk/partials/dylib-mac-error.mdx'; + +## Overview + +This guide will walk you through integrating the Discord Social SDK into a Unity project. By the end, you'll have a project that can: + +- Authenticate users with Discord +- Set up logging and status monitoring +- Start the SDK and establish a connection +- Request the number of Discord friends the player has +- Set the player's rich presence for your game + +### Prerequisites + +Before starting, ensure you have: + +- Unity 2021.3 or later + +Let's walk through the steps in detail. + +--- + + + +--- + +## Step 4: Download the Social SDK for Unity + +1. Click on the `Downloads` link under the Discord Social SDK section of the sidebar. +2. Select the latest version from the version dropdown and download the SDK for Unity. + + +A Unity sample project is available for download on this page, but we are not going to cover it in this guide. Explore it on your own after you finish this guide! + + +--- + +## Step 5: Project Setup + +Let's set up your Unity project to include the Social SDK package and add the necessary objects and scripts to use it. + +1. Create a new 2D project in Unity Hub using Unity version 2021.3 or later +2. Either: + 1. Unzip the zip file in the Unity `Packages` folder, or + 2. Unzip the zip file and [Install Package from Disk](https://docs.unity3d.com/Manual/upm-ui-local.html). Make sure the folder is in a directory that won't get moved or deleted as your Unity project will load it from that location. +3. In your project add a `Scripts` folder and create a `DiscordManager.cs` script +4. Add the following code to `DiscordManager.cs`: +```cs +using UnityEngine; +using UnityEngine.UI; +using Discord.Sdk; +using System.Linq; + +public class DiscordManager : MonoBehaviour +{ + [SerializeField] + private ulong clientId; // Set this in the Unity Inspector from the dev portal + + [SerializeField] + private Button loginButton; + + [SerializeField] + private Text statusText; + + private Client client; + private string codeVerifier; +} +``` + +6. Add an empty object to the scene (**GameObject > Create Empty**) called **DiscordManager** and attach the `DiscordManager.cs` script to it +7. Add a button to the scene **GameObject > UI > Legacy > Button** +8. Add text to the scene **GameObject > UI > Legacy > Text** +9. Position the button and text somewhere visible on the screen +10. Attach the button and text to the **DiscordManager** in the inspector +11. Run it! + +This is all we'll need to get started! You shouldn't see anything happen, but if you run into any issues, check out the troubleshooting section before moving to the next step. + +### Troubleshooting + +- Make sure the Social SDK package was successfully added to Unity + + +--- + +## Step 6: Setting Up SDK Event Handling + +Let's add some event handlers to monitor what's happening with our Discord connection. We'll set up two important callbacks: + +- A logging callback to see what the SDK is doing +- A status callback to know when we can start using Discord features + +We'll start by adding the following code to your `DiscordManager.cs`: + +```cs +void Start() +{ + client = new Client(); + + // Modifying LoggingSeverity will show you more or less logging information + client.AddLogCallback(OnLog, LoggingSeverity.Error); + client.SetStatusChangedCallback(OnStatusChanged); + + // Make sure the button has a listener + if (loginButton != null) + { + //loginButton.onClick.AddListener(StartOAuthFlow); + } + else + { + Debug.LogError("Login button reference is missing, connect it in the inspector!"); + } + + // Set initial status text + if (statusText != null) + { + statusText.text = "Ready to login"; + } + else + { + Debug.LogError("Status text reference is missing, connect it in the inspector!"); + } +} + +private void OnLog(string message, LoggingSeverity severity) +{ + Debug.Log($"Log: {severity} - {message}"); +} + +private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode) +{ + Debug.Log($"Status changed: {status}"); + statusText.text = status.ToString(); + if(error != Client.Error.None) + { + Debug.LogError($"Error: {error}, code: {errorCode}"); + } +} +``` + +This will hook up the API status changes to your text in the game. Then we'll need to get your Client ID from the OAuth2 tab in the developer portal and paste it into the ClientID on the **DiscordManager** in the inspector. + +### What These Callbacks Do + +- The **logging callback** shows you what's happening behind the scenes and is a powerful tool for debugging +- The **status callback** tells you when you're connected and ready to use Discord features + + +The Unity plugin handles running the SDK callbacks for you in Unity, no need to use [`RunCallbacks`] like we do in the [C++ guide](/developers/docs/discord-social-sdk/getting-started/using-c++). + + + +Most Discord features won't work until the status is `Ready`. The status callback lets you know when you can start using them. + + +To get to a `Ready` state, we need to authenticate with Discord. Let's do that next. + +--- + +## Step 7: Account Linking with Discord + +In this step, we'll implement OAuth2 authentication to support account linking with Discord. This process will: + +1. Open the Discord app or a browser window for Discord login +2. Get an authorization code +3. Exchange it for an access token +4. Connect to Discord + +### Add the Authentication Code + +To start we'll add this code to your `DiscordManager.cs`: +```cs +private void StartOAuthFlow() { + var authorizationVerifier = client.CreateAuthorizationCodeVerifier(); + codeVerifier = authorizationVerifier.Verifier(); + + var args = new AuthorizationArgs(); + args.SetClientId(clientId); + args.SetScopes(Client.GetDefaultPresenceScopes()); + args.SetCodeChallenge(authorizationVerifier.Challenge()); + client.Authorize(args, OnAuthorizeResult); +} + +private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) { + Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]"); + if (!result.Successful()) { + return; + } + GetTokenFromCode(code, redirectUri); +} + +private void GetTokenFromCode(string code, string redirectUri) { + client.GetToken(clientId, + code, + codeVerifier, + redirectUri, + (result, token, refreshToken, tokenType, expiresIn, scope) => {}); +} +``` + +and then we'll uncomment `loginButton.onClick.AddListener(StartOAuthFlow);` in your `Start()` method + +```cs +if (loginButton != null) +{ + loginButton.onClick.AddListener(StartOAuthFlow); +} +``` + +### What's Happening Here? + +1. We create a code verifier for OAuth2 PKCE security +2. Set up authorization arguments with your app ID and required scopes +3. Start the auth flow with [`Client::Authorize`], which opens a browser +4. When authorized, we exchange the code for an access token + + +Never log or store access tokens insecurely! They should be treated as sensitive credentials. + + +### Testing It Out + +Now, if you press play and click the button, it should start the OAuth flow! You'll be redirected to your browser to log in and authorize the game. There will be some logging to the console, but the status won't change yet. + +### Troubleshooting + +- Make sure you've uncommented `loginButton.onClick.AddListener(StartOAuthFlow);` if the button doesn't seem to do anything +- Double check your `ClientId` is correct +- Ensure you've added the redirect URL in your Discord Developer Portal +- Check the console for specific error messages + + +```cs +using UnityEngine; +using UnityEngine.UI; +using Discord.Sdk; +using System.Linq; + +public class DiscordManager : MonoBehaviour +{ + [SerializeField] + private ulong clientId; // Set this in the Unity Inspector from the dev portal + + [SerializeField] + private Button loginButton; + + [SerializeField] + private Text statusText; + + private Client client; + private string codeVerifier; + + void Start() + { + client = new Client(); + + // Modifying LoggingSeverity will show you more or less logging information + client.AddLogCallback(OnLog, LoggingSeverity.Error); + client.SetStatusChangedCallback(OnStatusChanged); + + // Make sure the button has a listener + if (loginButton != null) + { + loginButton.onClick.AddListener(StartOAuthFlow); + } + else + { + Debug.LogError("Login button reference is missing, connect it in the inspector!"); + } + + // Set initial status text + if (statusText != null) + { + statusText.text = "Ready to login"; + } + else + { + Debug.LogError("Status text reference is missing, connect it in the inspector!"); + } + } + + private void OnLog(string message, LoggingSeverity severity) + { + Debug.Log($"Log: {severity} - {message}"); + } + + private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode) + { + Debug.Log($"Status changed: {status}"); + statusText.text = status.ToString(); + if(error != Client.Error.None) + { + Debug.LogError($"Error: {error}, code: {errorCode}"); + } + } + + private void StartOAuthFlow() { + var authorizationVerifier = client.CreateAuthorizationCodeVerifier(); + codeVerifier = authorizationVerifier.Verifier(); + + var args = new AuthorizationArgs(); + args.SetClientId(clientId); + args.SetScopes(Client.GetDefaultPresenceScopes()); + args.SetCodeChallenge(authorizationVerifier.Challenge()); + client.Authorize(args, OnAuthorizeResult); + } + + private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) { + Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]"); + if (!result.Successful()) { + return; + } + GetTokenFromCode(code, redirectUri); + } + + private void GetTokenFromCode(string code, string redirectUri) { + client.GetToken(clientId, + code, + codeVerifier, + redirectUri, + (result, token, refreshToken, tokenType, expiresIn, scope) => {}); + } +} +``` + + +--- + +## Step 8: Connect the SDK to Discord + +Now that we have our access token, let's connect to Discord! + +To start, we'll add this code to your `DiscordManager.cs`: + +```cs +private void OnReceivedToken(string token) { + Debug.Log("Token received: " + token); + client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); }); +} + +private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; } +``` + +Then we'll update `GetTokenFromCode` to call these functions when it completes: + +```cs +private void GetTokenFromCode(string code, string redirectUri) { + client.GetToken(clientId, + code, + codeVerifier, + redirectUri, + (result, token, refreshToken, tokenType, expiresIn, scope) => { + if (token != "") { + OnReceivedToken(token); + } else { + OnRetrieveTokenFailed(); + } + }); +} +``` + +### What's Happening Here? + +1. [`Client::UpdateToken`] tells the SDK to use our access token for Discord API calls +2. Once the token is updated, we call [`Client::Connect`] in the callback +3. The SDK will begin connecting asynchronously +4. Our status text will tell you when the SDK is ready! + +### Testing the Connection + +Press play and click the button again to log in and authorize the game. This time you should see the status text change as it goes through the OAuth flow, and it should end up `Ready` + +### Troubleshooting + +If you don't see `Ready` status: + +- Check that your access token is valid +- Ensure you have internet connectivity +- Look for error messages from the SDK in the console +- Verify your `ClientID` set in the inspector is correct + + + +```cs +using UnityEngine; +using UnityEngine.UI; +using Discord.Sdk; +using System.Linq; + +public class DiscordManager : MonoBehaviour +{ + [SerializeField] + private ulong clientId; // Set this in the Unity Inspector from the dev portal + + [SerializeField] + private Button loginButton; + + [SerializeField] + private Text statusText; + + private Client client; + private string codeVerifier; + + void Start() + { + client = new Client(); + + // Modifying LoggingSeverity will show you more or less logging information + client.AddLogCallback(OnLog, LoggingSeverity.Error); + client.SetStatusChangedCallback(OnStatusChanged); + + // Make sure the button has a listener + if (loginButton != null) + { + loginButton.onClick.AddListener(StartOAuthFlow); + } + else + { + Debug.LogError("Login button reference is missing, connect it in the inspector!"); + } + + // Set initial status text + if (statusText != null) + { + statusText.text = "Ready to login"; + } + else + { + Debug.LogError("Status text reference is missing, connect it in the inspector!"); + } + } + + private void OnLog(string message, LoggingSeverity severity) + { + Debug.Log($"Log: {severity} - {message}"); + } + + private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode) + { + Debug.Log($"Status changed: {status}"); + statusText.text = status.ToString(); + if(error != Client.Error.None) + { + Debug.LogError($"Error: {error}, code: {errorCode}"); + } + } + + private void StartOAuthFlow() { + var authorizationVerifier = client.CreateAuthorizationCodeVerifier(); + codeVerifier = authorizationVerifier.Verifier(); + + var args = new AuthorizationArgs(); + args.SetClientId(clientId); + args.SetScopes(Client.GetDefaultPresenceScopes()); + args.SetCodeChallenge(authorizationVerifier.Challenge()); + client.Authorize(args, OnAuthorizeResult); + } + + private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) { + Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]"); + if (!result.Successful()) { + return; + } + GetTokenFromCode(code, redirectUri); + } + + private void GetTokenFromCode(string code, string redirectUri) { + client.GetToken(clientId, + code, + codeVerifier, + redirectUri, + (result, token, refreshToken, tokenType, expiresIn, scope) => { + if (token != "") { + OnReceivedToken(token); + } else { + OnRetrieveTokenFailed(); + } + }); + } + + private void OnReceivedToken(string token) { + Debug.Log("Token received: " + token); + client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); }); + } + + private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; } +} +``` + + +Now that your client is in a ready state, we can start implementing Discord social features. + +--- + +## Step 9: Access Discord Relationships + +Let's access the user's Discord relationships (friends list) and display the count. This will help you understand how to access and use Discord data in your game. + +Lets add some new code to your `DiscordManager.cs`: + +```cs +private void ClientReady() +{ + Debug.Log($"Friend Count: {client.GetRelationships().Count()}"); +} +``` + +We want to call this when the client is ready to use which we'll know in our `OnStatusChanged` method: + +```cs +private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode) +{ + Debug.Log($"Status changed: {status}"); + statusText.text = status.ToString(); + if(error != Client.Error.None) + { + Debug.LogError($"Error: {error}, code: {errorCode}"); + } + + if (status == Client.Status.Ready) + { + ClientReady(); + } +} +``` + +### What This Code Does + +When the client status is `Ready,` it'll call our `ClientReady` function, which will call [`Client::GetRelationships`], returning us a list of all the player's friends. We then log the number of friends directly to the console. + +### Testing It Out + +Hit play and then click the button. Once the OAuth flow completes and you see the status hit `Ready` check the console to see the output! + +### Example Output + +``` +Friend Count: 42 +``` + +### Troubleshooting + +- Verify your OAuth2 scopes include relationships access +- Ensure you're connected (status is `Ready`) +- Check that you have friends on Discord +- Look for errors in the logging callback + +Next, we'll learn how to show your game's activity on Discord with Rich Presence! + + +```cs +using UnityEngine; +using UnityEngine.UI; +using Discord.Sdk; +using System.Linq; + +public class DiscordManager : MonoBehaviour +{ + [SerializeField] + private ulong clientId; // Set this in the Unity Inspector from the dev portal + + [SerializeField] + private Button loginButton; + + [SerializeField] + private Text statusText; + + private Client client; + private string codeVerifier; + + void Start() + { + client = new Client(); + + // Modifying LoggingSeverity will show you more or less logging information + client.AddLogCallback(OnLog, LoggingSeverity.Error); + client.SetStatusChangedCallback(OnStatusChanged); + + // Make sure the button has a listener + if (loginButton != null) + { + loginButton.onClick.AddListener(StartOAuthFlow); + } + else + { + Debug.LogError("Login button reference is missing, connect it in the inspector!"); + } + + // Set initial status text + if (statusText != null) + { + statusText.text = "Ready to login"; + } + else + { + Debug.LogError("Status text reference is missing, connect it in the inspector!"); + } + } + + private void OnLog(string message, LoggingSeverity severity) + { + Debug.Log($"Log: {severity} - {message}"); + } + + private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode) + { + Debug.Log($"Status changed: {status}"); + statusText.text = status.ToString(); + if(error != Client.Error.None) + { + Debug.LogError($"Error: {error}, code: {errorCode}"); + } + + if (status == Client.Status.Ready) + { + ClientReady(); + } + } + + private void ClientReady() + { + Debug.Log($"Friend Count: {client.GetRelationships().Count()}"); + } + + + private void StartOAuthFlow() { + var authorizationVerifier = client.CreateAuthorizationCodeVerifier(); + codeVerifier = authorizationVerifier.Verifier(); + + var args = new AuthorizationArgs(); + args.SetClientId(clientId); + args.SetScopes(Client.GetDefaultPresenceScopes()); + args.SetCodeChallenge(authorizationVerifier.Challenge()); + client.Authorize(args, OnAuthorizeResult); + } + + private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) { + Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]"); + if (!result.Successful()) { + return; + } + GetTokenFromCode(code, redirectUri); + } + + private void GetTokenFromCode(string code, string redirectUri) { + client.GetToken(clientId, + code, + codeVerifier, + redirectUri, + (result, token, refreshToken, tokenType, expiresIn, scope) => { + if (token != "") { + OnReceivedToken(token); + } else { + OnRetrieveTokenFailed(); + } + }); + } + + private void OnReceivedToken(string token) { + Debug.Log("Token received: " + token); + client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); }); + } + + private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; } +} +``` + + +--- + +## Step 10: Set Rich Presence + +Let's show your game's activity on Discord using Rich Presence. This feature lets players see what others are doing in your game directly in their Discord friends list. + +Update your `ClientReady` method with this code: + +```cs +private void ClientReady() +{ + Debug.Log($"Friend Count: {client.GetRelationships().Count()}"); + + Activity activity = new Activity(); + activity.SetType(ActivityTypes.Playing); + activity.SetState("In Competitive Match"); + activity.SetDetails("Rank: Diamond II"); + client.UpdateRichPresence(activity, (ClientResult result) => { + if (result.Successful()) { + Debug.Log("Rich presence updated!"); + } else { + Debug.LogError("Failed to update rich presence"); + } + }); +} +``` + +### What This Code Does + +1. Creates an [`Activity`] object to represent what the player is doing +2. Sets basic information like: + - The activity type (Playing) + - Current state ("In Competitive Match") + - Additional details ("Rank: Diamond II") +3. Updates your rich presence on Discord + +### Testing It Out + +Hit play and then click the button. Once the OAuth flow is complete, you will see the status hit `Ready`. The console will tell you if setting rich presence was successful, and you can check your Discord profile to see it! + +### Troubleshooting + +If you don't see your presence: +- Ensure you're connected (status is `Ready`) +- Check the console for error messages +- Verify your activity settings are valid +- Make sure you're not invisible on Discord + +--- + + +```cs +using UnityEngine; +using UnityEngine.UI; +using Discord.Sdk; +using System.Linq; + +public class DiscordManager : MonoBehaviour +{ + [SerializeField] + private ulong clientId; // Set this in the Unity Inspector from the dev portal + + [SerializeField] + private Button loginButton; + + [SerializeField] + private Text statusText; + + private Client client; + private string codeVerifier; + + void Start() + { + client = new Client(); + + // Modifying LoggingSeverity will show you more or less logging information + client.AddLogCallback(OnLog, LoggingSeverity.Error); + client.SetStatusChangedCallback(OnStatusChanged); + + // Make sure the button has a listener + if (loginButton != null) + { + loginButton.onClick.AddListener(StartOAuthFlow); + } + else + { + Debug.LogError("Login button reference is missing, connect it in the inspector!"); + } + + // Set initial status text + if (statusText != null) + { + statusText.text = "Ready to login"; + } + else + { + Debug.LogError("Status text reference is missing, connect it in the inspector!"); + } + } + + private void OnLog(string message, LoggingSeverity severity) + { + Debug.Log($"Log: {severity} - {message}"); + } + + private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode) + { + Debug.Log($"Status changed: {status}"); + statusText.text = status.ToString(); + if(error != Client.Error.None) + { + Debug.LogError($"Error: {error}, code: {errorCode}"); + } + + if (status == Client.Status.Ready) + { + ClientReady(); + } + } + + private void ClientReady() + { + Debug.Log($"Friend Count: {client.GetRelationships().Count()}"); + + Activity activity = new Activity(); + activity.SetType(ActivityTypes.Playing); + activity.SetState("In Competitive Match"); + activity.SetDetails("Rank: Diamond II"); + client.UpdateRichPresence(activity, (ClientResult result) => { + if (result.Successful()) { + Debug.Log("Rich presence updated!"); + } else { + Debug.LogError("Failed to update rich presence"); + } + }); + } + + private void StartOAuthFlow() { + var authorizationVerifier = client.CreateAuthorizationCodeVerifier(); + codeVerifier = authorizationVerifier.Verifier(); + + var args = new AuthorizationArgs(); + args.SetClientId(clientId); + args.SetScopes(Client.GetDefaultPresenceScopes()); + args.SetCodeChallenge(authorizationVerifier.Challenge()); + client.Authorize(args, OnAuthorizeResult); + } + + private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) { + Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]"); + if (!result.Successful()) { + return; + } + GetTokenFromCode(code, redirectUri); + } + + private void GetTokenFromCode(string code, string redirectUri) { + client.GetToken(clientId, + code, + codeVerifier, + redirectUri, + (result, token, refreshToken, tokenType, expiresIn, scope) => { + if (token != "") { + OnReceivedToken(token); + } else { + OnRetrieveTokenFailed(); + } + }); + } + + private void OnReceivedToken(string token) { + Debug.Log("Token received: " + token); + client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); }); + } + + private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; } +} +``` + + +--- + +## Conclusion + +Congratulations! You've successfully integrated the Discord Social SDK into Unity. Let's review what you've accomplished: + +### What You've Built + +- ✅ Created a Discord application and configured OAuth2 +- ✅ Set up SDK logging and status monitoring +- ✅ Implemented user authentication flow +- ✅ Retrieved Discord relationships data +- ✅ Added Rich Presence support + +### Key Concepts Learned + +- How to initialize and configure the Discord SDK +- Managing authentication and connections +- Working with Discord's social features +- Handling asynchronous callbacks +- Monitoring SDK status and events + +--- + +## Next Steps + +### Social SDK Unity sample + +Check out our [in depth sample for using the Social SDK in Unity](https://github.com/discord/social-sdk-unity-sample) following the best practices laid out in these guides. It contains easy to drop in prefabs with both code and UI to quickly integrate Discord's social features into your game. + +![Video showing off the Unity sample with the Discord Social SDK](/images/social-sdk/getting-started/unity-sample-preview.webp) + + +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserStatusIcon} from '/snippets/icons/UserStatusIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Create a unified friends list combining Discord and game-specific friendships + + }> + Customize your game's rich presence to show more advanced information and game invites + + }> + Allow players to invite friends to join their game session or party. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | Initial release | + +{/* Autogenerated Reference Links */} +[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682 +[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468 +[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa +[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d +[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4 +[`RunCallbacks`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ab5dd8cf274f581ee1885de5816be3c29 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/getting-started/using-unreal-engine.mdx b/discord/developers/docs/discord-social-sdk/getting-started/using-unreal-engine.mdx new file mode 100644 index 0000000000..16328681a7 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/getting-started/using-unreal-engine.mdx @@ -0,0 +1,722 @@ +--- +title: Getting Started with Unreal Engine and the Discord Social SDK +sidebarTitle: Using Unreal Engine +description: Integrate the Discord Social SDK into Unreal Engine games. +--- +import AppCreationSteps from '/snippets/discord-social-sdk/partials/getting-started.mdx' +import ConsoleAccess from '/snippets/discord-social-sdk/callouts/console-access.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; +import DylibMacError from '/snippets/discord-social-sdk/partials/dylib-mac-error.mdx'; + +## Overview + +This guide will walk you through integrating the Discord Social SDK into an Unreal Engine project. By the end, you'll have a project that can: + +- Authenticate users with Discord +- Set up logging and status monitoring +- Start the SDK and establish a connection +- Request the number of Discord friends the player has + + +For the initial launch, the Unreal plugin has a name of `DiscordPartnerSDK` but this name will be changing to `DiscordSocialSDK` in a future version + + +### Prerequisites + +We are going to make a simple Unreal console application for this guide. Make sure you have the following prerequisites: + +- Windows +- Visual Studio 2022 +- Unreal Engine 5.5 + +Let's walk through the steps in detail. + +--- + + + +--- + +## Step 4: Download the Social SDK for Unreal Engine + +1. Click on the `Downloads` link under the Discord Social SDK section of the sidebar. +2. Select the latest version from the version dropdown and download the SDK for Unreal Engine. + + +A Unreal Engine sample project is available for download on this page, but we are not going to cover it in this guide. Explore it on your own after you finish this guide! + + +--- + +## Step 5: Project Setup + +### Create a new Unreal Engine project + +Select Third Person Unreal project (C++ language). We'll set the project name to `DiscordSocialUnreal`. + + +The code samples below assume the project name is `DiscordSocialUnreal`. If you use a different name, make sure to replace it in the code. + + +Wait for the shaders to compile. This can take a while. + +### Add the Discord SDK to the project + +Locate your Unreal project in the Windows file explorer. + +Create a new directory named `Plugins` in the base of your project directory (`DiscordSocialUnreal/Plugins`). + +Extract the Discord SDK archive in the Plugins directory. You should end up with a directory called `DiscordPartnerSDK` inside of Plugins `DiscordSocialUnreal/Plugins/DiscordPartnerSDK`. + + +Double check the name of the `DiscordPartnerSDK` folder once you've extracted it. It may change between versions and you'll need to add the correct name in the code below. + + +Reload Unreal Editor and open the project. It should ask if you'd like to recompile the `DiscordPartnerSDK` plugin on launch. + +Open Visual Studio from the `Tools -> Open Visual Studio` menu in Unreal Editor. + +Open your `DiscordSocialUnreal.Build.cs` file located at `/Source/DiscordSocialUnreal/` and add the following line after `PublicDependencyModuleNames` array: + +```cpp +PrivateDependencyModuleNames.AddRange(new string[] { "DiscordPartnerSDK" }); +``` + +Back in Unreal Editor, refresh the Visual Studio Project from the `Tools -> Refresh Visual Studio Project` menu. + +In the Unreal Editor, compile the code with `Ctrl-Alt-F11`. You should see a "Live coding succeeded" modal window if the compilation is successful. + +--- + +## Step 6: Initialize the SDK + +In this section, we'll add callbacks for logging and status of the SDK and OAuth2 authentication to support account linking with Discord. This process will: + +1. Open the Discord app or a browser window for Discord login +2. Get an authorization code +3. Exchange it for an access token +4. Connect to Discord + +Open `DiscordSocialUnrealCharacter.h` in Visual Studio, where we start integrating the Social SDK! + +### DiscordLocalPlayerSubsystem + +To use the Social SDK with Unreal Engine, we will need to include the `DiscordLocalPlayerSubsystem` which is responsible for managing the lifecycle of the client and executing the [`RunCallbacks`] event loop. + +Add the following code to `DiscordSocialUnrealCharacter.h`: + +```cpp +#include "DiscordLocalPlayerSubsystem.h" +``` + +Now let's add the following functions to the public section of the DiscordSocialUnrealCharacter class: + +##### **DiscordSocialUnrealCharacter.h** +```cpp +UFUNCTION(Exec) +void DiscordConnect(); + +UFUNCTION() +void OnStatusChanged(EDiscordClientStatus Status, EDiscordClientError Error, int32 ErrorDetail); + +UPROPERTY() +UDiscordAuthorizationCodeVerifier* CodeVerifier; + +UPROPERTY() +UDiscordLocalPlayerSubsystem* Discord; +``` + +And add the following functions to the protected section of the DiscordSocialUnrealCharacter class: + +##### **DiscordSocialUnrealCharacter.h** + +```cpp +void BeginPlay(); +void OnLogMessage(FString Message, EDiscordLoggingSeverity Severity); +void OnAuthorizeCompleted(UDiscordClientResult* Result, FString Code, FString RedirectUri); +void OnTokenExchange(UDiscordClientResult* Result, FString AccessToken, FString RefreshToken, EDiscordAuthorizationTokenType TokenType, int32 ExpiresIn, FString Scope); +void OnTokenUpdated(UDiscordClientResult* Result); +``` + +Now let's add the implementation of these functions to the `DiscordSocialUnrealCharacter.cpp` file. + +In the `DiscordSocialUnrealCharacter.cpp` file, add the following code: + +##### **DiscordSocialUnrealCharacter.cpp** + +```cpp +#define APPLICATION_ID 1111111111111111111 + +void ADiscordSocialUnrealCharacter::BeginPlay() { + auto PlayerController = Cast(GetController()); + auto LocalPlayer = PlayerController->GetLocalPlayer(); + Discord = ULocalPlayer::GetSubsystem(LocalPlayer); + auto LogCallback = FDiscordClientLogCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnLogMessage); + FScriptDelegate StatusChanged; + StatusChanged.BindUFunction(this, "OnStatusChanged"); + Discord->Client->AddLogCallback(LogCallback, EDiscordLoggingSeverity::Info); + Discord->OnStatusChanged.Add(StatusChanged); +} + +void ADiscordSocialUnrealCharacter::DiscordConnect() { + CodeVerifier = Discord->Client->CreateAuthorizationCodeVerifier(); + auto AuthArgs = NewObject(); + AuthArgs->Init(); + AuthArgs->SetClientId(APPLICATION_ID); + AuthArgs->SetScopes(UDiscordClient::GetDefaultPresenceScopes()); + AuthArgs->SetCodeChallenge(CodeVerifier->Challenge()); + Discord->Client->Authorize(AuthArgs, FDiscordClientAuthorizationCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnAuthorizeCompleted)); +} + +void ADiscordSocialUnrealCharacter::OnLogMessage(FString Message, EDiscordLoggingSeverity Severity) { + UE_LOG(LogTemplateCharacter, Log, TEXT("[%s] %s"), *UEnum::GetValueAsString(Severity), *Message); +} + +void ADiscordSocialUnrealCharacter::OnStatusChanged(EDiscordClientStatus Status, EDiscordClientError Error, int32 ErrorDetail) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Connection status: %s"), *UEnum::GetValueAsString(Status)); + if (Status == EDiscordClientStatus::Ready) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features.")); + } + else if (Error != EDiscordClientError::None) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Connection error: %s (Detail: %d)"), *UEnum::GetValueAsString(Error), ErrorDetail); + } +} + +void ADiscordSocialUnrealCharacter::OnAuthorizeCompleted(UDiscordClientResult* Result, FString Code, FString RedirectUri) { + if (!Result->Successful()) { + UE_LOG( LogTemplateCharacter, Error, TEXT("Discord authorization failed: %s"), *Result->Error()); + return; + } + + UE_LOG(LogTemplateCharacter, Log, TEXT("Authorization successful! Getting access token...")); + Discord->Client->GetToken(APPLICATION_ID, Code, CodeVerifier->Verifier(), RedirectUri, FDiscordClientTokenExchangeCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnTokenExchange)); +} + +void ADiscordSocialUnrealCharacter::OnTokenExchange(UDiscordClientResult* Result, FString AccessToken, FString RefreshToken, EDiscordAuthorizationTokenType TokenType, int32 ExpiresIn, FString Scope) { + if (!Result->Successful()) { + UE_LOG(LogTemplateCharacter, Error, TEXT("Discord token exchange failed: %s"), *Result->Error()); + } + + Discord->Client->UpdateToken(TokenType, AccessToken, FDiscordClientUpdateTokenCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnTokenUpdated)); +} + +void ADiscordSocialUnrealCharacter::OnTokenUpdated(UDiscordClientResult* Result) { + Discord->Client->Connect(); +} +``` + +### What's Happening Here? + +1. We create a code verifier for OAuth2 PKCE security +2. Set up authorization arguments with your app ID and required scopes +3. Start the auth flow with [`Client::Authorize`], which opens a browser +4. When authorized, we exchange the code for an access token +5. [`Client::UpdateToken`] tells the SDK to use our access token for Discord API calls +6. Once the token is updated, we call [`Client::Connect`], and the SDK will begin connecting asynchronously + +### Testing It Out + +Save your changes in Visual Studio and return to Unreal Editor. + +Compile the code in Unreal Editor with `Ctrl-Alt-F11` + +Enter play mode and find the console at the bottom of Unreal Editor. Run `DiscordConnect` as a command to start the account linking process. + +``` +DiscordConnect +``` + +If Discord is open on your computer, you should see a prompt to authorize the connection. Click "Authorize" to continue. + +If the connection is successful, you should see a message in the Unreal Editor console that says, "Connected to Discord! Ready to go! You can now start using Discord features." + + +Most Discord features won't work until the status is `Ready`. The status callback lets you know when you can start using them. + + +### Troubleshooting + +- Double check your `APPLICATION_ID` is correct +- Ensure you've added the redirect URL in your Discord Developer Portal +- Check the output log for specific error messages +- Check that your access token is valid +- Ensure you have internet connectivity + + + +Now that your client is in a ready state, we can start implementing Discord social features. + +## Step 7: Access Discord Relationships + +Let's access the user's Discord relationships (friends list) and display the count. This will help you understand how to access and use Discord data in your game. + +Add this code to the `Ready` section of `OnStatusChanged`: +```cpp +if (Status == EDiscordClientStatus::Ready) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features.")); + UE_LOG(LogTemplateCharacter, Log, TEXT("👥 Friends Count: %d"), Discord->Client->GetRelationships().Num()); +} +``` + +### What This Code Does + +Now, when the client status is `Ready,` we'll call [`Client::GetRelationships`], returning us a list of all the player's friends. We then log the number of friends directly to the output log. + +### Testing It Out + +Save your changes in Visual Studio and return to Unreal Editor. + +Compile the code in Unreal Editor with `Ctrl-Alt-F11` + +Enter play mode, and find the console at the bottom of Unreal Editor, run `DiscordConnect`. + +### Example Output + +``` +👥 Friends Count: 42 +``` + +### Troubleshooting + +- Verify your OAuth2 scopes include relationships access +- Ensure you're connected (status is `Ready`) +- Check that you have friends on Discord +- Look for errors in the output log + +Next, we'll learn how to show your game's activity on Discord with Rich Presence! + +## Step 8: Set Rich Presence + +Let's show your game's activity on Discord using Rich Presence. This feature lets players see what others are doing in your game directly in their Discord friends list. + +Add this code to the `Ready` section of `OnStatusChanged`: +```cpp +if (Status == EDiscordClientStatus::Ready) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features.")); + UE_LOG(LogTemplateCharacter, Log, TEXT("👥 Friends Count: %d"), Discord->Client->GetRelationships().Num()); + + UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features.")); + UDiscordActivity* Activity = NewObject(); + Activity->Init(); + Activity->SetType(EDiscordActivityTypes::Playing); + Activity->SetState("In competitive match"); + Activity->SetDetails("Rank: Diamond II"); + Discord->Client->UpdateRichPresence(Activity, FDiscordClientUpdateRichPresenceCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnRichPresenceUpdated)); +} +``` + +Then add this new method: +```cpp +void ADiscordSocialUnrealCharacter::OnRichPresenceUpdated(UDiscordClientResult* Result) { + if (Result->Successful()) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Rich Presence updated successfully!")); + } else { + UE_LOG(LogTemplateCharacter, Error, TEXT("Rich Presence update failed")); + } +} +``` + +### What This Code Does + +1. Creates an [`Activity`] object to represent what the player is doing +2. Sets basic information like: + - The activity type (Playing) + - Current state ("In Competitive Match") + - Additional details ("Rank: Diamond II") +3. Updates your rich presence on Discord + +### Testing It Out + +Save your changes in Visual Studio and return to Unreal Editor. + +Compile the code in Unreal Editor with `Ctrl-Alt-F11` + +Enter play mode and find the console at the bottom of Unreal Editor. Run `DiscordConnect.` + +Once the OAuth flow is complete, the output log will tell you if setting rich presence was successful, and you can check your Discord profile to see it! + +### Troubleshooting + +If you don't see your presence: +- Ensure you're connected (status is `Ready`) +- Check the output log for error messages +- Verify your activity settings are valid +- Make sure you're not invisible on Discord + +--- + + +```cs +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class DiscordSocialUnreal : ModuleRules +{ + public DiscordSocialUnreal(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" }); + PrivateDependencyModuleNames.AddRange(new string[] { "DiscordPartnerSDK" }); + } +} +``` + + + +```cpp +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "DiscordSocialUnrealCharacter.h" +#include "Engine/LocalPlayer.h" +#include "Camera/CameraComponent.h" +#include "Components/CapsuleComponent.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "GameFramework/SpringArmComponent.h" +#include "GameFramework/Controller.h" +#include "EnhancedInputComponent.h" +#include "EnhancedInputSubsystems.h" +#include "InputActionValue.h" + +DEFINE_LOG_CATEGORY(LogTemplateCharacter); + +#define APPLICATION_ID 1111111111111111111 + +void ADiscordSocialUnrealCharacter::BeginPlay() { + auto PlayerController = Cast(GetController()); + auto LocalPlayer = PlayerController->GetLocalPlayer(); + Discord = ULocalPlayer::GetSubsystem(LocalPlayer); + auto LogCallback = FDiscordClientLogCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnLogMessage); + FScriptDelegate StatusChanged; + StatusChanged.BindUFunction(this, "OnStatusChanged"); + Discord->Client->AddLogCallback(LogCallback, EDiscordLoggingSeverity::Info); + Discord->OnStatusChanged.Add(StatusChanged); +} + +void ADiscordSocialUnrealCharacter::DiscordConnect() { + CodeVerifier = Discord->Client->CreateAuthorizationCodeVerifier(); + auto AuthArgs = NewObject(); + AuthArgs->Init(); + AuthArgs->SetClientId(APPLICATION_ID); + AuthArgs->SetScopes(UDiscordClient::GetDefaultScopes()); + AuthArgs->SetCodeChallenge(CodeVerifier->Challenge()); + Discord->Client->Authorize(AuthArgs, FDiscordClientAuthorizationCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnAuthorizeCompleted)); +} + +void ADiscordSocialUnrealCharacter::OnLogMessage(FString Message, EDiscordLoggingSeverity Severity) { + UE_LOG(LogTemplateCharacter, Log, TEXT("[%s] %s"), *UEnum::GetValueAsString(Severity), *Message); +} + +void ADiscordSocialUnrealCharacter::OnStatusChanged(EDiscordClientStatus Status, EDiscordClientError Error, int32 ErrorDetail) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Connection status: %s"), *UEnum::GetValueAsString(Status)); + if (Status == EDiscordClientStatus::Ready) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Connected to Discord! Ready to go! You can now start using Discord features.")); + UE_LOG(LogTemplateCharacter, Log, TEXT("Number of Relationships: %d"), Discord->Client->GetRelationships().Num()); + + UDiscordActivity activity; + activity.SetDetails("In competitive"); + activity.SetState("Diamond II"); + } + else if (Error != EDiscordClientError::None) { + UE_LOG(LogTemplateCharacter, Log, TEXT("Connection error: %s (Detail: %d)"), *UEnum::GetValueAsString(Error), ErrorDetail); + } +} + + +void ADiscordSocialUnrealCharacter::OnAuthorizeCompleted(UDiscordClientResult* Result, FString Code, FString RedirectUri) { + if (!Result->Successful()) { + UE_LOG(LogTemplateCharacter, Error, TEXT("Discord authorization failed: %s"), *Result->Error()); + return; + } + + UE_LOG(LogTemplateCharacter, Log, TEXT("Authorization successful! Getting access token...")); + Discord->Client->GetToken(APPLICATION_ID, Code, CodeVerifier->Verifier(), RedirectUri, FDiscordClientTokenExchangeCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnTokenExchange)); +} + +void ADiscordSocialUnrealCharacter::OnTokenExchange(UDiscordClientResult* Result, FString AccessToken, FString RefreshToken, EDiscordAuthorizationTokenType TokenType, int32 ExpiresIn, FString Scope) { + if (!Result->Successful()) { + UE_LOG(LogTemplateCharacter, Error, TEXT("Discord token exchange failed: %s"), *Result->Error()); + } + + Discord->Client->UpdateToken(TokenType, AccessToken, FDiscordClientUpdateTokenCallback::CreateUObject(this, &ADiscordSocialUnrealCharacter::OnTokenUpdated)); +} + +void ADiscordSocialUnrealCharacter::OnTokenUpdated(UDiscordClientResult* Result) { + Discord->Client->Connect(); +} + +////////////////////////////////////////////////////////////////////////// +// ADiscordSocialUnrealCharacter + +ADiscordSocialUnrealCharacter::ADiscordSocialUnrealCharacter() +{ + // Set size for collision capsule + GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f); + + // Don't rotate when the controller rotates. Let that just affect the camera. + bUseControllerRotationPitch = false; + bUseControllerRotationYaw = false; + bUseControllerRotationRoll = false; + + // Configure character movement + GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input... + GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate + + // Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint + // instead of recompiling to adjust them + GetCharacterMovement()->JumpZVelocity = 700.f; + GetCharacterMovement()->AirControl = 0.35f; + GetCharacterMovement()->MaxWalkSpeed = 500.f; + GetCharacterMovement()->MinAnalogWalkSpeed = 20.f; + GetCharacterMovement()->BrakingDecelerationWalking = 2000.f; + GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f; + + // Create a camera boom (pulls in towards the player if there is a collision) + CameraBoom = CreateDefaultSubobject(TEXT("CameraBoom")); + CameraBoom->SetupAttachment(RootComponent); + CameraBoom->TargetArmLength = 400.0f; // The camera follows at this distance behind the character + CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller + + // Create a follow camera + FollowCamera = CreateDefaultSubobject(TEXT("FollowCamera")); + FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation + FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm + + // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) + // are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++) +} + +////////////////////////////////////////////////////////////////////////// +// Input + +void ADiscordSocialUnrealCharacter::NotifyControllerChanged() +{ + Super::NotifyControllerChanged(); + + // Add Input Mapping Context + if (APlayerController* PlayerController = Cast(Controller)) + { + if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PlayerController->GetLocalPlayer())) + { + Subsystem->AddMappingContext(DefaultMappingContext, 0); + } + } +} + +void ADiscordSocialUnrealCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) +{ + // Set up action bindings + if (UEnhancedInputComponent* EnhancedInputComponent = Cast(PlayerInputComponent)) { + + // Jumping + EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump); + EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping); + + // Moving + EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ADiscordSocialUnrealCharacter::Move); + + // Looking + EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ADiscordSocialUnrealCharacter::Look); + } + else + { + UE_LOG(LogTemplateCharacter, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this)); + } +} + +void ADiscordSocialUnrealCharacter::Move(const FInputActionValue& Value) +{ + // input is a Vector2D + FVector2D MovementVector = Value.Get(); + + if (Controller != nullptr) + { + // find out which way is forward + const FRotator Rotation = Controller->GetControlRotation(); + const FRotator YawRotation(0, Rotation.Yaw, 0); + + // get forward vector + const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X); + + // get right vector + const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y); + + // add movement + AddMovementInput(ForwardDirection, MovementVector.Y); + AddMovementInput(RightDirection, MovementVector.X); + } +} + +void ADiscordSocialUnrealCharacter::Look(const FInputActionValue& Value) +{ + // input is a Vector2D + FVector2D LookAxisVector = Value.Get(); + + if (Controller != nullptr) + { + // add yaw and pitch input to controller + AddControllerYawInput(LookAxisVector.X); + AddControllerPitchInput(LookAxisVector.Y); + } +} +``` + + + +```cpp +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "DiscordLocalPlayerSubsystem.h" +#include "GameFramework/Character.h" +#include "Logging/LogMacros.h" +#include "DiscordSocialUnrealCharacter.generated.h" + +class USpringArmComponent; +class UCameraComponent; +class UInputMappingContext; +class UInputAction; +struct FInputActionValue; + +DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All); + +UCLASS(config=Game) +class ADiscordSocialUnrealCharacter : public ACharacter +{ + GENERATED_BODY() + + /** Camera boom positioning the camera behind the character */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true")) + USpringArmComponent* CameraBoom; + + /** Follow camera */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true")) + UCameraComponent* FollowCamera; + + /** MappingContext */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UInputMappingContext* DefaultMappingContext; + + /** Jump Input Action */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UInputAction* JumpAction; + + /** Move Input Action */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UInputAction* MoveAction; + + /** Look Input Action */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) + UInputAction* LookAction; + + + +public: + ADiscordSocialUnrealCharacter(); + + UFUNCTION(Exec) + void DiscordConnect(); + + UFUNCTION() + void OnStatusChanged(EDiscordClientStatus Status, EDiscordClientError Error, int32 ErrorDetail); + + UPROPERTY() + UDiscordAuthorizationCodeVerifier* CodeVerifier; + + UPROPERTY() + UDiscordLocalPlayerSubsystem* Discord; + +protected: + + /** Called for movement input */ + void Move(const FInputActionValue& Value); + + /** Called for looking input */ + void Look(const FInputActionValue& Value); + + void BeginPlay(); + void OnLogMessage(FString Message, EDiscordLoggingSeverity Severity); + void OnAuthorizeCompleted(UDiscordClientResult* Result, FString Code, FString RedirectUri); + void OnRichPresenceUpdated(UDiscordClientResult* Result); + void OnTokenExchange(UDiscordClientResult* Result, FString AccessToken, FString RefreshToken, EDiscordAuthorizationTokenType TokenType, int32 ExpiresIn, FString Scope); + void OnTokenUpdated(UDiscordClientResult* Result); + +protected: + + virtual void NotifyControllerChanged() override; + + virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; + +public: + /** Returns CameraBoom subobject **/ + FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; } + /** Returns FollowCamera subobject **/ + FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; } +}; +``` + + +--- + +## Conclusion + +Congratulations! You've successfully integrated the Discord Social SDK into your C++ application. Let's review what you've accomplished: + +### What You've Built + +- ✅ Created a Discord application and configured OAuth2 +- ✅ Set up SDK logging and status monitoring +- ✅ Implemented user authentication flow +- ✅ Retrieved Discord relationships data +- ✅ Added Rich Presence support + +### Key Concepts Learned + +- How to initialize and configure the Discord SDK +- Managing authentication and connections +- Working with Discord's social features +- Handling asynchronous callbacks +- Monitoring SDK status and events + +--- + +## Next Steps + +You have successfully set up the Discord Social SDK with Unreal Engine and authenticated with Discord! You can now use the SDK to add more social features in your project. + +import {ListViewIcon} from '/snippets/icons/ListViewIcon.jsx' +import {UserStatusIcon} from '/snippets/icons/UserStatusIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Create a unified friends list combining Discord and game-specific friendships + + }> + Customize your game's rich presence to show more advanced information and game invites + + }> + Allow players to invite friends to join their game session or party. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Activity`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682 +[`Client::Authorize`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468 +[`Client::Connect`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa +[`Client::GetRelationships`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d +[`Client::UpdateToken`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4 +[`RunCallbacks`]: https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ab5dd8cf274f581ee1885de5816be3c29 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/how-to.mdx b/discord/developers/docs/discord-social-sdk/how-to.mdx new file mode 100644 index 0000000000..ee17d28105 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/how-to.mdx @@ -0,0 +1,41 @@ +--- +title: Discord Social SDK How To Guides Glossary +sidebarTitle: Glossary +description: Definitions and explanations of key terms used in Discord Social SDK how-to guides. +--- + +These how-to guides offer common solutions for integrating Discord Social SDK features into your game. + +import {BugIcon} from '/snippets/icons/BugIcon.jsx' +import {ClydeIcon} from '/snippets/icons/ClydeIcon.jsx' +import {ShieldIcon} from '/snippets/icons/ShieldIcon.jsx' +import {StarShootingIcon} from '/snippets/icons/StarShootingIcon.jsx' +import {LettersIcon} from '/snippets/icons/LettersIcon.jsx' + + + }> + Use logging and debugging tools to troubleshoot issues. + + }> + Make requests to Discord's HTTP APIs from your game. + + }> + Integrating and managing content moderation for your game when using the Discord Social SDK. + + }> + Guidelines and best practices for announcing your Discord Social SDK integration to players. + + }> + Handling Unicode characters in Discord Display Names for your game's chat and friend lists. + + + +--- + +## Change Log + +| Date | Changes | +|---------------|-----------------------| +| July 23, 2025 | migrated several docs | +| June 17, 2025 | added How To category | diff --git a/discord/developers/docs/discord-social-sdk/how-to/debug-log.mdx b/discord/developers/docs/discord-social-sdk/how-to/debug-log.mdx new file mode 100644 index 0000000000..73092ffd69 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/how-to/debug-log.mdx @@ -0,0 +1,89 @@ +--- +title: Debug & Log +description: Learn how to enable and use debug logging in the Discord Social SDK. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + +## Overview + +This guide will help you understand how to install debugging symbols and handle logging to assist in building your Discord Social SDK integration. + +## Debugging + +Debugging symbols are hosted at https://storage.googleapis.com/discord-public-symbols. If using Visual Studio, you can add this link to the pdb locations under `Tools > Options > Debugging > Symbols`. + + +Note: You won't be able to browse files using that link, but that's ok. Individual files are accessible under the domain, and the URL functions properly as a symbol server, so it works in Visual Studio. + + +## Logging + +You can access Discord's logs with [`Client::AddLogCallback`]. We recommend writing logs to a file and, during testing, including verbose logs. The [`Client::SetLogDir`] function will write the SDK's logs to that directory if set. + +```cpp +client->AddLogCallback([](auto message, auto severity) { + std::cout << "[" << EnumToString(severity) << "] " << message << std::endl; +}, discordpp::LoggingSeverity::Info); +``` + +### Audio Logging + +For diagnosing issues with acoustic echo cancellation (AEC), you can use the [`Client::SetAecDump`] function. This +enables diagnostic recording of audio input and output waveform data, which can be invaluable when troubleshooting echo, +feedback, or other audio processing problems. + +[`Client::SetAecDump`] Enables or disables AEC diagnostic recording. When enabled, the input and output waveform data will be written to the +log directory (the same directory specified by [`Client::SetLogDir`]). + +```cpp +// Enable AEC diagnostic recording +client->SetAecDump(true); + +// ... perform voice chat operations to reproduce the issue ... + +// Disable AEC diagnostic recording when done +client->SetAecDump(false); +``` + +**When to use:** +- Users report echo or feedback during voice chat +- Audio quality issues that may be related to echo cancellation +- Testing AEC performance in different environments +- Debugging audio processing pipeline issues + + +AEC dump files can become large quickly as they contain raw waveform data. Remember to disable the diagnostic recording +once you've captured the necessary data for analysis. + + +--- + +## Next Steps + + + + Learn how to get started with the Discord Social SDK. + + + Understand the core concepts of the Discord Social SDK. + + + Learn how to link user accounts with Discord. + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Client::AddLogCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af78996cff24a40f5dc7066beed16692c +[`Client::SetAecDump`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3a05b2cafaa546d915a5249c63f4059f +[`Client::SetLogDir`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a23bd5802dfa3072201ea864ee839c001 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/how-to/handle-special-characters-display-names.mdx b/discord/developers/docs/discord-social-sdk/how-to/handle-special-characters-display-names.mdx new file mode 100644 index 0000000000..b73ffbe9b4 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/how-to/handle-special-characters-display-names.mdx @@ -0,0 +1,48 @@ +--- +title: How To Handle Special Characters in Display Names +sidebarTitle: Handle Special Characters in Display Names +description: Properly handle and display Discord usernames with special characters and emojis. +--- + +The Social SDK [recommends](https://discord.com/developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list#step-1-fetch-relationships) using an account's [Display Name](https://discord.com/developers/docs/slayer-sdk/classdiscordpp_1_1UserHandle.html#af6447fa2011bfa4fcd7e55bc56847f5c) in the Unified Friends List. However, Display Names support a large set of characters across the Unicode spec, and some of these characters may not be supported in a game's specified font. + +There are several options to address this: + +## Use a font family that supports Unicode + +A family such as [Noto](https://notofonts.github.io/) aims to have broad Unicode support. If a font cannot render a Display Name, consider rendering it with a fallback font, or in the extreme case, choose a font with broad Unicode support for the Friends List. + +**Tradeoffs:** + +- One font family may not include 100% overlap with what Discord supports. +- A font that supports Unicode may not mesh with your game's aesthetics. +- Engine support is limited, especially for characters that rely on UTF surrogate pairs. + +## Map the Unicode characters to ASCII + +A library such as [Unidecode](https://metacpan.org/pod/Text::Unidecode) (which has been ported to many different languages) can map many Unicode characters to an ASCII equivalent. If you detect that a Display Name has characters that cannot be rendered, consider passing it through a library to transliterate the Display Name to ASCII. + +**Tradeoffs:** + +- This may result in unintended transliterations. It is impossible to create a perfect mapping between Unicode and ASCII in every situation. Libraries try their best, but this approach may unintentionally create an inaccurate or even offensive display name in some instances. +- Libraries will not have 100% coverage. Some Unicode characters may not have a map to an ASCII character. If someone's Display Name is `ʕ•͡-•ʔ` what would it map to? +- Similarly, most libraries assume English is the target language. +- Users may not appreciate their display names being changed without their knowledge or consent. + + +## Use the Discord Username as a Fallback + +If a Display Name is not renderable at all, consider falling back to the user's [`Username`](https://discord.com/developers/docs/slayer-sdk/classdiscordpp_1_1UserHandle.html#a0eda41fe18b50bce373fb5a1b88cb411). + +**Tradeoffs:** + +- This can break immersion in a game if a friend had expected to rely on their Display Name from within the game. +- Some users consider their Username to be more private information and do not expect it to be shared by others (such as when someone is streaming and their friends list is visible). + +--- + +## Change Log + +| Date | Changes | +|---------------|---------------------------------| +| June 17, 2025 | special characters how-to added | diff --git a/discord/developers/docs/discord-social-sdk/how-to/integrate-moderation.mdx b/discord/developers/docs/discord-social-sdk/how-to/integrate-moderation.mdx new file mode 100644 index 0000000000..5d375e4fab --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/how-to/integrate-moderation.mdx @@ -0,0 +1,315 @@ +--- +title: Integrate Moderation +description: Implement moderation features and safety controls in your application. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + +{/* +Script for converting mermaid sequence diagrams to SVG. + +# docker run --rm -u `id -u`:`id -g` -v "$(pwd):/data" minlag/mermaid-cli -i "/data/$file" -o "/data/$filename.svg" -t dark -b '#313338' +*/} + +This guide will walk you through integrating and managing content moderation for your game when using the Discord Social SDK. + +## Overview + +Effective moderation is essential for creating healthy social experiences. This guide will help you: + +- Better understand your moderation responsibilities +- Implement client-side moderation for text and audio content alongside the Discord Social SDK + +## Prerequisites + +Before you begin, make sure you have: + +- A basic understanding of how the SDK works from the [Getting Started Guide](/developers/docs/discord-social-sdk/getting-started) +- A basic understanding of your game's communication features +- Familiarity with [provisional accounts](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts) +- Reviewed the [Discord Social SDK Terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms) + +## Your Moderation Responsibilities + +### Moderation on Discord + +[Discord's Community Guidelines](https://discord.com/guidelines) and [Terms of Service](https://discord.com/terms) apply to any content that is rendered on Discord, including: + +- Text messages, audio, and video sent within Discord’s platform +- Text messages that are appear on Discord (such as in DMs or Linked Channels) after being sent by players in your game (whether they have linked their Discord account or are using a provisional account) + +Discord can take various actions against such content on Discord for violating its terms or policies, including through Discord platform-wide account bans and restrictions. Actions against a player’s Discord account will not affect their separate account in the game (see [below](https://www.notion.so/Text-and-Voice-Moderation-1d0f46fd48aa80eda914cd3134d65afb?pvs=21) for more details); however, if a player’s Discord account is banned, they will no longer have access to the SDK features that require an account connection. + +### Game Developer's Responsibility + +Your terms and policies apply to the content in your game. You are responsible for: + +- Ensuring you comply with the [Discord Social SDK Terms](https://support-dev.discord.com/hc/en-us/articles/30225844245271-Discord-Social-SDK-Terms) +- Creating game-specific content policies and enforcing them +- In-game content moderation for messages or audio within your game +- Implementing appropriate UIs for reporting and moderation; this includes providing players a way to report issues or violations of your policies and reviewing and taking appropriate action on such reports + +## Client-Side Chat Moderation + +While the Social SDK does not have a direct integration with external moderation toolkits, a client side approach to +outgoing and incoming messages can be implemented when sending messages through [`Client::SendUserMessage`] and/or +receiving them through [`Client::SetMessageCreatedCallback`]. + +### How it works: + +An example moderation integration would be the following: + +**Message Sending** + +- Before a user sends a message, your client validates it with your backend +- If it passes validation, the message is sent through the SDK +- Messages can be optimistically rendered while validation occurs + + +```mermaid +sequenceDiagram + participant User + participant GameClient + participant BackendService as Moderation Backend + participant DiscordSDK as Discord Social SDK + participant DiscordServers as Discord + + %% User initiates sending a message %% + User->>GameClient: Enters and submits message text + activate GameClient + + %% Optimistic rendering and validation request %% + Note right of GameClient: Optimistically render message in UI + GameClient->>BackendService: POST /validate (message) + activate BackendService + + %% Backend validates %% + Note right of BackendService: Perform validation logic + BackendService-->>GameClient: Response (isValid: boolean, moderatedText?: string) + deactivate BackendService + + %% Conditional sending based on validation %% + alt Message is Valid (isValid: true) + GameClient->>DiscordSDK: Client::SendUserMessage(recipient, message) + activate DiscordSDK + DiscordSDK->>DiscordServers: Forward message + activate DiscordServers + DiscordServers-->>DiscordSDK: Message Acknowledged (messageId) + deactivate DiscordServers + DiscordSDK-->>GameClient: Callback(result: Success, messageId) + deactivate DiscordSDK + Note right of GameClient: Confirm message sent (update UI if needed) + else Message is Invalid (isValid: false) + Note right of GameClient: Remove optimistically rendered message from UI + GameClient->>User: Notify: "Message violated content policy" + end + deactivate GameClient +``` +```cpp +// Example: Validating a message before sending +void validateAndSendMessage(const std::string& message) { + // Optimistically render the message immediately + renderMessage(myUserId, message); + + // Send to backend for validation + myBackend->validateMessage(message, [this, message](bool isValid, std::string moderated) { + if (isValid) { + // Send through Discord SDK + client->SendUserMessage(recipientId, message, [](auto result, uint64_t messageId) { + if (result.Successful()) { + std::cout << "✅ Message sent successfully\n"; + } else { + std::cout << "❌ Failed to send message: " << result.GetError() << "\n"; + } + }); + } else { + // Remove the invalid message from UI and notify user + removeMessage(myUserId, message); + notifyUser("Message violated content policy"); + } + }); +} +``` + +**Message Receiving** + +- When a message is received from players within Discord or a Game Client, your client sends it to your backend for validation +- Once validation succeeds, render the message in the game + + +Depending on how long your content moderation processing takes, you may wish to optimistically render the incoming +message, and remove or alter it if your moderation service deems it appropriate. However, this does pose the risk of +temporarily exposing content to your game players that does not align with your moderation policies. + + + + +```mermaid +sequenceDiagram + participant DiscordSDK as Discord Social SDK + participant GameClient as Game Client + participant BackendService as Moderation Backend + + %% Message Arrives %% + activate DiscordSDK + + %% SDK delivers message to Game Client via callback %% + Note right of DiscordSDK: Message arrives for subscribed channel/user + DiscordSDK->>GameClient: Trigger Client::SetMessageCreatedCallback(message) + deactivate DiscordSDK + activate GameClient + + %% Game Client sends received message for validation %% + GameClient->>BackendService: POST /validate (receivedMessage) + activate BackendService + + %% Backend validates %% + Note right of BackendService: Perform validation logic + BackendService-->>GameClient: Response (isValid: boolean) + deactivate BackendService + + %% Conditional rendering based on validation %% + alt Message is Valid (isValid: true) + Note right of GameClient: Render received message in game UI + else Message is Invalid (isValid: false) + Note right of GameClient: Discard message (do not render) + end + deactivate GameClient +``` + +You may wish to implement moderation caching on your backend to avoid redundant validation. This is especially true for +lobby messages, which are sent to multiple recipients. + + +As a reminder, you are responsible for any third-party moderation toolkits or services you use for your game and will ensure you comply with any applicable terms and laws, including obtaining consents from players as necessary for processing their data using such moderation services. + +## Handling Users with Banned Discord Accounts + +### Discord Platform Bans + + +If a player has connected their Discord account with your game, and it is banned, their [`Client`] will be +immediately disconnected, and that user will no longer be able to authenticate through Discord. + + +The recommended path for integrating the Discord Social SDK is that your game has a primary authentication other than Discord that initially sets up a provisional account, and have the player link their Discord account to this primary authentication. + +This approach protects your users' game access and data if they encounter issues with their Discord account, such as a permanent or temporary ban. To implement this recommended path: + +1. Create an account through a [non-Discord authentication provider](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#configuring-your-identity-provider), and create a provisional account attached to it. +2. When users later authenticate through Discord to link their account, have your game back end execute the [merge their provisional account with their Discord Account](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#merging-provisional-accounts). +3. The account merging process will internally store the `externalAuthToken` from the provisional account against their Discord account. If a ban of the Discord account happens, that `externalAuthToken` will be attached to the new provisional account that is created in its stead, with the original Discord account's in-game friends, and will be available through the authentication provider the account was initially setup with. +4. As a last step, your game back end should maintain the record of the `externalAuthToken` against the user account, even after the account merging process, since it will be needed to [authenticate via a provisional account](/developers/docs/discord-social-sdk/development-guides/using-provisional-accounts#implementing-provisional-accounts) should Discord authentication fails for a ban, or any other reason. + +```mermaid +sequenceDiagram + participant User + participant Game + participant NonDiscordAuth as Non-Discord Auth Provider + participant GameBackend as Game Backend + participant Discord + + User->>Game: Start game + Game->>NonDiscordAuth: Create account + NonDiscordAuth-->>Game: Authentication successful + Game->>GameBackend: Request provisional account + GameBackend->>Discord: Create provisional account + Discord-->>GameBackend: Account created + GameBackend-->>Game: Return Provisional Account + Note over GameBackend: Store externalAuthToken + + User->>Game: Choose to link Discord account + Game->>Discord: Request authentication + Discord-->>Game: Auth successful + Game->>GameBackend: Request account merge + GameBackend->>Discord: Merge Provisional Account + Note over Discord: Stores externalAuthToken against Discord account + Discord-->>GameBackend: Merge successful + + Note over GameBackend: Maintain externalAuthToken record + + alt Discord Authentication Fails (Ban or Other Issue) + User->>Game: Attempt to login + Game->>Discord: Request authentication + Discord-->>Game: Auth failed + Game->>NonDiscordAuth: Fallback authentication + NonDiscordAuth-->>Game: Authentication successful + Game->>GameBackend: Access provisional account with externalAuthToken + GameBackend-->>Game: Access granted with in-game friends list + Game-->>User: Access game + end +``` + + +If you use Discord as the primary or sole authentication mechanism for your game, you risk players permanently losing access to their in-game data if their Discord account is banned, as there is no way to migrate them to a provisional account that is connected to an external authentication provider. + + + +At this time, there is no API to look up if a player's Discord account has been banned. + + +### Discord Server Bans + +If you wish to tie your in-game moderation policies to a specific Discord server that you own, such as your official community server, you are able to retrieve ban information for your Discord Server via our REST APIs. + +See the references for the REST endpoints[`{guild.id}/guilds/{guild.id}/bans`](/developers/docs/resources/guild#get-guild-bans) +or [`/guilds/{guild.id}/bans/{user.id}`](/developers/docs/resources/guild#get-guild-ban) +for more information on retrieving all bans for your guild, or ban information for a specific user within your guild. + +## Voice Chat Moderation + +The Discord Social SDK provides access to audio streams for in-game voice calls, allowing you to implement audio +moderation for your game's voice chat functionality. The data for the call is available through +[`Client::StartCallWithAudioCallbacks`], and can be passed to your voice moderation system. + +```cpp +// Example: Capturing local voice chat audio for asynchronous moderation. + +// Callback for local users' audio with moderation +auto capturedCallback = [](int16_t const* data, + uint64_t samplesPerChannel, int32_t sampleRate, + uint64_t channels) { + // Call the moderation function + moderateCapturedVoice(data, samplesPerChannel); +}; + +// Start the call with our moderation callback +auto call = client->StartCallWithAudioCallbacks(lobbyId, receivedCallback, capturedCallback); +``` + + +--- + +## Next Steps + +import {ChatIcon} from '/snippets/icons/ChatIcon.jsx' +import {VoiceNormalIcon} from '/snippets/icons/VoiceNormalIcon.jsx' +import {TextControllerIcon} from '/snippets/icons/TextControllerIcon.jsx' + + + }> + Enable private messaging between players. + + }> + Add in-game voice communication. + + }> + Connect game lobbies to Discord text channels. + + + + + +--- + +## Change Log + +| Date | Changes | +|--------------|-----------------| +| May 22, 2025 | initial release | + +{/* Autogenerated Reference Links */} +[`Client`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91716140c699d8ef0bdf6bfd7ee0ae13 +[`Client::SendUserMessage`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3cf9d2b1b5a4a61dcad995dfc1009703 +[`Client::SetMessageCreatedCallback`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28325a8e8c688a84ac851da4bc86e148 +[`Client::StartCallWithAudioCallbacks`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#abcaa891769f9e912bfa0e06ff7221b05 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/how-to/market-your-integration.mdx b/discord/developers/docs/discord-social-sdk/how-to/market-your-integration.mdx new file mode 100644 index 0000000000..984670f8e0 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/how-to/market-your-integration.mdx @@ -0,0 +1,148 @@ +--- +title: Market Your Integration +description: Best practices for marketing and promoting your Discord Social SDK integration. +--- + +## Overview + +Congrats! We're so happy you've powered your game's social experiences with the Discord Social SDK, and now it's time to +let your players know about it. This toolkit is meant to provide you with guidelines and best practices to help you have +a strong go-to-market plan to make your players aware of the integration, answer their questions, and use Discord brand +guidelines appropriately. + +As the developer, the choice and ownership of marketing your integration with the Discord Social SDK to your players +is yours, but we want to ensure you had a place to start if you needed. As a reminder, you may not make any statement +that suggests a partnership with, sponsorship by, or endorsement by Discord without our prior written approval in each instance. + +## Announcement Goals And Messaging + +Announcing **Discord Social SDK** features to your players has two goals: + +* **For Players:** Drive awareness of how playing with friends is easier by linking your Discord account. +* **For Developer:** Encourage more persistent social interaction between your players. + +Player-facing messaging of Discord Social SDK is different from how we frame it to developers, like you. Below are +some key messages you can pull into your announcement materials. + + +Our guidance focuses on features that encourage Discord account linking. It's the most important step for you and your players to experience the benefits of the integration. [Learn more about the impact of account linking](https://discord.com/developers/social-sdk).

If your announcements do not focus on Discord Social SDK features that require account linking, you can still generally use this guidance and our brand guidelines. +
+ +### A Note on the Product Name +Technically, the full product name is "Discord Social SDK". However, we recommend announcing this integration to your players with more casual language and, in most cases, sharing the full product name with players is not needed. Please follow the below guidance that provides messaging we have found resonates well with players. + +If you decide to mention the full "Discord Social SDK" product name, after mentioning it once you may refer to it as the "SDK" for the remainder of your announcements. + +### Key Message to Players +Link your Discord account and play with friends more easily. + +### Key Benefits to Players + +* **Find your friends** + * Link your Discord account to see who's playing and easily join them. From the game, invite your friends to join—even if they're offline. +* **Stay connected** + * Keep the conversation going by syncing messages and friends lists to Discord + +## Detailed Feature Descriptions + +In case you'd like to reference specific features and their benefits to players, use this chart. + + +In most cases, players won't need to know the actual “Feature Name”, but still referencing the description and use cases below are helpful for them! + + +| Feature Name | Short Description | Use Cases | +|-----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Rich Presence** | Shows on Discord which games players are currently playing. | Knowing what your friends are doing in-game, so you know if they'd be available to play together

Joining a game directly from a friend's profile (similar to game invites) | +| **Unified Friends List** | Players can access their Discord friends list from within the game. Includes friend tiers. | Sending game invites directly to friends from in-game

Knowing which of your friends is playing, and who isn't playing but is online and could play

Not having to re-create your friends list social in a new game that you play | +| **Deeplink Game Invites** | Users can send invites that link directly to specific game instances or parts of a game. | Challenging users to specific game instances | +| **Flexible Account Requirements** | Users can choose to link their Discord account to unlock deeper integrated features such as the Unified Friends List and Rich Presence, but can also take advantage of the in-game social features without linking an account through an innovative "provisional account." | Account linking: Automatic Discord-based sign in, or, linking of your Discord account to a game account.
Take full advantage of the Discord integration

Provisional accounts: Provisional accounts let you use Discord's voice and text chat features while playing connected games \- without needing to create a Discord account first. | +| **Cross-Platform Messaging** | Allows players to communicate seamlessly both in-game and through Discord, even without a Discord account. | DMing your friends when they're in-game and out of game

Receiving messages from your friends when you aren't playing.

In-game text chat for guilds, matches, lobbies, etc. | +| **Linked Channels** | Players can link their in-game group chat with in-Discord channels, providing persistent group chats. | In-game guild/group chat mirrored in your guild's/group's Discord server

No more feeling isolated from the broader group if you're online when everyone else isn't, or vice-versa | +| **Discord Voice Chat** | High quality voice chat powered by Discord available directly in the game. | In-game voice chat for guilds, matches, lobbies, etc. | + +## Integration Announcement Plan Recommendations + +Below are channel recommendations for how to announce the Discord Social SDK integration in your game to your players. Please feel free to use additional channels that would resonate best with your players. + +* **In-Game Messaging or Reward (Best)** + * Display a pop-up screen, banner, or CTA informing players of the recent update, prompting them to link their Discord account, and possibly reward them. +* **Announcement Video (Best)** + * Show the integration and benefits in action through a video. +* **Individual Blog Post** + * Create a blog post to outline the integration in more detail. +* **Patch Notes** + * Include the Discord integration details and images as a key update in your patch notes. +* **Social Media** + * Use other social channels to spread the word of the announcement. +* **Discord Server Announcement** + * In your main announcements channel in Discord, post a message to let your community know about these updates. Create a dedicated space for players to discuss feedback. +* **Creator Partnership** + * Partner with a creator to walk through the integration with their community. +* **Paid Media** + * Run paid ads to encourage people to link their Discord account. +* **Press** + * Pitch your story of the integration to relevant press outlets. For any press inquiries, please loop + in [press@discord.com](mailto:press@discord.com). +* **Support Ticket Setup** + * Ensure you have a new support ticket category set up so players can easily troubleshoot any problems they may encounter, such as having a “Discord account linking” category in your support tickets. + +### Discord Social Handles to Tag + +If you would like to tag @Discord in any of your announcements, please feel free to tag our socials below. + +* X: [@discord](https://x.com/discord?lang=en) +* Instagram: [@discord](https://www.instagram.com/discord/?hl=en) +* TikTok: [@discord](https://www.tiktok.com/@discord?lang=en) +* YouTube: [@discord](https://www.youtube.com/@discord/shorts) +* LinkedIn: [@discord](https://www.linkedin.com/company/discord) + +## Discord Brand Assets & Guidance + +We're excited you're helping us spread the word about Discord. We'd love to make sure you have the right assets to use and guidelines from our brand team. + +When using Discord Marks and Brand Assets, you must always comply with Discord's [Brand Guidelines found here](https://discord.com/branding). +As a reminder, you may not make any statement that suggests a partnership with, sponsorship by, or endorsement by Discord without our prior written approval in each instance. We also have some additional guidance below specific for your Discord Social SDK launch. + +### Discord Logo and Wordmark + +* [Download Link](https://discord.com/branding) +* Guidance for Discord Social SDK + * When using our logo or wordmark in your CTA buttons in-game, in social and blog post visual assets, please +review the Logo & Symbol sections of our brand guidelines. If you're not sure about use or have questions, please reach out to your Discord point of contact. + * Please refer to our articles on in-game + [Connection Points](/developers/docs/discord-social-sdk/design-guidelines/connection-points) and + [Overall Flow](/developers/docs/discord-social-sdk/design-guidelines/signing-in#before-the-user-connects) for more guidance for in-game messaging. +* Usage Guidance + * [Logo Usage Do's and Don'ts](https://discord.com/branding) (Click "View Brand Kit" then "Logo" then "Usage"). + +### Discord Bumper Splash Animation + +* Downloads: + * [3 second bumper splash animation video](https://cdn.discordapp.com/assets/content/edbc1f6c8b7b0ea968888a2588ff552c2f8b2090fc2aa13e97e224427d5ad03c.mov) + * [3 second bumper splash animation audio (TV)](https://cdn.discordapp.com/assets/content/efe58ff7a83b8063a0b60f41f171932ce7d26de78fe2e2c2c531e26edadac3d4.wav) + * [3 second bumper splash animation audio (Streaming)](https://cdn.discordapp.com/assets/content/f3070170acd3f62b08ba9a63ab71080f6f7a72be5c84b74d1b247b4767696045.wav) +* Best Practice Guidance for Discord Social SDK + * This can be used as an intro splash video to your game, video launch announcement of the SDK, or game trailer + content where Discord Social SDK features are shown. You should not use this splash video in your content if Discord Social SDK features aren't present in the content. +* Usage Guidance + * When using this splash animation in your video, always include this at the beginning of the video. This splash + video should never go in the middle or end of your content. + * Using the 3 second video is preferred by Discord, but we do have 1 second and 2 second options available. + +## Legal Brand Guidelines + +DISCORD, DISCORD NITRO, the “Clyde” Logo and any other trademark owned by Discord and its affiliates (the “Discord Marks”) +and other brand materials such as logos, trade dress, the Discord look and feel, and other aesthetic features unique to +the brand (the “Brand Assets”) are the exclusive property of Discord Inc. You must have permission from Discord before +using any of the Discord Marks or Brand Assets except as permitted here in this document. + +Please visit our [Discord Brand Guidelines](https://discord.com/branding) for additional Legal guidelines that apply to your use of Discord's Brand Assets. + +--- + +## Change Log + +| Date | Changes | +|---------------|------------------| +| July 22, 2025 | initial creation | diff --git a/discord/developers/docs/discord-social-sdk/how-to/use-with-discord-apis.mdx b/discord/developers/docs/discord-social-sdk/how-to/use-with-discord-apis.mdx new file mode 100644 index 0000000000..2fcef6508e --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/how-to/use-with-discord-apis.mdx @@ -0,0 +1,147 @@ +--- +title: Use with Discord APIs +description: Combine Discord Social SDK with Discord's REST APIs for enhanced functionality. +--- +import PublicClient from '/snippets/discord-social-sdk/callouts/public-client.mdx'; +import SupportCallout from '/snippets/discord-social-sdk/callouts/support.mdx'; + +## Overview + +The Discord Social SDK provides client-side functionality for integrating Discord social features into your game. However, if you use a game backend, you should interact with Discord's HTTP APIs for server-side operations when possible. + +### Prerequisites + +Before you begin, make sure you have: +- A Discord application created in the Developer Portal +- Access to your application's bot token +- Required OAuth2 scopes configured + +--- + +## Authentication Types + +### Bot Token Authentication +For server-to-server communication: + +```bash +curl -X GET https://discord.com/api/v10/users/@me \ + -H "Authorization: Bot YOUR_BOT_TOKEN" +``` + + +Always prefix your bot token with `Bot ` in the Authorization header! + + +### Bearer Token Authentication +For authenticated user actions: + +```bash +curl -X GET https://discord.com/api/v10/users/@me \ + -H "Authorization: Bearer USER_ACCESS_TOKEN" +``` + +## Common API Operations + +### OAuth2 Token Exchange + +Exchange an authorization code for an access token: + +```python +# filepath: /your/backend/auth.py +import requests + +def exchange_code(code, redirect_uri): + data = { + 'client_id': 'YOUR_CLIENT_ID', + 'client_secret': 'YOUR_CLIENT_SECRET', + 'grant_type': 'authorization_code', + 'code': code, + 'redirect_uri': redirect_uri + } + + response = requests.post('https://discord.com/api/v10/oauth2/token', data=data) + return response.json() +``` + +### User Information + +Get information about the authenticated user: + +```python +def get_user_info(access_token): + headers = {'Authorization': f'Bearer {access_token}'} + response = requests.get('https://discord.com/api/v10/users/@me', headers=headers) + return response.json() +``` + +--- + +## Using the OpenAPI Specification + +You can use the [Discord OpenAPI Spec](https://github.com/discord/discord-api-spec) to generate a client in your preferred backend language to make it easier to call the various APIs. A good tool for that is https://openapi-generator.tech/. + +In python for example, that might look like this: + +```python +import openapi_client +import os +from pprint import pprint + +configuration = openapi_client.Configuration(host = "https://discord.com/api/v10") +configuration.api_key['BotToken'] = 'Bot ' + os.environ["API_KEY"] +with openapi_client.ApiClient(configuration) as api_client: + api_instance = openapi_client.DefaultApi(api_client) + + request = openapi_client.CreateLobbyRequest() + request.metadata={'foo':'bar'} + pprint(api_instance.create_lobby(request)) +``` + +--- + +## Best Practices + +1. Token Security + - Never expose bot tokens in client-side code + - Store tokens securely +2. Rate Limiting + - Respect Discord's rate limits + - Implement exponential backoff + - Cache responses when appropriate +3. Error Handling + - Handle HTTP errors gracefully + - Implement retry logic for transient failures + - Log API errors for debugging + +--- + +## Next Steps + +Now that you've set up Rich Presence, you might want to explore: + +import {UserStatusIcon} from '/snippets/icons/UserStatusIcon.jsx' +import {DoorEnterIcon} from '/snippets/icons/DoorEnterIcon.jsx' +import {InboxIcon} from '/snippets/icons/InboxIcon.jsx' + + + }> + Allow players to invite friends to join their game session or party. + + }> + Bring players together in a shared lobby with invites, text chat, and voice comms. + + }> + Best practices for Rich Presence UI/UX. + + + + + + +--- + +## Change Log + +| Date | Changes | +|----------------|-----------------| +| March 17, 2025 | Initial Release | diff --git a/discord/developers/docs/discord-social-sdk/overview.mdx b/discord/developers/docs/discord-social-sdk/overview.mdx new file mode 100644 index 0000000000..25fd562f84 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/overview.mdx @@ -0,0 +1,72 @@ +--- +title: Discord Social SDK +sidebarTitle: Overview +description: Learn about the Discord Social SDK for integrating Discord's social features. +--- + +![Discord Social SDK](/images/social-sdk/overview/social_sdk_header.png) + +The Discord Social SDK allows you to integrate social features directly into your game. Build engaging, social experiences where players can connect, communicate, and play together, all while staying immersed in your game. + +Start integrating the SDK into your game with our getting started guides, design guidelines, and code samples. + +## Integrate Social Features in Your Game + +import {GameControllerIcon} from '/snippets/icons/GameControllerIcon.jsx' +import {CompassIcon} from '/snippets/icons/CompassIcon.jsx' + + + }> + Learn how the SDK works and explore its key features. + + }> + Follow our step-by-step guides to add the SDK to your game engine. + + + +## Dive Deeper into the Discord Social SDK + +
+ +
+ +import {WrenchIcon} from '/snippets/icons/WrenchIcon.jsx' +import {PaintPaletteIcon} from '/snippets/icons/PaintPaletteIcon.jsx' +import {BrowserIcon} from '/snippets/icons/BrowserIcon.jsx' + + + }> + Dive into feature guides for **account linking, friends lists, game invites, and voice chat**. + + }> + Learn how to design your game's UI to integrate social features. + + }> + Explore the SDK's API reference documentation. + + + + + +--- + +## Communication Features +Discord Social SDK features for text and voice communication are available but capped with rate limits. + +For more information about current rate limits for communication features, +please see [Communication Features: Rate Limits](/developers/docs/discord-social-sdk/core-concepts/communication-features#rate-limits). + +For more information on how to access these features, please see [Core Concepts: Communication Features](/developers/docs/discord-social-sdk/core-concepts/communication-features). + +--- + +## Get Help & Join the Community + +Need support or looking to connect with other game developers? + +- Join our [DDevs Discord Server](https://discord.gg/discord-developers) and get help from the community, share best practices, and discover new ways to enhance your game. Find us in the `#social-sdk-dev-help` channel! +- For additional support, you can [file a ticket with Developer Support](https://dis.gd/social-sdk) to report issues. \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/partials/callouts/console-access.mdx b/discord/developers/docs/discord-social-sdk/partials/callouts/console-access.mdx new file mode 100644 index 0000000000..8f746f80df --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/partials/callouts/console-access.mdx @@ -0,0 +1,3 @@ + +To use the Discord Social SDK in your console games, you will need to request middleware approval and be an approved developer for the target console. Check out [this article](https://support-dev.discord.com/hc/en-us/articles/30209074764183) to learn more. + diff --git a/discord/developers/docs/discord-social-sdk/partials/callouts/oauth-comms-scopes.mdx b/discord/developers/docs/discord-social-sdk/partials/callouts/oauth-comms-scopes.mdx new file mode 100644 index 0000000000..1e20bb44ca --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/partials/callouts/oauth-comms-scopes.mdx @@ -0,0 +1,7 @@ + +To utilize this communication feature, you must enable [`Client::GetDefaultCommunicationScopes`] in your OAuth Scope configuration. +See the [OAuth Scopes Core Concepts Guide](/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes) for more details. + + +{/* Autogenerated Reference Links */} +[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1 \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/partials/callouts/public-client.mdx b/discord/developers/docs/discord-social-sdk/partials/callouts/public-client.mdx new file mode 100644 index 0000000000..96fe5d6caf --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/partials/callouts/public-client.mdx @@ -0,0 +1,3 @@ + +This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types) + \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/partials/callouts/rate-limit.mdx b/discord/developers/docs/discord-social-sdk/partials/callouts/rate-limit.mdx new file mode 100644 index 0000000000..d62d7bb73a --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/partials/callouts/rate-limit.mdx @@ -0,0 +1,5 @@ + +This feature is currently available with rate limits. +To remove the rate limits for your game, please follow +[Communication Features: Applying for Rate Limit Removal](/developers/docs/discord-social-sdk/core-concepts/communication-features#applying-for-rate-limit-removal). + \ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/partials/callouts/sony.mdx b/discord/developers/docs/discord-social-sdk/partials/callouts/sony.mdx new file mode 100644 index 0000000000..7ed72c4255 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/partials/callouts/sony.mdx @@ -0,0 +1,8 @@ + +Exception: Prohibited Off-Platform Interactions by Sony
+For integrations experienced on Sony Platforms (e.g. PlayStation 5)… +

1. **Do not allow access to Account Linking** entry points. +
2. **Do not allow access to Linked Channels**. +
3. **Limit messaging and Invites to other players currently playing** the game. +
4. **Do not display off-platform presence and logos** (including Discord). +
\ No newline at end of file diff --git a/discord/developers/docs/discord-social-sdk/partials/callouts/support.mdx b/discord/developers/docs/discord-social-sdk/partials/callouts/support.mdx new file mode 100644 index 0000000000..c8f9c6c069 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/partials/callouts/support.mdx @@ -0,0 +1,3 @@ +Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community. + +If you encounter a bug while working with the Social SDK, please report it here: https://dis.gd/social-sdk-bug-report diff --git a/discord/developers/docs/discord-social-sdk/social-sdk-reference.mdx b/discord/developers/docs/discord-social-sdk/social-sdk-reference.mdx new file mode 100644 index 0000000000..97c3ec7974 --- /dev/null +++ b/discord/developers/docs/discord-social-sdk/social-sdk-reference.mdx @@ -0,0 +1,6 @@ +--- +title: Social SDK Reference +icon: "arrow-up-right-from-square" +url: https://discord.com/developers/docs/social-sdk/index.html +description: Complete API reference for the Discord Social SDK. +--- diff --git a/discord/developers/docs/discovery/best-practices.mdx b/discord/developers/docs/discovery/best-practices.mdx new file mode 100644 index 0000000000..acaeac14c6 --- /dev/null +++ b/discord/developers/docs/discovery/best-practices.mdx @@ -0,0 +1,101 @@ +--- +title: Discovery Best Practices +sidebarTitle: Best Practices +description: Tips and best practices for optimizing your app's discovery. +--- + +So you’ve made an app on Discord and are ready to opt in to discovery on the App Directory! Or maybe you have already listed your app but aren’t seeing as much traction to it as you’d like? Whatever stage you’re at, this guide has some tips and tricks from your friendly Discord Staff members to help boost performance of your App Directory Product Page. + + +This guide references settings and information you can set up and modify within your [app's settings](https://discord.com/developers/applications). If you don't have an app yet, you can follow the [Getting Started guide](/developers/docs/quick-start/getting-started). + + +## The Elevator Pitch + +### Quickly tell your users what your app does + +App descriptions should convey the value of your app and what it does. Make your app descriptions punchy and to the point - letting folks know what your app does in simple terms while also exciting the potential user to add the app and start using it. + + +Be sure to spell check and review your grammar before posting your descriptions. Your app has the potential to be seen by millions of people! + + +There are a few places where you can define different descriptions of your app within your [app's settings](https://discord.com/developers/applications). + +#### App General Information Description + +On the **General Information** tab, there is a general **Description** field (max of 400 characters) that appears within your bot user's profile. When a new user clicks on your app within Discord, they'll see this description. + +![App description on the General Information tab](/images/discovery/app-description.png) + +#### App Summary + +On the **App Directory** tab, the **Summary** field is a short description (max of 200 characters) that users will see when you appear within search results on the App Directory. + +When writing your app's Summary, think about how to grab the user's attention and quickly convey the value of your app. Consider the following description: + +![Poorly-written app Summary on the App Directory tab](/images/discovery/summary-bad.png) + +While this could be true, it doesn’t really tell folks how the app makes servers better or more fun. The best descriptions start with an attention grabbing sentence that describes a problem a user might want to solve: + +![Well-written app Summary on the App Directory tab](/images/discovery/summary-good.png) + +#### App Expanded Description + +On the **App Directory** tab, there is an **Expanded Description** field near the bottom of the page. This description appears when someone clicks on your app and enters its profile within the App Directory. This is your opportunity to showcase why a user should install your app, and the best functionality your app has to offer. The description also supports formatting via markdown. + +When writing your app's Expanded Description, the first thing you do should be to convey the value of the app to users. After the value is clear, continue on with how to get started with your app after it’s installed. + +## Show Your Story + +### Include as many relevant visuals as possible + +#### Show Your App's Functionality + +Add images and gifs to your Product Page and give users a better idea of the amazing functionality of your app. You can start by showing off some of the commands you mentioned in your description and how they work in Discord. What are some of your app’s most popular commands? How do existing users generally interact with your app? The answers to these questions make a great starting point for images on your Product Page. + +Additionally, new users need to know what to expect when they install the app so great images should showcase your app in action within a Discord server. The more your images display what your app does and how it appears in Discord, the better. + +#### Include Your App's Lore + +Other images you may want to include are ones related to your app’s lore (if any). For example, say there was a “Hypesquad App,” I would include the Hypesquad badges and what each of them means (I’m personally Hypesquad balance myself). + +#### Add a Video + +After adding some images, make a quick screen recording to demonstrate your app’s functionality. Videos are awesome but it's definitely helpful to include images as well, especially for folks who may not watch the entire video. + +## Find Your Users + +### Utilize search to your advantage + +Think of up to five words that describe your app and add them as tags (see image below for an example). Consider what categories your app would fit under or keywords users would type into the search bar when looking for apps to add to their servers. + +![App tags which help categorize apps and make them more searchable](/images/discovery/tags.png) + +## Support Your Users + +### Engage with your users directly + +Your app’s support server is a paramount part of your App Product Page. It’s important to ensure your app has a dedicated server and channel for communication between your app’s users and its developers and maintainers. + + +If your support server isn't [discoverable](https://support.discord.com/hc/en-us/articles/360030843331-Enabling-Server-Discovery), be sure to include an invite link in the "links" section of your App Directory Product Page. + + +Some ways you can successfully use your support server includes: + +- Encourage users to share feedback or their experience with your app. +- Offer technical support for users of your app. +- Share app updates in your support server and update your App’s Product Page description if it changes the functionality of your app. + +For general best practices on how to run a server, check out our [Community page](https://discord.com/community). + +## General Housekeeping + +### Final checks to make sure you’re ready to go + +Before you can click the final “Enable Discovery” button, ensure that your App Directory Product Page complies with the rest of Discord’s Directory eligibility criteria, [app content requirements](https://support-dev.discord.com/hc/en-us/articles/9489299950487-App-Directory-App-Content-Requirements-Policy) and Developer [Terms of Service](https://support-dev.discord.com/hc/en-us/articles/8562894815383) and [Policy](https://support-dev.discord.com/hc/en-us/articles/8563934450327). + +For more details about what this information entails, read our [Help Center article on Profile Pages](https://support-dev.discord.com/hc/en-us/articles/6378525413143-App-Directory-App-profile-pages). + +Now you're ready to publish your new App Directory profile and watch your app grow! If you haven’t already, join our official [Discord Developers server](https://discord.gg/discord-developers) to receive more information on how to update your apps. Way to go, Discord Developer! diff --git a/discord/developers/docs/discovery/enabling-discovery.mdx b/discord/developers/docs/discovery/enabling-discovery.mdx new file mode 100644 index 0000000000..7cba0ecdfa --- /dev/null +++ b/discord/developers/docs/discovery/enabling-discovery.mdx @@ -0,0 +1,61 @@ +--- +title: Enabling Discovery +description: Step-by-step guide to verify your app and enable discovery. +--- + +import {BookCheckIcon} from '/snippets/icons/BookCheckIcon.jsx' + +Are you ready for your app to be discovered by new users and server admins? + +Enabling **Discovery** for your app will make it available in the [App Directory](/developers/docs/discovery/overview#app-directory) and [App Launcher](/developers/docs/discovery/overview#app-launcher) for users to search for and install. + +## Step 1: Verify Your App + +To enable **Discovery** for your app, we require your team owner to complete identity and application verification. + +**App Verification** also allows you to add [monetization features](/developers/docs/monetization/overview) to your app, such as in-app purchases and subscriptions. + +To see the list of requirements for **App Verification**: + +1. Visit the [Developer Portal](https://discord.com/developers/applications) and select your app. +2. Select `App Verification` from the left-hand menu to see the requirements for verification. +3. Complete the listed App Verification qualification criteria for your app. +4. Once you've completed the requirements, you can submit your app for verification. + +For more information on **App Verification**, check out our [Help Center article](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified). + +## Step 2: Opt into Discovery + +After you've verified your app, you can opt into **Discovery** in the Developer Portal, which will make your app available in the App Directory and App Launcher. + +To opt into **Discovery**: + +1. Visit the [Developer Portal](https://discord.com/developers/applications) and select your app. +2. Select `Discovery -> Discovery Status` from the left-hand menu to see the requirements for enabling discovery. +3. Complete the listed **Discovery** qualification criteria for your app. +4. Add your app metadata and images under the `Discovery -> Discovery Settings` menu to customize your app's appearance in discovery surfaces. +4. Once you've completed the requirements, you can enable **Discovery** for your app. + +For more information on **Discovery**, check out our [Help Center article](https://support-dev.discord.com/hc/en-us/articles/21204493235991-How-Can-Users-Discover-and-Play-My-Activity). + + +Once you enable **Discovery**, it may take up to 24 hours for your app to appear in the App Directory and App Launcher. + + +### Search for your App in Discord +To check if your app is discoverable, search for it in the App Directory or App Launcher. + +1. Head to the [App Directory](https://discord.com/application-directory) and search for your app by name. +2. If you see it, you're good to go! 🎉 +3. If not, make sure you've completed all the steps above and wait up to 24 hours for your app to appear. + + --- + +## Next Steps +Now that you've enabled **Discovery** for your app, you can start customizing your app's appearance in the App Directory and App Launcher. + + + } iconType="solid"> + Check out our guide on how to make your app stand out. + + \ No newline at end of file diff --git a/discord/developers/docs/discovery/overview.mdx b/discord/developers/docs/discovery/overview.mdx new file mode 100644 index 0000000000..6f1f92e611 --- /dev/null +++ b/discord/developers/docs/discovery/overview.mdx @@ -0,0 +1,77 @@ +--- +title: Discovery on Discord +sidebarTitle: Overview +description: Learn how to make your Discord app discoverable through the App Directory. +--- + +Learn how to make your app discoverable on Discord through various surfaces and best practices to drive more users to discover it. + +## Discovery Surfaces + +Once you've [enabled discovery for your app](/developers/docs/discovery/enabling-discovery), users can find your app in Discord through the App Directory and the App Launcher. + +### App Directory + +The App Directory is a central hub where users can discover new apps. They can search by name, category, or collection and view details about each app. + +- **Search**: Users can search for your app by name and install it. +- **App Directory Product Page**: Share information about your app, including descriptions, images, videos, and links. + +![Your Discord App Profile](/images/discovery/directory-product-page.png) + +### App Launcher + +The App Launcher lets users discover new apps through collections and search from the app shapes icon throughout Discord. + +- **Search**: Your app will be available to search and install. +- **Collections**: Includes Recent Apps, Installed Apps, Currated Apps, Partner Apps, and Promoted Apps. + +![App Launcher in Discord](/images/discovery/app-launcher.png) + +#### App Launcher Collections can include: +- **Recent Apps**: Apps you've recently used or installed will appear at the top of the App Launcher +- **Installed Apps**: Apps already added to your account or server (if applicable) +- **Curated Collections**: A mix of user favorites, staff picks, and recommended apps +- **Partner Apps**: Some apps may be developed in collaboration with Discord, marked with a "Partner" tag +- **Promoted Apps**: Apps given more visibility in the App Launcher or App Directory, identified by a "Promoted" tag + +Search results are ranked based on relevance to the query and popularity based on usage. Apps that use ads or offer in-app purchases are marked with "Ad-supported" and "In-App Purchases" tags on their details page. + +#### Editorial Recommendations + +Apps can be featured in the App Directory or App Launcher as part of a curated collection or promotion. Discord's editorial team will reach out to you if we are interested in featuring your app. + +--- + +## Social Discovery + +Once your app is discoverable, there are several ways users may find it through social interactions in Discord. Users may discover your app through other users using it. When your app is used in a server, it may be visible to other users in the server, allowing users to learn more about it and install it themselves from your app's profile. + +### Sharing Links + +Create shareable links to your app's profile page, store page, or specific items. Share these links in Discord, emails, or on your website. + +![A shared embed link in Discord for the Sandscape app](/images/discovery/sharing-links.png) + +### Rich Presence +Use [Rich Presence](/developers/docs/rich-presence/overview) to show what users are doing in your app, driving more users to discover it. + +![Examples of Rich Presence data on Discord user profiles](/images/rich-presence/examples.png) + +--- + +## Next Steps + +Ready to enable discovery for your app? + +import {GlobeEarthIcon} from '/snippets/icons/GlobeEarthIcon.jsx' +import {BookCheckIcon} from '/snippets/icons/BookCheckIcon.jsx' + + + }> + Enable discovery for your app to make it available in the App Directory and App Launcher. + + }> + Learn how to make your app stand out and drive more users to discover it. + + \ No newline at end of file diff --git a/discord/developers/docs/events/gateway-events.mdx b/discord/developers/docs/events/gateway-events.mdx new file mode 100644 index 0000000000..3b95890b6f --- /dev/null +++ b/discord/developers/docs/events/gateway-events.mdx @@ -0,0 +1,1585 @@ +--- +title: Gateway Events +description: Complete reference for all Gateway events. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +Gateway connections are WebSockets, meaning they're bidirectional and either side of the WebSocket can send events to the other. The following events are split up into two types: + +- **Send events** are Gateway events sent by an app to Discord (like when identifying with the Gateway) +- **Receive events** are Gateway events that are sent by Discord to an app. These events typically represent something happening inside of a server where an app is installed, like a channel being updated. + +All Gateway events are encapsulated in a [Gateway event payload](/developers/docs/events/gateway-events#payload-structure). + +For more information about interacting with the Gateway, you can reference the [Gateway documentation](/developers/docs/events/gateway). + + +Not all Gateway event fields are documented. You should assume that undocumented fields are not supported for apps, and their format and data may change at any time. + + +### Event Names + +In practice, event names are UPPER-CASED with under_scores joining each word in the name. For instance, [Channel Create](/developers/docs/events/gateway-events#channel-create) would be `CHANNEL_CREATE` and [Voice State Update](/developers/docs/events/gateway-events#voice-state-update) would be `VOICE_STATE_UPDATE`. + +For readability, event names in the following documentation are typically left in Title Case. + +### Payload Structure + +Gateway event payloads have a common structure, but the contents of the associated data (`d`) varies between the different events. + +| Field | Type | Description | +|-------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| op | integer | [Gateway opcode](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes), which indicates the payload type | +| d | ?mixed (any JSON value) | Event data | +| s | ?integer \* | Sequence number of event used for [resuming sessions](/developers/docs/events/gateway#resuming) and [heartbeating](/developers/docs/events/gateway#sending-heartbeats) | +| t | ?string \* | Event name | + +\* `s` and `t` are `null` when `op` is not `0` ([Gateway Dispatch opcode](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes)). + + +###### Example Gateway Event Payload + +```json +{ + "op": 0, + "d": {}, + "s": 42, + "t": "GATEWAY_EVENT_NAME" +} +``` + +## Send Events + +Send events are Gateway events encapsulated in an [event payload](/developers/docs/events/gateway-events#payload-structure), and are sent by an app to Discord through a Gateway connection. + + +Previously, Gateway send events were labeled as commands + + +| Name | Description | +|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------| +| [Identify](/developers/docs/events/gateway-events#identify) | Triggers the initial handshake with the gateway | +| [Resume](/developers/docs/events/gateway-events#resume) | Resumes a dropped gateway connection | +| [Heartbeat](/developers/docs/events/gateway-events#heartbeat) | Maintains an active gateway connection | +| [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) | Requests members for a guild | +| [Request Soundboard Sounds](/developers/docs/events/gateway-events#request-soundboard-sounds) | Requests soundboard sounds in a set of guilds | +| [Update Voice State](/developers/docs/events/gateway-events#update-voice-state) | Joins, moves, or disconnects the app from a voice channel | +| [Update Presence](/developers/docs/events/gateway-events#update-presence) | Updates an app's presence | + +#### Identify + +Used to trigger the initial handshake with the gateway. + +Details about identifying is in the [Gateway documentation](/developers/docs/events/gateway#identifying). + + +###### Identify Structure + +| Field | Type | Description | Default | +|------------------|----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|---------| +| token | string | Authentication token | - | +| properties | object | [Connection properties](/developers/docs/events/gateway-events#identify-identify-connection-properties) | - | +| compress? | boolean | Whether this connection supports compression of packets | false | +| large_threshold? | integer | Value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list | 50 | +| shard? | array of two integers (shard_id, num_shards) | Used for [Guild Sharding](/developers/docs/events/gateway#sharding) | - | +| presence? | [update presence](/developers/docs/events/gateway-events#update-presence) object | Presence structure for initial presence information | - | +| intents | integer | [Gateway Intents](/developers/docs/events/gateway#gateway-intents) you wish to receive | - | + + +###### Identify Connection Properties + +| Field | Type | Description | +|---------|--------|-----------------------| +| os | string | Your operating system | +| browser | string | Your library name | +| device | string | Your library name | + + +These fields originally were $ prefixed (i.e: `$browser`) but [this syntax is deprecated](/developers/docs/change-log#updated-connection-property-field-names). While they currently still work, it is recommended to move to non-prefixed fields. + + + +###### Example Identify + +```json +{ + "op": 2, + "d": { + "token": "my_token", + "properties": { + "os": "linux", + "browser": "disco", + "device": "disco" + }, + "compress": true, + "large_threshold": 250, + "shard": [0, 1], + "presence": { + "activities": [{ + "name": "Cards Against Humanity", + "type": 0 + }], + "status": "dnd", + "since": 91879201, + "afk": false + }, + // This intent represents 1 << 0 for GUILDS, 1 << 1 for GUILD_MEMBERS, and 1 << 2 for GUILD_BANS + // This connection will only receive the events defined in those three intents + "intents": 7 + } +} +``` + +#### Resume + +Used to replay missed events when a disconnected client resumes. + +Details about resuming are in the [Gateway documentation](/developers/docs/events/gateway#resuming). + + +###### Resume Structure + +| Field | Type | Description | +|------------|---------|-------------------------------| +| token | string | Session token | +| session_id | string | Session ID | +| seq | integer | Last sequence number received | + + +###### Example Resume + +```json +{ + "op": 6, + "d": { + "token": "randomstring", + "session_id": "evenmorerandomstring", + "seq": 1337 + } +} +``` + +#### Heartbeat + +Used to maintain an active gateway connection. Must be sent every `heartbeat_interval` milliseconds after the [Opcode 10 Hello](/developers/docs/events/gateway-events#hello) payload is received. The inner `d` key is the last sequence number—`s`—received by the client. If you have not yet received one, send `null`. + +Details about heartbeats are in the [Gateway documentation](/developers/docs/events/gateway#sending-heartbeats). + + +###### Example Heartbeat + +```json +{ + "op": 1, + "d": 251 +} +``` + +#### Request Guild Members + +Used to request all members for a guild or a list of guilds. When initially connecting, if you don't have the `GUILD_PRESENCES` [Gateway Intent](/developers/docs/events/gateway#gateway-intents), or if the guild is over 75k members, it will only send members who are in voice, plus the member for you (the connecting user). Otherwise, if a guild has over `large_threshold` members (value in the [Gateway Identify](/developers/docs/events/gateway-events#identify)), it will only send members who are online, have a role, have a nickname, or are in a voice channel, and if it has under `large_threshold` members, it will send all members. If a client wishes to receive additional members, they need to explicitly request them via this operation. The server will send [Guild Members Chunk](/developers/docs/events/gateway-events#guild-members-chunk) events in response with up to 1000 members per chunk until all members that match the request have been sent. + +Due to our privacy and infrastructural concerns with this feature, there are some limitations that apply: + +- `GUILD_PRESENCES` intent is required to set `presences = true`. Otherwise, it will always be false +- `GUILD_MEMBERS` intent is required to request the entire member list—`(query=‘’, limit=0<=n)` +- You will be limited to requesting 1 `guild_id` per request +- Requesting a prefix (`query` parameter) will return a maximum of 100 members +- Requesting `user_ids` will continue to be limited to returning 100 members + + +We are introducing a new rate limit to the Request Guild Members opcode. See [Introducing Rate Limit When Requesting All Guild Members](/developers/docs/change-log#introducing-rate-limit-when-requesting-all-guild-members) for more information and timeline on this new rate limit. + + + +###### Request Guild Members Structure + +| Field | Type | Description | Required | +|------------|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|----------------------------| +| guild_id | snowflake | ID of the guild to get members for | true | +| query? | string | string that username starts with, or an empty string to return all members | one of query or user_ids | +| limit | integer | maximum number of members to send matching the `query`; a limit of `0` can be used with an empty string `query` to return all members | true when specifying query | +| presences? | boolean | used to specify if we want the presences of the matched members | false | +| user_ids? | snowflake or array of snowflakes | used to specify which users you wish to fetch | one of query or user_ids | +| nonce? | string | nonce to identify the [Guild Members Chunk](/developers/docs/events/gateway-events#guild-members-chunk) response | false | + + +Nonce can only be up to 32 bytes. If you send an invalid nonce it will be ignored and the reply member_chunk(s) will not have a nonce set. + + + +###### Example Request Guild Members + +```json +{ + "op": 8, + "d": { + "guild_id": "41771983444115456", + "query": "", + "limit": 0 + } +} +``` + +#### Request Soundboard Sounds + +Used to request soundboard sounds for a list of guilds. The server will send [Soundboard Sounds](/developers/docs/events/gateway-events#soundboard-sounds) events for each guild in response. + + +###### Request Soundboard Sounds Structure + +| Field | Type | Description | +|-----------|---------------------|------------------------------------------------| +| guild_ids | array of snowflakes | IDs of the guilds to get soundboard sounds for | + + +###### Example Request Soundboard Sounds + +```json +{ + "op": 31, + "d": { + "guild_ids": ["613425648685547541", "81384788765712384"] + } +} +``` + +#### Update Voice State + +Sent when a client wants to join, move, or disconnect from a voice channel. + + +###### Gateway Voice State Update Structure + +| Field | Type | Description | +|------------|------------|----------------------------------------------------------------------| +| guild_id | snowflake | ID of the guild | +| channel_id | ?snowflake | ID of the voice channel client wants to join (null if disconnecting) | +| self_mute | boolean | Whether the client is muted | +| self_deaf | boolean | Whether the client deafened | + + +###### Example Gateway Voice State Update + +```json +{ + "op": 4, + "d": { + "guild_id": "41771983423143937", + "channel_id": "127121515262115840", + "self_mute": false, + "self_deaf": false + } +} +``` + +#### Update Presence + +Sent by the client to indicate a presence or status update. + + +###### Gateway Presence Update Structure + +| Field | Type | Description | +|------------|-------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| since | ?integer | Unix time (in milliseconds) of when the client went idle, or null if the client is not idle | +| activities | array of [activity](/developers/docs/events/gateway-events#activity-object) objects | User's activities | +| status | string | User's new [status](/developers/docs/events/gateway-events#update-presence-status-types) | +| afk | boolean | Whether or not the client is afk | + + +###### Status Types + +| Status | Description | +|-----------|--------------------------------| +| online | Online | +| dnd | Do Not Disturb | +| idle | AFK | +| invisible | Invisible and shown as offline | +| offline | Offline | + + +###### Example Gateway Presence Update + +```json +{ + "op": 3, + "d": { + "since": 91879201, + "activities": [{ + "name": "Save the Oxford Comma", + "type": 0 + }], + "status": "online", + "afk": false + } +} +``` + +## Receive Events + +Receive events are Gateway events encapsulated in an [event payload](/developers/docs/events/gateway-events#payload-structure), and are sent by Discord to an app through a Gateway connection. Receive events correspond to events that happen in a Discord server where the app is installed. + +| Name | Description | +|-------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Hello](/developers/docs/events/gateway-events#hello) | Defines the heartbeat interval | +| [Ready](/developers/docs/events/gateway-events#ready) | Contains the initial state information | +| [Resumed](/developers/docs/events/gateway-events#resumed) | Response to [Resume](/developers/docs/events/gateway-events#resume) | +| [Reconnect](/developers/docs/events/gateway-events#reconnect) | Server is going away, client should reconnect to gateway and resume | +| [Rate Limited](/developers/docs/events/gateway-events#rate-limited) | Application was rate limited for a gateway opcode | +| [Invalid Session](/developers/docs/events/gateway-events#invalid-session) | Failure response to [Identify](/developers/docs/events/gateway-events#identify) or [Resume](/developers/docs/events/gateway-events#resume) or invalid active session | +| [Application Command Permissions Update](/developers/docs/events/gateway-events#application-command-permissions-update) | Application command permission was updated | +| [Auto Moderation Rule Create](/developers/docs/events/gateway-events#auto-moderation-rule-create) | Auto Moderation rule was created | +| [Auto Moderation Rule Update](/developers/docs/events/gateway-events#auto-moderation-rule-update) | Auto Moderation rule was updated | +| [Auto Moderation Rule Delete](/developers/docs/events/gateway-events#auto-moderation-rule-delete) | Auto Moderation rule was deleted | +| [Auto Moderation Action Execution](/developers/docs/events/gateway-events#auto-moderation-action-execution) | Auto Moderation rule was triggered and an action was executed (e.g. a message was blocked) | +| [Channel Create](/developers/docs/events/gateway-events#channel-create) | New guild channel created | +| [Channel Update](/developers/docs/events/gateway-events#channel-update) | Channel was updated | +| [Channel Delete](/developers/docs/events/gateway-events#channel-delete) | Channel was deleted | +| [Channel Pins Update](/developers/docs/events/gateway-events#channel-pins-update) | Message was pinned or unpinned | +| [Thread Create](/developers/docs/events/gateway-events#thread-create) | Thread created, also sent when being added to a private thread | +| [Thread Update](/developers/docs/events/gateway-events#thread-update) | Thread was updated | +| [Thread Delete](/developers/docs/events/gateway-events#thread-delete) | Thread was deleted | +| [Thread List Sync](/developers/docs/events/gateway-events#thread-list-sync) | Sent when gaining access to a channel, contains all active threads in that channel | +| [Thread Member Update](/developers/docs/events/gateway-events#thread-member-update) | [Thread member](/developers/docs/resources/channel#thread-member-object) for the current user was updated | +| [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) | Some user(s) were added to or removed from a thread | +| [Entitlement Create](/developers/docs/events/gateway-events#entitlement-create) | Entitlement was created | +| [Entitlement Update](/developers/docs/events/gateway-events#entitlement-update) | Entitlement was updated or renewed | +| [Entitlement Delete](/developers/docs/events/gateway-events#entitlement-delete) | Entitlement was deleted | +| [Guild Create](/developers/docs/events/gateway-events#guild-create) | Lazy-load for unavailable guild, guild became available, or user joined a new guild | +| [Guild Update](/developers/docs/events/gateway-events#guild-update) | Guild was updated | +| [Guild Delete](/developers/docs/events/gateway-events#guild-delete) | Guild became unavailable, or user left/was removed from a guild | +| [Guild Audit Log Entry Create](/developers/docs/events/gateway-events#guild-audit-log-entry-create) | A guild audit log entry was created | +| [Guild Ban Add](/developers/docs/events/gateway-events#guild-ban-add) | User was banned from a guild | +| [Guild Ban Remove](/developers/docs/events/gateway-events#guild-ban-remove) | User was unbanned from a guild | +| [Guild Emojis Update](/developers/docs/events/gateway-events#guild-emojis-update) | Guild emojis were updated | +| [Guild Stickers Update](/developers/docs/events/gateway-events#guild-stickers-update) | Guild stickers were updated | +| [Guild Integrations Update](/developers/docs/events/gateway-events#guild-integrations-update) | Guild integration was updated | +| [Guild Member Add](/developers/docs/events/gateway-events#guild-member-add) | New user joined a guild | +| [Guild Member Remove](/developers/docs/events/gateway-events#guild-member-remove) | User was removed from a guild | +| [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) | Guild member was updated | +| [Guild Members Chunk](/developers/docs/events/gateway-events#guild-members-chunk) | Response to [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) | +| [Guild Role Create](/developers/docs/events/gateway-events#guild-role-create) | Guild role was created | +| [Guild Role Update](/developers/docs/events/gateway-events#guild-role-update) | Guild role was updated | +| [Guild Role Delete](/developers/docs/events/gateway-events#guild-role-delete) | Guild role was deleted | +| [Guild Scheduled Event Create](/developers/docs/events/gateway-events#guild-scheduled-event-create) | Guild scheduled event was created | +| [Guild Scheduled Event Update](/developers/docs/events/gateway-events#guild-scheduled-event-update) | Guild scheduled event was updated | +| [Guild Scheduled Event Delete](/developers/docs/events/gateway-events#guild-scheduled-event-delete) | Guild scheduled event was deleted | +| [Guild Scheduled Event User Add](/developers/docs/events/gateway-events#guild-scheduled-event-user-add) | User subscribed to a guild scheduled event | +| [Guild Scheduled Event User Remove](/developers/docs/events/gateway-events#guild-scheduled-event-user-remove) | User unsubscribed from a guild scheduled event | +| [Guild Soundboard Sound Create](/developers/docs/events/gateway-events#guild-soundboard-sound-create) | Guild soundboard sound was created | +| [Guild Soundboard Sound Update](/developers/docs/events/gateway-events#guild-soundboard-sound-update) | Guild soundboard sound was updated | +| [Guild Soundboard Sound Delete](/developers/docs/events/gateway-events#guild-soundboard-sound-delete) | Guild soundboard sound was deleted | +| [Guild Soundboard Sounds Update](/developers/docs/events/gateway-events#guild-soundboard-sounds-update) | Guild soundboard sounds were updated | +| [Soundboard Sounds](/developers/docs/events/gateway-events#soundboard-sounds) | Response to [Request Soundboard Sounds](/developers/docs/events/gateway-events#request-soundboard-sounds) | +| [Integration Create](/developers/docs/events/gateway-events#integration-create) | Guild integration was created | +| [Integration Update](/developers/docs/events/gateway-events#integration-update) | Guild integration was updated | +| [Integration Delete](/developers/docs/events/gateway-events#integration-delete) | Guild integration was deleted | +| [Interaction Create](/developers/docs/events/gateway-events#interaction-create) | User used an interaction, such as an [Application Command](/developers/docs/interactions/application-commands) | +| [Invite Create](/developers/docs/events/gateway-events#invite-create) | Invite to a channel was created | +| [Invite Delete](/developers/docs/events/gateway-events#invite-delete) | Invite to a channel was deleted | +| [Message Create](/developers/docs/events/gateway-events#message-create) | Message was created | +| [Message Update](/developers/docs/events/gateway-events#message-update) | Message was edited | +| [Message Delete](/developers/docs/events/gateway-events#message-delete) | Message was deleted | +| [Message Delete Bulk](/developers/docs/events/gateway-events#message-delete-bulk) | Multiple messages were deleted at once | +| [Message Reaction Add](/developers/docs/events/gateway-events#message-reaction-add) | User reacted to a message | +| [Message Reaction Remove](/developers/docs/events/gateway-events#message-reaction-remove) | User removed a reaction from a message | +| [Message Reaction Remove All](/developers/docs/events/gateway-events#message-reaction-remove-all) | All reactions were explicitly removed from a message | +| [Message Reaction Remove Emoji](/developers/docs/events/gateway-events#message-reaction-remove-emoji) | All reactions for a given emoji were explicitly removed from a message | +| [Presence Update](/developers/docs/events/gateway-events#presence-update) | User was updated | +| [Stage Instance Create](/developers/docs/events/gateway-events#stage-instance-create) | Stage instance was created | +| [Stage Instance Update](/developers/docs/events/gateway-events#stage-instance-update) | Stage instance was updated | +| [Stage Instance Delete](/developers/docs/events/gateway-events#stage-instance-delete) | Stage instance was deleted or closed | +| [Subscription Create](/developers/docs/events/gateway-events#subscription-create) | Premium App Subscription was created | +| [Subscription Update](/developers/docs/events/gateway-events#subscription-update) | Premium App Subscription was updated | +| [Subscription Delete](/developers/docs/events/gateway-events#subscription-delete) | Premium App Subscription was deleted | +| [Typing Start](/developers/docs/events/gateway-events#typing-start) | User started typing in a channel | +| [User Update](/developers/docs/events/gateway-events#user-update) | Properties about the user changed | +| [Voice Channel Effect Send](/developers/docs/events/gateway-events#voice-channel-effect-send) | Someone sent an effect in a voice channel the current user is connected to | +| [Voice State Update](/developers/docs/events/gateway-events#voice-state-update) | Someone joined, left, or moved a voice channel | +| [Voice Server Update](/developers/docs/events/gateway-events#voice-server-update) | Guild's voice server was updated | +| [Webhooks Update](/developers/docs/events/gateway-events#webhooks-update) | Guild channel webhook was created, update, or deleted | +| [Message Poll Vote Add](/developers/docs/events/gateway-events#message-poll-vote-add) | User voted on a poll | +| [Message Poll Vote Remove](/developers/docs/events/gateway-events#message-poll-vote-remove) | User removed a vote on a poll | + +#### Hello + +Sent on connection to the websocket. Defines the heartbeat interval that an app should heartbeat to. + + +###### Hello Structure + +| Field | Type | Description | +|--------------------|---------|---------------------------------------------------------| +| heartbeat_interval | integer | Interval (in milliseconds) an app should heartbeat with | + + +###### Example Hello + +```json +{ + "op": 10, + "d": { + "heartbeat_interval": 45000 + } +} +``` + +#### Ready + +The ready event is dispatched when a client has completed the initial handshake with the gateway (for new sessions). The ready event can be the largest and most complex event the gateway will send, as it contains all the state required for a client to begin interacting with the rest of the platform. + +`guilds` are the guilds of which your bot is a member. They start out as unavailable when you connect to the gateway. As they become available, your bot will be notified via [Guild Create](/developers/docs/events/gateway-events#guild-create) events. + + +###### Ready Event Fields + +| Field | Type | Description | +|--------------------|-------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------| +| v | integer | [API version](/developers/docs/reference#api-versioning-api-versions) | +| user | [user](/developers/docs/resources/user#user-object) object | Information about the user including email | +| guilds | array of [Unavailable Guild](/developers/docs/resources/guild#unavailable-guild-object) objects | Guilds the user is in | +| session_id | string | Used for resuming connections | +| resume_gateway_url | string | Gateway URL for resuming connections | +| shard? | array of two integers (shard_id, num_shards) | [Shard information](/developers/docs/events/gateway#sharding) associated with this session, if sent when identifying | +| application | partial [application object](/developers/docs/resources/application#application-object) | Contains `id` and `flags` | + +#### Resumed + +The resumed event is dispatched when a client has sent a [resume payload](/developers/docs/events/gateway-events#resume) to the gateway (for resuming existing sessions). + +#### Reconnect + +The reconnect event is dispatched when a client should reconnect to the gateway (and resume their existing session, if they have one). This can occur at any point in the gateway connection lifecycle, even before/in place of receiving a [Hello](/developers/docs/events/gateway-events#hello) event. A few seconds after the reconnect event is dispatched, the connection may be closed by the server. + + +###### Example Gateway Reconnect + +```json +{ + "op": 7, + "d": null +} +``` + +#### Invalid Session + +Sent to indicate one of at least three different situations: + +- the gateway could not initialize a session after receiving an [Opcode 2 Identify](/developers/docs/events/gateway-events#identify) +- the gateway could not resume a previous session after receiving an [Opcode 6 Resume](/developers/docs/events/gateway-events#resume) +- the gateway has invalidated an active session and is requesting client action + +The inner `d` key is a boolean that indicates whether the session may be resumable. See [Connecting](/developers/docs/events/gateway#connecting) and [Resuming](/developers/docs/events/gateway#resuming) for more information. + + +###### Example Gateway Invalid Session + +```json +{ + "op": 9, + "d": false +} +``` + +### Application Commands + +#### Application Command Permissions Update + +`APPLICATION_COMMAND_PERMISSIONS_UPDATE` event, sent when an application command's permissions are updated. The inner payload is an [application command permissions](/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object. + +### Auto Moderation + +All [Auto Moderation](/developers/docs/resources/auto-moderation) related events are only sent to bot users which have the `MANAGE_GUILD` permission. + +#### Auto Moderation Rule Create + +Sent when a rule is created. The inner payload is an [auto moderation rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) object. + +#### Auto Moderation Rule Update + +Sent when a rule is updated. The inner payload is an [auto moderation rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) object. + +#### Auto Moderation Rule Delete + +Sent when a rule is deleted. The inner payload is an [auto moderation rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) object. + +#### Auto Moderation Action Execution + +Sent when a rule is triggered and an action is executed (e.g. when a message is blocked). + + +###### Auto Moderation Action Execution Event Fields + +| Field | Type | Description | +|--------------------------|-----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------| +| guild_id | snowflake | ID of the guild in which action was executed | +| action | [auto moderation action](/developers/docs/resources/auto-moderation#auto-moderation-action-object) object | Action which was executed | +| rule_id | snowflake | ID of the rule which action belongs to | +| rule_trigger_type | [trigger_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) | Trigger type of rule which was triggered | +| user_id | snowflake | ID of the user which generated the content which triggered the rule | +| channel_id? | snowflake | ID of the channel in which user content was posted | +| message_id? | snowflake | ID of any user message which content belongs to * | +| alert_system_message_id? | snowflake | ID of any system auto moderation messages posted as a result of this action ** | +| content *** | string | User-generated text content | +| matched_keyword | ?string | Word or phrase configured in the rule that triggered the rule | +| matched_content *** | ?string | Substring in content that triggered the rule | + +\* `message_id` will not exist if message was blocked by [Auto Moderation](/developers/docs/resources/auto-moderation) or content was not part of any message + +\*\* `alert_system_message_id` will not exist if this event does not correspond to an action with type `SEND_ALERT_MESSAGE` + +\*\*\* `MESSAGE_CONTENT` (`1 << 15`) [gateway intent](/developers/docs/events/gateway#gateway-intents) is required to receive the `content` and `matched_content` fields + +### Channels + +#### Channel Create + +Sent when a new guild channel is created, relevant to the current user. The inner payload is a [channel](/developers/docs/resources/channel#channel-object) object. + +#### Channel Update + +Sent when a channel is updated. The inner payload is a [channel](/developers/docs/resources/channel#channel-object) object. This is not sent when the field `last_message_id` is altered. To keep track of the last_message_id changes, you must listen for [Message Create](/developers/docs/events/gateway-events#message-create) events (or [Thread Create](/developers/docs/events/gateway-events#thread-create) events for `GUILD_FORUM` and `GUILD_MEDIA` channels). + +This event may reference roles or guild members that no longer exist in the guild. + +#### Channel Delete + +Sent when a channel relevant to the current user is deleted. The inner payload is a [channel](/developers/docs/resources/channel#channel-object) object. + +#### Thread Create + +Sent when a thread is created, relevant to the current user, or when the current user is added to a thread. The inner payload is a [channel](/developers/docs/resources/channel#channel-object) object. + +- When a thread is created, includes an additional `newly_created` boolean field. +- When being added to an existing private thread, includes a [thread member](/developers/docs/resources/channel#thread-member-object) object. + +#### Thread Update + +Sent when a thread is updated. The inner payload is a [channel](/developers/docs/resources/channel#channel-object) object. This is not sent when the field `last_message_id` is altered. To keep track of the last_message_id changes, you must listen for [Message Create](/developers/docs/events/gateway-events#message-create) events. + +#### Thread Delete + +Sent when a thread relevant to the current user is deleted. The inner payload is a subset of the [channel](/developers/docs/resources/channel#channel-object) object, containing just the `id`, `guild_id`, `parent_id`, and `type` fields. + +#### Thread List Sync + +Sent when the current user _gains_ access to a channel. + + +###### Thread List Sync Event Fields + +| Field | Type | Description | +|--------------|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| guild_id | snowflake | ID of the guild | +| channel_ids? | array of snowflakes | Parent channel IDs whose threads are being synced. If omitted, then threads were synced for the entire guild. This array may contain channel_ids that have no active threads as well, so you know to clear that data. | +| threads | array of [channel](/developers/docs/resources/channel#channel-object) objects | All active threads in the given channels that the current user can access | +| members | array of [thread member](/developers/docs/resources/channel#thread-member-object) objects | All thread member objects from the synced threads for the current user, indicating which threads the current user has been added to | + +#### Thread Member Update + +Sent when the [thread member](/developers/docs/resources/channel#thread-member-object) object for the current user is updated. The inner payload is a [thread member](/developers/docs/resources/channel#thread-member-object) object with an extra `guild_id` field. This event is documented for completeness, but unlikely to be used by most bots. For bots, this event largely is just a signal that you are a member of the thread. See the [threads docs](/developers/docs/topics/threads) for more details. + + +###### Thread Member Update Event Extra Fields + +| Field | Type | Description | +|----------|-----------|-----------------| +| guild_id | snowflake | ID of the guild | + + +#### Thread Members Update + +Sent when anyone is added to or removed from a thread. If the current user does not have the `GUILD_MEMBERS` [Gateway Intent](/developers/docs/events/gateway#gateway-intents), then this event will only be sent if the current user was added to or removed from the thread. + + +###### Thread Members Update Event Fields + +| Field | Type | Description | +|---------------------|-------------------------------------------------------------------------------------------|-----------------------------------------------------------| +| id | snowflake | ID of the thread | +| guild_id | snowflake | ID of the guild | +| member_count | integer | Approximate number of members in the thread, capped at 50 | +| added_members?\* | array of [thread member](/developers/docs/resources/channel#thread-member-object) objects | Users who were added to the thread | +| removed_member_ids? | array of snowflakes | ID of the users who were removed from the thread | + +\* In this gateway event, the thread member objects will also include the [guild member](/developers/docs/resources/guild#guild-member-object) and nullable [presence](/developers/docs/events/gateway-events#presence) objects for each added thread member. + +#### Channel Pins Update + +Sent when a message is pinned or unpinned in a text channel. This is not sent when a pinned message is deleted. + + +###### Channel Pins Update Event Fields + +| Field | Type | Description | +|---------------------|--------------------|---------------------------------------------------------| +| guild_id? | snowflake | ID of the guild | +| channel_id | snowflake | ID of the channel | +| last_pin_timestamp? | ?ISO8601 timestamp | Time at which the most recent pinned message was pinned | + +### Entitlements + +#### Entitlement Create + + +Note: The`ENTITLEMENT_CREATE` event behavior changed on October 1, 2024. Please see the [Change Log and Entitlement Migration Guide](/developers/docs/change-log#premium-apps-entitlement-migration-and-new-subscription-api) for more information on what changed. + + +Sent when an entitlement is created. The inner payload is an [entitlement](/developers/docs/resources/entitlement#entitlement-object) object. + +#### Entitlement Update + + +Note: The`ENTITLEMENT_UPDATE` event behavior changed on October 1, 2024. Please see the [Change Log and Entitlement Migration Guide](/developers/docs/change-log#premium-apps-entitlement-migration-and-new-subscription-api) for more information on what changed. + + +Sent when an entitlement is updated. The inner payload is an [entitlement](/developers/docs/resources/entitlement#entitlement-object) object. + +For subscription entitlements, this event is triggered only when a user's subscription ends, providing an `ends_at` timestamp that indicates the end of the entitlement. + +#### Entitlement Delete + +Sent when an entitlement is deleted. The inner payload is an [entitlement](/developers/docs/resources/entitlement#entitlement-object) object. + +Entitlement deletions are infrequent, and occur when: + +- Discord issues a refund for a subscription +- Discord removes an entitlement from a user via internal tooling +- Discord deletes an app-managed entitlement they created via the API + +Entitlements are _not_ deleted when they expire. + +### Guilds + +#### Guild Create + +This event can be sent in three different scenarios: + +1. When a user is initially connecting, to lazily load and backfill information for all unavailable guilds sent in the [Ready](/developers/docs/events/gateway-events#ready) event. Guilds that are unavailable due to an outage will send a [Guild Delete](/developers/docs/events/gateway-events#guild-delete) event. +2. When a Guild becomes available again to the client. +3. When the current user joins a new Guild. + + +During an outage, the guild object in scenarios 1 and 3 may be marked as unavailable. + + +The inner payload can be: + +- An available Guild: a [guild](/developers/docs/resources/guild#guild-object) object with extra fields, as noted below. +- An unavailable Guild: an [unavailable guild](/developers/docs/resources/guild#unavailable-guild-object) object. + + +###### Guild Create Extra Fields + +| Field | Type | Description | +|------------------------|-------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| joined_at | ISO8601 timestamp | When this guild was joined at | +| large | boolean | `true` if this is considered a large guild | +| unavailable? | boolean | `true` if this guild is unavailable due to an outage | +| member_count | integer | Total number of members in this guild | +| voice_states | array of partial [voice state](/developers/docs/resources/voice#voice-state-object) objects | States of members currently in voice channels; lacks the `guild_id` key | +| members | array of [guild member](/developers/docs/resources/guild#guild-member-object) objects | Users in the guild | +| channels | array of [channel](/developers/docs/resources/channel#channel-object) objects | Channels in the guild | +| threads | array of [channel](/developers/docs/resources/channel#channel-object) objects | All active threads in the guild that current user has permission to view | +| presences | array of partial [presence update](/developers/docs/events/gateway-events#presence-update) objects | Presences of the members in the guild, will only include non-offline members if the size is greater than `large threshold` | +| stage_instances | array of [stage instance](/developers/docs/resources/stage-instance#stage-instance-object) objects | Stage instances in the guild | +| guild_scheduled_events | array of [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) objects | Scheduled events in the guild | +| soundboard_sounds | array of [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) objects | Soundboard sounds in the guild | + + +If your bot does not have the `GUILD_PRESENCES` [Gateway Intent](/developers/docs/events/gateway#gateway-intents), or if the guild has over 75k members, members and presences returned in this event will only contain your bot and users in voice channels. + + +#### Guild Update + +Sent when a guild is updated. The inner payload is a [guild](/developers/docs/resources/guild#guild-object) object. + +#### Guild Delete + +Sent when a guild becomes or was already unavailable due to an outage, or when the user leaves or is removed from a guild. The inner payload is an [unavailable guild](/developers/docs/resources/guild#unavailable-guild-object) object. If the `unavailable` field is not set, the user was removed from the guild. + +#### Guild Audit Log Entry Create + +Sent when a guild audit log entry is created. The inner payload is an [Audit Log Entry](/developers/docs/resources/audit-log#audit-log-entry-object) object with an extra `guild_id` key. This event is only sent to bots with the `VIEW_AUDIT_LOG` permission. + + +###### Guild Audit Log Entry Create Event Extra Fields + +| Field | Type | Description | +|----------|-----------|-----------------| +| guild_id | snowflake | ID of the guild | + +#### Guild Ban Add + +Sent when a user is banned from a guild. + + +###### Guild Ban Add Event Fields + +| Field | Type | Description | +|----------|--------------------------------------------------------------|---------------------| +| guild_id | snowflake | ID of the guild | +| user | a [user](/developers/docs/resources/user#user-object) object | User who was banned | + +#### Guild Ban Remove + +Sent when a user is unbanned from a guild. + + +###### Guild Ban Remove Event Fields + +| Field | Type | Description | +|----------|--------------------------------------------------------------|-----------------------| +| guild_id | snowflake | ID of the guild | +| user | a [user](/developers/docs/resources/user#user-object) object | User who was unbanned | + +#### Guild Emojis Update + +Sent when a guild's emojis have been updated. + + +###### Guild Emojis Update Event Fields + +| Field | Type | Description | +|----------|-----------|------------------------------------------------------------------| +| guild_id | snowflake | ID of the guild | +| emojis | array | Array of [emojis](/developers/docs/resources/emoji#emoji-object) | + +#### Guild Stickers Update + +Sent when a guild's stickers have been updated. + + +###### Guild Stickers Update Event Fields + +| Field | Type | Description | +|----------|-----------|------------------------------------------------------------------------| +| guild_id | snowflake | ID of the guild | +| stickers | array | Array of [stickers](/developers/docs/resources/sticker#sticker-object) | + +#### Guild Integrations Update + +Sent when a guild integration is updated. + + +###### Guild Integrations Update Event Fields + +| Field | Type | Description | +|----------|-----------|-------------------------------------------------| +| guild_id | snowflake | ID of the guild whose integrations were updated | + +#### Guild Member Add + + +If using [Gateway Intents](/developers/docs/events/gateway#gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event. + + +Sent when a new user joins a guild. The inner payload is a [guild member](/developers/docs/resources/guild#guild-member-object) object with an extra `guild_id` key: + + +###### Guild Member Add Extra Fields + +| Field | Type | Description | +|----------|-----------|-----------------| +| guild_id | snowflake | ID of the guild | + +#### Guild Member Remove + + +If using [Gateway Intents](/developers/docs/events/gateway#gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event. + + +Sent when a user is removed from a guild (leave/kick/ban). + + +###### Guild Member Remove Event Fields + +| Field | Type | Description | +|----------|--------------------------------------------------------------|----------------------| +| guild_id | snowflake | ID of the guild | +| user | a [user](/developers/docs/resources/user#user-object) object | User who was removed | + +#### Guild Member Update + + +If using [Gateway Intents](/developers/docs/events/gateway#gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event. + + +Sent when a guild member is updated. This will also fire when the user object of a guild member changes. + + +###### Guild Member Update Event Fields + +| Field | Type | Description | +|-------------------------------|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| guild_id | snowflake | ID of the guild | +| roles | array of snowflakes | User role ids | +| user | a [user](/developers/docs/resources/user#user-object) object | User | +| nick? | ?string | Nickname of the user in the guild | +| avatar | ?string | Member's [guild avatar hash](/developers/docs/reference#image-formatting) | +| banner | ?string | Member's [guild banner hash](/developers/docs/reference#image-formatting) | +| joined_at | ?ISO8601 timestamp | When the user joined the guild | +| premium_since? | ?ISO8601 timestamp | When the user starting [boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-) the guild | +| deaf? | boolean | Whether the user is deafened in voice channels | +| mute? | boolean | Whether the user is muted in voice channels | +| pending? | boolean | Whether the user has not yet passed the guild's [Membership Screening](/developers/docs/resources/guild#membership-screening-object) requirements | +| communication_disabled_until? | ?ISO8601 timestamp | When the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out | +| avatar_decoration_data? | ?[avatar decoration data](/developers/docs/resources/user#avatar-decoration-data-object) | Data for the member's guild avatar decoration | + +#### Guild Members Chunk + +Sent in response to [Guild Request Members](/developers/docs/events/gateway-events#request-guild-members). +You can use the `chunk_index` and `chunk_count` to calculate how many chunks are left for your request. + + +###### Guild Members Chunk Event Fields + +| Field | Type | Description | +|-------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| guild_id | snowflake | ID of the guild | +| members | array of [guild member](/developers/docs/resources/guild#guild-member-object) objects | Set of guild members | +| chunk_index | integer | Chunk index in the expected chunks for this response (`0 <= chunk_index < chunk_count`) | +| chunk_count | integer | Total number of expected chunks for this response | +| not_found? | array | When passing an invalid ID to `REQUEST_GUILD_MEMBERS`, it will be returned here | +| presences? | array of [presence](/developers/docs/events/gateway-events#presence) objects | When passing `true` to `REQUEST_GUILD_MEMBERS`, presences of the returned members will be here | +| nonce? | string | Nonce used in the [Guild Members Request](/developers/docs/events/gateway-events#request-guild-members) | + +#### Guild Role Create + +Sent when a guild role is created. + + +###### Guild Role Create Event Fields + +| Field | Type | Description | +|----------|------------------------------------------------------------------|-----------------------| +| guild_id | snowflake | ID of the guild | +| role | a [role](/developers/docs/topics/permissions#role-object) object | Role that was created | + +#### Guild Role Update + +Sent when a guild role is updated. + + +###### Guild Role Update Event Fields + +| Field | Type | Description | +|----------|------------------------------------------------------------------|-----------------------| +| guild_id | snowflake | ID of the guild | +| role | a [role](/developers/docs/topics/permissions#role-object) object | Role that was updated | + +#### Guild Role Delete + +Sent when a guild role is deleted. + + +###### Guild Role Delete Event Fields + +| Field | Type | Description | +|----------|-----------|-----------------| +| guild_id | snowflake | ID of the guild | +| role_id | snowflake | ID of the role | + +#### Guild Scheduled Event Create + +Sent when a guild scheduled event is created. The inner payload is a [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) object. + +#### Guild Scheduled Event Update + +Sent when a guild scheduled event is updated. The inner payload is a [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) object. + +#### Guild Scheduled Event Delete + +Sent when a guild scheduled event is deleted. The inner payload is a [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) object. + +#### Guild Scheduled Event User Add + +Sent when a user has subscribed to a guild scheduled event. + + +###### Guild Scheduled Event User Add Event Fields + +| Field | Type | Description | +|--------------------------|-----------|---------------------------------| +| guild_scheduled_event_id | snowflake | ID of the guild scheduled event | +| user_id | snowflake | ID of the user | +| guild_id | snowflake | ID of the guild | + +#### Guild Scheduled Event User Remove + +Sent when a user has unsubscribed from a guild scheduled event. + + +###### Guild Scheduled Event User Remove Event Fields + +| Field | Type | Description | +|--------------------------|-----------|---------------------------------| +| guild_scheduled_event_id | snowflake | ID of the guild scheduled event | +| user_id | snowflake | ID of the user | +| guild_id | snowflake | ID of the guild | + +#### Guild Soundboard Sound Create + +Sent when a guild soundboard sound is created. The inner payload is a [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) object. + +#### Guild Soundboard Sound Update + +Sent when a guild soundboard sound is updated. The inner payload is a [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) object. + +#### Guild Soundboard Sound Delete + +Sent when a guild soundboard sound is deleted. + + +###### Guild Soundboard Sound Delete Event Fields + +| Field | Type | Description | +|----------|-----------|----------------------------------| +| sound_id | snowflake | ID of the sound that was deleted | +| guild_id | snowflake | ID of the guild the sound was in | + +#### Guild Soundboard Sounds Update + +Sent when multiple guild soundboard sounds are updated. + + +###### Guild Soundboard Sounds Update Event Fields + +| Field | Type | Description | +|-------------------|----------------------------------------------------------------------------------------------------|-------------------------------| +| soundboard_sounds | array of [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) objects | The guild's soundboard sounds | +| guild_id | snowflake | ID of the guild | + +#### Soundboard Sounds + +Includes a guild's list of soundboard sounds. Sent in response to [Request Soundboard Sounds](/developers/docs/events/gateway-events#request-soundboard-sounds). + + +###### Soundboard Sounds Event Fields + +| Field | Type | Description | +|-------------------|----------------------------------------------------------------------------------------------------|-------------------------------| +| soundboard_sounds | array of [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) objects | The guild's soundboard sounds | +| guild_id | snowflake | ID of the guild | + +### Integrations + +#### Integration Create + +Sent when an integration is created. The inner payload is an [integration](/developers/docs/resources/guild#integration-object) object with `user` omitted and an additional `guild_id` key: + + +###### Integration Create Event Additional Fields + +| Field | Type | Description | +|----------|-----------|-----------------| +| guild_id | snowflake | ID of the guild | + +#### Integration Update + +Sent when an integration is updated. The inner payload is an [integration](/developers/docs/resources/guild#integration-object) object with `user` omitted and an additional `guild_id` key: + + +###### Integration Update Event Additional Fields + +| Field | Type | Description | +|----------|-----------|-----------------| +| guild_id | snowflake | ID of the guild | + +#### Integration Delete + +Sent when an integration is deleted. + + +###### Integration Delete Event Fields + +| Field | Type | Description | +|-----------------|-----------|---------------------------------------------------------------| +| id | snowflake | Integration ID | +| guild_id | snowflake | ID of the guild | +| application_id? | snowflake | ID of the bot/OAuth2 application for this discord integration | + +### Invites + +All [Invite](/developers/docs/resources/invite) related events are only sent to bot users with the `MANAGE_CHANNELS` permission on the channel. + +#### Invite Create + +Sent when a new invite to a channel is created. + + +###### Invite Create Event Fields + +| Field | Type | Description | +|---------------------|-----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| channel_id | snowflake | Channel the invite is for | +| code | string | Unique invite [code](/developers/docs/resources/invite#invite-object) | +| created_at | ISO8601 timestamp | Time at which the invite was created | +| guild_id? | snowflake | Guild of the invite | +| inviter? | [user](/developers/docs/resources/user#user-object) object | User that created the invite | +| max_age | integer | How long the invite is valid for (in seconds) | +| max_uses | integer | Maximum number of times the invite can be used | +| target_type? | integer | [Type of target](/developers/docs/resources/invite#invite-object-invite-target-types) for this voice channel invite | +| target_user? | [user](/developers/docs/resources/user#user-object) object | User whose stream to display for this voice channel stream invite | +| target_application? | partial [application](/developers/docs/resources/application#application-object) object | Embedded application to open for this voice channel embedded application invite | +| temporary | boolean | Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) | +| uses | integer | How many times the invite has been used (always will be 0) | +| expires_at | ?ISO8601 timestamp | the expiration date of this invite | + +#### Invite Delete + +Sent when an invite is deleted. + + +###### Invite Delete Event Fields + +| Field | Type | Description | +|------------|-----------|-----------------------------------------------------------------------| +| channel_id | snowflake | Channel of the invite | +| guild_id? | snowflake | Guild of the invite | +| code | string | Unique invite [code](/developers/docs/resources/invite#invite-object) | + +### Messages + + +Unlike persistent messages, ephemeral messages are sent directly to the user and the bot who sent the message rather than through the guild channel. Because of this, ephemeral messages are tied to the [`DIRECT_MESSAGES` intent](/developers/docs/events/gateway#list-of-intents), and the message object won't include `guild_id` or `member`. + + +#### Message Create + +Sent when a message is created. The inner payload is a [message](/developers/docs/resources/message#message-object) object with the following extra fields: + + +###### Message Create Extra Fields + +| Field | Type | Description | +|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| guild_id? | snowflake | ID of the guild the message was sent in - unless it is an ephemeral message | +| member? | partial [guild member](/developers/docs/resources/guild#guild-member-object) object | Member properties for this message's author. Missing for ephemeral messages and messages from webhooks | +| mentions | array of [user](/developers/docs/resources/user#user-object) objects, optionally with an additional partial [member](/developers/docs/resources/guild#guild-member-object) field | Users specifically mentioned in the message | + +#### Message Update + +Sent when a message is updated. The inner payload is a [message](/developers/docs/resources/message#message-object) object with the same extra fields as [MESSAGE_CREATE](/developers/docs/events/gateway-events#message-create). + + +The value for `tts` will always be false in message updates. + + +#### Message Delete + +Sent when a message is deleted. + + +###### Message Delete Event Fields + +| Field | Type | Description | +|------------|-----------|-------------------| +| id | snowflake | ID of the message | +| channel_id | snowflake | ID of the channel | +| guild_id? | snowflake | ID of the guild | + +#### Message Delete Bulk + +Sent when multiple messages are deleted at once. + + +###### Message Delete Bulk Event Fields + +| Field | Type | Description | +|------------|---------------------|---------------------| +| ids | array of snowflakes | IDs of the messages | +| channel_id | snowflake | ID of the channel | +| guild_id? | snowflake | ID of the guild | + +#### Message Reaction Add + +Sent when a user adds a reaction to a message. + + +###### Message Reaction Add Event Fields + +| Field | Type | Description | +|--------------------|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------| +| user_id | snowflake | ID of the user | +| channel_id | snowflake | ID of the channel | +| message_id | snowflake | ID of the message | +| guild_id? | snowflake | ID of the guild | +| member? | [member](/developers/docs/resources/guild#guild-member-object) object | Member who reacted if this happened in a guild | +| emoji | a partial [emoji](/developers/docs/resources/emoji#emoji-object) object | Emoji used to react - [example](/developers/docs/resources/emoji#emoji-object-standard-emoji-example) | +| message_author_id? | snowflake | ID of the user who authored the message which was reacted to | +| burst | boolean | true if this is a super-reaction | +| burst_colors? | array of strings | Colors used for super-reaction animation in "#rrggbb" format | +| type | integer | The [type of reaction](/developers/docs/resources/message#get-reactions-reaction-types) | + +#### Message Reaction Remove + +Sent when a user removes a reaction from a message. + + +###### Message Reaction Remove Event Fields + +| Field | Type | Description | +|------------|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------| +| user_id | snowflake | ID of the user | +| channel_id | snowflake | ID of the channel | +| message_id | snowflake | ID of the message | +| guild_id? | snowflake | ID of the guild | +| emoji | a partial [emoji](/developers/docs/resources/emoji#emoji-object) object | Emoji used to react - [example](/developers/docs/resources/emoji#emoji-object-standard-emoji-example) | +| burst | boolean | true if this was a super-reaction | +| type | integer | The [type of reaction](/developers/docs/resources/message#get-reactions-reaction-types) | + +#### Message Reaction Remove All + +Sent when a user explicitly removes all reactions from a message. + + +###### Message Reaction Remove All Event Fields + +| Field | Type | Description | +|------------|-----------|-------------------| +| channel_id | snowflake | ID of the channel | +| message_id | snowflake | ID of the message | +| guild_id? | snowflake | ID of the guild | + +#### Message Reaction Remove Emoji + +Sent when a bot removes all instances of a given emoji from the reactions of a message. + + +###### Message Reaction Remove Emoji Event Fields + +| Field | Type | Description | +|------------|-----------------------------------------------------------------------|------------------------| +| channel_id | snowflake | ID of the channel | +| guild_id? | snowflake | ID of the guild | +| message_id | snowflake | ID of the message | +| emoji | [partial emoji object](/developers/docs/resources/emoji#emoji-object) | Emoji that was removed | + +### Presence + +#### Presence Update + + +If you are using [Gateway Intents](/developers/docs/events/gateway#gateway-intents), you _must_ specify the `GUILD_PRESENCES` intent in order to receive Presence Update events + + +A user's presence is their current state on a guild. This event is sent when a user's presence or info, such as name or avatar, is updated. + + +The user object within this event can be partial, the only field which must be sent is the `id` field, everything else is optional. Along with this limitation, no fields are required, and the types of the fields are **not** validated. Your client should expect any combination of fields and types within this event. + + + +###### Presence Update Event Fields + +| Field | Type | Description | +|---------------|-------------------------------------------------------------------------------------|----------------------------------------------| +| user | [user](/developers/docs/resources/user#user-object) object | User whose presence is being updated | +| guild_id | snowflake | ID of the guild | +| status | string | Either "idle", "dnd", "online", or "offline" | +| activities | array of [activity](/developers/docs/events/gateway-events#activity-object) objects | User's current activities | +| client_status | [client_status](/developers/docs/events/gateway-events#client-status-object) object | User's platform-dependent status | + +#### Client Status Object + +Active sessions are indicated with an "online", "idle", or "dnd" string per platform. If a user is offline or invisible, the corresponding field is not present. + +| Field | Type | Description | +|----------|--------|-----------------------------------------------------------------------------------| +| desktop? | string | User's status set for an active desktop (Windows, Linux, Mac) application session | +| mobile? | string | User's status set for an active mobile (iOS, Android) application session | +| web? | string | User's status set for an active web (browser, bot user) application session | + +#### Activity Object + + +###### Activity Structure + +| Field | Type | Description | +|----------------------|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | Activity's name | +| type | integer | [Activity type](/developers/docs/events/gateway-events#activity-object-activity-types) | +| url? | ?string | Stream URL, is validated when type is 1 | +| created_at | integer | Unix timestamp (in milliseconds) of when the activity was added to the user's session | +| timestamps? | [timestamps](/developers/docs/events/gateway-events#activity-object-activity-timestamps) object | Unix timestamps for start and/or end of the game | +| application_id? | snowflake | Application ID for the game | +| status_display_type? | ?integer | [Status display type](/developers/docs/events/gateway-events#activity-object-status-display-types); controls which field is displayed in the user's status text in the member list | +| details? | ?string | What the player is currently doing | +| details_url? | ?string | URL that is linked when clicking on the details text | +| state? | ?string | User's current party status, or text used for a custom status | +| state_url? | ?string | URL that is linked when clicking on the state text | +| emoji? | ?[emoji](/developers/docs/events/gateway-events#activity-object-activity-emoji) object | Emoji used for a custom status | +| party? | [party](/developers/docs/events/gateway-events#activity-object-activity-party) object | Information for the current party of the player | +| assets? | [assets](/developers/docs/events/gateway-events#activity-object-activity-assets) object | Images for the presence and their hover texts | +| secrets? | [secrets](/developers/docs/events/gateway-events#activity-object-activity-secrets) object | Secrets for Rich Presence joining and spectating | +| instance? | boolean | Whether or not the activity is an instanced game session | +| flags? | integer | [Activity flags](/developers/docs/events/gateway-events#activity-object-activity-flags) `OR`d together, describes what the payload includes | +| buttons? | array of [buttons](/developers/docs/events/gateway-events#activity-object-activity-buttons) | Custom buttons shown in the Rich Presence (max 2) | + + +Bot users are only able to set `name`, `state`, `type`, and `url`. + + + +###### Activity Types + +| ID | Name | Format | Example | +|----|-----------|-----------------------|--------------------------------------| +| 0 | Playing | Playing `{name}` | "Playing Rocket League" | +| 1 | Streaming | Streaming `{details}` | "Streaming Rocket League" | +| 2 | Listening | Listening to `{name}` | "Listening to Spotify" | +| 3 | Watching | Watching `{name}` | "Watching YouTube Together" | +| 4 | Custom | `{emoji}` `{state}` | ":smiley: I am cool" | +| 5 | Competing | Competing in `{name}` | "Competing in Arena World Champions" | + + +The streaming type currently only supports Twitch and YouTube. Only `https://twitch.tv/` and `https://youtube.com/` urls will work. + + + +###### Status Display Types + +| ID | Name | Example | +|----|---------|----------------------------------------| +| 0 | Name | "Listening to Spotify" | +| 1 | State | "Listening to Rick Astley" | +| 2 | Details | "Listening to Never Gonna Give You Up" | + + +This applies to all activity types. "Listening" was used to serve as a consistent example of what the different fields might be used for. + + + +###### Activity Timestamps + +| Field | Type | Description | +|--------|---------|----------------------------------------------------------| +| start? | integer | Unix time (in milliseconds) of when the activity started | +| end? | integer | Unix time (in milliseconds) of when the activity ends | + + +For Listening and Watching activities, you can include both start and end timestamps to display a time bar. + + + +###### Activity Emoji + +| Field | Type | Description | +|-----------|-----------|-------------------------------| +| name | string | Name of the emoji | +| id? | snowflake | ID of the emoji | +| animated? | boolean | Whether the emoji is animated | + + +###### Activity Party + +| Field | Type | Description | +|-------|------------------------------------------------|---------------------------------------------------| +| id? | string | ID of the party | +| size? | array of two integers (current_size, max_size) | Used to show the party's current and maximum size | + + +###### Activity Assets + +| Field | Type | Description | +|---------------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| large_image? | string | See [Activity Asset Image](/developers/docs/events/gateway-events#activity-object-activity-asset-image) | +| large_text? | string | Text displayed when hovering over the large image of the activity | +| large_url? | string | URL that is opened when clicking on the large image | +| small_image? | string | See [Activity Asset Image](/developers/docs/events/gateway-events#activity-object-activity-asset-image) | +| small_text? | string | Text displayed when hovering over the small image of the activity | +| small_url? | string | URL that is opened when clicking on the small image | +| invite_cover_image? | string | See [Activity Asset Image](/developers/docs/events/gateway-events#activity-object-activity-asset-image). Displayed as a banner on a [Game Invite](/developers/docs/discord-social-sdk/development-guides/managing-game-invites). | + + +###### Activity Asset Image + +Activity asset images are arbitrary strings which usually contain snowflake IDs or prefixed image IDs. Treat data within this field carefully, as it is user-specifiable and not sanitized. + +To use an external image via media proxy, specify the URL as the field's value when sending. You will only receive the `mp:` prefix via the gateway. + +| Type | Format | Image URL | +|-------------------|--------------------------|---------------------------------------------------------------------------------------| +| Application Asset | `{application_asset_id}` | See [Application Asset Image Formatting](/developers/docs/reference#image-formatting) | +| Media Proxy Image | `mp:{image_id}` | `https://media.discordapp.net/{image_id}` | + + +###### Activity Secrets + +| Field | Type | Description | +|-----------|--------|---------------------------------------| +| join? | string | Secret for joining a party | +| spectate? | string | Secret for spectating a game | +| match? | string | Secret for a specific instanced match | + + +###### Activity Flags + +| Name | Value | +|-----------------------------|----------| +| INSTANCE | `1 << 0` | +| JOIN | `1 << 1` | +| SPECTATE | `1 << 2` | +| JOIN_REQUEST | `1 << 3` | +| SYNC | `1 << 4` | +| PLAY | `1 << 5` | +| PARTY_PRIVACY_FRIENDS | `1 << 6` | +| PARTY_PRIVACY_VOICE_CHANNEL | `1 << 7` | +| EMBEDDED | `1 << 8` | + + +###### Activity Buttons + +When received over the gateway, the `buttons` field is an array of strings, which are the button labels. Bots cannot access a user's activity button URLs. When sending, the `buttons` field must be an array of the below object: + +| Field | Type | Description | +|-------|--------|--------------------------------------------------------| +| label | string | Text shown on the button (1-32 characters) | +| url | string | URL opened when clicking the button (1-512 characters) | + + +###### Example Activity + +```json +{ + "details": "24H RL Stream for Charity", + "state": "Rocket League", + "name": "Twitch", + "type": 1, + "url": "https://www.twitch.tv/discord" +} +``` + + +###### Example Activity with Rich Presence + +```json +{ + "name": "Rocket League", + "type": 0, + "application_id": "379286085710381999", + "state": "In a Match", + "details": "Ranked Duos: 2-1", + "timestamps": { + "start": 15112000660000 + }, + "party": { + "id": "9dd6594e-81b3-49f6-a6b5-a679e6a060d3", + "size": [2, 2] + }, + "assets": { + "large_image": "351371005538729000", + "large_text": "DFH Stadium", + "small_image": "351371005538729111", + "small_text": "Silver III" + }, + "secrets": { + "join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f", + "spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0", + "match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f" + } +} +``` + + +Clients may only update their game status 5 times per 20 seconds. + + +#### Typing Start + +Sent when a user starts typing in a channel. + + +###### Typing Start Event Fields + +| Field | Type | Description | +|------------|-----------------------------------------------------------------------|--------------------------------------------------------| +| channel_id | snowflake | ID of the channel | +| guild_id? | snowflake | ID of the guild | +| user_id | snowflake | ID of the user | +| timestamp | integer | Unix time (in seconds) of when the user started typing | +| member? | [member](/developers/docs/resources/guild#guild-member-object) object | Member who started typing if this happened in a guild | + +#### User Update + +Sent when properties about the current bot's user change. Inner payload is a [user](/developers/docs/resources/user#user-object) object. + +### Voice + +#### Voice Channel Effect Send + +Sent when someone sends an effect, such as an emoji reaction or a soundboard sound, in a voice channel the current user is connected to. + + +###### Voice Channel Effect Send Event Fields + +| Field | Type | Description | +|-----------------|----------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| channel_id | snowflake | ID of the channel the effect was sent in | +| guild_id | snowflake | ID of the guild the effect was sent in | +| user_id | snowflake | ID of the user who sent the effect | +| emoji? | ?[emoji](/developers/docs/resources/emoji#emoji-object) object | The emoji sent, for emoji reaction and soundboard effects | +| animation_type? | ?integer | The [type of emoji animation](/developers/docs/events/gateway-events#voice-channel-effect-send-animation-types), for emoji reaction and soundboard effects | +| animation_id? | integer | The ID of the emoji animation, for emoji reaction and soundboard effects | +| sound_id? | snowflake or integer | The ID of the soundboard sound, for soundboard effects | +| sound_volume? | double | The volume of the soundboard sound, from 0 to 1, for soundboard effects | + + +###### Animation Types + +| Type | Value | Description | +|---------|-------|---------------------------------------------| +| PREMIUM | 0 | A fun animation, sent by a Nitro subscriber | +| BASIC | 1 | The standard animation | + +#### Voice State Update + +Sent when someone joins/leaves/moves voice channels. Inner payload is a [voice state](/developers/docs/resources/voice#voice-state-object) object. + +#### Voice Server Update + +Sent when a guild's voice server is updated. This is sent when initially connecting to voice, and when the current voice instance fails over to a new server. + + +A null endpoint means that the voice server allocated has gone away and is trying to be reallocated. You should attempt to disconnect from the currently connected voice server, and not attempt to reconnect until a new voice server is allocated. + + + +###### Voice Server Update Event Fields + +| Field | Type | Description | +|----------|-----------|---------------------------------------| +| token | string | Voice connection token | +| guild_id | snowflake | Guild this voice server update is for | +| endpoint | ?string | Voice server host | + + +###### Example Voice Server Update Payload + +```json +{ + "token": "my_token", + "guild_id": "41771983423143937", + "endpoint": "sweetwater-12345.discord.media:2048" +} +``` + +### Webhooks + +#### Webhooks Update + +Sent when a guild channel's webhook is created, updated, or deleted. + + +###### Webhooks Update Event Fields + +| Field | Type | Description | +|------------|-----------|-------------------| +| guild_id | snowflake | ID of the guild | +| channel_id | snowflake | ID of the channel | + +### Interactions + +#### Interaction Create + +Sent when a user uses an [Application Command](/developers/docs/interactions/application-commands) or [Message Component](/developers/docs/components/reference#component-object). Inner payload is an [Interaction](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure). + +### Stage Instances + +#### Stage Instance Create + +Sent when a [Stage instance](/developers/docs/resources/stage-instance) is created (i.e. the Stage is now "live"). Inner payload is a [Stage instance](/developers/docs/resources/stage-instance#stage-instance-object) + +#### Stage Instance Update + +Sent when a [Stage instance](/developers/docs/resources/stage-instance) has been updated. Inner payload is a [Stage instance](/developers/docs/resources/stage-instance#stage-instance-object) + +#### Stage Instance Delete + +Sent when a [Stage instance](/developers/docs/resources/stage-instance) has been deleted (i.e. the Stage has been closed). Inner payload is a [Stage instance](/developers/docs/resources/stage-instance#stage-instance-object) + +### Subscriptions + +#### Subscription Create + + +Subscription status should not be used to grant perks. Use [entitlements](/developers/docs/resources/entitlement#entitlement-object) as an indication of whether a user should have access to a specific SKU. See our guide on [Implementing App Subscriptions](/developers/docs/monetization/implementing-app-subscriptions) for more information. + + +Sent when a [Subscription](/developers/docs/resources/subscription) for a Premium App is created. Inner payload is a [Subscription](/developers/docs/resources/subscription#subscription-object). + +A Subscription's `status` can be either **inactive** or **active** when this event is received. You will receive subsequent `SUBSCRIPTION_UPDATE` events if the `status` is updated to **active**. As a best practice, you should not grant any perks to users until the entitlements are created. + +#### Subscription Update + +Sent when a [Subscription](/developers/docs/resources/subscription) for a Premium App has been updated. Inner payload is a [Subscription](/developers/docs/resources/subscription#subscription-object) object. + +#### Subscription Delete + +Sent when a [Subscription](/developers/docs/resources/subscription) for a Premium App has been deleted. Inner payload is a [Subscription](/developers/docs/resources/subscription#subscription-object) object. + +### Polls + +#### Message Poll Vote Add + +Sent when a user votes on a poll. If the poll allows multiple selection, one event will be sent per answer. + + +###### Message Poll Vote Add Fields + +| Field | Type | Description | +|------------|-----------|-------------------| +| user_id | snowflake | ID of the user | +| channel_id | snowflake | ID of the channel | +| message_id | snowflake | ID of the message | +| guild_id? | snowflake | ID of the guild | +| answer_id | integer | ID of the answer | + +#### Message Poll Vote Remove + +Sent when a user removes their vote on a poll. If the poll allows for multiple selections, one event will be sent per answer. + + +###### Message Poll Vote Remove Fields + +| Field | Type | Description | +|------------|-----------|-------------------| +| user_id | snowflake | ID of the user | +| channel_id | snowflake | ID of the channel | +| message_id | snowflake | ID of the message | +| guild_id? | snowflake | ID of the guild | +| answer_id | integer | ID of the answer | + +### Rate Limits + +#### Rate Limited + +Sent when an app encounters a gateway rate limit for an event, such as [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members). + + +See changelog for [Introducing Rate Limit When Requesting All Guild Members](/developers/docs/change-log#introducing-rate-limit-when-requesting-all-guild-members) for more information and timeline on this new rate limit. + + +###### Rate Limited Fields + +| Field | Type | Description | +|-------------|--------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| opcode | integer | [Gateway opcode](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes) of the event that was rate limited | +| retry_after | float | The number of seconds to wait before submitting another request | +| meta | [Rate Limit Metadata for Opcode](/developers/docs/events/gateway-events#rate-limited-rate-limit-metadata-for-opcode-structure) | Metadata for the event that was rate limited | + +###### Rate Limit Metadata for Opcode Structure + +| Opcode | Type | +|--------|----------------------------------------------------------------------------------------------------------------------------------------------------| +| 8 | [Request Guild Member Rate Limit Metadata](/developers/docs/events/gateway-events#rate-limited-request-guild-member-rate-limit-metadata-structure) | + +###### Request Guild Member Rate Limit Metadata Structure + +| Field | Type | Description | +|----------|-----------|------------------------------------------------------------------------------------------------------------------| +| guild_id | snowflake | ID of the guild to get members for | +| nonce? | string | nonce to identify the [Guild Members Chunk](/developers/docs/events/gateway-events#guild-members-chunk) response | diff --git a/discord/developers/docs/events/gateway.mdx b/discord/developers/docs/events/gateway.mdx new file mode 100644 index 0000000000..878ae8e803 --- /dev/null +++ b/discord/developers/docs/events/gateway.mdx @@ -0,0 +1,779 @@ +--- +title: Gateway +sidebarTitle: Using Gateway +description: Learn how to establish and maintain Gateway API connections with Discord. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +import {Route} from '/snippets/route.jsx' + +The Gateway API lets apps open secure WebSocket connections with Discord to receive events about actions that take place in a server/guild, like when a channel is updated or a role is created. There are a few cases where apps will *also* use Gateway connections to update or request resources, like when updating voice state. + + +In *most* cases, performing REST operations on Discord resources can be done using the [HTTP API](/developers/docs/reference#http-api) rather than the Gateway API. + + +The Gateway is Discord's form of real-time communication used by clients (including apps), so there are nuances and data passed that simply isn't relevant to apps. Interacting with the Gateway can be tricky, but there are [community-built libraries](/developers/docs/developer-tools/community-resources#libraries) with built-in support that simplify the most complicated bits and pieces. If you're planning on writing a custom implementation, be sure to read the following documentation in its entirety so you understand the sacred secrets of the Gateway (or at least those that matter for apps). + +## Gateway Events + +Gateway events are [payloads](/developers/docs/events/gateway-events#payload-structure) sent over a [Gateway connection](/developers/docs/events/gateway#connections)—either from an app to Discord, or from Discord to an app. An app typically [*sends* events](/developers/docs/events/gateway#sending-events) when connecting and managing its connection to the Gateway, and [*receives* events](/developers/docs/events/gateway#receiving-events) when listening to actions taking place in a server. + +All Gateway events are encapsulated in a [Gateway event payload](/developers/docs/events/gateway-events#payload-structure). + +A full list of Gateway events and their details are in the [Gateway events documentation](/developers/docs/events/gateway-events). + + +###### Example Gateway Event + +```json +{ + "op": 0, + "d": {}, + "s": 42, + "t": "GATEWAY_EVENT_NAME" +} +``` + +Details about Gateway event payloads are in the [Gateway events documentation](/developers/docs/events/gateway-events#payload-structure). + +### Sending Events + +When sending a Gateway event (like when [performing an initial handshake](/developers/docs/events/gateway-events#identify) or [updating presence](/developers/docs/events/gateway-events#update-presence)), your app must send an [event payload object](/developers/docs/events/gateway-events#payload-structure) with a valid opcode (`op`) and inner data object (`d`). + + +Specific rate limits are applied when sending events, which you can read about in the [Rate Limiting](/developers/docs/events/gateway#rate-limiting) section. + + +Event payloads sent over a Gateway connection: + +1. Must be serialized in [plain-text JSON or binary ETF](/developers/docs/events/gateway#encoding-and-compression). +2. Must not exceed 4096 bytes. If an event payload *does* exceed 4096 bytes, the connection will be closed with a [`4002` close event code](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes). + +All events that your app can send via a connection are in [Gateway event documentation](/developers/docs/events/gateway-events#send-events). + +### Receiving Events + +Receiving a Gateway event from Discord (like when [a reaction is added to a message](/developers/docs/events/gateway-events#message-reaction-add)) is much more common (and slightly more complex) than sending them. + +While some events are sent to your app automatically, most events require your app to define intents when [Identifying](/developers/docs/events/gateway#identifying). Intents are bitwise values that can be ORed (`|`) to indicate which events (or groups of events) you want Discord to send your app. A list of intents and their corresponding events are listed in the [intents section](/developers/docs/events/gateway#gateway-intents). + +When receiving events, you can also configure *how* events will be sent to your app, like the [encoding and compression](/developers/docs/events/gateway#encoding-and-compression), or whether [sharding should be enabled](/developers/docs/events/gateway#sharding)). + +All events that your app can receive via a connection are in the [Gateway event documentation](/developers/docs/events/gateway-events#receive-events). + +#### Dispatch Events + +[Dispatch (opcode `0`)](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes) events are the most common type of event your app will receive. *Most* Gateway events which represent actions taking place in a guild will be sent to your app as Dispatch events. + +When your app is parsing a Dispatch event: + +- The `t` field can be used to determine which [Gateway event](/developers/docs/events/gateway-events#receive-events) the payload represents the data you can expect in the `d` field. +- The `s` field represents the sequence number of the event, which is the relative order in which it occurred. You need to cache the most recent non-null `s` value for heartbeats, and to pass when [Resuming](/developers/docs/events/gateway#resuming) a connection. + +## Connections + +Gateway connections are persistent WebSockets which introduce more complexity than sending HTTP requests or responding to interactions (like Slash Commands). When interacting with the Gateway, your app must know how to open the initial connection, as well as maintain it and handle any disconnects. + +### Connection Lifecycle + + +There are nuances that aren't included in the overview below. More details about each step and event can be found in the individual sections below. + + +At a high-level, Gateway connections consist of the following cycle: + +![Flowchart with an overview of Gateway connection lifecycle](/images/events/gateway-lifecycle.svg) + +1. App establishes a connection with the Gateway after fetching and caching a WSS URL using the [Get Gateway](/developers/docs/events/gateway#get-gateway) or [Get Gateway Bot](/developers/docs/events/gateway#get-gateway-bot) endpoint. +2. Discord sends the app a [Hello (opcode `10`)](/developers/docs/events/gateway#hello-event) event containing a heartbeat interval in milliseconds. **Read the section on [Connecting](/developers/docs/events/gateway#connecting)** +3. Start the Heartbeat interval. App must send a [Heartbeat (opcode `1`)](/developers/docs/events/gateway-events#heartbeat) event, then continue to send them every heartbeat interval until the connection is closed. **Read the section on [Sending Heartbeats](/developers/docs/events/gateway#sending-heartbeats)** + - Discord will respond to each Heartbeat event with a [Heartbeat ACK (opcode `11`)](/developers/docs/events/gateway#sending-heartbeats) event to confirm it was received. If an app doesn't receive a Heartbeat ACK, it should close the connection and reconnect. + - Discord may send the app a [Heartbeat (opcode `1`)](/developers/docs/events/gateway-events#heartbeat) event, in which case the app should send a Heartbeat event immediately. +4. App sends an [Identify (opcode `2`)](/developers/docs/events/gateway-events#identify) event to perform the initial handshake with the Gateway. **Read the section on [Identifying](/developers/docs/events/gateway#identifying)** +5. Discord sends the app a [Ready (opcode `0`)](/developers/docs/events/gateway-events#ready) event which indicates the handshake was successful and the connection is established. The Ready event contains a `resume_gateway_url` that the app should keep track of to determine the WebSocket URL an app should use to Resume. **Read the section on [the Ready event](/developers/docs/events/gateway#ready-event)** +6. The connection may be dropped for a variety of reasons at any time. Whether the app can [Resume](/developers/docs/events/gateway#resuming) the connection or whether it must re-identify is determined by a variety of factors like the [opcode](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes) and [close code](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) that it receives. **Read the section on [Disconnecting](/developers/docs/events/gateway#disconnecting)** +7. If an app **can** resume/reconnect, it should open a new connection using `resume_gateway_url` with the same version and encoding, then send a [Resume (opcode `6`)](/developers/docs/events/gateway-events#resume) event. If an app **cannot** resume/reconnect, it should open a new connection using the cached URL from step #1, then repeat the whole Gateway cycle. *Yipee!* **Read the section on [Resuming](/developers/docs/events/gateway#resuming)** + +### Connecting + +Before your app can establish a connection to the Gateway, it should call the [Get Gateway](/developers/docs/events/gateway#get-gateway) or the [Get Gateway Bot](/developers/docs/events/gateway#get-gateway-bot) endpoint. Either endpoint will return a payload with a `url` field whose value is the WSS URL you can use to open a WebSocket connection. In addition to the URL, [Get Gateway Bot](/developers/docs/events/gateway#get-gateway-bot) contains additional information about the recommended number of shards and the session start limits for your app. + +When initially calling either [Get Gateway](/developers/docs/events/gateway#get-gateway) or [Get Gateway Bot](/developers/docs/events/gateway#get-gateway-bot), you should cache the value of the `url` field and use that when re-connecting to the Gateway. + +When connecting to the URL, it's a good idea to explicitly pass the API version and [encoding](/developers/docs/events/gateway#encoding-and-compression) as query parameters. You can also optionally include whether Discord should [compress](/developers/docs/events/gateway#encoding-and-compression) data that it sends your app. + + +`wss://gateway.discord.gg/?v=10&encoding=json` is an example of a WSS URL an app may use to connect to the Gateway. + + + +For security reasons, the Gateway cannot be accessed directly from a Cloudflare Worker. Attempts will result in a 401 Unauthorized status code. + + + +###### Gateway URL Query String Params + +| Field | Type | Description | Accepted Values | +|-----------|---------|----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------| +| v | integer | [API Version](/developers/docs/reference#api-versioning) to use | [API version](/developers/docs/reference#api-versioning-api-versions) | +| encoding | string | The [encoding](/developers/docs/events/gateway#encoding-and-compression) of received gateway packets | `json` or `etf` | +| compress? | string | The optional [transport compression](/developers/docs/events/gateway#transport-compression) of gateway packets | `zlib-stream` or `zstd-stream` | + +#### Hello Event + +Once connected to the Gateway, your app will receive a [Hello (opcode `10`)](/developers/docs/events/gateway#hello-event) event that contains your connection's heartbeat interval (`heartbeat_interval`). + +The heartbeat interval indicates a length of time in milliseconds that you should use to determine how often your app needs to send a Heartbeat event in order to maintain the active connection. Heartbeating is detailed in the [Sending Heartbeats](/developers/docs/events/gateway#sending-heartbeats) section. + + +###### Example Hello Event + +```json +{ + "op": 10, + "d": { + "heartbeat_interval": 45000 + } +} +``` + +### Sending Heartbeats + +Heartbeats are pings used to let Discord know that your app is still actively using a Gateway connection. After connecting to the Gateway, your app should send heartbeats (as described below) in a background process until the Gateway connection is closed. + +#### Heartbeat Interval + +When your app opens a Gateway connection, it will receive a [Hello (opcode `10`)](/developers/docs/events/gateway#hello-event) event which includes a `heartbeat_interval` field that has a value representing a length of time in milliseconds. + +Upon receiving the Hello event, your app should wait `heartbeat_interval * jitter` where `jitter` is any random value between 0 and 1, then send its first [Heartbeat (opcode `1`)](/developers/docs/events/gateway-events#heartbeat) event. From that point until the connection is closed, your app must continually send Discord a heartbeat every `heartbeat_interval` milliseconds. If your app fails to send a heartbeat event in time, your connection will be closed and you will be forced to [Resume](/developers/docs/events/gateway#resuming). + +When sending a heartbeat, your app will need to include the last sequence number your app received in the `d` field. The sequence number is sent to your app in the [event payload](/developers/docs/events/gateway-events#payload-structure) in the `s` field. If your app hasn't received any events yet, you can just pass `null` in the `d` field. + + +In the first heartbeat, `jitter` is an offset value between 0 and `heartbeat_interval` that is meant to prevent too many clients (both desktop and apps) from reconnecting their sessions at the exact same time (which could cause an influx of traffic). + + +You *can* send heartbeats before the `heartbeat_interval` elapses, but you should avoid doing so unless necessary. There is already tolerance in the `heartbeat_interval` that will cover network latency, so you don't need to account for it in your implementation. + +When you send a Heartbeat event, Discord will respond with a [Heartbeat ACK (opcode `11`)](/developers/docs/events/gateway#heartbeat-interval-example-heartbeat-ack) event, which is an acknowledgement that the heartbeat was received: + + +###### Example Heartbeat ACK + +```json +{ + "op": 11 +} +``` + + +In the event of a service outage where you stay connected to the Gateway, you should continue to send heartbeats and receive heartbeat ACKs. The Gateway will eventually respond and issue a session once it's able to. + + +If a client does not receive a heartbeat ACK between its attempts at sending heartbeats, this may be due to a failed or "zombied" connection. The client should immediately terminate the connection with any close code besides `1000` or `1001`, then reconnect and attempt to [Resume](/developers/docs/events/gateway#resuming). + +#### Heartbeat Requests + +In addition to the Heartbeat interval, Discord may request additional heartbeats from your app by sending a [Heartbeat (opcode `1`)](/developers/docs/events/gateway-events#heartbeat) event. Upon receiving the event, your app should immediately send back another Heartbeat event without waiting the remainder of the current interval. + +Just like with the interval, Discord will respond with an [Heartbeat ACK (opcode `11`)](/developers/docs/events/gateway#heartbeat-interval-example-heartbeat-ack) event. + +### Identifying + +After the connection is open and your app is sending heartbeats, you should send an [Identify (opcode `2`)](/developers/docs/events/gateway-events#identify) event. The Identify event is an initial handshake with the Gateway that's required before your app can begin sending or receiving most Gateway events. + +Apps are limited by maximum concurrency (`max_concurrency` in the [session start limit object](/developers/docs/events/gateway#session-start-limit-object)) when identifying. If your app exceeds this limit, Discord will respond with a [Invalid Session (opcode `9`)](/developers/docs/events/gateway-events#invalid-session) event. + +After your app sends a valid Identify payload, Discord will respond with a [Ready](/developers/docs/events/gateway-events#ready) event which indicates that your app is in a successfully-connected state with the Gateway. The Ready event is sent as a standard [Dispatch (opcode `0`)](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes). + + +Clients are limited to 1000 `IDENTIFY` calls to the websocket in a 24-hour period. This limit is global and across all shards, but does not include `RESUME` calls. Upon hitting this limit, all active sessions for the app will be terminated, the bot token will be reset, and the owner will receive an email notification. It's up to the owner to update their application with the new token. + + + +###### Example Identify Payload + +Below is a minimal `IDENTIFY` payload. `IDENTIFY` supports additional fields for other session properties like payload compression and an initial presence state. + +See the [Identify Structure](/developers/docs/events/gateway-events#identify-identify-structure) for details about the event. + +```json +{ + "op": 2, + "d": { + "token": "my_token", + "intents": 513, + "properties": { + "os": "linux", + "browser": "my_library", + "device": "my_library" + } + } +} +``` + +#### Ready event + +As mentioned above, the [Ready](/developers/docs/events/gateway-events#ready) event is sent to your app after it sends a valid Identify payload. The Ready event includes state, like the guilds your app is in, that it needs to start interacting with the rest of the platform. + +The Ready event also includes fields that you'll need to cache in order to eventually [Resume](/developers/docs/events/gateway#resuming) your connection after disconnects. Two fields in particular are important to call out: +- `resume_gateway_url` is a WebSocket URL that your app should use when it Resumes after a disconnect. The `resume_gateway_url` should be used instead of the URL [used when connecting](/developers/docs/events/gateway#connecting). +- `session_id` is the ID for the Gateway session for the new connection. It's required to know which stream of events were associated with your disconnection connection. + +Full details about the Ready event is in the [Gateway events documentation](/developers/docs/events/gateway-events). + +### Disconnecting + +Gateway disconnects happen for a variety of reasons, and may be initiated by Discord or by your app. + +#### Handling a Disconnect + +Due to Discord's architecture, disconnects are a semi-regular event and should be expected and handled. When your app encounters a disconnect, it will typically be sent a [close code](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) which can be used to determine whether you can reconnect and [Resume](/developers/docs/events/gateway#resuming) the session, or whether you have to start over and re-Identify. + +After you determine whether or not your app can reconnect, you will do one of the following: + +- If you determine that your app *can* reconnect and resume the previous session, then you should reconnect using the `resume_gateway_url` and `session_id` from the [Ready](/developers/docs/events/gateway-events#ready) event. Details about when and how to resume can be found in the [Resuming](/developers/docs/events/gateway#resuming) section. +- If you *cannot* reconnect **or the reconnect fails**, you should open a new connection using the URL from the initial call to [Get Gateway](/developers/docs/events/gateway#get-gateway) or [Get Gateway Bot](/developers/docs/events/gateway#get-gateway-bot). In the case you cannot reconnect, you'll have to re-identify after opening a new connection. + +A full list of the close codes can be found in the [Response Codes](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) documentation. + +#### Initiating a Disconnect + +When you close the connection to the gateway with close code `1000` or `1001`, your session will be invalidated and your bot will appear offline. + +If you simply close the TCP connection or use a different close code, the session will remain active and timeout after a few minutes. This can be useful when you're [Resuming](/developers/docs/events/gateway#resuming) the previous session. + +### Resuming + +When your app is disconnected, Discord has a process for reconnecting and resuming, which allows your app to replay any lost events starting from the last sequence number it received. After Resuming, your app will receive the missed events in the same way it would have had the connection had stayed active. Unlike the initial connection, your app does **not** need to re-Identify when Resuming. + +There are a handful of scenarios when your app should attempt to resume: + +1. It receives a [Reconnect (opcode `7`)](/developers/docs/events/gateway-events#reconnect) event +2. It's disconnected with a close code that indicates it can reconnect. A list of close codes is in the [Opcodes and Status Codes](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) documentation. +3. It's disconnected but doesn't receive *any* close code. +4. It receives an [Invalid Session (opcode `9`)](/developers/docs/events/gateway-events#invalid-session) event with the `d` field set to `true`. This is an unlikely scenario, but it is possible. + +#### Preparing to Resume + +Before your app can send a [Resume (opcode `6`)](/developers/docs/events/gateway-events#resume) event, it will need three values: the `session_id` and the `resume_gateway_url` from the [Ready](/developers/docs/events/gateway#ready-event) event, and the sequence number (`s`) from the last Dispatch (opcode `0`) event it received before the disconnect. + +After the connection is closed, your app should open a new connection using `resume_gateway_url` rather than the URL used to initially connect, with the same query parameters from the initial [Connection](/developers/docs/events/gateway#connecting). If your app doesn't use the `resume_gateway_url` when reconnecting, it will experience disconnects at a higher rate than normal. + +Once the new connection is opened, your app should send a [Gateway Resume](/developers/docs/events/gateway-events#resume) event using the `session_id` and sequence number mentioned above. When sending the event, `session_id` will have the same field name, but the last sequence number will be passed as `seq` in the data object (`d`). + +When Resuming, you do not need to send an Identify event after opening the connection. + +If successful, the Gateway will send the missed events in order, finishing with a [Resumed](/developers/docs/events/gateway-events#resumed) event to signal event replay has finished and that all subsequent events will be new. + + +When resuming with the `resume_gateway_url` you need to provide the same version and encoding as the initial connection. + + +It's possible your app won't reconnect in time to Resume, in which case it will receive an [Invalid Session (opcode `9`)](/developers/docs/events/gateway-events#invalid-session) event. If the `d` field is set to `false` (which is most of the time), your app should disconnect. After disconnect, your app should create a new connection with your cached URL from the [Get Gateway](/developers/docs/events/gateway#get-gateway) or the [Get Gateway Bot](/developers/docs/events/gateway#get-gateway-bot) endpoint, then send an [Identify (opcode `2`)](/developers/docs/events/gateway-events#identify) event. + + +###### Example Gateway Resume Event + +```json +{ + "op": 6, + "d": { + "token": "my_token", + "session_id": "session_id_i_stored", + "seq": 1337 + } +} +``` + +## Gateway Intents + +Maintaining a stateful application can be difficult when it comes to the amount of data your app is expected to process over a Gateway connection, especially at scale. Gateway intents are a system to help you lower the computational burden. + +Intents are bitwise values passed in the `intents` parameter when [Identifying](/developers/docs/events/gateway#identifying) which correlate to a set of related events. For example, the event sent when a guild is created (`GUILD_CREATE`) and when a channel is updated (`CHANNEL_UPDATE`) both require the same `GUILDS (1 << 0)` intent (as listed in the table below). If you do not specify an intent when identifying, you will not receive *any* of the Gateway events associated with that intent. + + +Intents are optionally supported on the v6 gateway but required as of v8 + + +Two types of intents exist: + +- **Standard intents** can be passed by default. You don't need any additional permissions or configurations. +- [**Privileged intents**](/developers/docs/events/gateway#privileged-intents) require you to toggle the intent for your app in your app's settings within the Developer Portal before passing said intent. For verified apps (required for apps in 100+ guilds), the intent must also be approved after the verification process to use the intent. More information about privileged intents can be found [in the section below](/developers/docs/events/gateway#privileged-intents). + +The connection with your app will be closed if it passes invalid intents ([`4013` close code](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes)), or a privileged intent that hasn't been configured or approved for your app ([`4014` close code](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes)). + +### List of Intents + +Below is a list of all intents and the Gateway events associated with them. Any events *not* listed means it's not associated with an intent and will always be sent to your app. + +All events, including those that aren't associated with an intent, are in the [Gateway events](/developers/docs/events/gateway-events) documentation. + +``` +GUILDS (1 << 0) + - GUILD_CREATE + - GUILD_UPDATE + - GUILD_DELETE + - GUILD_ROLE_CREATE + - GUILD_ROLE_UPDATE + - GUILD_ROLE_DELETE + - CHANNEL_CREATE + - CHANNEL_UPDATE + - CHANNEL_DELETE + - CHANNEL_PINS_UPDATE + - THREAD_CREATE + - THREAD_UPDATE + - THREAD_DELETE + - THREAD_LIST_SYNC + - THREAD_MEMBER_UPDATE + - THREAD_MEMBERS_UPDATE * + - STAGE_INSTANCE_CREATE + - STAGE_INSTANCE_UPDATE + - STAGE_INSTANCE_DELETE + +GUILD_MEMBERS (1 << 1) ** + - GUILD_MEMBER_ADD + - GUILD_MEMBER_UPDATE + - GUILD_MEMBER_REMOVE + - THREAD_MEMBERS_UPDATE * + +GUILD_MODERATION (1 << 2) + - GUILD_AUDIT_LOG_ENTRY_CREATE + - GUILD_BAN_ADD + - GUILD_BAN_REMOVE + +GUILD_EXPRESSIONS (1 << 3) + - GUILD_EMOJIS_UPDATE + - GUILD_STICKERS_UPDATE + - GUILD_SOUNDBOARD_SOUND_CREATE + - GUILD_SOUNDBOARD_SOUND_UPDATE + - GUILD_SOUNDBOARD_SOUND_DELETE + - GUILD_SOUNDBOARD_SOUNDS_UPDATE + +GUILD_INTEGRATIONS (1 << 4) + - GUILD_INTEGRATIONS_UPDATE + - INTEGRATION_CREATE + - INTEGRATION_UPDATE + - INTEGRATION_DELETE + +GUILD_WEBHOOKS (1 << 5) + - WEBHOOKS_UPDATE + +GUILD_INVITES (1 << 6) + - INVITE_CREATE + - INVITE_DELETE + +GUILD_VOICE_STATES (1 << 7) + - VOICE_CHANNEL_EFFECT_SEND + - VOICE_STATE_UPDATE + +GUILD_PRESENCES (1 << 8) ** + - PRESENCE_UPDATE + +GUILD_MESSAGES (1 << 9) + - MESSAGE_CREATE + - MESSAGE_UPDATE + - MESSAGE_DELETE + - MESSAGE_DELETE_BULK + +GUILD_MESSAGE_REACTIONS (1 << 10) + - MESSAGE_REACTION_ADD + - MESSAGE_REACTION_REMOVE + - MESSAGE_REACTION_REMOVE_ALL + - MESSAGE_REACTION_REMOVE_EMOJI + +GUILD_MESSAGE_TYPING (1 << 11) + - TYPING_START + +DIRECT_MESSAGES (1 << 12) + - MESSAGE_CREATE + - MESSAGE_UPDATE + - MESSAGE_DELETE + - CHANNEL_PINS_UPDATE + +DIRECT_MESSAGE_REACTIONS (1 << 13) + - MESSAGE_REACTION_ADD + - MESSAGE_REACTION_REMOVE + - MESSAGE_REACTION_REMOVE_ALL + - MESSAGE_REACTION_REMOVE_EMOJI + +DIRECT_MESSAGE_TYPING (1 << 14) + - TYPING_START + +MESSAGE_CONTENT (1 << 15) *** + +GUILD_SCHEDULED_EVENTS (1 << 16) + - GUILD_SCHEDULED_EVENT_CREATE + - GUILD_SCHEDULED_EVENT_UPDATE + - GUILD_SCHEDULED_EVENT_DELETE + - GUILD_SCHEDULED_EVENT_USER_ADD + - GUILD_SCHEDULED_EVENT_USER_REMOVE + +AUTO_MODERATION_CONFIGURATION (1 << 20) + - AUTO_MODERATION_RULE_CREATE + - AUTO_MODERATION_RULE_UPDATE + - AUTO_MODERATION_RULE_DELETE + +AUTO_MODERATION_EXECUTION (1 << 21) + - AUTO_MODERATION_ACTION_EXECUTION + +GUILD_MESSAGE_POLLS (1 << 24) + - MESSAGE_POLL_VOTE_ADD + - MESSAGE_POLL_VOTE_REMOVE + +DIRECT_MESSAGE_POLLS (1 << 25) + - MESSAGE_POLL_VOTE_ADD + - MESSAGE_POLL_VOTE_REMOVE +``` + +\* [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) contains different data depending on which intents are used. + +\*\* Events under the `GUILD_PRESENCES` and `GUILD_MEMBERS` intents are turned **off by default on all API versions**. If you are using **API v6**, you will receive those events if you are authorized to receive them and have enabled the intents in the Developer Portal. You do not need to use intents on API v6 to receive these events; you just need to enable the flags. If you are using **API v8** or above, intents are mandatory and must be specified when identifying. + +\*\*\* `MESSAGE_CONTENT` does not represent individual events, but rather affects what data is present for events that could contain message content fields. More information is in the [message content intent](/developers/docs/events/gateway#message-content-intent) section. + +### Caveats + +[Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) is sent for current-user updates regardless of whether the `GUILD_MEMBERS` intent is set. + +[Guild Create](/developers/docs/events/gateway-events#guild-create) and [Request Guild Members](/developers/docs/events/gateway-events#request-guild-members) are uniquely affected by intents. See these sections for more information. + +[Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) by default only includes if the current user was added to or removed from a thread. To receive these updates for other users, request the `GUILD_MEMBERS` [Gateway Intent](/developers/docs/events/gateway#gateway-intents). + +### Privileged Intents + +Some intents are defined as "privileged" due to the sensitive nature of the data. Currently, those intents include: + +- `GUILD_PRESENCES` +- `GUILD_MEMBERS` +- [`MESSAGE_CONTENT`](/developers/docs/events/gateway#message-content-intent) + +Apps that qualify for verification **must** be approved for the privileged intent(s) before they can use them. After your app is verified, you can request privileged intents within the app's settings within the Developer Portal. + +Before you specify privileged intents in your `IDENTIFY` payload, you must enable the privileged intents your app requires. [Verified apps](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified) can only use privileged intents *after* they've been approved for them. + + +Unverified apps can use privileged intents without approval, but still must enable them in their app's settings. If the app's verification status changes, it will then have to apply for the privileged intent(s). + + +In addition to the gateway restrictions described here, Discord's REST API is also affected by Privileged Intents. For example, to use the [List Guild Members](/developers/docs/resources/guild#list-guild-members) endpoint, you must have the `GUILD_MEMBERS` intent enabled for your application. This behavior is independent of whether the intent is set during `IDENTIFY`. + +#### Enabling Privileged Intents + +Before using privileged intents, you must enable them in your app's settings. In the [Developer Portal](https://discord.com/developers/applications), you can navigate to your app's settings then toggle the privileged intents on the **Bots** page under the "Privileged Gateway Intents" section. You should only toggle privileged intents that your bot *requires to function*. + +If your app qualifies for [verification](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified), you must first [verify your app](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified) and request access to these intents during the verification process. If your app is already verified and you need to request additional privileged intents, you can [contact support](https://dis.gd/support). + +#### Gateway Restrictions + +Privileged intents affect which Gateway events your app is permitted to receive. When using **API v8** and above, all intents (privileged and not) must be specified in the `intents` parameter when Identifying. If you pass a privileged intent in the `intents` parameter without configuring it in your app's settings, or being approved for it during verification, your Gateway connection will be closed with a ([`4014` close code](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes)). + + +For **API v6**, you will receive events associated with the privileged intents your app has configured and is authorized to receive *without* passing those intents into the `intents` parameter when Identifying. + + +Events associated with the `GUILD_PRESENCES` and `GUILD_MEMBERS` intents are turned off by default regardless of the API version. + +#### HTTP Restrictions + +In addition to Gateway restrictions, privileged intents also affect the [HTTP API](/developers/docs/reference#http-api) endpoints your app is permitted to call, and the data it can receive. For example, to use the [List Guild Members](/developers/docs/resources/guild#list-guild-members) endpoint, your app must enable the `GUILD_MEMBERS` intent (and be approved for it if eligible for verification). + +HTTP API restrictions are independent of Gateway restrictions, and are unaffected by which intents your app passes in the `intents` parameter when Identifying. + +#### Message Content Intent + +`MESSAGE_CONTENT (1 << 15)` is a unique privileged intent that isn't directly associated with any Gateway events. Instead, access to `MESSAGE_CONTENT` permits your app to receive message content data across the APIs. + +Any fields affected by the message content intent are noted in the relevant documentation. For example, the `content`, `embeds`, `attachments`, `components`, and `poll` fields in [message objects](/developers/docs/resources/message#message-object) all contain message content and therefore require the intent. + + +Like other privileged intents, `MESSAGE_CONTENT` must be approved for your app. After your app is verified, you can apply for the intent from your app's settings within the Developer Portal. You can read more about the message content intent review policy [in the Help Center](https://support-dev.discord.com/hc/en-us/articles/5324827539479). + + +Apps **without** the intent will receive empty values in fields that contain user-inputted content with a few exceptions: +- Content in messages that an app sends +- Content in DMs with the app +- Content in which the app is [mentioned](/developers/docs/reference#message-formatting-formats) +- Content of the message a [message context menu command](/developers/docs/interactions/application-commands#message-commands) is used on + +## Rate Limiting + + +This section refers to Gateway rate limits, not [HTTP API rate limits](/developers/docs/topics/rate-limits) + + +Apps can send 120 [gateway events](/developers/docs/events/gateway-events) per [connection](/developers/docs/events/gateway#connections) every 60 seconds, meaning an average of 2 commands per second. Apps that surpass the limit are immediately disconnected from the Gateway. Similar to other rate limits, repeat offenders will have their API access revoked. + +Apps also have a limit for [concurrent](/developers/docs/events/gateway#session-start-limit-object) [Identify](/developers/docs/events/gateway#identifying) requests allowed per 5 seconds. If you hit this limit, the Gateway will respond with an [Invalid Session (opcode `9`)](/developers/docs/events/gateway-events#invalid-session). + +## Encoding and Compression + +When [establishing a connection](/developers/docs/events/gateway#connecting) to the Gateway, apps can use the `encoding` parameter to choose whether to communicate with Discord using a plain-text JSON or binary [ETF](https://erlang.org/doc/apps/erts/erl_ext_dist.html) encoding. You can pick whichever encoding type you're more comfortable with, but both have their own quirks. If you aren't sure which encoding to use, JSON is generally recommended. + +Apps can also optionally enable compression to receive zlib-compressed or zstd-compressed packets. [Payload compression](/developers/docs/events/gateway#payload-compression) can only be enabled when using a JSON encoding, but [transport compression](/developers/docs/events/gateway#transport-compression) can be used regardless of encoding type. + +### Using JSON Encoding + +When using the plain-text JSON encoding, apps have the option to enable [payload compression](/developers/docs/events/gateway#payload-compression). + +#### Payload Compression + + +If an app is using payload compression, it cannot use [transport compression](/developers/docs/events/gateway#transport-compression). + + +Payload compression enables optional per-packet compression for *some* events when Discord is sending events over the connection. + +Payload compression uses the zlib format (see [RFC1950 2.2](https://tools.ietf.org/html/rfc1950#section-2.2)) when sending payloads. To enable payload compression, your app can set `compress` to `true` when sending an [Identify (opcode `2`)](/developers/docs/events/gateway-events#identify) event. Note that even when payload compression is enabled, not all payloads will be compressed. + +When payload compression is enabled, your app (or library) _must_ detect and decompress these payloads to plain-text JSON before attempting to parse them. If you are using payload compression, the gateway does not implement a shared compression context between events sent. + +Payload compression will be disabled if you use [transport compression](/developers/docs/events/gateway#transport-compression). + +### Using ETF Encoding + +When using ETF (External Term Format) encoding, there are some specific behaviors you should know: + +- Snowflake IDs are transmitted as 64-bit integers or strings. +- Your app can't send compressed messages to the server. +- When sending payloads, you must use string keys. Using atom keys will result in a [`4002`](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-close-event-codes) decode error. + +See [erlpack](https://github.com/discord/erlpack) for an ETF implementation example. + +### Transport Compression + +Transport compression enables optional compression for all packets when Discord is sending events over the connection. The currently-available transport compression options are `zlib-stream` and `zstd-stream`. + +#### zlib-stream + +When zlib transport compression is enabled, your app needs to process received data through a single Gateway connection using a shared zlib context. However, each Gateway connection should use its own unique zlib context. + +When processing transport-compressed data, you should push received data to a buffer until you receive the 4-byte `Z_SYNC_FLUSH` suffix (`00 00 ff ff`). After you receive the `Z_SYNC_FLUSH` suffix, you can then decompress the buffer. + + +###### Transport Compression Example + +```python +# Z_SYNC_FLUSH suffix +ZLIB_SUFFIX = b'\x00\x00\xff\xff' +# initialize a buffer to store chunks +buffer = bytearray() +# create a shared zlib inflation context to run chunks through +inflator = zlib.decompressobj() + +# ... +def on_websocket_message(msg): + # always push the message data to your cache + buffer.extend(msg) + + # check if the last four bytes are equal to ZLIB_SUFFIX + if len(msg) < 4 or msg[-4:] != ZLIB_SUFFIX: + return + + # if the message *does* end with ZLIB_SUFFIX, + # get the full message by decompressing the buffers + # NOTE: the message is utf-8 encoded. + msg = inflator.decompress(buffer) + buffer = bytearray() + + # here you can treat `msg` as either JSON or ETF encoded, + # depending on your `encoding` param +``` + +#### zstd-stream + +When zstd-stream transport compression is enabled, all data needs to be processed through a zstd decompression context that stays alive for the lifetime of the gateway connection. + +When processing data, each websocket message corresponds to a single gateway message, but does not end a zstd frame. You will need to repeatedly call ZSTD_decompressStream until all data in the frame has been processed (ZSTD_decompressStream will not necessarily return 0, though). Take a look at this [Erlang](https://github.com/silviucpp/ezstd/blob/f3f33b2f6b917f7e8aaa2b4d71338620537df81b/src/ezstd.erl#L151-L169) + [C++](https://github.com/silviucpp/ezstd/blob/f3f33b2f6b917f7e8aaa2b4d71338620537df81b/c_src/ezstd_nif.cc#L520-L568) implementation for inspiration. + +## Tracking State + +Most of a client's state is provided during the initial [Ready](/developers/docs/events/gateway#ready-event) event and in the [Guild Create](/developers/docs/events/gateway-events#guild-create) events that follow. + +As resources continue to be created, updated, and deleted, Gateway events are sent to notify the app of these changes and to provide associated data. To avoid excessive API calls, apps should cache as many relevant resource states as possible, and update them as new events are received. + + +For larger apps, client state can grow to be very large. Therefore, we recommend only storing data in memory that are *needed* for the app to operate. In some cases, there isn't a need to cache member information (like roles or permissions) since some events like [MESSAGE_CREATE](/developers/docs/events/gateway-events#message-create) have the full member object included. + + +An example of state tracking can be considered in the case of an app that wants to track member status: when initially connecting to the Gateway, the app will receive information about the online status of guild members (whether they're online, idle, dnd, or offline). To keep the state updated, the app will track and parse [Presence Update](/developers/docs/events/gateway-events#presence-update) events as they're received, then update the cached member objects accordingly. + +## Guild Availability + +When connecting to the gateway as a bot user, guilds that the bot is a part of will start out as unavailable. Don't fret! The gateway will automatically attempt to reconnect on your behalf. As guilds become available to you, you will receive [Guild Create](/developers/docs/events/gateway-events#guild-create) events. + +## Sharding + +As apps grow and are added to an increasing number of guilds, some developers may find it necessary to divide portions of their app's operations across multiple processes. As such, the Gateway implements a method of user-controlled guild sharding which allows apps to split events across a number of Gateway connections. Guild sharding is entirely controlled by an app, and requires no state-sharing between separate connections to operate. While all apps *can* enable sharding, it's not necessary for apps in a smaller number of guilds. + + +Each shard can only support a maximum of 2500 guilds, and apps that are in 2500+ guilds *must* enable sharding. + + +To enable sharding on a connection, the app should send the `shard` array in the [Identify](/developers/docs/events/gateway-events#identify) payload. The first item in this array should be the zero-based integer value of the current shard, while the second represents the total number of shards. + + +The [Get Gateway Bot](/developers/docs/events/gateway#get-gateway-bot) endpoint provides a recommended number of shards for your app in the `shards` field + + +To calculate which events will be sent to which shard, the following formula can be used: + + +###### Sharding Formula + +```python +shard_id = (guild_id >> 22) % num_shards +``` + +As an example, if you wanted to split the connection between three shards, you'd use the following values for `shard` for each connection: `[0, 3]`, `[1, 3]`, and `[2, 3]`. + + +Gateway events that do not contain a `guild_id` will only be sent to the first shard (`shard_id: 0`). This includes Direct Message (DM), subscription and entitlement events. + + +Note that `num_shards` does not relate to (or limit) the total number of potential sessions. It is only used for *routing* traffic. As such, sessions do not have to be identified in an evenly-distributed manner when sharding. You can establish multiple sessions with the same `[shard_id, num_shards]`, or sessions with different `num_shards` values. This allows you to create sessions that will handle more or less traffic for more fine-tuned load balancing, or to orchestrate "zero-downtime" scaling/updating by handing off traffic to a new deployment of sessions with a higher or lower `num_shards` count that are prepared in parallel. + + +###### Max Concurrency + +If you have multiple shards, you may start them concurrently based on the [`max_concurrency`](/developers/docs/events/gateway#session-start-limit-object) value returned to you on session start. Which shards you can start concurrently are assigned based on a key for each shard. The rate limit key for a given shard can be computed with + +``` +rate_limit_key = shard_id % max_concurrency +``` + +This puts your shards into "buckets" of `max_concurrency` size. When you start your bot, you may start up to `max_concurrency` shards at a time, and you must start them by "bucket" **in order**. To explain another way, let's say you have 16 shards, and your `max_concurrency` is 16: + +``` +shard_id: 0, rate limit key (0 % 16): 0 +shard_id: 1, rate limit key (1 % 16): 1 +shard_id: 2, rate limit key (2 % 16): 2 +shard_id: 3, rate limit key (3 % 16): 3 +shard_id: 4, rate limit key (4 % 16): 4 +shard_id: 5, rate limit key (5 % 16): 5 +shard_id: 6, rate limit key (6 % 16): 6 +shard_id: 7, rate limit key (7 % 16): 7 +shard_id: 8, rate limit key (8 % 16): 8 +shard_id: 9, rate limit key (9 % 16): 9 +shard_id: 10, rate limit key (10 % 16): 10 +shard_id: 11, rate limit key (11 % 16): 11 +shard_id: 12, rate limit key (12 % 16): 12 +shard_id: 13, rate limit key (13 % 16): 13 +shard_id: 14, rate limit key (14 % 16): 14 +shard_id: 15, rate limit key (15 % 16): 15 +``` + +You may start all 16 of your shards at once, because each has a `rate_limit_key` which fills the bucket of 16 shards. However, let's say you had 32 shards: + +``` +shard_id: 0, rate limit key (0 % 16): 0 +shard_id: 1, rate limit key (1 % 16): 1 +shard_id: 2, rate limit key (2 % 16): 2 +shard_id: 3, rate limit key (3 % 16): 3 +shard_id: 4, rate limit key (4 % 16): 4 +shard_id: 5, rate limit key (5 % 16): 5 +shard_id: 6, rate limit key (6 % 16): 6 +shard_id: 7, rate limit key (7 % 16): 7 +shard_id: 8, rate limit key (8 % 16): 8 +shard_id: 9, rate limit key (9 % 16): 9 +shard_id: 10, rate limit key (10 % 16): 10 +shard_id: 11, rate limit key (11 % 16): 11 +shard_id: 12, rate limit key (12 % 16): 12 +shard_id: 13, rate limit key (13 % 16): 13 +shard_id: 14, rate limit key (14 % 16): 14 +shard_id: 15, rate limit key (15 % 16): 15 +shard_id: 16, rate limit key (16 % 16): 0 +shard_id: 17, rate limit key (17 % 16): 1 +shard_id: 18, rate limit key (18 % 16): 2 +shard_id: 19, rate limit key (19 % 16): 3 +shard_id: 20, rate limit key (20 % 16): 4 +shard_id: 21, rate limit key (21 % 16): 5 +shard_id: 22, rate limit key (22 % 16): 6 +shard_id: 23, rate limit key (23 % 16): 7 +shard_id: 24, rate limit key (24 % 16): 8 +shard_id: 25, rate limit key (25 % 16): 9 +shard_id: 26, rate limit key (26 % 16): 10 +shard_id: 27, rate limit key (27 % 16): 11 +shard_id: 28, rate limit key (28 % 16): 12 +shard_id: 29, rate limit key (29 % 16): 13 +shard_id: 30, rate limit key (30 % 16): 14 +shard_id: 31, rate limit key (31 % 16): 15 +``` + +In this case, you must start the shard buckets **in "order"**. That means that you can start shard 0 -> shard 15 concurrently, and then you can start shard 16 -> shard 31. + +### Sharding for Large Bots + +If your bot is in more than 150,000 guilds, there are some additional considerations you must take around sharding. Discord will migrate your bot to large bot sharding when it starts to get near the large bot sharding threshold. The bot owner(s) will receive a system DM and email confirming this move has completed as well as what shard number has been assigned. + +The number of shards you run must be a multiple of the shard number provided when reaching out to you. If you attempt to start your bot with an invalid number of shards, your Gateway connection will close with a `4010` Invalid Shard close code. + +The [Get Gateway Bot endpoint](/developers/docs/events/gateway#get-gateway-bot) will always return the correct amount of shards, so if you're already using this endpoint to determine your number of shards, you shouldn't require any changes. + +The session start limit for these bots will also be increased from 1000 to `max(2000, (guild_count / 1000) * 5)` per day. You also receive an increased `max_concurrency`, the number of [shards you can concurrently start](/developers/docs/events/gateway#session-start-limit-object). + +## Get Gateway +/gateway + + +This endpoint does not require authentication. + + +Returns an object with a valid WSS URL which the app can use when [Connecting](/developers/docs/events/gateway#connecting) to the Gateway. Apps should cache this value and only call this endpoint to retrieve a new URL when they are unable to properly establish a connection using the cached one. + + +###### Example Response + +```json +{ + "url": "wss://gateway.discord.gg/" +} +``` + +## Get Gateway Bot +/gateway/bot + + +This endpoint requires authentication using a valid bot token. + + +Returns an object based on the information in [Get Gateway](/developers/docs/events/gateway#get-gateway), plus additional metadata that can help during the operation of large or [sharded](/developers/docs/events/gateway#sharding) bots. Unlike the [Get Gateway](/developers/docs/events/gateway#get-gateway), this route should not be cached for extended periods of time as the value is not guaranteed to be the same per-call, and changes as the bot joins/leaves guilds. + + +###### JSON Response + +| Field | Type | Description | +|---------------------|------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| +| url | string | WSS URL that can be used for connecting to the Gateway | +| shards | integer | Recommended number of [shards](/developers/docs/events/gateway#sharding) to use when connecting | +| session_start_limit | [session_start_limit](/developers/docs/events/gateway#session-start-limit-object) object | Information on the current session start limit | + + +###### Example Response + +```json +{ + "url": "wss://gateway.discord.gg/", + "shards": 9, + "session_start_limit": { + "total": 1000, + "remaining": 999, + "reset_after": 14400000, + "max_concurrency": 1 + } +} +``` + +## Session Start Limit Object + + +###### Session Start Limit Structure + +| Field | Type | Description | +|-----------------|---------|----------------------------------------------------------------| +| total | integer | Total number of session starts the current user is allowed | +| remaining | integer | Remaining number of session starts the current user is allowed | +| reset_after | integer | Number of milliseconds after which the limit resets | +| max_concurrency | integer | Number of identify requests allowed per 5 seconds | diff --git a/discord/developers/docs/events/overview.mdx b/discord/developers/docs/events/overview.mdx new file mode 100644 index 0000000000..42e12e880e --- /dev/null +++ b/discord/developers/docs/events/overview.mdx @@ -0,0 +1,53 @@ +--- +title: Overview of Events +sidebarTitle: Overview +description: Learn about the different ways apps can receive events from Discord. +--- + +Apps can listen to events happening in Discord to stay up-to-date with changes and updates to servers, users, and even your app. The following sections cover basic information about the different transport methods that can be used to receive events, and link out to relevant documentation. + +## Receiving Events + +There are many event types that can be accessed using different transport methods: +- **[Gateway events](/developers/docs/events/overview#using-gateway)** are sent over a WebSocket connection between your app and Discord, and is the primary way to receive and send events. **Most events are only available via Gateway connections.** +- **[Webhook events](/developers/docs/events/overview#using-webhooks)** are sent to your app's Webhook Event URL over HTTP. +- **[SDK events](/developers/docs/events/overview#using-the-embedded-app-sdk)** are sent to your app when using the Embedded App SDK. + +Read details about each way to receive events in the sections below. + +### Using Gateway + +Gateway events are the primary way apps can listen and send events. Most events related to resources in Discord, like updates to channels, guilds, roles, and messages, are only available over [Gateway](/developers/docs/events/gateway). + +Gateway events are sent over a WebSocket-based [Gateway connection](/developers/docs/events/gateway#connections) between Discord and your app. To receive Gateway events, your app must open and maintain a persistent Gateway connection which you can read details about in the [Gateway documentation](/developers/docs/events/gateway#connections). To make receiving Gateway events simpler, we suggest using a [developer library](/developers/docs/developer-tools/community-resources#libraries) which helps setup, maintain, and handle common pitfalls with Gateway connections (like [rate limits](/developers/docs/events/gateway#rate-limiting)). + +Details about receiving events using the Gateway API is in the [Gateway documentation](/developers/docs/events/gateway#receiving-events), and the full list of available Gateway events is in the [Gateway Events documentation](/developers/docs/events/gateway-events). + +### Using Webhooks + +Webhook events let you receive a small number of events over HTTP. While many events aren't supported over HTTP, some events like [Application Authorized](/developers/docs/events/webhook-events#application-authorized) (sent when your app is installed to a user or server) aren't available using other transport methods like Gateway. + +Find the [list of webhook events](/developers/docs/events/webhook-events#event-types) and details about subscribing and handling them in the [Webhook Events documentation](/developers/docs/events/webhook-events). + + +### Using the Embedded App SDK + +When developing [Activities](/developers/docs/activities/overview), you can listen to a collection of [SDK events](/developers/docs/developer-tools/embedded-app-sdk#sdk-events), like updates to a user's voice status or screen orientation. To listen to SDK events, you can call [`subscribe()`](/developers/docs/developer-tools/embedded-app-sdk#subscribe) with the SDK event name. + +Details about listening to events using the Embedded App SDK is in the [Embedded App SDK documentation](/developers/docs/developer-tools/embedded-app-sdk). + +## Next Steps + + + + Gateway is the primary apps receive events on Discord. Read details about using the Gateway and receiving Gateway Events. + + + Read details about receiving and responding a small number of HTTP-based Webhook Events. + + + Read details about subscribing and receiving events specific to your Activity using the Embedded App SDK. + + + + diff --git a/discord/developers/docs/events/webhook-events.mdx b/discord/developers/docs/events/webhook-events.mdx new file mode 100644 index 0000000000..182a9f8f51 --- /dev/null +++ b/discord/developers/docs/events/webhook-events.mdx @@ -0,0 +1,520 @@ +--- +title: Webhook Events +description: Learn how to receive Discord events over HTTP using webhooks. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +Webhook events are one-way events sent to your app over HTTP to notify you when an event occurred. Unlike events that are [sent over Gateway connections](/developers/docs/events/gateway), events sent over webhooks are not realtime or guaranteed to be in order. + +While [incoming webhooks](/developers/docs/resources/webhook) are triggered by an external service, webhook events (i.e. outgoing webhooks) are triggered by events happening in Discord. This means your app will need to set up a public URL where you can receive HTTP events, which is detailed in the [preparing for events](/developers/docs/events/webhook-events#preparing-for-events) section. + +## Subscribing to Events + +To configure webhook events, you'll need to configure your URL and select the events you want your app to receive. + + +The steps below walk through subscribing using the developer portal. If you prefer to use the API, you can call [Edit Current Application](/developers/docs/resources/application#edit-current-application). + + +In your [app's settings](https://discord.com/developers/applications), navigate to the **Webhooks** page from the left-hand sidebar then complete the following: + +1. Under **Endpoint**, add a public URL that is set up to receive and acknowledge webhook events. Details about setting up a Webhook Events URL is in the [preparing for events](/developers/docs/events/webhook-events#preparing-for-events) section. +2. Enable Events by clicking the toggle in the **Events** section. +3. Select the [webhook events](/developers/docs/events/webhook-events#event-types) you want your app to receive. +4. Click **Save Changes**. + +If your URL is successfully verified, your app should begin receiving the events you selected. + +## Preparing for Events + +To receive webhook events, you'll need to configure your app's **Webhook Event URL** in your app's settings. + +### Configuring a Webhook Events URL + +A **Webhook Events URL** is a public endpoint for your app where Discord can send your app HTTP-based events. If your app is using [Gateway events](/developers/docs/events/gateway), you don't need to configure a Webhook Events URL. + +#### Setting Up an Endpoint + +Before you can add a Webhook Events URL to your app, your endpoint must be prepared for two things ahead of time: + +1. Acknowledging `PING` events from Discord +2. Validate security-related request headers (`X-Signature-Ed25519` and `X-Signature-Timestamp`) + +If either of these are not complete, your Webhook Events URL will not be validated. Details on acknowledging PING events and validating security-related headers are below. + + +###### Acknowledging PING requests + +When adding your Webhook Events URL, Discord will send a `POST` request with a `PING` payload with a `type: 0` to your endpoint. Your app is expected to acknowledge the request by returning a `204` response with an empty body. + + +You must provide a valid `Content-Type` when responding to `PING`s. See [here](/developers/docs/reference#http-api) for further information. + + + +To properly acknowledge a `PING` payload, return a `204` response with no body: + +```py +@app.route('/', methods=['POST']) +def my_command(): + if request.json["type"] == 0: + return Response(status=204) +``` + + + +###### Validating Security Request Headers + +To receive events via HTTP, there are some security steps you **must** take before your app is eligible to receive requests. + +Each webhook is sent with the following headers: + +- `X-Signature-Ed25519` as a signature +- `X-Signature-Timestamp` as a timestamp + +Using your favorite security library, you **must validate the request each time you receive an event**. If the signature fails validation, your app should respond with a `401` error code. Code examples of validating security headers is in the [Interactions documentation](/developers/docs/interactions/overview#setting-up-an-endpoint-validating-security-request-headers). + +In addition to ensuring your app validates security-related request headers at the time of saving your endpoint, Discord will also perform automated, routine security checks against your endpoint, including purposefully sending you invalid signatures. If you fail the validation, we will remove your Webhook Events URL and alert you via email and System DM. + +We recommend checking out our [Community Resources](/developers/docs/developer-tools/community-resources) and the libraries found there. + +#### Adding an Webhook Events Endpoint URL + +After you have a public endpoint to use as your app's Event Webhooks URL, you can add it to your app by going to your [app's settings](https://discord.com/developers/applications). + +On the **Webhooks** page, look for the **Endpoint URL** field. Paste your public URL that is set up to acknowledge `PING` messages and correctly handles security-related signature headers. + +After you configure your Webhook Events URL, you can [enable and subscribe to events](/developers/docs/events/webhook-events#subscribing-to-events) on the same page. + + +## Responding to Events + +When your Webhook Event URL receives a webhook event, your app should respond with a `204` status code with no body **within 3 seconds** to acknowledge that your app successfully received it. If your app doesn't respond to the webhook event, Discord will retry sending it several times using exponential backoff for up to 10 minutes. + +If your app fails to respond too often, Discord will stop sending you webhook events and notify you via email. + +## Webhook Event Payloads + +Webhook events are wrapped in an outer payload, with an inner `event` object. + +### Payload Structure + +Structure of the outer webhook payload + +| Field | Type | Description | +|----------------|-------------------------------------------------------------------------------|----------------------------------------------------------------| +| version | integer | Version scheme for the webhook event. Currently always `1` | +| application_id | snowflake | ID of your app | +| type | [webhook type](/developers/docs/events/webhook-events#webhook-types) | Type of webhook, either `0` for PING or `1` for webhook events | +| event? | [event body](/developers/docs/events/webhook-events#event-body-object) object | Event data payload | + +#### Webhook Types + +| Type | Value | Description | +|-------|-------|--------------------------------------------------------------------------------------------------------------------| +| PING | `0` | PING event sent to verify your Webhook Event URL is active | +| Event | `1` | Webhook event (details for event in [event body](/developers/docs/events/webhook-events#event-body-object) object) | + +#### Event Body Object + +The event body contains high-level data about the event, like the type and time it was triggered. + +The inner `data` object contains information specific to the [event type](/developers/docs/events/webhook-events#event-types). + +| Field | Type | Description | +|-----------|--------|---------------------------------------------------------------------------------------------------------------| +| type | string | [Event type](/developers/docs/events/webhook-events#event-types) | +| timestamp | string | Timestamp of when the event occurred in [ISO8601 format](/developers/docs/reference#iso8601-datetime) | +| data? | object | Data for the event. The shape depends on the [event type](/developers/docs/events/webhook-events#event-types) | + + +## Event Types + +The table below includes the different webhook event types your app can subscribe to. + +The "Value" column corresponds to the event's `type` field value in the [event body object](/developers/docs/events/webhook-events#event-body-object). + +| Name | Value | Description | +|-------------------------------------------------------------------------------------------------|------------------------------|---------------------------------------------------------------------------| +| [Application Authorized](/developers/docs/events/webhook-events#application-authorized) | `APPLICATION_AUTHORIZED` | Sent when an app was authorized by a user to a server or their account | +| [Application Deauthorized](/developers/docs/events/webhook-events#application-deauthorized) | `APPLICATION_DEAUTHORIZED` | Sent when an app was deauthorized by a user | +| [Entitlement Create](/developers/docs/events/webhook-events#entitlement-create) | `ENTITLEMENT_CREATE` | Entitlement was created | +| [Quest User Enrollment](/developers/docs/events/webhook-events#quest-user-enrollment) | `QUEST_USER_ENROLLMENT` | User was added to a Quest (currently unavailable) | +| [Lobby Message Create](/developers/docs/events/webhook-events#lobby-message-create) | `LOBBY_MESSAGE_CREATE` | Sent when a message is created in a lobby | +| [Lobby Message Update](/developers/docs/events/webhook-events#lobby-message-update) | `LOBBY_MESSAGE_UPDATE` | Sent when a message is updated in a lobby | +| [Lobby Message Delete](/developers/docs/events/webhook-events#lobby-message-delete) | `LOBBY_MESSAGE_DELETE` | Sent when a message is deleted from a lobby | +| [Game Direct Message Create](/developers/docs/events/webhook-events#game-direct-message-create) | `GAME_DIRECT_MESSAGE_CREATE` | Sent when a direct message is created during an active Social SDK session | +| [Game Direct Message Update](/developers/docs/events/webhook-events#game-direct-message-update) | `GAME_DIRECT_MESSAGE_UPDATE` | Sent when a direct message is updated during an active Social SDK session | +| [Game Direct Message Delete](/developers/docs/events/webhook-events#game-direct-message-delete) | `GAME_DIRECT_MESSAGE_DELETE` | Sent when a direct message is deleted during an active Social SDK session | + +#### Application Authorized + +`APPLICATION_AUTHORIZED` is sent when the app is added to a server or user account. + + +###### Application Authorized Structure + +| Field | Type | Description | +|-------------------|-------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| integration_type? | integer | [Installation context](/developers/docs/resources/application#application-object-application-integration-types) for the authorization. Either guild (`0`) if installed to a server or user (`1`) if installed to a user's account | +| user | [user object](/developers/docs/resources/user#user-object-user-structure) | User who authorized the app | +| scopes | array of strings | List of [scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) the user authorized | +| guild? | [guild object](/developers/docs/resources/guild#guild-object-guild-structure) | Server which app was authorized for (when integration type is `0`) | + + +###### Application Authorized Example + +```json +{ + "version": 1, + "application_id": "1234560123453231555", + "type": 1, + "event": { + "type": "APPLICATION_AUTHORIZED", + "timestamp": "2024-10-18T14:42:53.064834", + "data": { + "integration_type": 1, + "scopes": [ + "applications.commands" + ], + "user": { + // user data + } + } + } +} +``` + +#### Application Deauthorized + +`APPLICATION_DEAUTHORIZED` is sent when the app is deauthorized by a user. + + +###### Application Deauthorized Structure + +| Field | Type | Description | +|-------|---------------------------------------------------------------------------|-------------------------------| +| user | [user object](/developers/docs/resources/user#user-object-user-structure) | User who deauthorized the app | + + +###### Application Deauthorized Example + +```json +{ + "version": 1, + "application_id": "1234560123453231555", + "type": 1, + "event": { + "type": "APPLICATION_DEAUTHORIZED", + "timestamp": "2024-10-18T14:42:53.064834", + "data": { + "user": { + // user data + } + } + } +} +``` + +#### Entitlement Create + +`ENTITLEMENT_CREATE` is sent when an [entitlement](/developers/docs/resources/entitlement) is created when a user purchases or is otherwise granted one of your app's SKUs. Refer to the [Monetization documentation](/developers/docs/monetization/overview) for details. + + +###### Entitlement Create Structure + +The inner payload is an [entitlement](/developers/docs/resources/entitlement#entitlement-object) object. + + +###### Entitlement Create Example + +```json +{ + "version": 1, + "application_id": "1234560123453231555", + "type": 1, + "event": { + "type": "ENTITLEMENT_CREATE", + "timestamp": "2024-10-18T18:41:21.109604", + "data": { + "application_id": "1234560123453231555", + "consumed": false, + "deleted": false, + "gift_code_flags": 0, + "id": "1234505980407808808", + "promotion_id": null, + "sku_id": "123489045643835123", + "type": 4, + "user_id": "111178765189277770" + } + } +} +``` + +#### Quest User Enrollment + + +This event cannot be received by apps at this time. It's documented because it appears on the Webhooks settings page. + + +`QUEST_USER_ENROLLMENT` is sent when a user is added to a Quest on Discord. + +#### Lobby Message Create + +`LOBBY_MESSAGE_CREATE` is sent when a message is created in a lobby. + +###### Lobby Message Create Structure + +The inner payload is a [lobby message object](/developers/docs/events/webhook-events#lobby-message-object). + +###### Lobby Message Create Example + +```json +{ + "version": 1, + "application_id": "1234567765431056709", + "type": 1, + "event": { + "type": "LOBBY_MESSAGE_CREATE", + "timestamp": "2024-10-18T18:41:21.109604", + "data": { + "id": "1397729799727878254", + "type": 0, + "content": "welcome to the party!", + "lobby_id": "1397729744753266719", + "channel_id": "1397729744753266719", + "author": { + // user data + }, + "flags": 65536, + "application_id": "1234567765431056709" + } + } +} +``` + +#### Lobby Message Update + +`LOBBY_MESSAGE_UPDATE` is sent when a message is updated in a lobby. + +###### Lobby Message Update Structure + +The inner payload is a [lobby message object](/developers/docs/events/webhook-events#lobby-message-object) with additional fields for message updates. + +###### Lobby Message Update Example + +```json +{ + "version": 1, + "application_id": "1234567765431056709", + "type": 1, + "event": { + "type": "LOBBY_MESSAGE_UPDATE", + "timestamp": "2025-08-05T20:39:19.587872", + "data": { + "id": "1402390388030832792", + "type": 0, + "content": "noice", + "lobby_id": "1402385687281537066", + "channel_id": "1402389638311841883", + "author": { + // user data + }, + "edited_timestamp": "2025-08-05T20:39:19.557970+00:00", + "flags": 0, + "timestamp": "2025-08-05T20:38:43.660000+00:00" + } + } +} +``` + +#### Lobby Message Delete + +`LOBBY_MESSAGE_DELETE` is sent when a message is deleted from a lobby. + +###### Lobby Message Delete Structure + +| Field | Type | Description | +|----------|-----------|-----------------------------------------------| +| id | snowflake | ID of the deleted message | +| lobby_id | snowflake | ID of the lobby where the message was deleted | + +###### Lobby Message Delete Example + +```json +{ + "version": 1, + "application_id": "1234567765431056709", + "type": 1, + "event": { + "type": "LOBBY_MESSAGE_DELETE", + "timestamp": "2025-08-05T21:44:09.412957", + "data": { + "id": "1402406637632884857", + "lobby_id": "1402399883394285659" + } + } +} +``` + +#### Game Direct Message Create + +`GAME_DIRECT_MESSAGE_CREATE` is sent when a direct message is created while at least one user has an active Social SDK session. + +###### Game Direct Message Create Structure + +The inner payload is a [message object](/developers/docs/events/webhook-events#message-object) or [passthrough message object](/developers/docs/events/webhook-events#passthrough-message-object). + +###### Game Direct Message Create Example + +```json +{ + "version": 1, + "application_id": "1234567765431056709", + "type": 1, + "event": { + "type": "GAME_DIRECT_MESSAGE_CREATE", + "timestamp": "2025-08-14T18:09:38.063234", + "data": { + "id": "1405614357781545021", + "type": 0, + "content": "get in friend, we're going raiding", + "channel_id": "1405604229820715098", + "author": { + // user data + }, + "timestamp": "2025-08-14T18:09:37.947000+00:00", + "application_id": "1234567765431056709", + "attachments": [] + } + } +} +``` + +#### Game Direct Message Update + +`GAME_DIRECT_MESSAGE_UPDATE` is sent when a direct message is updated while at least one user has an active Social SDK session. + +###### Game Direct Message Update Structure + +The inner payload is a [message object](/developers/docs/events/webhook-events#message-object) or [passthrough message object](/developers/docs/events/webhook-events#passthrough-message-object). + +###### Game Direct Message Update Example + +```json +{ + "version": 1, + "application_id": "1234567765431056709", + "type": 1, + "event": { + "type": "GAME_DIRECT_MESSAGE_UPDATE", + "timestamp": "2025-08-14T16:44:31.847073", + "data": { + "id": "1405591838810706081", + "content": "almost ready to queue?", + "channel_id": "1404960877324533784", + "author": { + // user data + }, + "recipient_id": "1404960877324533784" + } + } +} +``` + +#### Game Direct Message Delete + +`GAME_DIRECT_MESSAGE_DELETE` is sent when a direct message is deleted while at least one user has an active Social SDK session. + +###### Game Direct Message Delete Structure + +The inner payload is a [message object](/developers/docs/events/webhook-events#message-object) or [passthrough message object](/developers/docs/events/webhook-events#passthrough-message-object). + +###### Game Direct Message Delete Example + +```json +{ + "version": 1, + "application_id": "1234567765431056709", + "type": 1, + "event": { + "type": "GAME_DIRECT_MESSAGE_DELETE", + "timestamp": "2025-08-20T17:01:50.099204", + "data": { + "id": "1407771600643686503", + "type": 0, + "content": "cant make it in time", + "channel_id": "1405604229820715098", + "author": { + // user data + }, + "timestamp": "2025-08-20T17:01:44.725000+00:00", + "flags": 0, + "attachments": [], + "components": [] + } + } +} +``` + +## Social SDK Message Objects + +Discord Social SDK utilizes specialized message objects for lobby and direct message events that occur during active game sessions. These objects extend or modify the standard Discord message structure to support communication features. + +- [Lobby messages](/developers/docs/events/webhook-events#lobby-message-object) include lobby-specific fields like `lobby_id` +- [Standard Discord messages](/developers/docs/events/webhook-events#message-object) in SDK contexts may include additional fields +- [Passthrough messages](/developers/docs/events/webhook-events#passthrough-message-object) are used for communication between provisional accounts + +These objects are used in the webhook events `LOBBY_MESSAGE_*` and `GAME_DIRECT_MESSAGE_*` depending on the messaging context. + +### Lobby Message Object + +Represents a message sent in a lobby or [Linked Channel](/developers/docs/discord-social-sdk/development-guides/linked-channels). + +###### Lobby Message Structure + +| Field | Type | Description | +|-----------------|---------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the message | +| type | integer | [Type of message](/developers/docs/resources/message#message-object-message-types) | +| content | string | Contents of the message | +| lobby_id | snowflake | ID of the lobby where the message was sent | +| channel_id | snowflake | ID of the channel the message was sent in | +| author | [user object](/developers/docs/resources/user#user-object-user-structure) | Author of the message | +| metadata? | object | Additional metadata for the message (key-value pairs) | +| flags | integer | [Message flags](/developers/docs/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | +| application_id? | snowflake | ID of the application (only present during active Social SDK sessions) | + +### Message Object + +Standard [Message Object](/developers/docs/resources/message#message-object) with additional fields. + +###### Additional Fields + +| Field | Type | Description | +|-----------|---------------------------------------------------------------------|-----------------------------------------------------------------------------------------| +| lobby_id? | snowflake | ID of the lobby where the message was created (only present in Linked Channel messages) | +| channel | [channel object](/developers/docs/resources/channel#channel-object) | Channel object with recipient information | + +### Passthrough Message Object + +Represents a message between provisional users that exists only in-game. + +###### Passthrough Message Structure + +| Field | Type | Description | +|----------------|---------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the message | +| type | integer | [Type of message](/developers/docs/resources/message#message-object-message-types) | +| content | string | Contents of the message | +| channel_id | snowflake | ID of the channel the message was sent in | +| recipient_id | snowflake | ID of the message recipient | +| author | [user object](/developers/docs/resources/user#user-object-user-structure) | Author of the message | +| flags | integer | [Message flags](/developers/docs/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | +| application_id | snowflake | ID of the application that created the message | +| channel | [channel object](/developers/docs/resources/channel#channel-object) | Channel object with recipient information | + + +When both users in a direct message are provisional accounts, messages become "passthrough messages" that are only visible in-game and use this specialized structure. + diff --git a/discord/developers/docs/interactions/application-commands.mdx b/discord/developers/docs/interactions/application-commands.mdx new file mode 100644 index 0000000000..869eeaf513 --- /dev/null +++ b/discord/developers/docs/interactions/application-commands.mdx @@ -0,0 +1,1411 @@ +--- +title: Application Commands +description: Complete guide to creating slash commands, user commands, and message commands. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +Application commands are native ways to interact with apps in the Discord client. There are 3 types of commands accessible in different interfaces: the chat input, a message's context menu (top-right menu or right-clicking in a message), and a user's context menu (right-clicking on a user). + +![Client interfaces showing the different types of application commands](/images/command-types.png) + +## Application Command Object + + +###### Application Command Naming + +`CHAT_INPUT` command names and command option names must match the following regex `^[-_'\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` with the unicode flag set. If there is a lowercase variant of any letters used, you must use those. Characters with no lowercase variants and/or uncased letters are still allowed. `USER` and `MESSAGE` commands may be mixed case and can include spaces. + + +###### Application Command Structure + +| Field | Type | Description | Valid Types | +|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------| +| id | snowflake | Unique ID of command | all | +| type? | one of [command types](/developers/docs/interactions/application-commands#application-command-object-application-command-types) | [Type of command](/developers/docs/interactions/application-commands#application-command-object-application-command-types), defaults to `1` | all | +| application_id | snowflake | ID of the parent application | all | +| guild_id? | snowflake | Guild ID of the command, if not global | all | +| name | string | [Name of command](/developers/docs/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters | all | +| name_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for `name` field. Values follow the same restrictions as `name` | all | +| description | string | Description for `CHAT_INPUT` commands, 1-100 characters. Empty string for `USER` and `MESSAGE` commands | all | +| description_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for `description` field. Values follow the same restrictions as `description` | all | +| options? \* | array of [command options](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command, max of 25 | CHAT_INPUT | +| default_member_permissions | ?string | Set of [permissions](/developers/docs/topics/permissions) represented as a bit set | all | +| dm_permission? | boolean | **Deprecated (use `contexts` instead)**; Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | all | +| default_permission? | ?boolean | Not recommended for use as field will soon be deprecated. Indicates whether the command is enabled by default when the app is added to a guild, defaults to `true` | all | +| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/docs/interactions/application-commands#agerestricted-commands), defaults to `false` | all | +| integration_types? | list of [integration types](/developers/docs/resources/application#application-object-application-integration-types) | [Installation contexts](/developers/docs/resources/application#installation-context) where the command is available, only for globally-scoped commands. Defaults to your app's [configured contexts](/developers/docs/resources/application#setting-supported-installation-contexts) | all | +| contexts? | ?list of [interaction context types](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) | [Interaction context(s)](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) where the command can be used, only for globally-scoped commands. | all | +| version | snowflake | Autoincrementing version identifier updated during substantial record changes | all | +| handler? | one of [command handler types](/developers/docs/interactions/application-commands#application-command-object-entry-point-command-handler-types) | Determines whether the interaction is handled by the app's interactions handler or by Discord | PRIMARY_ENTRY_POINT | + +\* `options` can only be set for application commands of type `CHAT_INPUT`. + +\* `handler` can only be set for application commands of type `PRIMARY_ENTRY_POINT` for applications with the `EMBEDDED` flag (i.e. applications that have an Activity). + + +`default_permission` will soon be deprecated. You can instead set `default_member_permissions` to `"0"` to disable the command for everyone except admins by default, and/or use `contexts` to disable globally-scoped commands inside of DMs with your app + + + +###### Application Command Types + +| Name | Type | Description | +|---------------------|------|------------------------------------------------------------------------------------------------------------------------| +| CHAT_INPUT | 1 | Slash commands; a text-based command that shows up when a user types `/` | +| USER | 2 | A UI-based command that shows up when you right click or tap on a user | +| MESSAGE | 3 | A UI-based command that shows up when you right click or tap on a message | +| PRIMARY_ENTRY_POINT | 4 | A UI-based command that represents the primary way to invoke an app's [Activity](/developers/docs/activities/overview) | + + +###### Application Command Option Structure + + +Required `options` must be listed before optional options + + +| Field | Type | Description | Valid Option Types | +|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| +| type | one of [application command option type](/developers/docs/interactions/application-commands#application-command-object-application-command-option-type) | Type of option | all | +| name \* | string | [1-32 character name](/developers/docs/interactions/application-commands#application-command-object-application-command-naming) | all | +| name_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | all | +| description | string | 1-100 character description | all | +| description_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | all | +| required? | boolean | Whether the parameter is required or optional, default `false` | all but `SUB_COMMAND` and `SUB_COMMAND_GROUP` | +| choices? | array of [application command option choice](/developers/docs/interactions/application-commands#application-command-object-application-command-option-choice-structure) | Choices for the user to pick from, max 25 | `STRING`, `INTEGER`, `NUMBER` | +| options? | array of [application command option](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) | If the option is a subcommand or subcommand group type, these nested options will be the parameters or subcommands respectively; up to 25 | `SUB_COMMAND` , `SUB_COMMAND_GROUP` | +| channel_types? | array of [channel types](/developers/docs/resources/channel#channel-object-channel-types) | The channels shown will be restricted to these types | `CHANNEL` | +| min_value? | integer for `INTEGER` options, double for `NUMBER` options | The minimum value permitted | `INTEGER` , `NUMBER` | +| max_value? | integer for `INTEGER` options, double for `NUMBER` options | The maximum value permitted | `INTEGER` , `NUMBER` | +| min_length? | integer | The minimum allowed length (minimum of `0`, maximum of `6000`) | `STRING` | +| max_length? | integer | The maximum allowed length (minimum of `1`, maximum of `6000`) | `STRING` | +| autocomplete? \*\* | boolean | If autocomplete interactions are enabled for this option | `STRING`, `INTEGER`, `NUMBER` | + +\* `name` must be unique within an array of [application command options](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure). + +\*\* `autocomplete` may not be set to true if `choices` are present. + + +Options using `autocomplete` are not confined to only use choices given by the application. + + + +###### Application Command Option Type + +| Name | Value | Note | +|-------------------|-------|---------------------------------------------------------------------------| +| SUB_COMMAND | 1 | | +| SUB_COMMAND_GROUP | 2 | | +| STRING | 3 | | +| INTEGER | 4 | Any integer between -2^53+1 and 2^53-1 | +| BOOLEAN | 5 | | +| USER | 6 | | +| CHANNEL | 7 | Includes all channel types + categories | +| ROLE | 8 | | +| MENTIONABLE | 9 | Includes users and roles | +| NUMBER | 10 | Any double between -2^53 and 2^53 | +| ATTACHMENT | 11 | [attachment](/developers/docs/resources/message#attachment-object) object | + + +###### Application Command Option Choice Structure + + +If you specify `choices` for an option, they are the **only** valid values for a user to pick + + +| Field | Type | Description | +|---------------------|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| name | string | 1-100 character choice name | +| name_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | +| value | string, integer, or double \* | Value for the choice, up to 100 characters if string | + +\* Type of `value` depends on the [option type](/developers/docs/interactions/application-commands#application-command-object-application-command-option-type) that the choice belongs to. + + +###### Entry Point Command Handler Types + +| Name | Value | Note | +|-------------------------|-------|----------------------------------------------------------------------------------------------------------------------------| +| APP_HANDLER | 1 | The app handles the interaction using an interaction token | +| DISCORD_LAUNCH_ACTIVITY | 2 | Discord handles the interaction by launching an Activity and sending a follow-up message without coordinating with the app | + +Details about Entry Point command handler types are in the [Entry Point handlers](/developers/docs/interactions/application-commands#entry-point-handlers) section. + +## Authorizing Your Application + +Application commands do not depend on a bot user in the guild; they use the [interactions](/developers/docs/interactions/receiving-and-responding) model. To create commands in a guild, your app must be authorized with the `applications.commands` scope which can be used independently, but is also automatically included with the `bot` scope. + +When requesting this scope, we "shortcut" the OAuth2 flow similar to adding a bot. You don't need to complete the flow, exchange for a token, or any of that. + +If your application does not require a bot user in the guild for its commands to work, **you don't need to add the bot scope or a permission bitfield to the URL**. + + +## Registering a Command + + +Commands can only be registered via HTTP endpoint. + + +Commands can be scoped either globally or to a specific guild. Global commands are available for every guild that adds your app. An individual app's global commands are also available in DMs if that app has a bot that shares a mutual guild with the user. + +Guild commands are specific to the guild you specify when making them. Guild commands are not available in DMs. Command names are unique per application, per type, within each scope (global and guild). That means: + +- Your app **cannot** have two global `CHAT_INPUT` commands with the same name +- Your app **cannot** have two guild `CHAT_INPUT` commands within the same name **on the same guild** +- Your app **cannot** have two global `USER` commands with the same name +- Your app **can** have a global and guild `CHAT_INPUT` command with the same name +- Your app **can** have a global `CHAT_INPUT` and `USER` command with the same name +- Your app **cannot** have a `PRIMARY_ENTRY_POINT` guild command +- Multiple apps **can** have commands with the same names + +This list is non-exhaustive. In general, remember that command names must be unique per application, per type, and within each scope (global and guild). + +An app can have the following number of commands: + +- 100 global `CHAT_INPUT` commands +- 5 global `USER` commands +- 5 global `MESSAGE` commands +- 1 global `PRIMARY_ENTRY_POINT` command + +For all command types except `PRIMARY_ENTRY_POINT`, you can have the same amount of guild-specific commands per guild. + + +There is a global rate limit of 200 application command creates per day, per guild + + +### Making a Global Command + +Global commands are available on _all_ your app's guilds. + +Global commands have inherent read-repair functionality. That means that if you make an update to a global command, and a user tries to use that command before it has updated for them, Discord will do an internal version check and reject the command, and trigger a reload for that command. + +To make a **global** command, make an HTTP POST call like this: + +```py +import requests + + +url = "https://discord.com/api/v10/applications//commands" + +# This is an example CHAT_INPUT or Slash Command, with a type of 1 +json = { + "name": "blep", + "type": 1, + "description": "Send a random adorable animal photo", + "options": [ + { + "name": "animal", + "description": "The type of animal", + "type": 3, + "required": True, + "choices": [ + { + "name": "Dog", + "value": "animal_dog" + }, + { + "name": "Cat", + "value": "animal_cat" + }, + { + "name": "Penguin", + "value": "animal_penguin" + } + ] + }, + { + "name": "only_smol", + "description": "Whether to show only baby animals", + "type": 5, + "required": False + } + ] +} + +# For authorization, you can use either your bot token +headers = { + "Authorization": "Bot " +} + +# or a client credentials token for your app with the applications.commands.update scope +headers = { + "Authorization": "Bearer " +} + +r = requests.post(url, headers=headers, json=json) +``` + +### Making a Guild Command + +Guild commands are available only within the guild specified on creation. Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use. + +To make a **guild** command, make a similar HTTP POST call, but scope it to a specific `guild_id`: + +```py +import requests + + +url = "https://discord.com/api/v10/applications//guilds//commands" + +# This is an example USER command, with a type of 2 +json = { + "name": "High Five", + "type": 2 +} + +# For authorization, you can use either your bot token +headers = { + "Authorization": "Bot " +} + +# or a client credentials token for your app with the applications.commands.update scope +headers = { + "Authorization": "Bearer " +} + +r = requests.post(url, headers=headers, json=json) +``` + +## Updating and Deleting a Command + +Commands can be deleted and updated by making `DELETE` and `PATCH` calls to the command endpoint. Those endpoints are + +- `applications//commands/` for global commands, or +- `applications//guilds//commands/` for guild commands + +Because commands have unique names within a type and scope, we treat `POST` requests for new commands as upserts. That means **making a new command with an already-used name for your application will update the existing command**. + +Detailed documentation about application command endpoints and their parameters are [in the endpoints section](/developers/docs/interactions/application-commands#endpoints). + +## Contexts + +Commands have two sets of contexts on the [application command object](/developers/docs/interactions/application-commands#application-command-object) that let you to configure when and where it can be used: +- `integration_types` defines the **[installation contexts](/developers/docs/interactions/application-commands#installation-context)** that a command supports +- `contexts` defines the **[interaction contexts](/developers/docs/interactions/application-commands#interaction-contexts)** where a command can be used + +Details for both types of command contexts are in the sections below. + + +Contexts are distinct from, and do not affect, any [command permissions](/developers/docs/interactions/application-commands#permissions) for apps installed to a server. + + +### Installation Context + +The [installation context](/developers/docs/resources/application#installation-context) is where your app was installed—to a server, a user, or both. If your app supports both installation contexts, there may be cases where you want some of your app's commands to only be available for one or the other. For example, maybe your app has a `/profile` command that is only relevant when it's installed to a user. + +A command's supported installation context(s) can be set using the [`integration_types` field](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) when creating or updating a command as long as any included contexts are already [supported on the application-level](/developers/docs/resources/application#setting-supported-installation-contexts). + +A command's value for `integration_types` may affect which [interaction contexts](/developers/docs/interactions/application-commands#interaction-contexts) a command is visible in. + +### Interaction Contexts + +The interaction contexts for a command determines where in the Discord client it can be used, and can be configured by setting the [`contexts` field](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) when creating or updating a command. + +There are three [interaction context types](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) that correspond to different surfaces: `GUILD` (`0`), `BOT_DM` (`1`), and `PRIVATE_CHANNEL` (`2`). However, the `PRIVATE_CHANNEL` interaction context is only meaningful for commands installed to a user (when the command's `integration_types` includes `USER_INSTALL`). + +## Permissions + +Application command permissions allow your app to enable or disable commands for up to 100 users, roles, and channels within a guild. Command permissions can also be updated by users in the client if they have the necessary permissions. + + +Command permissions can only be updated using a [Bearer token](/developers/docs/topics/oauth2#client-credentials-grant). Authenticating with a bot token will result in an error. + + +A command's current permissions can be retrieved using the [`GET /applications/{application.id}/guilds/{guild.id}/commands/{command.id}/permissions`](/developers/docs/interactions/application-commands#get-application-command-permissions) endpoint. The response will include an array called `permissions` with associated IDs and permission types. + +Command permissions can be updated with the [`PUT /applications/{application.id}/guilds/{guild.id}/commands/{command.id}/permissions`](/developers/docs/interactions/application-commands#edit-application-command-permissions) endpoint. To call the endpoint, apps must use a Bearer token that's authorized with the [`applications.commands.permissions.update`](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) scope from a user with sufficient permissions. For their permissions to be considered sufficient, all of the following must be true for **the authenticating user** (not your app or bot user): +- Has [permission to Manage Guild and Manage Roles](/developers/docs/topics/permissions) in the guild where the command is being edited +- Has the ability to run the command being edited +- Has permission to manage the resources that will be affected (roles, users, and/or channels depending on the [permission types](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permission-type)) + +### Syncing and Unsyncing Permissions + +The command permissions interface can be accessed in the client by navigating to `Server Settings` > `Integrations`, then clicking `Manage` to the right of an installed app. At the top of the interface, users can edit permissions for a specific user, role, or channel. By default, these top-level permissions will apply to all of an app's commands. However, each permission can also be unsynced and customized for individual commands to provide more granular control. + +When the permissions for a specific command are unsynced, meaning it doesn't align with the top-level permissions, the interface will display "Not Synced" to users. + +### Application Command Permissions Object + + +###### Guild Application Command Permissions Structure + +Returned when fetching the permissions for an app's command(s) in a guild. + +| Field | Type | Description | +|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------| +| id | snowflake | ID of the command or the application ID | +| application_id | snowflake | ID of the application the command belongs to | +| guild_id | snowflake | ID of the guild | +| permissions | array of [application command permissions](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure) | Permissions for the command in the guild, max of 100 | + +When the `id` field is the application ID instead of a command ID, the permissions apply to all commands that do not contain explicit overwrites. + + +###### Application Command Permissions Structure + +Application command permissions allow you to enable or disable commands for specific users, roles, or channels within a guild. + +| Field | Type | Description | +|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the role, user, or channel. It can also be a [permission constant](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-constants) | +| type | [application command permission type](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permission-type) | role (`1`), user (`2`), or channel (`3`) | +| permission | boolean | `true` to allow, `false`, to disallow | + +###### Application Command Permissions Constants + +The following constants can be used in the `id` field for command permissions payloads. + +| Permission | Value | Type | Description | +|--------------|----------------|-----------|-------------------------| +| `@everyone` | `guild_id` | snowflake | All members in a guild | +| All Channels | `guild_id - 1` | snowflake | All channels in a guild | + + +###### Application Command Permission Type + +| Name | Value | +|---------|-------| +| ROLE | 1 | +| USER | 2 | +| CHANNEL | 3 | + +To allow for fine-tuned access to commands, application command permissions are supported for guild and global commands of all types. Guild members and apps with the [necessary permissions](/developers/docs/interactions/application-commands#permissions) can allow or deny specific users and roles from using a command, or toggle commands for entire channels. + +Similar to how threads [inherit user and role permissions from the parent channel](/developers/docs/topics/threads#permissions), any command permissions for a channel will apply to the threads it contains. + + +If you don't have permission to use a command, it will not show up in the command picker. Members with the Administrator permission can use all commands. + + + +###### Using Default Permissions + +Default permissions can be added to a command during creation using the `default_member_permissions` and `context` fields. Adding default permissions doesn't require any Bearer token since it's configured during command creation and isn't targeting specific roles, users, or channels. + +The `default_member_permissions` field can be used when creating a command to set the permissions a user must have to use it. The value for `default_member_permissions` is a bitwise OR-ed set of [permissions](/developers/docs/topics/permissions#permissions-bitwise-permission-flags), serialized as a string. Setting it to `"0"` will prohibit anyone in a guild from using the command unless a specific overwrite is configured or the user has admin permissions. + +You can also include `BOT_DM` (`1`) in `contexts` when setting a global command's [interaction contexts](/developers/docs/interactions/application-commands#interaction-contexts) to control whether it can be run in DMs with your app. Guild commands don't support the `BOT_DM` interaction context. + + +###### Example of editing permissions + +As an example, the following command would not be usable by anyone except admins in any guilds by default: + +```json +{ + "name": "permissions_test", + "description": "A test of default permissions", + "type": 1, + "default_member_permissions": "0" +} +``` + +Or this would enable it just for users that have the `MANAGE_GUILD` permission: + +```py +permissions = str(1 << 5) + +command = { + "name": "permissions_test", + "description": "A test of default permissions", + "type": 1, + "default_member_permissions": permissions +} +``` + +And the following would disable a command for a specific channel: + +```py +A_SPECIFIC_CHANNEL = "" +url = "https://discord.com/api/v10/applications//guilds//commands//permissions" + +json = { + "permissions": [ + { + "id": A_SPECIFIC_CHANNEL, + "type": 3, + "permission": False + } + ] +} + +headers = { + "Authorization": "Bearer " +} + +r = requests.put(url, headers=headers, json=json) +``` + +## Slash Commands + +Slash commands—the `CHAT_INPUT` type—are a type of application command. They're made up of a name, description, and a block of `options`, which you can think of like arguments to a function. The name and description help users find your command among many others, and the `options` validate user input as they fill out your command. + +Slash commands can also have groups and subcommands to further organize commands. More on those later. + + +Slash commands can have a maximum of 8000 characters for combined name, description, and value properties for each command, its options (including subcommands and groups), and choices. When [localization fields](/developers/docs/interactions/application-commands#localization) are present, only the longest localization for each field (including the default value) is counted towards the size limit. + + + + +###### Example Slash Command + +```json +{ + "name": "blep", + "type": 1, + "description": "Send a random adorable animal photo", + "options": [ + { + "name": "animal", + "description": "The type of animal", + "type": 3, + "required": true, + "choices": [ + { + "name": "Dog", + "value": "animal_dog" + }, + { + "name": "Cat", + "value": "animal_cat" + }, + { + "name": "Penguin", + "value": "animal_penguin" + } + ] + }, + { + "name": "only_smol", + "description": "Whether to show only baby animals", + "type": 5, + "required": false + } + ] +} +``` + +When someone uses a slash command, your application will receive an interaction: + + +###### Example Interaction + + + +```json +{ + "type": 2, + "token": "A_UNIQUE_TOKEN", + "member": { + "user": { + "id": "53908232506183680", + "username": "Mason", + "avatar": "a_d5efa99b3eeaa7dd43acca82f5692432", + "discriminator": "1337", + "public_flags": 131141 + }, + "roles": ["539082325061836999"], + "premium_since": null, + "permissions": "2147483647", + "pending": false, + "nick": null, + "mute": false, + "joined_at": "2017-03-13T19:19:14.040000+00:00", + "is_pending": false, + "deaf": false + }, + "id": "786008729715212338", + "guild_id": "290926798626357999", + "app_permissions": "442368", + "guild_locale": "en-US", + "locale": "en-US", + "data": { + "options": [{ + "type": 3, + "name": "cardname", + "value": "The Gitrog Monster" + }], + "type": 1, + "name": "cardsearch", + "id": "771825006014889984" + }, + "channel_id": "645027906669510667" +} +``` + + + +## Subcommands and Subcommand Groups + + +Currently, subcommands and subcommand groups all appear at the top level in the command explorer. This may change in the future to include them as nested autocomplete options. + + +For those developers looking to make more organized and complex groups of commands, look no further than subcommands and groups. + +**Subcommands** organize your commands by **specifying actions within a command or group**. + +**Subcommand Groups** organize your **subcommands** by **grouping subcommands by similar action or resource within a command**. + +These are not enforced rules. You are free to use subcommands and groups however you'd like; it's just how we think about them. + + +Using subcommands or subcommand groups will make your base command unusable. You can't send the base `/permissions` command as a valid command if you also have `/permissions add | remove` as subcommands or subcommand groups + + +We support nesting one level deep within a group, meaning your top level command can contain subcommand groups, and those groups can contain subcommands. **That is the only kind of nesting supported.** Here's some visual examples: + +``` +VALID + +command +| +|__ subcommand +| +|__ subcommand + +---- + +VALID + +command +| +|__ subcommand-group + | + |__ subcommand +| +|__ subcommand-group + | + |__ subcommand + +---- + +VALID + +command +| +|__ subcommand-group + | + |__ subcommand +| +|__ subcommand + +------- + +INVALID + +command +| +|__ subcommand-group + | + |__ subcommand-group +| +|__ subcommand-group + | + |__ subcommand-group + +---- + +INVALID + +command +| +|__ subcommand + | + |__ subcommand-group +| +|__ subcommand + | + |__ subcommand-group +``` + +### Example Walkthrough + +Let's look at an example. Let's imagine you run a moderation bot. You want to make a `/permissions` command that can do the following: + +- Get the guild permissions for a user or a role +- Get the permissions for a user or a role on a specific channel +- Change the guild permissions for a user or a role +- Change the permissions for a user or a role on a specific channel + +We'll start by defining the top-level information for `/permissions`: + +```js +{ + "name": "permissions", + "description": "Get or edit permissions for a user or a role", + "options": [] +} +``` + +![A command with no arguments. It says /permissions](/images/command.png) + +Now we have a command named `permissions`. We want this command to be able to affect users and roles. Rather than making two separate commands, we can use subcommand groups. We want to use subcommand groups here because we are grouping commands on a similar resource: `user` or `role`. + +```js +{ + "name": "permissions", + "description": "Get or edit permissions for a user or a role", + "options": [ + { + "name": "user", + "description": "Get or edit permissions for a user", + "type": 2 // 2 is type SUB_COMMAND_GROUP + }, + { + "name": "role", + "description": "Get or edit permissions for a role", + "type": 2 + } + ] +} +``` + +You'll notice that a command like this **will not show up** in the command explorer. That's because groups are effectively "folders" for commands, and we've made two empty folders. So let's continue. + +Now that we've effectively made `user` and `role` "folders", we want to be able to either `get` and `edit` permissions. Within the subcommand groups, we can make subcommands for `get` and `edit`: + +```js +{ + "name": "permissions", + "description": "Get or edit permissions for a user or a role", + "options": [ + { + "name": "user", + "description": "Get or edit permissions for a user", + "type": 2, // 2 is type SUB_COMMAND_GROUP + "options": [ + { + "name": "get", + "description": "Get permissions for a user", + "type": 1 // 1 is type SUB_COMMAND + }, + { + "name": "edit", + "description": "Edit permissions for a user", + "type": 1 + } + ] + }, + { + "name": "role", + "description": "Get or edit permissions for a role", + "type": 2, + "options": [ + { + "name": "get", + "description": "Get permissions for a role", + "type": 1 + }, + { + "name": "edit", + "description": "Edit permissions for a role", + "type": 1 + } + ] + } + ] +} +``` + +![A command with grouped subcommands. It says /permissions user get](/images/command-with-groups-subcommands.png) + +Now, we need some arguments! If we chose `user`, we need to be able to pick a user; if we chose `role`, we need to be able to pick a role. We also want to be able to pick between guild-level permissions and channel-specific permissions. For that, we can use optional arguments: + +```js +{ + "name": "permissions", + "description": "Get or edit permissions for a user or a role", + "options": [ + { + "name": "user", + "description": "Get or edit permissions for a user", + "type": 2, // 2 is type SUB_COMMAND_GROUP + "options": [ + { + "name": "get", + "description": "Get permissions for a user", + "type": 1, // 1 is type SUB_COMMAND + "options": [ + { + "name": "user", + "description": "The user to get", + "type": 6, // 6 is type USER + "required": true + }, + { + "name": "channel", + "description": "The channel permissions to get. If omitted, the guild permissions will be returned", + "type": 7, // 7 is type CHANNEL + "required": false + } + ] + }, + { + "name": "edit", + "description": "Edit permissions for a user", + "type": 1, + "options": [ + { + "name": "user", + "description": "The user to edit", + "type": 6, + "required": true + }, + { + "name": "channel", + "description": "The channel permissions to edit. If omitted, the guild permissions will be edited", + "type": 7, + "required": false + } + ] + } + ] + }, + { + "name": "role", + "description": "Get or edit permissions for a role", + "type": 2, + "options": [ + { + "name": "get", + "description": "Get permissions for a role", + "type": 1, + "options": [ + { + "name": "role", + "description": "The role to get", + "type": 8, // 8 is type ROLE + "required": true + }, + { + "name": "channel", + "description": "The channel permissions to get. If omitted, the guild permissions will be returned", + "type": 7, + "required": false + } + ] + }, + { + "name": "edit", + "description": "Edit permissions for a role", + "type": 1, + "options": [ + { + "name": "role", + "description": "The role to edit", + "type": 8, + "required": true + }, + { + "name": "channel", + "description": "The channel permissions to edit. If omitted, the guild permissions will be edited", + "type": 7, + "required": false + } + ] + } + ] + } + ] +} +``` + +And, done! The JSON looks a bit complicated, but what we've ended up with is a single command that can be scoped to multiple actions, and then further scoped to a particular resource, and then even _further_ scope with optional arguments. Here's what it looks like all put together. + +![A command with grouped subcommands and parameters. It says /permissions user get with arguments for a user and a channel.](/images/command-with-groups-subcommands-parameters.png) + +## User Commands + +User commands are application commands that appear on the context menu (right click or tap) of users. They're a great way to surface quick actions for your app that target users. They don't take any arguments, and will return the user on whom you clicked or tapped in the interaction response. + + +A user must have permission to send text messages in the channel they invoke a user command in. If they don't have this permission, they will receive a 'Permission Denied' error from the interaction. + + + +The `description` field is not allowed when creating user commands. However, to avoid breaking changes to data models, `description` will be an **empty string** (instead of `null`) when fetching commands. + + + +###### Example User Command + +```json +{ + "name": "High Five", + "type": 2 +} +``` + +![An example user command. The context menu has an Apps section open to a High Five command](/images/user-command.png) + +When someone uses a user command, your application will receive an interaction: + + +###### Example Interaction + + + +```json +{ + "application_id": "775799577604522054", + "channel_id": "772908445358620702", + "data": { + "id": "866818195033292850", + "name": "context-menu-user-2", + "resolved": { + "members": { + "809850198683418695": { + "avatar": null, + "is_pending": false, + "joined_at": "2021-02-12T18:25:07.972000+00:00", + "nick": null, + "pending": false, + "permissions": "246997699136", + "premium_since": null, + "roles": [] + } + }, + "users": { + "809850198683418695": { + "avatar": "afc428077119df8aabbbd84b0dc90c74", + "bot": true, + "discriminator": "7302", + "id": "809850198683418695", + "public_flags": 0, + "username": "VoltyDemo" + } + } + }, + "target_id": "809850198683418695", + "type": 2 + }, + "guild_id": "772904309264089089", + "guild_locale": "en-US", + "app_permissions": "442368", + "id": "867794291820986368", + "locale": "en-US", + "member": { + "avatar": null, + "deaf": false, + "is_pending": false, + "joined_at": "2020-11-02T20:46:57.364000+00:00", + "mute": false, + "nick": null, + "pending": false, + "permissions": "274877906943", + "premium_since": null, + "roles": ["785609923542777878"], + "user": { + "avatar": "a_f03401914fb4f3caa9037578ab980920", + "discriminator": "6538", + "id": "167348773423415296", + "public_flags": 1, + "username": "ian" + } + }, + "token": "UNIQUE_TOKEN", + "type": 2, + "version": 1 +} +``` + + + + +## Message Commands + +Message commands are application commands that appear on the context menu (right click or tap) of messages. They're a great way to surface quick actions for your app that target messages. They don't take any arguments, and will return the message on whom you clicked or tapped in the interaction response. + + +The `description` field is not allowed when creating message commands. However, to avoid breaking changes to data models, `description` will be an **empty string** (instead of `null`) when fetching commands. + + + +###### Example Message Command + +```json +{ + "name": "Bookmark", + "type": 3 +} +``` + +![An example message command. The context menu has an Apps section open to a Bookmark command](/images/message-command.png) + +When someone uses a message command, your application will receive an interaction: + + +###### Example Interaction + + + +```json +{ + "application_id": "775799577604522054", + "channel_id": "772908445358620702", + "data": { + "id": "866818195033292851", + "name": "context-menu-message-2", + "resolved": { + "messages": { + "867793854505943041": { + "attachments": [], + "author": { + "avatar": "a_f03401914fb4f3caa9037578ab980920", + "discriminator": "6538", + "id": "167348773423415296", + "public_flags": 1, + "username": "ian" + }, + "channel_id": "772908445358620702", + "components": [], + "content": "some message", + "edited_timestamp": null, + "embeds": [], + "flags": 0, + "id": "867793854505943041", + "mention_everyone": false, + "mention_roles": [], + "mentions": [], + "pinned": false, + "timestamp": "2021-07-22T15:42:57.744000+00:00", + "tts": false, + "type": 0 + } + } + }, + "target_id": "867793854505943041", + "type": 3 + }, + "guild_id": "772904309264089089", + "guild_locale": "en-US", + "app_permissions": "442368", + "id": "867793873336926249", + "locale": "en-US", + "member": { + "avatar": null, + "deaf": false, + "is_pending": false, + "joined_at": "2020-11-02T20:46:57.364000+00:00", + "mute": false, + "nick": null, + "pending": false, + "permissions": "274877906943", + "premium_since": null, + "roles": ["785609923542777878"], + "user": { + "avatar": "a_f03401914fb4f3caa9037578ab980920", + "discriminator": "6538", + "id": "167348773423415296", + "public_flags": 1, + "username": "ian" + } + }, + "token": "UNIQUE_TOKEN", + "type": 2, + "version": 1 +} +``` + + + +## Entry Point Commands + +An Entry Point command serves as the primary way for users to open an app's [Activity](/developers/docs/activities/overview) from the [App Launcher](https://support.discord.com/hc/articles/21334461140375-Using-Apps-on-Discord#h_01HRQSA6C8TRHS722P1H3HW1TV). + +For the Entry Point command to be visible to users, an app must have [Activities](/developers/docs/activities/overview) enabled. +![Entry Point command in App Launcher](/images/command-entry-point.png) + + +###### Example Entry Point Command + +```json +{ + "name": "launch", + "description": "Launch Racing with Friends", + "type": 4, + "handler": 2 +} +``` + +### Entry Point handlers + +When a user invokes an app's Entry Point command, the value of [`handler`](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) will determine how the interaction is handled: + +- For `APP_HANDLER` (`1`), the app is responsible for [responding to the interaction](/developers/docs/interactions/receiving-and-responding#responding-to-an-interaction). It can respond by launching the app's associated Activity using the `LAUNCH_ACTIVITY` (type `12`) [interaction callback type](/developers/docs/interactions/receiving-and-responding##interaction-response-object-interaction-callback-type), or take another action (like sending a follow-up message in channel). +- For `DISCORD_LAUNCH_ACTIVITY` (`2`), Discord will handle the interaction automatically by launching the associated Activity and sending a message to the channel where it was launched. + +### Default Entry Point command + +When you enable Activities, an Entry Point command (named "Launch") is automatically created for your app with `DISCORD_LAUNCH_ACTIVITY` (`2`) set as the [Entry Point handler](/developers/docs/interactions/application-commands#entry-point-handlers). You can retrieve details for the automatically-created command, like its ID, by calling the [Get Global Application Commands](/developers/docs/interactions/application-commands##get-global-application-commands) endpoint and looking for the "Launch" command. + +Details about updating or replacing the default Entry Point command is in the [Setting Up an Entry Point Command guide](/developers/docs/activities/development-guides/user-actions#setting-up-an-entry-point-command). + +## Autocomplete + +Autocomplete interactions allow your application to dynamically return option suggestions to a user as they type. + +An autocomplete interaction **can return partial data** for option values. Your application will receive partial data for any existing user input, as long as that input passes client-side validation. For example, you may receive partial strings, but not invalid numbers. The option the user is currently typing will be sent with a `focused: true` boolean field and options the user has already filled will also be sent but without the `focused` field. This is a special case where options that are otherwise required might not be present, due to the user not having filled them yet. + + +This validation is **client-side only**. + + +```json +{ + "type": 4, + "data": { + "id": "816437322781949972", + "name": "airhorn", + "type": 1, + "version": "847194950382780532", + "options": [ + { + "type": 3, + "name": "variant", + "value": "data a user is typ", + "focused": true + } + ] + } +} +``` + +## Localization + +Application commands can be localized, which will cause them to use localized names and descriptions depending on the client's selected language. This is entirely optional. Localization is available for names and descriptions of commands, subcommands, and options, as well as the names of choices, by submitting the appropriate `name_localizations` and `description_localizations` fields when creating or updating the application command. + +Application commands may be partially localized - not all [available locales](/developers/docs/reference#locales) are required, nor do different fields within a command need to support the same set of locales. If a locale is not present in a localizations dictionary for a field, users in that locale will see the default value for that field. It's not necessary to fill out all locales with the default value. Any localized values that are identical to the default will be ignored. + +Localized option names are subject to an additional constraint, which is that they must be distinct from all other default option names of that command, as well as all other option names within that locale on that command. + +When taking advantage of command localization, the interaction payload received by your client will still use default command, subcommand, and option names. To localize your interaction response, you can determine the client's selected language by using the `locale` key in the interaction payload. + +An application command furnished with localizations might look like this: + +```json +{ + "name": "birthday", + "type": 1, + "description": "Wish a friend a happy birthday", + "name_localizations": { + "zh-CN": "生日", + "el": "γενέθλια" + }, + "description_localizations": { + "zh-CN": "祝你朋友生日快乐" + }, + "options": [ + { + "name": "age", + "type": 4, + "description": "Your friend's age", + "name_localizations": { + "zh-CN": "岁数" + }, + "description_localizations": { + "zh-CN": "你朋友的岁数" + } + } + ] +} +``` + +### Locale fallbacks + +For application commands, there are built-in fallbacks in case a user's locale isn't present in the localizations. If the fallback locale is also missing, it will use the default. + + +You should make sure to include your default value in its proper locale key, otherwise it may use a fallback value unexpectedly. For example, if your default value is `en-US`, but you don't specify the `en-US` value in your localizations, users with `en-US` selected will see the `en-GB` value if it's specified. For example, if you have a command with the default name "color", and your localizations specify only the `en-GB` value as "colour", users in the `en-US` locale will see "colour" because the `en-US` key is missing. + + +| Locale | Fallback | +|--------|----------| +| en-US | en-GB | +| en-GB | en-US | +| es-419 | es-ES | + +### Retrieving localized commands + +While most endpoints that return application command objects will return the `name_localizations` and `description_localizations` fields, some will not by default. This includes `GET` endpoints that return all of an application's guild or global commands. Instead, those endpoints will supply additional `name_localized` or `description_localized` fields, which only contain the localization relevant to the requester's locale. (The full dictionaries can still be obtained by supplying the appropriate query argument). + +For example, if a batch `GET` request were made with locale `zh-CN`, including the above command, the returned object would look as follows: + +```json +{ + "name": "birthday", + "type": 1, + "description": "Wish a friend a happy birthday", + "name_localized": "生日", + "description_localized": "祝你朋友生日快乐", + "options": [ + { + "name": "age", + "type": 4, + "description": "Your friend's age", + "name_localized": "岁数", + "description_localized": "你朋友的岁数", + } + ] +} +``` + +If the requester's locale is not found in a localizations dictionary, then the corresponding `name_localization` or `description_localization` for that field will also not be present. + +Locale is determined by looking at the `X-Discord-Locale` header, then the `Accept-Language` header if not present, then lastly the user settings locale. + +## Age-Restricted Commands + +A command that contains age-restricted content should have the [`nsfw` field](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) set to `true` upon creation or update. Marking a command as age-restricted will limit who can see and access the command, and from which channels. + + +Apps with [discovery enabled](https://support-dev.discord.com/hc/en-us/articles/9489299950487) (which is required to appear in the App Directory) cannot contain any age-restricted commands or content. + + +### Using Age-Restricted Commands + +To use an age-restricted command, a user must be 18 years or older and access the command from either: +- an [age-restricted channel](https://support.discord.com/hc/articles/115000084051-Age-Restricted-Channels-and-Content) or +- a DM with the app *after* [enabling age-restricted commands](https://support.discord.com/hc/en-us/articles/10123937946007) within their User Settings. + +Details about accessing and using age-restricted commands is in [the Help Center](https://support.discord.com/hc/en-us/articles/10123937946007). + +### Endpoints + + +For authorization, all endpoints take either a [bot token](/developers/docs/reference#authentication) or [client credentials token](/developers/docs/topics/oauth2#client-credentials-grant) for your application + + +## Get Global Application Commands +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/commands + + +The objects returned by this endpoint may be augmented with [additional fields if localization is active](/developers/docs/interactions/application-commands#retrieving-localized-commands). + + +Fetch all of the global commands for your application. Returns an array of [application command](/developers/docs/interactions/application-commands#application-command-object) objects. + + +###### Query String Params + +| Field | Type | Description | +|---------------------|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| with_localizations? | [boolean](/developers/docs/reference#boolean-query-strings) | Whether to include full localization dictionaries (`name_localizations` and `description_localizations`) in the returned objects, instead of the `name_localized` and `description_localized` fields. Default `false`. | + +## Create Global Application Command +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/commands + + +Creating a command with the same name as an existing command for your application will overwrite the old command. + + +Create a new global command. Returns `201` if a command with the same name does not already exist, or a `200` if it does (in which case the previous command will be overwritten). Both responses include an [application command](/developers/docs/interactions/application-commands#application-command-object) object. + + +###### JSON Params + +| Field | Type | Description | +|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | [Name of command](/developers/docs/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters | +| name_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | +| description? | string | 1-100 character description for `CHAT_INPUT` commands | +| description_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | +| options? | array of [application command option](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) | the parameters for the command, max of 25 | +| default_member_permissions? | ?string | Set of [permissions](/developers/docs/topics/permissions) represented as a bit set | +| dm_permission? | ?boolean | **Deprecated (use `contexts` instead)**; Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | +| default_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` | +| integration_types? | list of [integration types](/developers/docs/resources/application#application-object-application-integration-types) | [Installation context(s)](/developers/docs/resources/application#installation-context) where the command is available | +| contexts? | list of [interaction context types](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) | [Interaction context(s)](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) where the command can be used | +| type? | one of [application command type](/developers/docs/interactions/application-commands#application-command-object-application-command-types) | Type of command, defaults `1` if not set | +| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/docs/interactions/application-commands#agerestricted-commands) | + +## Get Global Application Command +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/commands/[\{command.id\}](/developers/docs/interactions/application-commands#application-command-object) + +Fetch a global command for your application. Returns an [application command](/developers/docs/interactions/application-commands#application-command-object) object. + +## Edit Global Application Command +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/commands/[\{command.id\}](/developers/docs/interactions/application-commands#application-command-object) + + +All parameters for this endpoint are optional. + + +Edit a global command. Returns `200` and an [application command](/developers/docs/interactions/application-commands#application-command-object) object. All fields are optional, but any fields provided will entirely overwrite the existing values of those fields. + + +###### JSON Params + +| Field | Type | Description | +|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name? | string | [Name of command](/developers/docs/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters | +| name_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | +| description? | string | 1-100 character description | +| description_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | +| options? | array of [application command option](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) | the parameters for the command | +| default_member_permissions? | ?string | Set of [permissions](/developers/docs/topics/permissions) represented as a bit set | +| dm_permission? | ?boolean | **Deprecated (use `contexts` instead)**; Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | +| default_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` | +| integration_types? | list of [integration types](/developers/docs/resources/application#application-object-application-integration-types) | [Installation context(s)](/developers/docs/resources/application#installation-context) where the command is available | +| contexts? | list of [interaction context types](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) | [Interaction context(s)](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) where the command can be used | +| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/docs/interactions/application-commands#agerestricted-commands) | + +## Delete Global Application Command +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/commands/[\{command.id\}](/developers/docs/interactions/application-commands#application-command-object) + +Deletes a global command. Returns `204 No Content` on success. + +## Bulk Overwrite Global Application Commands +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/commands + +Takes a list of application commands, overwriting the existing global command list for this application. Returns `200` and a list of [application command](/developers/docs/interactions/application-commands#application-command-object) objects. Commands that do not already exist will count toward daily application command create limits. + + +This will overwrite **all** types of application commands: slash commands, user commands, and message commands. + + +## Get Guild Application Commands +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands + + +The objects returned by this endpoint may be augmented with [additional fields if localization is active](/developers/docs/interactions/application-commands#retrieving-localized-commands). + + +Fetch all of the guild commands for your application for a specific guild. Returns an array of [application command](/developers/docs/interactions/application-commands#application-command-object) objects. + + +###### Query String Params + +| Field | Type | Description | +|---------------------|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| with_localizations? | [boolean](/developers/docs/reference#boolean-query-strings) | Whether to include full localization dictionaries (`name_localizations` and `description_localizations`) in the returned objects, instead of the `name_localized` and `description_localized` fields. Default `false`. | + +## Create Guild Application Command +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands + + +Creating a command with the same name as an existing command for your application will overwrite the old command. + + +Create a new guild command. New guild commands will be available in the guild immediately. Returns `201` if a command with the same name does not already exist, or a `200` if it does (in which case the previous command will be overwritten). Both responses include an [application command](/developers/docs/interactions/application-commands#application-command-object) object. + + +###### JSON Params + +| Field | Type | Description | +|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | [Name of command](/developers/docs/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters | +| name_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | +| description? | string | 1-100 character description for `CHAT_INPUT` commands | +| description_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | +| options? | array of [application command option](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command, max of 25 | +| default_member_permissions? | ?string | Set of [permissions](/developers/docs/topics/permissions) represented as a bit set | +| default_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` | +| type? | one of [application command type](/developers/docs/interactions/application-commands#application-command-object-application-command-types) | Type of command, defaults `1` if not set | +| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/docs/interactions/application-commands#agerestricted-commands) | + +## Get Guild Application Command +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands/[\{command.id\}](/developers/docs/interactions/application-commands#application-command-object) + +Fetch a guild command for your application. Returns an [application command](/developers/docs/interactions/application-commands#application-command-object) object. + +## Edit Guild Application Command +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands/[\{command.id\}](/developers/docs/interactions/application-commands#application-command-object) + + +All parameters for this endpoint are optional. + + +Edit a guild command. Updates for guild commands will be available immediately. Returns `200` and an [application command](/developers/docs/interactions/application-commands#application-command-object) object. All fields are optional, but any fields provided will entirely overwrite the existing values of those fields. + + +###### JSON Params + +| Field | Type | Description | +|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name? | string | [Name of command](/developers/docs/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters | +| name_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | +| description? | string | 1-100 character description | +| description_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | +| options? | array of [application command option](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command, max of 25 | +| default_member_permissions? | ?string | Set of [permissions](/developers/docs/topics/permissions) represented as a bit set | +| default_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` | +| nsfw? | boolean | Indicates whether the command is [age-restricted](/developers/docs/interactions/application-commands#agerestricted-commands) | + +## Delete Guild Application Command +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands/[\{command.id\}](/developers/docs/interactions/application-commands#application-command-object) + +Delete a guild command. Returns `204 No Content` on success. + +## Bulk Overwrite Guild Application Commands +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands + +Takes a list of application commands, overwriting the existing command list for this application for the targeted guild. Returns `200` and a list of [application command](/developers/docs/interactions/application-commands#application-command-object) objects. + + +This will overwrite **all** types of application commands: slash commands, user commands, and message commands. + + + +###### JSON Params + +| Field | Type | Description | +|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id? | snowflake | ID of the command, if known | +| name | string | [Name of command](/developers/docs/interactions/application-commands#application-command-object-application-command-naming), 1-32 characters | +| name_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | +| description | string | 1-100 character description | +| description_localizations? | ?dictionary with keys in [available locales](/developers/docs/reference#locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | +| options? | array of [application command option](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) | Parameters for the command | +| default_member_permissions? | ?string | Set of [permissions](/developers/docs/topics/permissions) represented as a bit set | +| dm_permission? | ?boolean | **Deprecated (use `contexts` instead)**; Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | +| default_permission? | boolean | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. Defaults to `true` | +| integration_types | list of [integration types](/developers/docs/resources/application#application-object-application-integration-types) | [Installation context(s)](/developers/docs/resources/application#installation-context) where the command is available, defaults to `GUILD_INSTALL` (`[0]`) | +| contexts | list of [interaction context types](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) | [Interaction context(s)](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) where the command can be used, defaults to all contexts `[0,1,2]` | +| type? | one of [application command type](/developers/docs/interactions/application-commands#application-command-object-application-command-types) | Type of command, defaults `1` if not set | + +## Get Guild Application Command Permissions +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands/permissions + +Fetches permissions for all commands for your application in a guild. Returns an array of [guild application command permissions](/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) objects. + +## Get Application Command Permissions +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands/[\{command.id\}](/developers/docs/interactions/application-commands#application-command-object)/permissions + +Fetches permissions for a specific command for your application in a guild. Returns a [guild application command permissions](/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object. + +## Edit Application Command Permissions +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands/[\{command.id\}](/developers/docs/interactions/application-commands#application-command-object)/permissions + + +This endpoint will overwrite existing permissions for the command in that guild + + +Edits command permissions for a specific command for your application in a guild and returns a [guild application command permissions](/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object. Fires an [Application Command Permissions Update](/developers/docs/events/gateway-events#application-command-permissions-update) Gateway event. + +You can add up to 100 permission overwrites for a command. + + +This endpoint requires authentication with a Bearer token that has permission to manage the guild and its roles. For more information, read above about [application command permissions](/developers/docs/interactions/application-commands#permissions). + + + +Deleting or renaming a command will permanently delete all permissions for the command + + + +###### JSON Params + +| Field | Type | Description | +|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------| +| permissions | array of [application command permissions](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure) | Permissions for the command in the guild | + +## Batch Edit Application Command Permissions +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/commands/permissions + + +This endpoint has been disabled with [updates to command permissions (Permissions v2)](/developers/docs/change-log#updated-command-permissions). Instead, you can [edit each application command permissions](/developers/docs/interactions/application-commands#edit-application-command-permissions) (though you should be careful to handle any potential [rate limits](/developers/docs/topics/rate-limits)). + diff --git a/discord/developers/docs/interactions/overview.mdx b/discord/developers/docs/interactions/overview.mdx new file mode 100644 index 0000000000..62df0b5523 --- /dev/null +++ b/discord/developers/docs/interactions/overview.mdx @@ -0,0 +1,174 @@ +--- +title: Overview of Interactions +sidebarTitle: Overview +description: Learn about Discord's interactive features including commands and message components. +--- + +Interactive features like commands and message components allows users to invoke an app natively within Discord. When a user engages with one of your app's interactive features, your app will receive an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object). + +This overview includes an overview of the main types of interactions, and steps for you to prepare your app to use and receive interactions. Reference documentation and details about handling interactions are in [Receiving and Responding](/developers/docs/interactions/receiving-and-responding). + +--- + +## Types of Interactions + +There are different types of interactions in your app's toolbelt that can pick and choose to build engaging, interactive experiences in Discord. + +### Commands + +**[Application commands](/developers/docs/interactions/application-commands)** provide users a native way to invoke an app in Discord. They often map to an app's core features or functionality. + +![Command launcher in the Desktop client](/images/overview-command-desktop.png) + +When an app creates a command it can choose the command's type, which determines where it appears in the Discord client and the metadata the app will receive when the command is invoked. There are three types for application commands: + +- **Slash commands** are the most common type of command and are accessed by typing `/` in the chat input, or by opening the command picker. +- **Message commands** are commands related to a message or a message's content. They're accessed by clicking on the context menu (the three dots) at the top-right of a message (or right clicking on a message), then navigating to the "Apps" section. +- **User commands** are commands that related to a user in Discord. They're accessed by right clicking on a user profile, then navigating to the "Apps" section. +- **Entry Point commands** are commands used as the primary way to launch [Activities](/developers/docs/activities/overview) from the App Launcher. + +Details about creating commands and handling command interactions are in the [Application Commands](/developers/docs/interactions/application-commands) documentation. + +### Message Components + +**[Message components](/developers/docs/components/reference)** are interactive elements that can be included in the content of a message that your app sends in Discord. + +![Button message components in a message](/images/overview-components.png) + +The main interactive components that apps can send in messages include: + +- [Buttons](/developers/docs/components/reference#button) are clickable components that can be customized with different styles, texts, and emoji. +- [Static select menus](/developers/docs/components/reference#string-select) are components that a user can open to see a list of developer-defined select options with custom labels and descriptions. +- [Auto-populated select menus](/developers/docs/components/reference#string-select) are a set of four different select components that are populated with contextual Discord resources, like a list of users or channels in a server. + +A list of all message components and details on sending and receiving component interactions is in the [Message Components](/developers/docs/components/reference) documentation. + +### Modals + +**[Modals](/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal)** are single-user pop-up interfaces that allow apps to collect form-like data. Modals can only be opened in response to a user invoking one of your app's commands or message components. + +![Modals in the Discord client](/images/overview-modals.png) + +The only interactive component that modals can contain are [text inputs](/developers/docs/components/reference#text-input), which allow users to fill out single-or-multi line form inputs. + +Details about creating and using modals is in the [Receiving and Responding](/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal) documentation. + +--- + +## Preparing for Interactions + +When a user interacts with your app, you have the option for your app to receive interactions in two mutually-exclusive ways: +- WebSocket-based Gateway connection +- HTTP via outgoing webhooks + +By default your app will receive interactions via a Gateway connection, but you can opt-in to HTTP-based interactions by adding a **Interactions Endpoint URL** to your app's settings. Technical details about handling interactions is in the [Receiving and Responding](/developers/docs/interactions/receiving-and-responding) documentation. + +### Configuring an Interactions Endpoint URL + +A **Interactions Endpoint URL** is a public endpoint for your app where Discord can send your app HTTP-based interactions. If your app is using [Gateway](/developers/docs/events/gateway)-based interactions, you don't need to configure an Interactions Endpoint URL. + +#### Setting Up an Endpoint + +Before you can add your Interactions Endpoint URL to your app, your endpoint must be prepared for two things ahead of time: + +1. Acknowledging `PING` requests from Discord +2. Validate security-related request headers (`X-Signature-Ed25519` and `X-Signature-Timestamp`) + +If either of these are not complete, your Interactions Endpoint URL will not be validated. Details on acknowledging PING requests and validating security-related headers are in the sections below. + +#### Acknowledging PING requests + +When adding your Interactions Endpoint URL, Discord will send a `POST` request with a `PING` payload with a `type: 1` to your endpoint. Your app is expected to acknowledge the request by returning a `200` response with a `PONG` payload (which has the same `type: 1`). Details about interaction responses are in the [Receiving and Responding documentation](/developers/docs/interactions/receiving-and-responding). + + +You must provide a valid `Content-Type` when responding to `PING`s. See [here](/developers/docs/reference#http-api) for further information. + + + +To properly acknowledge a `PING` payload, return a `200` response with a payload of `type: 1`: + +```py +@app.route('/', methods=['POST']) +def my_command(): + if request.json["type"] == 1: + return jsonify({ + "type": 1 + }) +``` + + + +###### Validating Security Request Headers + +The internet is a scary place, especially for people hosting public, unauthenticated endpoints. To receive interactions via HTTP, there are some security steps you **must** take before your app is eligible to receive requests. + +Each interaction is sent with the following headers: + +- `X-Signature-Ed25519` as a signature +- `X-Signature-Timestamp` as a timestamp + +Using your favorite security library, you **must validate the request each time you receive an [interaction](/developers/docs/interactions/receiving-and-responding#interaction-object)**. If the signature fails validation, your app should respond with a `401` error code. + + +Below are some code examples that show how to validate the headers sent in interactions requests. + +**JavaScript** + +```js +const nacl = require("tweetnacl"); + +// Your public key can be found on your application in the Developer Portal +const PUBLIC_KEY = "APPLICATION_PUBLIC_KEY"; + +const signature = req.get("X-Signature-Ed25519"); +const timestamp = req.get("X-Signature-Timestamp"); +const body = req.rawBody; // rawBody is expected to be a string, not raw bytes + +const isVerified = nacl.sign.detached.verify( + Buffer.from(timestamp + body), + Buffer.from(signature, "hex"), + Buffer.from(PUBLIC_KEY, "hex") +); + +if (!isVerified) { + return res.status(401).end("invalid request signature"); +} +``` + +**Python** + +```py +from nacl.signing import VerifyKey +from nacl.exceptions import BadSignatureError + +# Your public key can be found on your application in the Developer Portal +PUBLIC_KEY = 'APPLICATION_PUBLIC_KEY' + +verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY)) + +signature = request.headers["X-Signature-Ed25519"] +timestamp = request.headers["X-Signature-Timestamp"] +body = request.data.decode("utf-8") + +try: + verify_key.verify(f'{timestamp}{body}'.encode(), bytes.fromhex(signature)) +except BadSignatureError: + abort(401, 'invalid request signature') +``` + + +In addition to ensuring your app validates security-related request headers at the time of saving your endpoint, Discord will also perform automated, routine security checks against your endpoint, including purposefully sending you invalid signatures. If you fail the validation, we will remove your interactions URL and alert you via email and System DM. + +We highly recommend checking out our [Community Resources](/developers/docs/developer-tools/community-resources#interactions) and the libraries found there. They not only provide typing for Interactions data models, but also include decorators for API frameworks like Flask and Express to make validation easy. + +#### Adding an Interactions Endpoint URL + +After you have a public endpoint to use as your app's Interactions Endpoint URL, you can add it to your app by going to your [app's settings](https://discord.com/developers/applications). + +On the **General Overview** page, look for the **Interactive Endpoint URL** field. Paste your public URL that is set up to acknowledge `PING` messages and correctly handles security-related signature headers. + +--- + +## Handling Interactions + +Once your app is prepared for interactions, you can explore the [Receiving and Responding](/developers/docs/interactions/receiving-and-responding) documentation which goes into the technical details of handling interactions requests in your app. diff --git a/discord/developers/docs/interactions/receiving-and-responding.mdx b/discord/developers/docs/interactions/receiving-and-responding.mdx new file mode 100644 index 0000000000..16a076bbc7 --- /dev/null +++ b/discord/developers/docs/interactions/receiving-and-responding.mdx @@ -0,0 +1,489 @@ +--- +title: Interactions +sidebarTitle: Receiving and Responding +description: Learn how to receive and respond to user interactions. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +An **[Interaction](/developers/docs/interactions/receiving-and-responding#interaction-object)** is the message that your application receives when a user uses an application command or a message component. + +For [Slash Commands](/developers/docs/interactions/application-commands#slash-commands), it includes the values that the user submitted. + +For [User Commands](/developers/docs/interactions/application-commands#user-commands) and [Message Commands](/developers/docs/interactions/application-commands#message-commands), it includes the resolved user or message on which the action was taken. + +For [Message Components](/developers/docs/components/reference) it includes identifying information about the component that was used. It will also include some metadata about how the interaction was triggered: the `guild_id`, `channel`, `member` and other fields. You can find all the values in our data models below. + +### Interaction Object + + +###### Interaction Structure + +| Field | Type | Description | +|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the interaction | +| application_id | snowflake | ID of the application this interaction is for | +| type | [interaction type](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction | +| data?\* | [interaction data](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-data) | Interaction data payload | +| guild? | [partial guild](/developers/docs/resources/guild#guild-object) object | Guild that the interaction was sent from | +| guild_id? | snowflake | Guild that the interaction was sent from | +| channel? | [partial channel](/developers/docs/resources/channel#channel-object) object | Channel that the interaction was sent from | +| channel_id? | snowflake | Channel that the interaction was sent from | +| member?\*\* | [guild member](/developers/docs/resources/guild#guild-member-object) object | Guild member data for the invoking user, including permissions | +| user? | [user](/developers/docs/resources/user#user-object) object | User object for the invoking user, if invoked in a DM | +| token | string | Continuation token for responding to the interaction | +| version | integer | Read-only property, always `1` | +| message? | [message](/developers/docs/resources/message#message-object) object | For components or modals triggered by components, the message they were attached to | +| app_permissions\*\*\* | string | Bitwise set of permissions the app has in the source location of the interaction | +| locale?\*\*\*\* | string | Selected [language](/developers/docs/reference#locales) of the invoking user | +| guild_locale? | string | [Guild's preferred locale](/developers/docs/resources/guild#guild-object), if invoked in a guild | +| entitlements | array of [entitlement](/developers/docs/resources/entitlement#entitlement-object) objects | For [monetized apps](/developers/docs/monetization/overview), any entitlements for the invoking user, representing access to premium [SKUs](/developers/docs/resources/sku) | +| authorizing_integration_owners | dictionary with keys of [application integration types](/developers/docs/resources/application#application-object-application-integration-types) | Mapping of installation contexts that the interaction was authorized for to related user or guild IDs. See [Authorizing Integration Owners Object](/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) for details | +| context? | [interaction context type](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) | Context where the interaction was triggered from | +| attachment_size_limit | integer | Attachment size limit in bytes | + +\* This is always present on application command, message component, and modal submit interaction types. It is optional for future-proofing against new interaction types + +\*\* `member` is sent when the interaction is invoked in a guild, and `user` is sent when invoked in a DM + +\*\*\* `app_permissions` includes `ATTACH_FILES | EMBED_LINKS | MENTION_EVERYONE` permissions for (G)DMs with other users, and additionally includes `USE_EXTERNAL_EMOJIS` for DMs with the app's bot user + +\*\*\*\* This is available on all interaction types except PING + + +###### Interaction Type + +| Name | Value | +|----------------------------------|-------| +| PING | 1 | +| APPLICATION_COMMAND | 2 | +| MESSAGE_COMPONENT | 3 | +| APPLICATION_COMMAND_AUTOCOMPLETE | 4 | +| MODAL_SUBMIT | 5 | + + +###### Interaction Context Types + +Context in Discord where an interaction can be used, or where it was triggered from. Details about using interaction contexts for application commands is in the [commands context documentation](/developers/docs/interactions/application-commands#interaction-contexts). + +| Name | Type | Description | +|-----------------|------|--------------------------------------------------------------------------------| +| GUILD | 0 | Interaction can be used within servers | +| BOT_DM | 1 | Interaction can be used within DMs with the app's bot user | +| PRIVATE_CHANNEL | 2 | Interaction can be used within Group DMs and DMs other than the app's bot user | + + +###### Authorizing Integration Owners Object + +The `authorizing_integration_owners` field includes details about the authorizing user or server for the installation(s) relevant to the interaction. For apps installed to a user, it can be used to tell the difference between the authorizing user and the user that triggered an interaction (like a message component). + +A key will only be present if the following are true: +- The app has been authorized to the [installation context](/developers/docs/resources/application#application-object-application-integration-types) corresponding to the key (`GUILD_INSTALL` or `USER_INSTALL`) +- The interaction is supported in the source [interaction context](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types) (`GUILD`, `BOT_DM`, or `PRIVATE_CHANNEL`) for the installation context corresponding to the key +- And for command invocations, the command must be supported in the installation context (using [`integration_types`](/developers/docs/interactions/application-commands#contexts)) + +The values in `authorizing_integration_owners` depend on the key— + +- If the key is `GUILD_INSTALL` (`"0"`), the value depends on the source of the interaction: + - The value will be the guild ID if the interaction is triggered from a server + - The value will be `"0"` if the interaction is triggered from a DM with the app's bot user +- If the key is `USER_INSTALL` (`"1"`), the value will be the ID of the authorizing user + + +###### Interaction Data + +While the `data` field is guaranteed to be present for all [interaction types](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type) besides `PING`, its structure will vary. The following tables detail the inner `data` payload for each interaction type. + +| Interaction Type | Interaction Data | +|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| +| PING (`1`) | N / A | +| APPLICATION_COMMAND (`2`) | [Application Command Data Structure](/developers/docs/interactions/receiving-and-responding#interaction-object-application-command-data-structure) | +| MESSAGE_COMPONENT (`3`) | [Message Component Data Structure](/developers/docs/interactions/receiving-and-responding#interaction-object-message-component-data-structure) | +| APPLICATION_COMMAND_AUTOCOMPLETE (`4`) | [Application Command Data Structure](/developers/docs/interactions/receiving-and-responding#interaction-object-application-command-data-structure) | +| MODAL_SUBMIT (`5`) | [Modal Submit Data Structure](/developers/docs/interactions/receiving-and-responding#interaction-object-modal-submit-data-structure) | + + +###### Application Command Data Structure + + +Sent in `APPLICATION_COMMAND` and `APPLICATION_COMMAND_AUTOCOMPLETE` interactions. + + +| Field | Type | Description | +|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | [`ID`](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) of the invoked command | +| name | string | [`name`](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) of the invoked command | +| type | integer | [`type`](/developers/docs/interactions/application-commands#application-command-object-application-command-structure) of the invoked command | +| resolved? | [resolved data](/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Converted users + roles + channels + attachments | +| options?\* | array of [application command interaction data option](/developers/docs/interactions/receiving-and-responding#interaction-object-application-command-interaction-data-option-structure) | Params + values from the user | +| guild_id? | snowflake | ID of the guild the command is registered to | +| target_id? | snowflake | ID of the user or message targeted by a [user](/developers/docs/interactions/application-commands#user-commands) or [message](/developers/docs/interactions/application-commands#message-commands) command | + +\* This [can be partial](/developers/docs/interactions/application-commands#autocomplete) when in response to `APPLICATION_COMMAND_AUTOCOMPLETE` + + +###### Message Component Data Structure + +| Field | Type | Description | +|----------------|--------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| +| custom_id | string | [`custom_id`](/developers/docs/components/reference#anatomy-of-a-component-custom-id) of the component | +| component_type | integer | [type](/developers/docs/components/reference#component-object-component-types) of the component | +| values?\* | array of [select option values](/developers/docs/components/reference#string-select-select-option-structure) | Values the user selected in a [select menu](/developers/docs/components/reference#string-select) component | +| resolved? | [resolved data](/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options | + +\* This is always present for select menu components + + +###### Modal Submit Data Structure + +| Field | Type | Description | +|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------| +| custom_id | string | The custom ID provided for the modal | +| components | array of [component interaction response](/developers/docs/interactions/receiving-and-responding#interaction-object-component-interaction-response-structures) | Values submitted by the user | +| resolved? | [resolved data](/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) | Resolved entities from selected options | + + +###### Component Interaction Response Structures + +| Component | +|----------------------------------------------------------------------------------------------------------------------------------| +| [String Select](/developers/docs/components/reference#string-select-string-select-interaction-response-structure) | +| [Text Input](/developers/docs/components/reference#text-input-text-input-interaction-response-structure) | +| [User Select](/developers/docs/components/reference#user-select-user-select-interaction-response-structure) | +| [Role Select](/developers/docs/components/reference#role-select-role-select-interaction-response-structure) | +| [Mentionable Select](/developers/docs/components/reference#mentionable-select-mentionable-select-interaction-response-structure) | +| [Channel Select](/developers/docs/components/reference#channel-select-channel-select-interaction-response-structure) | +| [Text Display](/developers/docs/components/reference#text-display-text-display-interaction-response-structure) | +| [Label](/developers/docs/components/reference#label-label-interaction-response-structure) | +| [File Upload](/developers/docs/components/reference#file-upload-file-upload-interaction-response-structure) | + + +###### Resolved Data Structure + + +If data for a Member is included, data for its corresponding User will also be included. + + +| Field | Type | Description | +|---------------|-----------------------------------------------------------------------------------------------------|---------------------------------| +| users? | Map of Snowflakes to [user](/developers/docs/resources/user#user-object) objects | IDs and User objects | +| members?\* | Map of Snowflakes to [partial member](/developers/docs/resources/guild#guild-member-object) objects | IDs and partial Member objects | +| roles? | Map of Snowflakes to [role](/developers/docs/topics/permissions#role-object) objects | IDs and Role objects | +| channels?\*\* | Map of Snowflakes to [partial channel](/developers/docs/resources/channel#channel-object) objects | IDs and partial Channel objects | +| messages? | Map of Snowflakes to [partial messages](/developers/docs/resources/message#message-object) objects | IDs and partial Message objects | +| attachments? | Map of Snowflakes to [attachment](/developers/docs/resources/message#attachment-object) objects | IDs and attachment objects | + +\* Partial `Member` objects are missing `user`, `deaf` and `mute` fields + +\*\* Partial `Channel` objects only have `id`, `name`, `type`, `permissions`, `last_message_id`, `last_pin_timestamp`, `nsfw`, `parent_id`, `guild_id`, `flags`, `rate_limit_per_user`, `topic` and `position` fields. Threads will also have the `thread_metadata` field. + + +###### Application Command Interaction Data Option Structure + +All options have names, and an option can either be a parameter and input value--in which case `value` will be set--or it can denote a subcommand or group--in which case it will contain a top-level key and another array of `options`. + +`value` and `options` are mutually exclusive. + +| Field | Type | Description | +|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | Name of the parameter | +| type | integer | Value of [application command option type](/developers/docs/interactions/application-commands#application-command-object-application-command-option-type) | +| value? | string, integer, double, or boolean | Value of the option resulting from user input | +| options? | array of [application command interaction data option](/developers/docs/interactions/receiving-and-responding#interaction-object-application-command-interaction-data-option-structure) | Present if this option is a group or subcommand | +| focused? | boolean | `true` if this option is the currently focused option for autocomplete | + +### Message Interaction Object + +This is sent on the [message object](/developers/docs/resources/message#message-object) when the message is a response to an Interaction without an existing message. + + +This means responses to [Message Components](/developers/docs/components/reference) do not include this property, instead including a [message reference](/developers/docs/resources/message#message-reference-structure) object as components _always_ exist on preexisting messages. + + + +###### Message Interaction Structure + +| Field | Type | Description | +|---------|----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the interaction | +| type | [interaction type](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction | +| name | string | Name of the [application command](/developers/docs/interactions/application-commands#application-command-object-application-command-structure), including subcommands and subcommand groups | +| user | [user object](/developers/docs/resources/user#user-object) | User who invoked the interaction | +| member? | [partial member](/developers/docs/resources/guild#guild-member-object) object | Member who invoked the interaction in the guild | + +## Receiving an Interaction + +When a user interacts with your app, your app will receive an **[Interaction](/developers/docs/interactions/receiving-and-responding#interaction-object)**. Your app can receive an interaction in one of two ways: + +- Via [Interaction Create](/developers/docs/events/gateway-events#interaction-create) gateway event +- Via outgoing webhook + +These two methods are **mutually exclusive**; you can _only_ receive Interactions one of the two ways. The `INTERACTION_CREATE` [Gateway Event](/developers/docs/events/gateway-events#interaction-create) may be handled by connected clients, while the webhook method detailed below does not require a connected client. + +If you want to receive interactions via HTTP-based outgoing webhooks, you must configure an Interactions Endpoint URL for your app. You can read about preparing and adding an Interactions Endpoint URL to your app in the [Preparing for Interactions](/developers/docs/interactions/overview#preparing-for-interactions) section in Interactions Overview. + +### Interaction Metadata + +An [Interaction](/developers/docs/interactions/receiving-and-responding#interaction-object) includes metadata to aid your application in handling it as well as `data` specific to the interaction type. You can find samples for each interaction type on their respective pages: + +- [Slash Commands](/developers/docs/interactions/application-commands#slash-commands-example-interaction) +- [User Commands](/developers/docs/interactions/application-commands#user-commands-example-interaction) +- [Message Commands](/developers/docs/interactions/application-commands#message-commands-example-interaction) +- [Message Components](/developers/docs/components/using-message-components) +- [Modal Components](/developers/docs/components/using-modal-components) + +An explanation of all the fields can be found in our [data models](/developers/docs/interactions/receiving-and-responding#interaction-object). + +Now that you've gotten the data from the user, it's time to respond to them. + +## Responding to an Interaction + +Interactions--both receiving and responding--are webhooks under the hood. So responding to an Interaction is just like sending a webhook request! + + +Interaction responses have the same header requirements as normal HTTP API requests. See [here](/developers/docs/reference#http-api) for further information. + + +There are a number of ways you can respond to an interaction: + +### Interaction Response Object + + +###### Interaction Response Structure + +| Field | Type | Description | +|-------|-----------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------| +| type | [interaction callback type](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) | Type of response | +| data? | [interaction callback data](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-data-structure) | An optional response message | + + +###### Interaction Callback Type + +| Name | Value | Description | +|-----------------------------------------|-------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| PONG | 1 | ACK a `Ping` | +| CHANNEL_MESSAGE_WITH_SOURCE | 4 | Respond to an interaction with a message | +| DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE | 5 | ACK an interaction and edit a response later, the user sees a loading state | +| DEFERRED_UPDATE_MESSAGE\* | 6 | For components, ACK an interaction and edit the original message later; the user does not see a loading state | +| UPDATE_MESSAGE\* | 7 | For components, edit the message the component was attached to | +| APPLICATION_COMMAND_AUTOCOMPLETE_RESULT | 8 | Respond to an autocomplete interaction with suggested choices | +| MODAL\*\* | 9 | Respond to an interaction with a popup modal | +| PREMIUM_REQUIRED | 10 | [**Deprecated**](/developers/docs/change-log#premium-apps-new-premium-button-style-deep-linking-url-schemes); respond to an interaction with an upgrade button, only available for apps with [monetization](/developers/docs/monetization/overview) enabled | +| LAUNCH_ACTIVITY | 12 | Launch the Activity associated with the app. Only available for apps with [Activities](/developers/docs/activities/overview) enabled | + +\* Only valid for [component-based](/developers/docs/components/reference) interactions + +\*\* Not available for `MODAL_SUBMIT` and `PING` interactions. + + +###### Interaction Callback Data Structure + + +###### Messages + +Not all message fields are currently supported. + + +| Field | Type | Description | +|-------------------|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| tts? | boolean | Whether the response is TTS | +| content? | string | Message content | +| embeds? | array of [embeds](/developers/docs/resources/message#embed-object) | Supports up to 10 embeds | +| allowed_mentions? | [allowed mentions](/developers/docs/resources/message#allowed-mentions-object) | [Allowed mentions](/developers/docs/resources/message#allowed-mentions-object) object | +| flags? \* | integer | [Message flags](/developers/docs/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS`, `EPHEMERAL`, `IS_COMPONENTS_V2`, `IS_VOICE_MESSAGE`, and `SUPPRESS_NOTIFICATIONS` can be set) | +| components? | array of [components](/developers/docs/components/reference#component-object) | Message components | +| attachments? \*\* | array of partial [attachment](/developers/docs/resources/message#attachment-object) objects | Attachment objects with filename and description | +| poll? | [poll](/developers/docs/resources/poll#poll-create-request-object) request object | Details about the poll | + +\* If you create a callback with the [type](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE` the only valid [message flag](/developers/docs/resources/message#message-object-message-flags) you may use is `EPHEMERAL`. If you'd like to create a component based message with `IS_COMPONENTS_V2` you must do that with the [edit original response](/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response) endpoint, not this one. + +\*\* See [Uploading Files](/developers/docs/reference#uploading-files) for details. + + +###### Autocomplete + +| Field | Type | Description | +|---------|-----------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------| +| choices | array of [choices](/developers/docs/interactions/application-commands#application-command-object-application-command-option-choice-structure) | autocomplete choices (max of 25 choices) | + + +###### Modal + +| Field | Type | Description | +|------------|-------------------------------------------------------------------------------|----------------------------------------------------------------| +| custom_id | string | Developer-defined identifier for the modal, max 100 characters | +| title | string | Title of the popup modal, max 45 characters | +| components | array of [components](/developers/docs/components/reference#component-object) | Between 1 and 5 (inclusive) components that make up the modal | + + +If your application responds with user data, you should use [`allowed_mentions`](/developers/docs/resources/message#allowed-mentions-object) to filter which mentions in the content actually ping. + + +## Interaction Callback + +When responding to an interaction received, you can make a `POST` request to `/interactions///callback`. `interaction_id` is the unique id of that individual Interaction from the received payload. `interaction_token` is the unique token for that interaction from the received payload. + +If you are receiving Interactions over the gateway, you **have to respond via HTTP**. Responses to Interactions **are not sent as commands over the gateway**. + +**If you send this request for an interaction received over HTTP, respond to the original HTTP request with a 202 and no body.** + +```py +import requests + +url = "https://discord.com/api/v10/interactions///callback" + +json = { + "type": 4, + "data": { + "content": "Congrats on sending your command!" + } +} +r = requests.post(url, json=json) +``` + + +Interaction `tokens` are valid for **15 minutes** and can be used to send followup messages but you **must send an initial response within 3 seconds of receiving the event**. If the 3 second deadline is exceeded, the token will be invalidated. + + + + If you receive interactions over HTTP, your server can also respond to the received `POST` request. You'll want to respond with a `200` status code (if everything went well), as well as specifying a `type` and `data`, which is an [Interaction Response](/developers/docs/interactions/receiving-and-responding#interaction-response-object) object: + + ```py + @app.route('/', methods=['POST']) + def my_command(): + if request.json["type"] == 1: + return jsonify({ + "type": 1 + }) + + else: + return jsonify({ + "type": 4, + "data": { + "tts": False, + "content": "Congrats on sending your command!", + "embeds": [], + "allowed_mentions": { "parse": [] } + } + }) + ``` + + + +###### Interaction Callback Response Object + +| Field | Type | Description | +|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------| +| interaction | [interaction callback object](/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-object) | The interaction object associated with the interaction response. | +| resource? | [interaction resource object](/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-resource-object) | The resource that was created by the interaction response. | + + +###### Interaction Callback Object + +| Field | Type | Description | +|-----------------------------|-----------|----------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the interaction | +| type | integer | [Interaction type](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type) | +| activity_instance_id? | string | Instance ID of the Activity if one was launched or joined | +| response_message_id? | snowflake | ID of the message that was created by the interaction | +| response_message_loading? | boolean | Whether the message is in a loading state | +| response_message_ephemeral? | boolean | Whether the response message is ephemeral | + + +###### Interaction Callback Resource Object + +| Field | Type | Description | +|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| +| type | integer | [Interaction callback type](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) | +| activity_instance?\* | [Activity instance resource](/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-activity-instance-resource) | Represents the Activity launched by this interaction. | +| message?\*\* | [message object](/developers/docs/resources/message#message-object) | Message created by the interaction. | + +\* Only present if type is `LAUNCH_ACTIVITY`. + +\*\* Only present if type is either `CHANNEL_MESSAGE_WITH_SOURCE` or `UPDATE_MESSAGE`. + + +###### Interaction Callback Activity Instance Resource + +| Field | Type | Description | +|-------|--------|------------------------------------------------------------| +| id | string | Instance ID of the Activity if one was launched or joined. | + + +## Followup Messages + +Sometimes, you want to send followup messages to a user after responding to an interaction. Or, you may want to edit your original response. Whether you receive Interactions over the gateway or by outgoing webhook, you can use the following endpoints to edit your initial response or send followup messages: + +- [`PATCH /webhooks///messages/@original`](/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response) to edit your initial response to an Interaction +- [`DELETE /webhooks///messages/@original`](/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response) to delete your initial response to an Interaction +- [`POST /webhooks//`](/developers/docs/interactions/receiving-and-responding#create-followup-message) to send a new followup message +- [`PATCH /webhooks///messages/`](/developers/docs/interactions/receiving-and-responding#edit-followup-message) to edit a message sent with that `token` + + +Interactions webhooks share the same rate limit properties as normal webhooks. + + +Interaction tokens are valid for **15 minutes**, meaning you can respond to an interaction within that amount of time. + +### Endpoints + + +The endpoints below are not bound to the application's [Global Rate Limit](/developers/docs/topics/rate-limits#global-rate-limit). + + +## Create Interaction Response +/interactions/[\{interaction.id\}](/developers/docs/interactions/receiving-and-responding#interaction-object)/[\{interaction.token\}](/developers/docs/interactions/receiving-and-responding#interaction-object)/callback + +Create a response to an Interaction. Body is an [interaction response](/developers/docs/interactions/receiving-and-responding#interaction-response-object). Returns `204` unless `with_response` is set to `true` which returns `200` with the body as [interaction callback response](/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-response-object). + +This endpoint also supports file attachments similar to the webhook endpoints. Refer to [Uploading Files](/developers/docs/reference#uploading-files) for details on uploading files and `multipart/form-data` requests. + + +###### Query String Params + +| Field | Type | Description | +|----------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| with_response? | [boolean](/developers/docs/reference#boolean-query-strings) | Whether to include an [interaction callback object](/developers/docs/interactions/receiving-and-responding#interaction-callback-interaction-callback-response-object) as the response | + + +## Get Original Interaction Response +/webhooks/[\{application.id\}](/developers/docs/resources/application#application-object)/[\{interaction.token\}](/developers/docs/interactions/receiving-and-responding#interaction-object)/messages/@original + +Returns the initial Interaction response. Functions the same as [Get Webhook Message](/developers/docs/resources/webhook#get-webhook-message). + +## Edit Original Interaction Response +/webhooks/[\{application.id\}](/developers/docs/resources/application#application-object)/[\{interaction.token\}](/developers/docs/interactions/receiving-and-responding#interaction-object)/messages/@original + +Edits the initial Interaction response. Functions the same as [Edit Webhook Message](/developers/docs/resources/webhook#edit-webhook-message). + +## Delete Original Interaction Response +/webhooks/[\{application.id\}](/developers/docs/resources/application#application-object)/[\{interaction.token\}](/developers/docs/interactions/receiving-and-responding#interaction-object)/messages/@original + +Deletes the initial Interaction response. Returns `204 No Content` on success. + +## Create Followup Message +/webhooks/[\{application.id\}](/developers/docs/resources/application#application-object)/[\{interaction.token\}](/developers/docs/interactions/receiving-and-responding#interaction-object) + + +Apps are limited to 5 followup messages per interaction if it was initiated from a user-installed app and isn't installed in the server (meaning the [authorizing integration owners object](/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) only contains `USER_INSTALL`) + + +Create a followup message for an Interaction. Functions the same as [Execute Webhook](/developers/docs/resources/webhook#execute-webhook), but `wait` is always true. The `thread_id`, `avatar_url`, and `username` parameters are not supported when using this endpoint for interaction followups. You can use the `EPHEMERAL` [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 6` (64) to send a message that only the user can see. You can also use the `IS_COMPONENTS_V2` [message flag](/developers/docs/resources/message#message-object-message-flags) `1 << 15` (32768) to send a [component](/developers/docs/components/reference)-based message. + +When using this endpoint directly after responding to an interaction with `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE`, this endpoint will function as [Edit Original Interaction Response](/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response) for backwards compatibility. In this case, no new message will be created, and the loading message will be edited instead. The ephemeral flag will be ignored, and the value you provided in the initial defer response will be preserved, as an existing message's ephemeral state cannot be changed. This behavior is deprecated, and you should use the [Edit Original Interaction Response](/developers/docs/interactions/receiving-and-responding#edit-original-interaction-response) endpoint in this case instead. + +## Get Followup Message +/webhooks/[\{application.id\}](/developers/docs/resources/application#application-object)/[\{interaction.token\}](/developers/docs/interactions/receiving-and-responding#interaction-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Returns a followup message for an Interaction. Functions the same as [Get Webhook Message](/developers/docs/resources/webhook#get-webhook-message). + +## Edit Followup Message +/webhooks/[\{application.id\}](/developers/docs/resources/application#application-object)/[\{interaction.token\}](/developers/docs/interactions/receiving-and-responding#interaction-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Edits a followup message for an Interaction. Functions the same as [Edit Webhook Message](/developers/docs/resources/webhook#edit-webhook-message). + +## Delete Followup Message +/webhooks/[\{application.id\}](/developers/docs/resources/application#application-object)/[\{interaction.token\}](/developers/docs/interactions/receiving-and-responding#interaction-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Deletes a followup message for an Interaction. Returns `204 No Content` on success. diff --git a/discord/developers/docs/intro.mdx b/discord/developers/docs/intro.mdx new file mode 100644 index 0000000000..5b363fdea1 --- /dev/null +++ b/discord/developers/docs/intro.mdx @@ -0,0 +1,192 @@ +--- +title: "" +sidebarTitle: "Intro" +mode: "wide" +--- + +import {RobotIcon} from '/snippets/icons/RobotIcon.jsx' +import {WrenchIcon} from '/snippets/icons/WrenchIcon.jsx' +import {SlashBoxIcon} from '/snippets/icons/SlashBoxIcon.jsx' +import {ActivitiesIcon} from '/snippets/icons/ActivitiesIcon.jsx' +import {GameControllerIcon} from '/snippets/icons/GameControllerIcon.jsx' +import {PaintPaletteIcon} from '/snippets/icons/PaintPaletteIcon.jsx' +import {ClydeIcon} from '/snippets/icons/ClydeIcon.jsx' +import {BugIcon} from '/snippets/icons/BugIcon.jsx' +import {CircleQuestionIcon} from '/snippets/icons/CircleQuestionIcon.jsx' + +
+
+
+
+

Build Where the World Plays

+

Create social games, experiences, and integrations for millions of users on Discord

+
+
+ Discord Developers +
+
+
+ +## Build with Discord + +Discord provides a platform for building social experiences, whether you're +creating apps within Discord or integrating Discord's features into your game. + + + } + iconType="solid" + horizontal + > + Develop apps, bots, and integrations to enhance the Discord experience. + + + } + iconType="solid" + horizontal + > + Build multiplayer games and social experiences that run directly in Discord. + + + } + iconType="solid" + horizontal + > + Add rich social features into your game across desktop, mobile, and console. + + + +## Extend Discord with Custom Integrations + +Enhance the Discord experience with custom apps, commands, and integrations. + + + } + href="/developers/docs/quick-start/overview-of-apps" + horizontal + > + Learn the fundamentals of building Discord apps and commands. + + } + href="/developers/docs/quick-start/getting-started" + horizontal + > + Create a bot user that plays 'rock, paper, scissors' with users. + + } + href="/developers/docs/interactions/overview#message-components" + horizontal + > + Create custom commands and interactions for your app. + + + +## Build Multiplayer Games and Social Experiences + +Use the Embedded App SDK to create real-time games and social experiences that users can launch directly inside Discord. + + + } + href="/developers/docs/activities/overview" + horizontal + > + Discover how activities work in Discord. + + + } + href="/developers/docs/activities/building-an-activity" + horizontal + > + Build and test an Activity using the Embedded App SDK. + + } + href="/developers/docs/activities/design-patterns" + horizontal + > + Best practices for multiplayer, game design, and player experience. + + + +## Bring Social Features to Your Game + +Enable rich presence, voice chat, and more to create a seamless social experience for your players and grow your game. + + + } + href="/developers/docs/discord-social-sdk/overview" + horizontal + > + Explore rich presence, relationships, voice chat, and more. + + } + href="/developers/docs/discord-social-sdk/getting-started" + horizontal + > + Start here for a step-by-step guide to adding social features to your game. + + } + href="/developers/docs/discord-social-sdk/design-guidelines" + horizontal + > + Best practices for integrating Discord Social SDK features into your game. + + + +## Find Support + +Connect with the developer community, report issues, and stay updated with API changes. + +
+ + + } + horizontal + > + Get support, API announcements, and participate in developer events. + + } + horizontal + > + Report issues with the Discord API and SDKs. + + } + horizontal + > + Find articles, FAQs, and reach out to Discord's developer support team. + + +
diff --git a/discord/developers/docs/monetization/enabling-monetization.mdx b/discord/developers/docs/monetization/enabling-monetization.mdx new file mode 100644 index 0000000000..0c3beed021 --- /dev/null +++ b/discord/developers/docs/monetization/enabling-monetization.mdx @@ -0,0 +1,91 @@ +--- +title: Enabling Monetization +description: Step-by-step guide to meet eligibility requirements and enable monetization. +--- + +Before you can add monetization to your app, you must ensure that your app and team meet the eligibility criteria. + +## Steps to Enable Monetization + +1. Set up your developer team and app to be eligible for monetization +2. Complete the eligibility criteria for monetization +3. Set up developer team payouts to get paid +4. Create your premium offering +5. Implement monetization in your app +6. Start offering your premium features + +Once these are complete, you can [create SKUs](/developers/docs/monetization/managing-skus#creating-a-sku) to represent your premium offerings and [add support for your premium offering](/developers/docs/monetization/enabling-monetization#step-5-implement-monetization-in-your-app) in your app. + +## Step 1. Set Up Your Developer Team and App + +Before monetization can be enabled, you will need: + +- A [team](/developers/docs/topics/teams) in the developer portal. If you don't have one, you can [create one on the Teams page](https://discord.com/developers/teams) +- A [verified app](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified) that is _owned by that team_ +- Your app and team must be eligible for monetization. See the Eligibility Checklist below for details. + +## Step 2. Complete the Eligibility Checklist + +Before you can start creating SKUs and offering payments in your app, your app and team must be eligible for monetization. When a team owner enables monetization, they'll be taken through a series of steps and checks to ensure the following criteria are met: + + +- App must be verified +- App belongs to a developer team +- Team owner must be at least 18 years old +- Team must have verified emails and 2FA set up +- App uses slash commands, or has been approved for the privileged `Message Content` intent +- App has a link to your Terms of Service + - This document is an agreement between you and users governing the use of your app. +- App has a link to your Privacy Policy + - This document should clearly and accurately describe to users of your app the user data you collect and how you use and share such data with us and third parties, consistent with our Developer Terms of Service and Developer Policy. +- App must not contain any harmful or bad language in the name, description, commands, or role connection metadata. +- Payouts must be set up with a valid payment method +- Agreement to the [Monetization Terms](https://support.discord.com/hc/articles/5330075836311) and [Discord Developer Policy](https://support-dev.discord.com/hc/en-us/articles/8563934450327-Discord-Developer-Policy). + + +## Step 3. Set Up Team Payouts + +Let's set up Team Payouts so you can get paid! Discord processes all payouts through Stripe, so part of setting up payouts will go through Stripe's onboarding flow. + +- Only the owner of the team can enable payout settings for the team. +- Once your app has made its first $100 it will become eligible for payout. +- A review will be conducted and if everything looks good, your team will begin to receive payouts. + +#### If You are Based in the United States, European Union, or United Kingdom + +- Click on [your team](https://discord.com/developers/teams) on the Teams page. +- Select **Payout Settings**. + - If you do not see **Payout Settings**, you are not the owner of the team. Only the owner of the team can enable payout settings for the team. +- Complete the onboarding flow through Stripe. + +#### If You are Based Outside of the United States, European Union, or United Kingdom + + +Premium Apps is not currently available outside of these regions. These features will be made available to more regions soon. + + +For more information, read the [Premium Apps Payouts](https://support-dev.discord.com/hc/articles/17299902720919) Help Center article. + +## Step 4: Create Your Premium Offering + +You are now ready to start setting up your SKUs and offering premium features in your app. Check out our guide on [Managing your Premium Offerings](/developers/docs/monetization/managing-skus#creating-a-sku) to create one-time purchases and subscriptions for your app. + +## Step 5: Implement Monetization in your App + +Now that you've set up your app for monetization, you can start adding code to support your premium features. We have guides for the following monetization strategies: + +import {ShopIcon} from '/snippets/icons/ShopIcon.jsx' + + + }> + Learn how to start and manage recurring subscriptions within your app. + + }> + Learn how to implement one-time purchases in your app. + + + +## Step 6: Start Offering Your Premium Features +Congratulations! You've successfully set up your app for monetization. Now you can start earning money from your app and providing premium features to your users. + +You can now [link to your Store](/developers/docs/monetization/managing-skus#linking-to-your-store) page, [link to a specific SKU](/developers/docs/monetization/managing-skus#linking-to-a-specific-sku), or [include a premium styled button in Message Components](/developers/docs/monetization/managing-skus#responding-with-a-premium-button) to allow your users to make purchases. diff --git a/discord/developers/docs/monetization/implementing-app-subscriptions.mdx b/discord/developers/docs/monetization/implementing-app-subscriptions.mdx new file mode 100644 index 0000000000..e8fa7f939a --- /dev/null +++ b/discord/developers/docs/monetization/implementing-app-subscriptions.mdx @@ -0,0 +1,223 @@ +--- +title: Implementing App Subscriptions +description: Learn how to implement recurring user and guild subscriptions for premium functionality. +--- + +Charge users for premium app functionality with a recurring user or guild subscription. + +- Before you can add an app subscription to your app, you must [Enable Monetization](/developers/docs/monetization/enabling-monetization) for your app. +- Once you've confirmed eligibility for your app and team, you will be able to set up a [SKU](/developers/docs/resources/sku) (stock-keeping unit) to represent your subscription. + +--- + +## Types of Subscriptions + +When creating subscriptions, you will need to choose between user or guild subscriptions: + +- **User Subscriptions**: Offers premium features to an individual user across any server where your app installed. +- **Guild Subscriptions**: Provides premium benefits to all members within a specific server. + +--- + +## How App Subscriptions Work + +- When a user purchases your subscription SKU, Discord creates an [Entitlement](/developers/docs/resources/entitlement) for the user (or guild) and that specific Subscription [SKU](/developers/docs/resources/sku). +- You will receive an `ENTITLEMENT_CREATE` event via the Gateway. +- This entitlement will be available via the `LIST Entitlements` API endpoint. +- This entitlement will be available on `Interaction Payloads` initiated from the entitled user or users in a guild (for guild subscriptions). +- This subscription will be available via the `LIST Subscriptions` API endpoint. +- This entitlement is granted indefinitely until the user decides to cancel their subscription. `ends_at` will be null. +- When a user cancels their subscription, your app will not receive any entitlement events. +- When a subscription ends, the entitlement to the subscription will end. Developers will receive an `ENTITLEMENT_UPDATE` event with an `ends_at` timestamp indicating when the subscription ended. + +### Using Subscription Events for the Subscription Lifecycle + +Because entitlements are granted indefinitely and don't update on renewal or cancellation, you can use subscription events to track the lifecycle of a subscription. + + +This is not a complete list of when events may occur. You should use the presence of an entitlement to determine if a user has access to your premium features. The Subscription API and related events are intended for reporting and lifecycle management purposes and **should not be used as the source of truth of whether a user has access to your premium features**. + + +| Event Name | Subscription Behavior | Updated Fields | +|-----------------------|--------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| `SUBSCRIPTION_CREATE` | Subscription is created | `status` is either `0 (active)` if an entitlement has been granted or `1 (ending)` if an entitlement has not yet been granted | +| `SUBSCRIPTION_UPDATE` | Subscription is granted an entitlement | `status` is `0 (active)` | +| `SUBSCRIPTION_UPDATE` | Subscription is renewed | `current_period_start`, `current_period_end` timestamps updated | +| `SUBSCRIPTION_UPDATE` | Subscription is upgraded or downgraded | `sku_ids`, `entitlement_ids`, `renewal_sku_ids` may be updated | +| `SUBSCRIPTION_UPDATE` | Subscription is canceled | `canceled_at` timestamp updated, `status` is `1 (ending)` | +| `SUBSCRIPTION_UPDATE` | Subscription ends | `status` is `2 (inactive)`, this event is processed asynchronously and will not be immediate | +| `SUBSCRIPTION_UPDATE` | Subscription is resumed/uncanceled by user | `status` is `0 (active)` | + +--- + +## Working with Entitlements + +When a user purchases a subscription, an entitlement is created. [Entitlements](/developers/docs/resources/entitlement) represent the user's access to your app's premium features. + +Depending on your app's features, you can use a combination of [Gateway events](/developers/docs/events/gateway-events#entitlements), the [Entitlement HTTP API](/developers/docs/resources/entitlement), and [interaction payloads](/developers/docs/interactions/receiving-and-responding) to keep track of user and guild entitlements and grant features to users who are subscribed to your app. + +### Accessing Entitlements with Gateway Events + +When users make a purchase in your app, Discord will emit [Entitlement Gateway events](/developers/docs/events/gateway-events#entitlements). + +For subscription SKUs, you will receive the following entitlement events: + +| Event | Description | +|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ENTITLEMENT_CREATE` | When a user is granted an entitlement to your app's subscription SKU | +| `ENTITLEMENT_UPDATE` | When an entitlement to a subscription SKU ends | +| `ENTITLEMENT_DELETE` | When Discord refunds a subscription, removes an entitlement, or when a developer [deletes a Test Entitlement](/developers/docs/resources/entitlement#delete-test-entitlement) | + +### Accessing Entitlements with the HTTP API + +For apps requiring background processing or not solely reliant on interactions, keeping track of entitlements is essential. You can utilize the [List Entitlements](/developers/docs/resources/entitlement#list-entitlements) endpoint to list active and expired entitlements. Your app can filter entitlements by a specific user or guild by using the `?user_id=XYZ` or `?guild_id=XYZ` query params. + +For example, you might keep track of our entitlements in a database and check if a user still has access to a specific SKU before performing a cron job or other task. + +### Accessing Entitlements on Interaction Payloads + +Entitlements are also available on the `Interaction Payload` when a user interacts with your app. You can find the entitlements on the `entitlements` field of the `Interaction Payload` when [receiving and responding to interactions](/developers/docs/interactions/receiving-and-responding). You can use this field to determine if the user or guild is subscribed to your app. + +### Accessing Entitlements with the Embedded App SDK + +When using the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) to build an [Activity](/developers/docs/activities/overview), you can also [access a user's entitlements](/developers/docs/developer-tools/embedded-app-sdk#getentitlements). Check out the [Implementing In-App Purchases for Activities](/developers/docs/monetization/implementing-iap-for-activities) guide to learn more about monetization with the Embedded App SDK. + +--- + +## Prompting Users to Subscribe + +### Responding with a Premium Button + +[Responding with a premium button](/developers/docs/monetization/managing-skus#responding-with-a-premium-button) gives you the ability to prompt users to subscribe to your app when they attempt to use a premium feature without a subscription. + +You can do this by sending a message with a [button](/developers/docs/components/reference#button) with a [premium style](/developers/docs/components/reference#button-button-styles) and a `sku_id` that allows the user to upgrade to your premium offering. + +### Starting a Purchase from an Activity + +If you are using the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) to build an [Activity](/developers/docs/activities/overview), you can also launch the purchase flow for a specific SKU using the SDK. Check out the [Implementing In-App Purchases for Activities](/developers/docs/monetization/implementing-iap-for-activities) guide to learn more about monetization with the Embedded App SDK. + +### Purchasing from the Store Page + +Users can start, upgrade, or downgrade their subscription from your app's [Store](/developers/docs/monetization/managing-skus#viewing-your-store-page) page. You can link directly to your Store page using our [Application Directory Store URL scheme](/developers/docs/monetization/managing-skus#linking-to-your-store). + +--- + +## Supporting Subscriptions + +To support subscriptions in your app, you need to [create a subscription SKU](/developers/docs/monetization/managing-skus#creating-a-sku) and handle the following scenarios: + +### Starting a new subscription + +When a user subscribes to a new subscription, you will receive the following events: + +| Event | Event Trigger | +|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `SUBSCRIPTION_CREATE` | when the subscription is initially created. `status` is `0 (active)` if the entitlement has been granted or `1 (ending)` if the entitlement has not yet been granted. | +| `ENTITLEMENT_CREATE` | when the user is granted an entitlement for the new subscription | +| `SUBSCRIPTION_UPDATE` | when the subscription is updated with the `entitlement_ids`, `renewal_sku_ids`, and `status` (`0 (active)`) | + +### Cancelling an existing subscription + +Users can cancel their subscription at any time from their Subscription settings. + +When a user cancels their subscription, you will receive the following events: + +| Event | Event Trigger | +|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------| +| `SUBSCRIPTION_UPDATE` | when the subscription is updated to end with a `status` of `1 (ending)` and `canceled_at` is set to the timestamp the user canceled | + +The user's subscription and entitlement are still valid until the subscription `current_period_end` is reached. + +If the subscription is not resumed before the subscription `current_period_end`, it will end and you will receive the following events: + +| Event | Event Trigger | +|-----------------------|----------------------------------------------------------------------------| +| `ENTITLEMENT_UPDATE` | when the current entitlement ends. `ends_at` gets updated with a timestamp | +| `SUBSCRIPTION_UPDATE` | when the subscription is updated with the `status` of `2 (inactive)` | + +### Resuming a cancelled subscription + +Users can resume their subscription at any time before the `current_period_end` is reached in their Subscription settings. + +When a user resumes their subscription, you will receive the following events: + +| Event | Event Trigger | +|-----------------------|-----------------------------------------------------------------------------------------------------------| +| `SUBSCRIPTION_UPDATE` | when the subscription is set to continue with a `status` of `0 (active)` and `canceled_at` is set to null | + +--- + +## Supporting Upgrades and Downgrades + +If you offer multiple subscription tiers in your app, users can upgrade or downgrade their subscription at any time from your [Store page](/developers/docs/monetization/managing-skus#viewing-your-store-page) or their App Subscription settings. + +To create multiple subscription tiers, you will need to [create multiple subscription SKUs](/developers/docs/monetization/managing-skus#creating-a-sku) and support the following scenarios in your app: + +### Upgrading an existing subscription + +If an user is on a lower tier subscription and upgrades to subscription tier that is the same price or higher, the user is charged the difference in price between the two subscriptions and the subscription period resets at the time of upgrading. + +When the subscription is upgraded, the current entitlement for the lower tier will end immediately and you will receive the following events: + +| Event | Event Trigger | +|-----------------------|--------------------------------------------------------------------------------------------------------------------------| +| `ENTITLEMENT_UPDATE` | when the current entitlement ends. `ends_at` gets updated with a timestamp | +| `ENTITLEMENT_CREATE` | when a new entitlement is created for the upgrade subscription SKU | +| `SUBSCRIPTION_UPDATE` | when the subscription is updated with the new `entitlement_ids`, `sku_ids`, `current_period_start`, `current_period_end` | + + +### Downgrading an existing subscription + +If an user is on a higher tier subscription and downgrades to a lower tier subscription, the user is not charged immediately because the price is lower than what was already paid. + +The user has already paid for their current plan until `subscription.current_period_end` so their current plan will be valid until then and you will receive the following event: + +| Event | Event Trigger | +|-----------------------|--------------------------------------------------------------------------------------------------| +| `SUBSCRIPTION_UPDATE` | when the subscription is updated to reflect the renewal SKU ID in `subscription.renewal_sku_ids` | + +Once the user's current subscription expires on `subscription.current_period_end`, you will receive the following events: + +| Event | Event Trigger | +|-----------------------|--------------------------------------------------------------------------------------------------------------------------| +| `ENTITLEMENT_UPDATE` | when the current entitlement ends. `ends_at` gets updated with a timestamp | +| `ENTITLEMENT_CREATE` | when a new entitlement is created for the downgraded subscription SKU | +| `SUBSCRIPTION_UPDATE` | when the subscription is updated with the new `entitlement_ids`, `sku_ids`, `current_period_start`, `current_period_end` | + +--- + +## Using the Subscription API + + +When implementing monetization, [Entitlements](/developers/docs/resources/entitlement) should be considered the source of truth for a user's access to a specific SKU. The Subscription API is intended for reporting and lifecycle management purposes that happen outside the flow of a user's interaction with your app. + + +You can use the [Subscription API](/developers/docs/resources/subscription) to check on the status of your app subscriptions. This API allows you to list subscriptions by user for reporting purposes and to check on the status of subscriptions without having to access entitlements directly. + +- [List SKU Subscriptions](/developers/docs/resources/subscription#list-sku-subscriptions): List all subscriptions for a specific SKU in your app. +- [Get SKU Subscription](/developers/docs/resources/subscription#get-sku-subscription): Get a specific subscription in your app. +- [Subscription Gateway events](/developers/docs/events/gateway-events#subscriptions): Discord will emit gateway events when a subscription is created, updated, and very rarely, deleted. + +--- + +## Testing Your App Subscription Implementation + +### Using Test Entitlements + +You can test your implementation by [creating](/developers/docs/resources/entitlement#create-test-entitlement) and [deleting](/developers/docs/resources/entitlement#delete-test-entitlement) test entitlements. These entitlements will allow you to test your premium offering in both a subscribed and unsubscribed state as a user or guild. + +This method will not let you test out the full payment flow in Discord but will allow you to test your app's behavior when a user is subscribed or unsubscribed. See [Using Live Entitlements](/developers/docs/monetization/implementing-app-subscriptions#using-live-entitlements) if you'd like to see the full payment flow. + + +Test Entitlements do not have a `starts_at` or `ends_at` field as they are valid until they are deleted. + + +### Using Live Entitlements + +If you'd like to test the full payment flow for your app, you can do so by interacting with your Store page or a [premium styled button](/developers/docs/monetization/implementing-app-subscriptions#prompting-users-to-subscribe). Any team members associated with your app will automatically see a 100% discount on the price of the subscription, allowing you to purchase without the use of live payment method. + +After checkout, you will have a live subscription. This subscription will renew until canceled and can be used in testing subscription renewals in your app. If you cancel this subscription, it will remain an active entitlement until the end of the subscription billing period, represented by the `period_ends_at` field on the [Subscription](/developers/docs/resources/subscription#subscription-object). + + +You can only delete entitlements created using the [create entitlement](/developers/docs/resources/entitlement#create-test-entitlement) endpoint. If you need to toggle access to your premium features during your development process, it is best to use Test Entitlements. + \ No newline at end of file diff --git a/discord/developers/docs/monetization/implementing-iap-for-activities.mdx b/discord/developers/docs/monetization/implementing-iap-for-activities.mdx new file mode 100644 index 0000000000..be8e6f5c0d --- /dev/null +++ b/discord/developers/docs/monetization/implementing-iap-for-activities.mdx @@ -0,0 +1,184 @@ +--- +title: Implementing In-App Purchases for Activities +sidebarTitle: Implementing IAP for Activities +description: Step-by-step guide to add monetization to Discord Activities. +--- + +In-App Purchases (IAP) for [Activities](/developers/docs/activities/overview) allows developers to easily monetize their Activity by allowing users to buy premium subscriptions or items natively in Discord. This guide will walk you through the process of implementing monetization using the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk). + +Before you can add premium products with the Embedded App SDK in an Activity, you must [Enable Monetization](/developers/docs/monetization/enabling-monetization) for your app. + +--- + +## Key Concepts + +Before implementing monetization in your app, it's important to understand the features of Discord you'll be working with: + +- **SKUs**: Represent your app's premium products. +- **Entitlements**: Represent the user's access to your premium products. +- **Subscriptions**: Represent an ongoing agreement for a user to pay for an entitlement on a recurring basis until canceled. + +You can learn more about these concepts in the [Monetization Overview](/developers/docs/monetization/overview). + +--- + +## Working with SKUs + +SKUs represent the premium products you offer in your app. Before you can start implementing monetization in your app, you will need to create a SKU for each premium product. + +To learn more about creating and managing SKUs and the different types of SKUs you can create, see the [Managing SKUs](/developers/docs/monetization/managing-skus) guide. + +### Publishing SKUs for Activities + +When publishing SKUs, you can choose to publish them to your **Store and the API** or **API Only**. Which method you select will depend on the purchase experience you want to offer your users. + +#### Published to Store and the API + +- Your SKUs will be visible to users in the Discord client in your [app's store](/developers/docs/monetization/managing-skus#viewing-your-store-page) or your Activity's custom storefront. +- Your users will be able to purchase them directly from the Discord client without having your Activity open so you should handle the purchase flow accordingly. + +#### Published to API Only + +- Your SKUs will **not** be visible to users in your [app's store](/developers/docs/monetization/managing-skus#viewing-your-store-page), and users will only be able to purchase them through your Activity's custom storefront. + + +Next, we'll cover how to create and render your own custom storefront in your Activity. + +--- + +## Building a Storefront + +Once you have created your SKUs, you will need to build and render your own custom storefront in your Activity to display your premium products to users. This section will guide you through the process of listing SKUs, mapping SKUs to your premium perks, and displaying prices. + +### Listing SKUs + +To fetch the list of products for displaying in your Activity, you can call the [`getSkus()`](/developers/docs/developer-tools/embedded-app-sdk#getskus) command from the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk). + +You can also fetch SKUs using the [HTTP API](/developers/docs/resources/sku#list-skus). + +### Mapping SKUs to Your Premium Perks + +When a user purchases a SKU, an entitlement is created. Entitlements represent the user's access to a specific SKU. You will need to map your SKUs to the premium perks you are offering in your application to ensure users receive the correct perks when they purchase a SKU. + +When mapping SKUs to premium perks, keep the following in mind: +- SKUs should be mapped to product based on `id` attribute, **not** other attributes (such as `name`) +- SKUs can never be deleted, so once purchased it cannot be revoked and should always be mapped to some perk or product in your application + +### Formatting and Displaying Prices + +The methods for listing SKUs automatically localize currency and prices. + +This means `sku.price` has a number `amount` attribute and a string `currency` attribute. Properly rendering prices with proper currency can be challenging, given the large number of currencies. The Embedded App SDK provides a `PriceUtils` utility to make this easier. + +```javascript +import {PriceUtils} from '@discord/embedded-app-sdk'; + +const displayPrice = PriceUtils.formatPrice(sku.price); +console.log(`The price is ${displayPrice}!`); +``` + +--- + +## Working with Entitlements + +When a user purchases a SKU, an entitlement is created. [Entitlements](/developers/docs/resources/entitlement) represent the user's access to your premium product. + +Depending on your app's features, you can use a combination of the [Embedded App SDK events](/developers/docs/developer-tools/embedded-app-sdk#getentitlements), [Gateway events](/developers/docs/events/gateway-events#entitlements), the [Entitlement HTTP API](/developers/docs/resources/entitlement), and [interaction payloads](/developers/docs/interactions/receiving-and-responding) to keep track of entitlements to users who have purchased items in your app. + +### Fetching Entitlements with the Embedded App SDK + +Use the [`getEntitlements()`](/developers/docs/developer-tools/embedded-app-sdk#getentitlements) SDK command to fetch a list of entitlements for a user. This command will return a list of entitlement objects that represent the user's access to your premium products. + +### Handling Subscription Entitlements + +When a user purchases a subscription SKU, an entitlement is created. For more information on handling subscription entitlements, see the [Implementing App Subscriptions](/developers/docs/monetization/implementing-app-subscriptions) guide. + +### Handling One-Time Purchase Entitlements + +When a user purchases a one-time purchase SKU, an entitlement is created. For more information on handling one-time purchase entitlements, see the [Implementing One-Time Purchases](/developers/docs/monetization/implementing-one-time-purchases) guide. + +#### One-Time Purchases: Consumable and Durable Items + +It is common in Activities to have consumable or one-time-use items, such as a single-use potion or power-up. + +When a user purchases a consumable SKU, an entitlement is created. This entitlement will be marked as `consumed: false`. Your application should process the item purchase and [consume the entitlement](/developers/docs/resources/entitlement#consume-an-entitlement) as soon as possible to grant the user the perks associated with the item. + +If you want to offer an item that grants perks for an unlimited amount of time, you should use a durable SKU instead of a consumable SKU. + +Learn more about consumable and durable items in the [Implementing One-Time Purchases](/developers/docs/monetization/implementing-one-time-purchases) guide. + +--- + +## Initiating Purchases + +After displaying your SKUs in your custom storefront, you will need to initiate a purchase when a user selects a SKU to purchase. + +To initiate a purchase in your activity, use the Embedded App SDK to call the [`startPurchase()`](/developers/docs/developer-tools/embedded-app-sdk#startpurchase) command with the selected SKU `id`. This command will open the purchase flow modal in the Discord client, allowing users to purchase that SKU. + +Learn more about the [`startPurchase()`](/developers/docs/developer-tools/embedded-app-sdk#startpurchase) command in the [Embedded App SDK Reference](/developers/docs/developer-tools/embedded-app-sdk). + +To know when it has been completed, you can subscribe to `ENTITLEMENT_CREATE` events. + +### Subscribing to Purchase Events with the Embedded App SDK + +Once a user has completed a purchase, Discord will emit an `ENTITLEMENT_CREATE` event. You can subscribe to this event using the Embedded App SDK to know when a user has successfully purchased a SKU and is granted an entitlement. + +You can subscribe to the `ENTITLEMENT_CREATE` event using the [`subscribe()`](/developers/docs/developer-tools/embedded-app-sdk#subscribe) method in the Embedded App SDK. + +Here's an example of how to subscribe to the `ENTITLEMENT_CREATE` event: + +```js +import {DiscordSDK} from '@discord/embedded-app-sdk'; +const discordSdk = new DiscordSDK(clientId); +await discordSdk.ready(); + +const handleEntitlementCreate = () => { + // refetch entitlements from server using the Entitlement HTTP API endpoint +}; +discordSdk.subscribe('ENTITLEMENT_CREATE', handleEntitlementCreate); +``` + +You can also subscribe to the `ENTITLEMENT_CREATE` event using the [Gateway API](/developers/docs/events/gateway-events#entitlements) to receive the event in your app's backend or use the [List Entitlements](/developers/docs/resources/entitlement#list-entitlements) HTTP API endpoint to fetch a user's entitlements. + +--- + +## Data Security Considerations + +When working with SKUs and Entitlements in an Activity, it's crucial to ensure the security and integrity of your application's data. Here are some things to keep in mind: + +Developers should ensure the accuracy of data obtained via [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) commands and events. A malicious actor could potentially establish their own RPC connection and interact with your application client, posing as Discord. While this might not be an issue for most SDK commands and events, it becomes critical when dealing with perks offered through In-App purchases. + +If your application relies solely on SDK data to determine a user's entitlements, a malicious actor could exploit this to gain access to premium products, features, or advantages without paying. This is particularly relevant for commands like [`getEntitlements()`](/developers/docs/developer-tools/embedded-app-sdk#getentitlements). + +### Use the Discord HTTP API for Verification + +Data fetched from the Discord HTTP API from your application's backend servers can be trusted and should be treated as the source of truth. This data should be used to validate any inconsistent client-side data. + +### Recommended Approach + +- **Optimistically** use client-side techniques such as SDK commands and events to fetch SKUs and Entitlements. +- **Verify** the results via the Discord HTTP API from your application's backend. + +In summary, use the principle of "Trust (the SDK), but Verify (via the API)" to ensure the security and integrity of your application's premium features. + +--- + +## Testing Your In-App Purchases + +To test your In-App Purchases in your Activity, you will need to follow testing guidelines for both types of SKUs: [One-Time Purchases](/developers/docs/monetization/implementing-one-time-purchases#testing-your-onetime-purchase-implementation) and [Subscriptions](/developers/docs/monetization/implementing-app-subscriptions#testing-your-app-subscription-implementation). + +After you've tested your In-App Purchase flows, verify that your application has correctly granted the user the premium perks associated with the SKUs that were purchased during testing. + +--- + +## Next Steps + +Check out our example implementation of In-App Purchase in our [SDK Playground Example Application](https://github.com/discord/embedded-app-sdk-examples/tree/main/sdk-playground). + +The example implementation includes client-side and server-side code. It also follows the [security considerations](/developers/docs/monetization/implementing-iap-for-activities#data-security-considerations) you should make when working with SKUs and entitlements. + + + + This reference example implements the commands and events available to you within the Embedded App SDK, including In-App Purchases. + + diff --git a/discord/developers/docs/monetization/implementing-one-time-purchases.mdx b/discord/developers/docs/monetization/implementing-one-time-purchases.mdx new file mode 100644 index 0000000000..198a2e9fcc --- /dev/null +++ b/discord/developers/docs/monetization/implementing-one-time-purchases.mdx @@ -0,0 +1,126 @@ +--- +title: Implementing One-Time Purchases +description: Guide to implementing durable and consumable one-time purchases. +--- +One-time purchases enable you to charge your users for premium functionality with in-app items. + +- Before you can add one-time purchases to your app, you must [Enable Monetization](/developers/docs/monetization/enabling-monetization) for your app. +- Once you've confirmed eligibility for your app and team, you will be able to set up a [SKU](/developers/docs/resources/sku) (stock-keeping unit) to represent your one-time purchases. + +--- + +## Types of One-Time Purchases + +When creating items for one-time purchase, you can choose between durable and consumable items: + +- **Durable Items**: A one-time purchase that is permanent and is not subject to either renewal or consumption, such as lifetime access to an app's premium features. +- **Consumable Items**: A one-time, non-renewable purchase that provides access, such as a temporary power-up or boost in a game. + +--- + +## How One-Time Purchases Work + +#### For Durable SKUs +- When a user purchases your durable SKU, Discord creates an [Entitlement](/developers/docs/resources/entitlement) for the purchasing user and that specific [SKU](/developers/docs/resources/sku). +- You will receive an `ENTITLEMENT_CREATE` event via the Gateway. +- This entitlement is now available via the `LIST Entitlements` API endpoint. +- This entitlement will be available on `Interaction Payloads` initiated from the entitled user. + +#### For Consumable SKUs +- When a user purchases your consumable SKU, Discord creates an [Entitlement](/developers/docs/resources/entitlement) for the purchasing user and that specific SKU. +- You will receive an `ENTITLEMENT_CREATE` event via the Gateway. +- This entitlement is now available via the `LIST Entitlements` API endpoint. +- This entitlement will be available on `Interaction Payloads` initiated from the entitled user or users in a guild (for guild subscriptions). +- Users cannot repurchase this SKU until you consume the entitlement using the [Consume Entitlement API](/developers/docs/resources/entitlement#consume-an-entitlement) endpoint. + - In [Test Mode](/developers/docs/monetization/implementing-one-time-purchases#using-application-test-mode), repeated purchases are permitted without consumption for developer convenience. +- When you receive an `ENTITLEMENT_CREATE` event for a consumable SKU, you should process the item purchase in your app and consume the entitlement as soon as possible. + +--- + +## Working with Entitlements + +When a user purchases a one-time purchase SKU, an entitlement is created. [Entitlements](/developers/docs/resources/entitlement) represent the user's access to your consumable or durable item. + +Depending on your app's features, you can use a combination of [Gateway events](/developers/docs/events/gateway-events#entitlements), the [Entitlement HTTP API](/developers/docs/resources/entitlement), and [interaction payloads](/developers/docs/interactions/receiving-and-responding) to keep track of entitlements to users who have purchased items in your app. + +### Accessing Entitlements with Gateway Events + +When a user purchases a SKU, Discord will emit an [`ENTITLEMENT_CREATE`](/developers/docs/events/gateway-events#entitlements) event. This event will contain the entitlement object that represents the user's access to the SKU. You can use this event to keep track of the user's entitlements in near-time. For One-Time Purchases, you may also receive an `ENTITLEMENT_DELETE` event if the user's entitlement is revoked. + +### Accessing Entitlements with the HTTP API + +Entitlements are available via the [List Entitlements](/developers/docs/resources/entitlement#list-entitlements) endpoint. You can filter entitlements by a specific user or set of SKUs by using the `?user_id=XYZ` or `?sku_ids=XYZ` query parameters. + +### Accessing Entitlements on Interaction Payloads + +Entitlements are also available on the `Interaction Payload` when a user interacts with your app. You can find the entitlements on the `entitlements` field of the `Interaction Payload` when [receiving and responding to interactions](/developers/docs/interactions/receiving-and-responding). + +### Accessing Entitlements with the Embedded App SDK + +When using the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) to build an [Activity](/developers/docs/activities/overview), you can also [access a user's entitlements](/developers/docs/developer-tools/embedded-app-sdk#getentitlements). Check out the [Implementing In-App Purchases for Activities](/developers/docs/monetization/implementing-iap-for-activities) guide to learn more about monetization with the Embedded App SDK. + +Depending on your app's needs, you can use a combination of these methods to keep track of user entitlements. + +--- + +## One-Time Purchase Considerations + +When implementing one-time purchases, you should consider the following: + +### For Durable One-Time Purchases + +When offering durable items, users will have access to the SKU indefinitely. Durable items can't be consumed, so you don't need to worry about the user losing access to the item except in the case of a refund. + +### For Consumable One-Time Purchases +When offering consumable items, users can only have one unconsumed entitlement at a time. To handle consumable items in your app or game, you should process and store the consumable item in your app and then make a call to the [Consume Entitlement](/developers/docs/resources/entitlement#consume-an-entitlement) endpoint so that the user can purchase more of this item in the future. + +Consuming the entitlement will update the entitlement to return a true value in the entitlement's `consumed` field. You will need to think through how your app keeps track of consumable items to decide on the best strategy for when to consume these entitlements and store the state of the consumable item and quantity in your app. + +--- + +## Prompting Users to Purchase an Item + +### Responding with a Premium Button + +[Responding with a premium button](/developers/docs/monetization/managing-skus#responding-with-a-premium-button) gives you the ability to prompt users to subscribe to your app when they attempt to use a premium feature without a subscription. + +You can do this by sending a message with a [button](/developers/docs/components/reference#button) with a [premium style](/developers/docs/components/reference#button-button-styles) and a `sku_id` that allows the user to upgrade to your premium offering. + +### Starting a Purchase from an Activity + +If you are using the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) to build an [Activity](/developers/docs/activities/overview), you can also launch the purchase flow for a specific SKU using the SDK. Check out the [Implementing In-App Purchases for Activities](/developers/docs/monetization/implementing-iap-for-activities) guide to learn more about monetization with the Embedded App SDK. + +--- + +## Testing Your One-Time Purchase Implementation + + +The method of testing purchases for One-Time Purchases differs from the method for App Subscriptions. **Do NOT use Test Entitlements for One-Time Purchases.** + + +### Using Application Test Mode + +While in Application Test Mode, you can freely make "purchases" of One-Time Purchase SKUs tied to your application. That means you can test buying your consumable and durable items by going through the In-App Purchase flow without any credit card charges. + + +You still need to have a valid payment method on file to "purchase" SKUs in Application Test Mode. It just won't be charged at checkout. + + +To enable it, first, make sure you have a payment method on file in `User Settings -> Billing` and then: + +1. Open up the Discord app +2. Click on the Settings cog in the bottom left corner +3. Go to the `Advanced` page under App Settings +4. Toggle "Developer Mode" **on** and "Application Test Mode" **on**, and enter your application ID. You can leave the other settings as-is. +5. Exit user settings + +Once you enabled Application Test Mode successfully, you should see an orange bar across the top of your screen with the name of your app. + +You can now navigate to your Store page and purchase your one-time purchase items without being charged. + +The entitlements tied to items purchased in Application Test Mode can be identified by entitlements with a `type` value of 4 to represent `TEST_MODE_PURCHASE`. + + +The "Go To SKU" button does not currently work. To purchase your SKU in test mode, go to your Store page. + + diff --git a/discord/developers/docs/monetization/managing-skus.mdx b/discord/developers/docs/monetization/managing-skus.mdx new file mode 100644 index 0000000000..f44500295b --- /dev/null +++ b/discord/developers/docs/monetization/managing-skus.mdx @@ -0,0 +1,242 @@ +--- +title: Managing SKUs +description: Learn how to create and manage SKUs that represent your app's premium offerings. +--- + +The premium items and subscriptions you offer in your app are represented by SKUs. **SKU** stands for Stock Keeping Unit and is a unique identifier for your premium offerings. + +[SKUs](/developers/docs/resources/sku) are the building blocks of your premium offerings and you can manage them in the Developer Portal. + +--- + +## Creating a SKU + +To create a new SKU, navigate to your [app's settings](https://discord.com/developers/applications) and select the `Monetization -> Manage SKUs` tab. From there, you can create a new SKU by clicking the `Create SKU` button. + +When you click on `Create SKU`, you have the option to select from the following: + +- **User Subscription**: An auto-recurring subscription that grants benefits to one user in all servers +- **Guild Subscription**: An auto-recurring subscription that grants benefits to all users in one server +- **Consumable**: A one-time purchase that provides a temporary benefit, which is consumed upon use. +- **Durable**: A one-time purchase that **grants** a permanent addition or enhancement. + +Once you select the SKU type, enter a name for your SKU to continue. + +### Creating Subscription Tiers + +You can create multiple subscription tiers to offer different benefits at different price points. Each tier can have its own set of benefits and price and is represented by unique SKUs. + +To support upgrading and downgrading between subscription tiers, see our guide on [Implementing App Subscriptions](/developers/docs/monetization/implementing-app-subscriptions#supporting-upgrades-and-downgrades). + +![Supporting multiple subscription tiers](/images/monetization/multisub.png) + +### SKU Limitations + +There are some limitations to the number of SKUs you can create: +- You can create up to 50 total SKUs per app. +- You can offer either user subscription SKUs or guild subscription SKUs, but not both simultaneously. +- SKU prices must be selected from the list of available prices. + + +If you need more SKUs than the 50 limit, consider creating a consumable in-app currency SKU that can be used to purchase items that are tracked in your app. + + +--- + +## Customizing Your SKUs + +Once you've created a SKU, you can customize it to match your app's branding and the benefits you want to offer. You can customize: + +- A name for your premium SKU, max 80 characters. +- A description for your premium SKU, max 160 characters +- An image for your premium SKU +- A price for your premium SKU + +Your list of benefits will displayed on your app's Store page, the App Directory, and during the purchase and cancellation flows to explain to users the benefits of your premium offering. These benefits can have: +- Up to 6 benefits +- An emoji, standard or custom +- A name, max 80 characters +- A description, max 160 characters + +![Example of SKU benefits](/images/monetization/sku-benefits.png) + +### Pricing Your SKUs + +When setting the price for your SKU, you can select from a list of predefined prices. The prices are automatically converted to the user's local currency based on their locale. + +Subscription SKUs are automatically charged each month unless canceled. Changing the price of this SKU will only change it for new subscribers. Existing subscribers will continue to be charged the existing price. + + +To set an icon using a standard Unicode emoji, enter the emoji in the `Unicode Emoji or Custom Emoji Name` field. + + +Using an emoji keyboard can make it easier to pick an icon to display alongside your SKU benefit. + +MacOS: `control + command + space bar` + +Windows: `Windows + .` + + +![Set a unicode emoji](/images/sku-unicode.webp) + + + +To use a custom emoji, set a value for both fields: + +- Name of your custom emoji +- ID of the custom emoji + + +You can find the ID of the emoji in the Discord app by escaping the emoji in a message with a backslash character `\`. For example, `\:uwu:` will render with the name and ID of the emoji. + + +![Set a custom emoji](/images/sku-custom.webp) + + +--- + +## Publishing and Unpublishing SKUs + +When you initially create a SKU, it will be in an `Unavailable` state. This SKU is not yet available for purchase by users. You can edit the SKU to add a price, benefits, and other details before publishing it. + +While creating and editing SKUs in your [app's settings](https://discord.com/developers/applications) on the `Monetization -> Manage SKUs` tab, you have a few options for managing your SKUs visibility and publishing to your users: + +- Publish SKU +- Unpublish SKU +- Delete SKU + +### Publishing a SKU + +When publishing a SKU, you have the option to make it **Available via the Store and API** or **Available via the API Only**. + +#### Publishing to Store & API + +Available to be purchased and visible in your app's store. + +#### Publishing to API Only + +You can only make API calls or use the Embedded App SDK to grant entitlements for this SKU. + +### Unpublishing a SKU + + +**Danger:** Unpublishing a SKU can affect your users' existing subscriptions and entitlements + + +Unpublishing a SKU removes it from the Store and the API, making it unavailable for purchase. + +Unpublishing a SKU has the following effects: +- For subscription SKUs, subscriptions will not be renewed for users and guilds that have this SKU at the end of the billing period. +- Users and guilds will still be entitled to the SKU until the end of the billing period. +- For consumable and durable SKUs, users will still be entitled to the SKU if they purchased it before it was unpublished. +- Does not delete a SKU. + +### Deleting a SKU + + +**Danger:** Deleting a SKU can affect your users' existing subscriptions and entitlements + + +Deletes a SKU in the UI and makes it unavailable for publishing. Deleted SKUs are still listed when calling [List SKUs](/developers/docs/resources/sku#list-skus) in the API. + +Deleting a SKU has the following effects: +- For subscription SKUs, users and guilds will be immediately unsubscribed from the SKU. Their entitlement will still be valid until the end of the billing period. +- For consumable and durable SKUs, users will still be entitled to the SKU if they purchased it before it was unpublished. + +--- + +## Editing a Published SKU +If you wish to change a SKU that is published, you can do so at any time by first unpublishing the currently published one. When you unpublish a SKU, it is no longer available for sale and users who have already subscribed will not renew at the end of their billing period. You must continue to make the premium offering available to them until the end of their subscription. + +### Changing a Subscription SKU Price + +When you change the price of a user or guild subscription SKU, it will only affect new subscribers. Existing subscribers will continue to be charged the price of the SKU at the time they subscribed. + +--- + +## Integrating SKUs in Your App + +After you've published your SKUs, you are ready to start implementing your premium features in your app. See our guides to get started. + +import {ShopIcon} from '/snippets/icons/ShopIcon.jsx' + + + }> + Learn how to start and manage recurring subscriptions within your app. + + }> + Learn how to implement one-time purchases in your app. + + + +--- + +## Viewing your Store Page + +Users can access an app's Store page from the Bot User's profile in a server. This allows users to view an available subscription and one-time purchases, select a subscription to view its perks, benefits, and details, and make a purchase directly from an app's Store page. + + +Only subscriptions and items that have been published to the Store will be visible to users on the Store page. + + +#### Accessing your Store page from a Bot User's Profile +![Accessing the store as a user](/images/monetization/botuser-profile.png) + +#### Subscriptions in Your Store page +![Subscriptions in your Store View](/images/monetization/multisub.png) + +#### Items in Your Store page +![Items in your Store View](/images/monetization/premium-items.png) + +--- + +## Linking to a Specific SKU + +You can link directly to a specific SKU using our Application Directory Store URL scheme: + +`https://discord.com/application-directory/:appID/store/:skuID` + +- When used in chat, it will render as a rich embed that allows users to launch a modal to view either the SKU details or checkout flow +- When used as a direct URL in a browser, it will take the user to your product in the Application Directory on web + +![Embed for direct link to SKU](/images/monetization/sku_embed.png) + +--- + +## Linking To Your Store + +You can link directly to your Store page using our Application Directory Store URL scheme: + +`https://discord.com/application-directory/:appID/store` + +- When used in chat, it will render as a rich embed that allows users to launch a modal to your Store page +- When used as a direct URL in a browser, it will take the user to your Store page in the Application Directory on web + +![Embed for direct link to Store](/images/monetization/store_embed.png) + +--- + +## Responding with a Premium Button + +You can prompt users to purchase item or subscription SKUs using a [button](/developers/docs/components/reference#button) with a [premium style](/developers/docs/components/reference#button-button-styles) and a `sku_id`. You can use this premium button style anywhere you would use message components, such as in a command response. + +```javascript +return new JsonResponse({ + type: 4, // InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE + data: { + content: "This command requires Nelly Premium! Upgrade now to get access to these features!", + components: [{ + type: MessageComponentTypes.ACTION_ROW, + components: [ + { + type: MessageComponentTypes.BUTTON, + style: 6, // ButtonStyleTypes.PREMIUM + sku_id: '1234965026943668316', + }, + ], + }] + }, +}); +``` + +![A premium button](/images/monetization/premium-button.png) diff --git a/discord/developers/docs/monetization/overview.mdx b/discord/developers/docs/monetization/overview.mdx new file mode 100644 index 0000000000..4caa928d09 --- /dev/null +++ b/discord/developers/docs/monetization/overview.mdx @@ -0,0 +1,52 @@ +--- +title: Monetizing Your Discord App +sidebarTitle: Overview +description: Learn how to add premium features to your Discord app with subscriptions and purchases. +--- + +![Monetizing Your Discord App](/images/monetization/overview.png) + +### Offer native payment and checkout in your app using our Monetization APIs. + +Premium Apps lets you prompt customers to start subscriptions or purchase one-time items with our easy-to-use checkout and payment process. Discord notifies your app of the user's purchase, allowing you to grant access to your premium features. + +- Sell monthly recurring [subscriptions](/developers/docs/monetization/implementing-app-subscriptions) for your app's premium functionality within Discord +- Sell [one-time purchases](/developers/docs/monetization/implementing-one-time-purchases) for both durable and consumable items or functionality within your app +- Highlight your app's premium benefits on the App Directory and on your own [Store](/developers/docs/monetization/managing-skus#viewing-your-store-page) page +- Offer native product tie-ins and upsells on the App Directory, app profiles, and in-chat +- With secure transactions & fraud detection, your customers can securely pay for purchases without leaving Discord + +--- + +## Components of a Premium App + +To integrate a premium feature into your application, there are three primary components of our Monetization API: + +- [SKUs](/developers/docs/resources/sku) represent specific items or subscription options your app offers. Each SKU is a unique offering. +- [Entitlements](/developers/docs/resources/entitlement) indicate whether a user has access to a specific premium offering or SKU. +- [Subscriptions](/developers/docs/resources/subscription) represent an ongoing agreement where a user commits to paying for an entitlement on a recurring basis until canceled. + +### Types of SKUs + +There are two types of SKUs that you can create for your app: + +#### One-Time Purchase SKUs + +A one-time purchase SKU represents a single item or feature that a user can purchase once. Developers can offer two types of one-time purchases: +- Durable items: Items that a user can purchase once and keep forever. For example, a user might purchase a "premium" upgrade that unlocks premium features in an app. +- Consumable items: Items that a user can purchase once and use up. For example, a user might purchase a "boost" item that gives them a temporary boost in an app. + +#### Subscription SKUs + +A subscription SKU represents a recurring purchase that a user can subscribe to for a set period of time. Developers can offer two types of subscriptions: + +- User subscriptions: A user subscribes to a SKU for themselves. In this case, only the purchasing user is considered entitled to the SKU. +- Guild subscriptions: A user subscribes to a SKU for their guild. All members of that guild are considered entitled to the SKU. + +--- + +## Next Steps + +Ready to start monetizing your app? + +Follow our full guide for [Enabling Monetization](/developers/docs/monetization/enabling-monetization) and implementing premium features in your app. \ No newline at end of file diff --git a/discord/developers/docs/policies/developer-policy.mdx b/discord/developers/docs/policies/developer-policy.mdx new file mode 100644 index 0000000000..f552b4fbc1 --- /dev/null +++ b/discord/developers/docs/policies/developer-policy.mdx @@ -0,0 +1,5 @@ +--- +title: Developer Policy +icon: "arrow-up-right-from-square" +url: https://support-dev.discord.com/hc/en-us/articles/8563934450327-Discord-Developer-Policy +--- diff --git a/discord/developers/docs/policies/developer-terms-of-service.mdx b/discord/developers/docs/policies/developer-terms-of-service.mdx new file mode 100644 index 0000000000..b78ba27182 --- /dev/null +++ b/discord/developers/docs/policies/developer-terms-of-service.mdx @@ -0,0 +1,5 @@ +--- +title: Developer Terms of Service +icon: "arrow-up-right-from-square" +url: https://support-dev.discord.com/hc/en-us/articles/8562894815383-Discord-Developer-Terms-of-Service +--- diff --git a/discord/developers/docs/quick-start/getting-started.mdx b/discord/developers/docs/quick-start/getting-started.mdx new file mode 100644 index 0000000000..5a5937435c --- /dev/null +++ b/discord/developers/docs/quick-start/getting-started.mdx @@ -0,0 +1,623 @@ +--- +title: Building your first Discord app +sidebarTitle: Getting Started +description: Step-by-step tutorial for building your first Discord app. +--- +import {LinkButton} from '/snippets/linkbutton.jsx' + +[Discord apps](/developers/docs/quick-start/overview-of-apps) let you customize and extend Discord using a collection of APIs and interactive features. This guide will walk you through building your first Discord app using JavaScript and by the end you'll have an app that uses slash commands, sends messages, and responds to component interactions. + + +If you're interested in building a game or social experience in an iframe, you can follow the tutorial for [building an Activity](/developers/docs/activities/building-an-activity) + + +We'll be building a Discord app that lets users play rock-paper-scissors (with 7 choices instead of 3). This guide is beginner-focused, but it assumes a basic understanding of [JavaScript](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics). + + +Here's what the finished app will look like: + +![Demo of example app](/images/getting-started-demo.gif) + +To make the user flow a bit more explicit: + +1. User A initiates a new game and picks their object using the app's `/challenge` slash command +2. A message is sent to channel with a button inviting others to accept the challenge +3. User B presses the **Accept** button +4. User B is sent an ephemeral message where they select their object of choice +5. The result of the game is posted back into the original channel for all to see + + + +- **[GitHub repository](https://github.com/discord/discord-example-app)** where the code from this guide lives along with some additional feature-specific code examples. +- **[discord-interactions](https://github.com/discord/discord-interactions-js)**, a library that provides types and helper functions for Discord apps. +- **[Express](https://expressjs.com)**, a popular JavaScript web framework we'll use to create a server where Discord can send us requests. +- **[ngrok](https://ngrok.com/)**, a tool that lets us tunnel our local server to a public URL where Discord can send requests. + + +--- + +## Step 0: Project Setup + +Before we get started, you'll need to set up your local environment and get the project code from the [sample app repository](https://github.com/discord/discord-example-app). + + +We'll be developing our app locally with a little help from [ngrok](https://ngrok.com/), but you can use your preferred development environment. + + +If you don't have [NodeJS](https://nodejs.org/en/download/) installed, install that first. + +After NodeJS is installed, open your command line and clone the project code: + +```bash +git clone https://github.com/discord/discord-example-app.git +``` + +Then navigate to the directory and install the project's dependencies: + +```bash +# navigate to directory +cd discord-example-app + +# install dependencies +npm install +``` + + + +``` +├── examples -> short, feature-specific sample apps +├──── app.js -> finished app.js code +├──── button.js +├──── command.js +├──── modal.js +├──── selectMenu.js +├── .env -> your credentials and IDs +├── app.js -> main entrypoint for app +├── commands.js -> slash command payloads + helpers +├── game.js -> logic specific to Rock, Paper, Scissors +├── utils.js -> utility functions and constants +├── package.json +├── README.md +└── .gitignore +``` + + +With that out of the way, open your new project in the code editor of your choice, and we'll move ahead to setting up your Discord app. + +## Step 1: Creating an app + +First, you'll need to create an app in the developer portal if you don't have one already: + +Create App + +Enter a name for your app, then press **Create**. + +After you create your app, you'll land on the **General Information** page of the app's settings where you can update basic information about your app like its description and icon. You'll also see an **Application ID** and **Interactions Endpoint URL**, which we'll use a bit later in the guide. + +### Fetching your credentials + +We'll need to set up and fetch a few sensitive values for your app, like its token and ID. + + +Your token is used to authorize API requests and carry your app's permissions, so they are *highly* sensitive. Make sure to never share your token or check it into any kind of version control. + + +Back in your project folder, rename the `.env.sample` file to `.env`. This is where we'll store all of your app's credentials. + +We'll need three values from your app's settings for your `.env` file: + +- On the **General Information** page, copy the value for **Application ID**. In `.env`, replace `` with the ID you copied. +- Back on the **General Information** page, copy the value for **Public Key**, which is used to ensure HTTP requests are coming from Discord. In `.env`, replace `` with the value you copied. +- On the **Bot** page under **Token**, click "Reset Token" to generate a new bot token. In `.env`, replace `` with your new token. + + +You won't be able to view your token again unless you regenerate it, so make sure to keep it somewhere safe (like in a password manager). + + +Now that you have the credentials you need, lets configure your bot user and installation settings. + +### Configuring your bot + +Newly-created apps have a bot user enabled by default. Bot users allow your app to appear and behave similarly to other server members when it's [installed to a server](/developers/docs/quick-start/overview-of-apps#where-are-apps-installed). + +On the left hand sidebar in your app's settings, there's a **Bot** page (where we fetched the token from). On this page, you can also configure settings like its [privileged intents](/developers/docs/events/gateway#privileged-intents) or whether it can be installed by other users. + + +Intents determine which events Discord will send your app when you're creating a [Gateway API connection](/developers/docs/events/gateway). For example, if you want your app to perform an action when users add a reaction to a message, you can pass the `GUILD_MESSAGE_REACTIONS` (`1 << 10`) intent. + +Some intents are [privileged](/developers/docs/events/gateway#privileged-intents), meaning they allow your app to access data that may be considered sensitive (like the contents of messages). Privileged intents can be toggled on the **Bot** page in your app's settings, but they must be approved before you [verify your app](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified). Standard, non-privileged intents don't require any additional permissions or configurations. + +More information about intents and a full list of available intents (along with their associated events) is in the [Gateway documentation](/developers/docs/events/gateway#gateway-intents). + + +For now, we don't need to configure anything additional here, but you may need to in the future depending on your app's use case. Let's go ahead and get our app ready for installation. + +### Choosing installation contexts + +Now we'll select where your app can be installed in Discord, which is determined by the [installation contexts](/developers/docs/resources/application#installation-context) that your app supports. + + +**Installation contexts** determine where your app can be installed: to servers, to users, or both. Apps can choose which installation contexts they support within the app's settings. + +- Apps installed in a **[server context](/developers/docs/resources/application#server-context)** (server-installed apps) must be authorized by a server member with the `MANAGE_GUILD` permission, and are visible to all members of the server. +- Apps installed in a **[user context](/developers/docs/resources/application#user-context)** (user-installed apps) are visible only to the authorizing user, and therefore don't require any server-specific permissions. Apps installed to a user context are visible across all of the user's servers, DMs, and GDMs—however, they're limited to using commands. + + +Click on **Installation** in the left sidebar, then under **Installation Contexts** make sure both "User Install" and "Guild Install" are selected. + + +Some apps may only want to support one installation context—for example, a moderation app may only support a server context. However, by default, we recommend supporting both installation contexts. For detailed information about supporting user-installed apps, you can read the [user-installable app tutorial](/developers/docs/tutorials/developing-a-user-installable-app). + + +### Setting up an install link + +[Install links](/developers/docs/resources/application#install-links) provide an easy way for users to install your app in Discord. We'll set up the default [Discord Provided Link](/developers/docs/resources/application#discord-provided-link), but you can read more about the different type of install links in the [Application documentation](/developers/docs/resources/application#types-of-install-links). + +On the **Installation** page, go to the **Install Link** section and select "Discord Provided Link" if it's not already selected. + +When Discorded Provided Link is selected, a new **Default Install Settings** section will appear, which we'll configure next. + +### Adding scopes and bot permissions + +Apps need approval from installing users to perform actions in Discord (like creating a slash command or fetching a list of server members). Let's add scopes and permissions before installing the app. + + +When creating an app, scopes and permissions determine what your app can do and access in Discord. + +- [OAuth2 Scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) determine what data access and actions your app can take, granted on behalf of an installing or authenticating user. +- [Permissions](/developers/docs/topics/permissions#permission-overwrites) are the granular permissions for your bot user, the same as other users in Discord have. They can be approved by the installing user or later updated within server settings or with [permission overwrites](/developers/docs/topics/permissions#permission-overwrites). Since apps installed to a user context can only respond to commands, these permissions are only relevant to apps installed to a server. + + +On the **Installation** page in the **Default Install Settings** section: +- For **User Install**, add the `applications.commands` scope +- For **Guild Install**, add the `applications.commands` scope and `bot` scope. When you select `bot`, a new **Permissions** menu will appear to select the bot user's permissions. Select any permissions that you may want for your app—for now, I'll just select `Send Messages`. + +![Default Install Settings](/images/getting-started-default-install.png) + +See a list of all [OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes), or read more on [permissions](/developers/docs/topics/permissions) in the documentation. + +### Installing your app + + +When developing apps, you should build and test on your user account (for user-installable apps) and in a server that isn't actively used by others (for server-installable apps). If you don't have your own server already, you can [create one for free](https://support.discord.com/hc/en-us/articles/204849977-How-do-I-create-a-server-). + + +Once you add scopes, copy the URL from the **Install Link** section from before. + +Since our app is supporting both installation contexts, we'll install your new app to both a test server and your user account so that we can test in both [installation contexts](/developers/docs/resources/application#installation-context). + +###### Install to server + +To install your app to your test server, copy the default Install Link for your app from the **Installation** page. Paste the link in your browser and hit enter, then select "Add to server" in the installation prompt. + +Select your test server, and follow the installation prompt. Once your app is added to your test server, you should see it appear in the member list. + +###### Install to user account + +Next, install your app to your user account. Paste the same Install Link in your browser and hit enter. This time, select "Add to my apps" in the installation prompt. + +Follow the installation prompt to install your app to your user account. Once it's installed you can open a DM with it. + +--- + +## Step 2: Running your app + +With your app configured and installed to your test server and account, let's take a look at the code. + + +To make development a bit simpler, the app uses [discord-interactions](https://github.com/discord/discord-interactions-js), which provides types and helper functions. If you prefer to use other languages or libraries, check out the [Community Resources](/developers/docs/developer-tools/community-resources) documentation. + + +### Installing slash commands + + +To install slash commands, the app is using [`node-fetch`](https://github.com/node-fetch/node-fetch). You can see the implementation for the installation in `utils.js` within the `DiscordRequest()` function. + + +The project contains a `register` script you can use to install the commands in `ALL_COMMANDS`, which is defined at the bottom of `commands.js`. It installs the commands as global commands by calling the HTTP API's [`PUT /applications//commands`](/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands) endpoint. + +If you want to customize your commands or add additional ones, you can reference the command structure in the [commands documentation](/developers/docs/interactions/application-commands#application-command-object-application-command-structure). + +In your terminal within the project folder, run the following command: + +``` +npm run register +``` + +If you navigate back to your server, you should see the slash commands appear. But if you try to run them, nothing will happen since your app isn't receiving or handling any requests from Discord. + + +Apps have access to [APIs](/developers/docs/quick-start/overview-of-apps#what-apis-can-apps-use) that you can mix-and-match to build apps: + +- **[HTTP API](/developers/docs/reference#http-api)** is a REST-like API for general operations like sending and updating data in Discord, or fetching data about a resource. +- **[Gateway API](/developers/docs/reference#gateway-websocket-api)** is a WebSocket-based API that is helpful for maintaining state or listening to events happening in a Discord server. We won't be using it in this guide, but more information about how to create a Gateway connection and the different events you can listen to are in the [Gateway documentation](/developers/docs/events/gateway). + + +--- + +## Step 3: Handling interactivity + +To enable your app to receive slash command and other interactions requests, Discord needs a public URL to send them. This URL can be configured in your app's settings as **Interaction Endpoint URL**. + +### Set up a public endpoint + +To set up a public endpoint, we'll start our app which runs an [Express](https://expressjs.com/) server, then use [ngrok](https://ngrok.com/) to expose our server publicly. + +First, go to your project's folder and run the following to start your app: + +``` +npm run start +``` + +There should be output indicating your app is running on port `3000`. Behind the scenes, our app is ready to handle interactions from Discord, which includes verifying security request headers and responding to `PING` requests. We're skipping over a lot of the details in this tutorial, but details about preparing apps for interactions is in the [Interactions Overview](/developers/docs/interactions/overview#preparing-for-interactions) documentation. + + +By default, the server will listen to requests sent to port 3000, but if you want to change the port, you can specify a `PORT` variable in your `.env` file. + + +Next, we'll start our ngrok tunnel. If you don't have ngrok installed locally, you can install it by following the instructions on the [ngrok download page](https://ngrok.com/download). + +After ngrok is installed, open a new terminal and create a public endpoint that will forward requests to your Express server: + +``` +ngrok http 3000 +``` + +You should see your connection open with output similar to the following: + +``` +Tunnel Status online +Version 2.0/2.0 +Web Interface http://127.0.0.1:4040 +Forwarding https://1234-someurl.ngrok.io -> localhost:3000 + +Connections ttl opn rt1 rt5 p50 p90 + 0 0 0.00 0.00 0.00 0.00 +``` + +We'll use **Forwarding** URL as the publicly-accessible URL where Discord will send interactions requests in the next step. + +### Adding an interaction endpoint URL + +Go to your [app's settings](https://discord.com/developers/applications) and on the **General Information** page under **Interaction Endpoint URL**, paste your new ngrok forwarding URL and append `/interactions`. + +![Interactions Endpoint URL](/images/getting-started-interactions-endpoint.png) + +Click **Save Changes** and ensure your endpoint is successfully verified. + + +If you have troubles verifying your endpoint, make sure both ngrok and your app are running on the same port, and that you've copied the ngrok URL correctly + + +The verification is handled automatically by the sample app in two ways: +- It uses the `PUBLIC_KEY` and [discord-interactions package](https://github.com/discord/discord-interactions-js#usage) with a wrapper function (imported from `utils.js`) that makes it conform to [Express's `verify` interface](http://expressjs.com/en/5x/api.html#express.json). This is run on every incoming request to your app. +- It responds to incoming `PING` requests. + +You can learn more about preparing your app to receive interactions in [the interactions documentation](/developers/docs/interactions/overview#preparing-for-interactions). + +### Handling slash command requests + +With the endpoint verified, navigate to your project's `app.js` file and find the code block that handles the `/test` command: + +```javascript +// "test" command +if (name === 'test') { + // Send a message into the channel where command was triggered from + return res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + flags: InteractionResponseFlags.IS_COMPONENTS_V2, + components: [ + { + type: MessageComponentTypes.TEXT_DISPLAY, + // Fetches a random emoji to send from a helper function + content: `hello world ${getRandomEmoji()}` + } + ] + }, + }); +} +``` + +The code above is responding to the interaction with a message in the channel, DM, or Group DM it originated from. You can see all available response types, like responding with a modal, [in the documentation](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type). + + +`InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE` is a constant [exported from `discord-interactions`](https://github.com/discord/discord-interactions-js/blob/main/src/index.ts#L33) + + +Go to your server and make sure your app's `/test` slash command works. When you trigger it, your app should send a message that contains “hello world” followed by a random emoji. + +In the following section, we'll add an additional command that uses slash command options, buttons, and select menus to build the rock-paper-scissors game. + +--- + +## Step 4: Adding message components + +The `/challenge` command will be how our rock-paper-scissors-style game is initiated. When the command is triggered, the app will send message components to the channel, which will guide the users to complete the game. + +### Adding a command with options + +The `/challenge` command, called `CHALLENGE_COMMAND` in `commands.js`, has an array of `options`. In our app, the options are objects representing different things that a user can select while playing rock-paper-scissors, generated using keys of `RPSChoices` in `game.js`. + +You can read more about command options and their structure [in the documentation](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure). + + +While this guide won't touch much on the `game.js` file, feel free to poke around and change commands or the options in the commands. + + + + +To handle the `/challenge` command, add the following code after the `if name === “test”` if block: + +```javascript +// "challenge" command +if (name === 'challenge' && id) { + // Interaction context + const context = req.body.context; + // User ID is in user field for (G)DMs, and member for servers + const userId = context === 0 ? req.body.member.user.id : req.body.user.id; + // User's object choice + const objectName = req.body.data.options[0].value; + + // Create active game using message ID as the game ID + activeGames[id] = { + id: userId, + objectName, + }; + + return res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + flags: InteractionResponseFlags.IS_COMPONENTS_V2, + components: [ + { + type: MessageComponentTypes.TEXT_DISPLAY, + // Fetches a random emoji to send from a helper function + content: `Rock papers scissors challenge from <@${userId}>`, + }, + { + type: MessageComponentTypes.ACTION_ROW, + components: [ + { + type: MessageComponentTypes.BUTTON, + // Append the game ID to use later on + custom_id: `accept_button_${req.body.id}`, + label: 'Accept', + style: ButtonStyleTypes.PRIMARY, + }, + ], + }, + ], + }, + }); +} +``` + + +If you aren't sure where to paste the code, you can see the full code in `examples/app.js`. + + +The above code is doing a few things: +1. Parses the request body to get the ID of the user who triggered the slash command (`userId`), and the option (object choice) they selected (`objectName`). If the interaction is run in a server (`context === 0`), the user ID will be nested in the `member` object. If it's in a DM or Group DM, it will be in the `user` object. +2. Adds a new game to the `activeGames` object using the interaction ID. The active game records the `userId` and `objectName`. +3. Sends a message back to the channel with a button with a `custom_id` of `accept_button_`. + + +The sample code uses an object as in-memory storage, but for production apps you should use a database. + + +When sending a message with [message components](/developers/docs/components/reference), the individual payloads are appended to a `components` array. Actionable components (like buttons) need to be inside of an [action row](/developers/docs/components/reference#action-row), which you can see in the code sample. + +Note the unique `custom_id` sent with message components, in this case `accept_button_` with the active game's ID appended to it. A `custom_id` can be used to handle requests that Discord sends you when someone interacts with the component, which you'll see in a moment. + +Now when you run the `/challenge` command and pick an option, your app will send a message with an **Accept** button. Let's add code to handle the button press. + + + + + +When users interact with a message component, Discord will send a request with an [interaction type](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type) of `3` (or the `MESSAGE_COMPONENT` value when using `discord-interactions`). + +To set up a handler for the button, we'll check the `type` of interaction, followed by matching the `custom_id`. + +Paste the following code under the type handler for `APPLICATION_COMMAND`s: + +```javascript +if (type === InteractionType.MESSAGE_COMPONENT) { + // custom_id set in payload when sending message component + const componentId = data.custom_id; + + if (componentId.startsWith('accept_button_')) { + // get the associated game ID + const gameId = componentId.replace('accept_button_', ''); + // Delete message with token in request body + const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`; + try { + await res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + // Indicates it'll be an ephemeral message + flags: InteractionResponseFlags.EPHEMERAL | InteractionResponseFlags.IS_COMPONENTS_V2, + components: [ + { + type: MessageComponentTypes.TEXT_DISPLAY, + content: 'What is your object of choice?', + }, + { + type: MessageComponentTypes.ACTION_ROW, + components: [ + { + type: MessageComponentTypes.STRING_SELECT, + // Append game ID + custom_id: `select_choice_${gameId}`, + options: getShuffledOptions(), + }, + ], + }, + ], + }, + }); + // Delete previous message + await DiscordRequest(endpoint, { method: 'DELETE' }); + } catch (err) { + console.error('Error sending message:', err); + } + } + return; +} +``` + +The above code: +1. Checks for a `custom_id` that matches what we originally sent (in this case, it starts with `accept_button_`). The custom ID also has the active game ID appended, so we store that in `gameID`. +2. [Deletes the original message](/developers/docs/interactions/receiving-and-responding#delete-original-interaction-response) calling a webhook using `node-fetch` and passing the unique interaction `token` in the request body. This is done to clean up the channel, and so other users can't click the button. +3. Responds to the request by sending a message that contains a select menu with the object choices for the game. The payload should look fairly similar to the previous one, with the exception of the `options` array and `flags: 64`, [which indicates that the message is ephemeral](/developers/docs/interactions/receiving-and-responding#create-followup-message). + +The `options` array is populated using the `getShuffledOptions()` method in `game.js`, which manipulates the `RPSChoices` values to conform to the shape of [string select options](/developers/docs/components/reference#string-select-select-option-structure). + + + + + +The last thing to add is code to handle select menu interactions, and to send the result of the game to channel. + +Since select menus are just another message component, the code to handle their interactions will be almost identical to buttons. + +Modify the code above to handle the select menu: + +```javascript +if (type === InteractionType.MESSAGE_COMPONENT) { + // custom_id set in payload when sending message component + const componentId = data.custom_id; + + if (componentId.startsWith('accept_button_')) { + // get the associated game ID + const gameId = componentId.replace('accept_button_', ''); + // Delete message with token in request body + const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`; + try { + await res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + // Indicates it'll be an ephemeral message + flags: InteractionResponseFlags.EPHEMERAL | InteractionResponseFlags.IS_COMPONENTS_V2, + components: [ + { + type: MessageComponentTypes.TEXT_DISPLAY, + content: 'What is your object of choice?', + }, + { + type: MessageComponentTypes.ACTION_ROW, + components: [ + { + type: MessageComponentTypes.STRING_SELECT, + // Append game ID + custom_id: `select_choice_${gameId}`, + options: getShuffledOptions(), + }, + ], + }, + ], + }, + }); + // Delete previous message + await DiscordRequest(endpoint, { method: 'DELETE' }); + } catch (err) { + console.error('Error sending message:', err); + } + } else if (componentId.startsWith('select_choice_')) { + // get the associated game ID + const gameId = componentId.replace('select_choice_', ''); + + if (activeGames[gameId]) { + // Interaction context + const context = req.body.context; + // Get user ID and object choice for responding user + // User ID is in user field for (G)DMs, and member for servers + const userId = context === 0 ? req.body.member.user.id : req.body.user.id; + const objectName = data.values[0]; + // Calculate result from helper function + const resultStr = getResult(activeGames[gameId], { + id: userId, + objectName, + }); + + // Remove game from storage + delete activeGames[gameId]; + // Update message with token in request body + const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`; + + try { + // Send results + await res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: { + flags: InteractionResponseFlags.IS_COMPONENTS_V2, + components: [ + { + type: MessageComponentTypes.TEXT_DISPLAY, + content: resultStr + } + ] + }, + }); + // Update ephemeral message + await DiscordRequest(endpoint, { + method: 'PATCH', + body: { + components: [ + { + type: MessageComponentTypes.TEXT_DISPLAY, + content: 'Nice choice ' + getRandomEmoji() + } + ], + }, + }); + } catch (err) { + console.error('Error sending message:', err); + } + } + } + + return; +} +``` + +Similar to earlier code, the code above is getting the user ID and their object selection from the interaction request. + +That information, along with the original user's ID and selection from the `activeGames` object, are passed to the `getResult()` function. `getResult()` determines the winner, then builds a readable string to send back to the channel. + +We're also calling another webhook, this time to [update the follow-up ephemeral message](/developers/docs/interactions/receiving-and-responding#edit-followup-message) since it can't be deleted. + +Finally, the results are sent in the channel using the `CHANNEL_MESSAGE_WITH_SOURCE` interaction response type. + + + +....and that's it 🎊 Go ahead and test your app and make sure everything works. + +--- + +## Next steps + +Congrats on building your first Discord app! 🤖 + +Hopefully you learned a bit about Discord apps, how to configure them, and how to make them interactive. From here, you can continue building out your app or explore what else is possible. + +import {AppsIcon} from '/snippets/icons/AppsIcon.jsx' +import {WrenchIcon} from '/snippets/icons/WrenchIcon.jsx' +import {UserIcon} from '/snippets/icons/UserIcon.jsx' +import {GlobeEarthIcon} from '/snippets/icons/GlobeEarthIcon.jsx' + + + }> + Explore the platform features and APIs you have access to when building an app on Discord + + }> + Explore 1st party and community-built libraries and tools to speed up and simplify your development + + }> + Tutorial on building and handling interactions for apps installed to a user + + }> + Join our community to ask questions about the API, attend events hosted by the Discord platform team, and interact with other devs + + diff --git a/discord/developers/docs/quick-start/overview-of-apps.mdx b/discord/developers/docs/quick-start/overview-of-apps.mdx new file mode 100644 index 0000000000..0e23536759 --- /dev/null +++ b/discord/developers/docs/quick-start/overview-of-apps.mdx @@ -0,0 +1,103 @@ +--- +title: Overview of Discord Apps +description: Build Discord apps to customize, extend, and enhance Discord for millions of users. +sidebarTitle: Overview of Apps +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + + + +On this page we'll answer the questions: + +* [What can apps do?](/developers/docs/quick-start/overview-of-apps#what-can-apps-do) +* [Where are apps installed?](/developers/docs/quick-start/overview-of-apps#where-are-apps-installed) +* [What APIs can apps use?](/developers/docs/quick-start/overview-of-apps#what-apis-can-apps-use) + + +## What can apps do? + +You will discover the full possibility of apps as you explore the documentation and start building, but for now let's take a glance at some features you can build and integrate as you're developing your app. + +### Send and manage messages + +Messages are a core part of Discord, and that's true for apps too. Apps can send messages in a few ways—they can call the [Create Message endpoint](/developers/docs/resources/message#create-message), create and execute [webhooks](/developers/docs/resources/webhook), or respond with a message when responding to an [interaction](/developers/docs/interactions/overview). Apps can also manage messages if they have the proper permissions, which is covered more in the [Message documentation](/developers/docs/resources/message). + +### Interact with users + +Apps can use [interactions](/developers/docs/interactions/overview) to create more engaging and intuitive experiences for users. When sending messages, apps can send interactive components like [buttons](/developers/docs/components/reference#button) and [select menus](/developers/docs/components/reference#string-select) in the `components` field. Apps can also open form-like modals or launch an Activity [in response to interactions](/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal). + +### Build embedded games and experiences + +Using the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk), apps can create [Activities](/developers/docs/activities/overview) which are cross-platform interactive games and social experiences in Discord. They run in iframes in Discord, where people who play games are already hanging out. + +### Customize servers + +With the right API endpoints and proper [permissions](/developers/docs/topics/permissions), apps can customize the experience of using and moderating servers by accessing and customizing all sorts of resources core to Discord—including [users](/developers/docs/resources/user), [channels](/developers/docs/resources/channel), and [AutoMod](/developers/docs/resources/auto-moderation) to name a few. Explore the **Resources** documentation category to learn about the different Discord resources and how apps can use them. + +### Update user metadata and presence + +Apps can update a Discord's user metadata with data from a party game or app in a few ways. Apps can also update a user's profile with actionable data from a game or app by integrating [Rich Presence](/developers/docs/rich-presence/overview). Apps can also use [role connection metadata](/developers/docs/resources/application-role-connection-metadata) to associate third-party metadata (like stats or account type) with Discord users, which server admins can use to set up roles based on. You can explore more in the [configuring metadata for linked roles](/developers/docs/tutorials/configuring-app-metadata-for-linked-roles) tutorial. + +### Add premium features + +[App subscriptions](/developers/docs/monetization/implementing-app-subscriptions) let apps charge users and/or servers for premium functionality on a recurring basis natively within Discord. You can read more about eligibility and adding monetization features to your app in the [Monetization](/developers/docs/monetization/overview) documentation. + +### ...and more + +This developer documentation is full of nooks and crannies with all sorts of features to explore. Discover the possibilities by exploring more of the docs, or by [building your own app](https://discord.com/developers/applications). + + + +## Where are apps installed? + +Discord apps can be installed in two different contexts: + +1. Apps installed **to a server** (called a [guild](/developers/docs/resources/guild) throughout the API) by a user with the Manage Server ([`MANAGE_GUILD`](/developers/docs/topics/permissions#permissions-bitwise-permission-flags)) permission. Apps installed to a server can only be used within that server and DMs with the app's bot user, and are visible to all server members. +2. Apps installed **to a user account**. Apps installed to a user are visible only to that user, across all of their servers, DMs, and Group DMs by default. + +The installation contexts that an app supports can be limited by the developer when [setting up the app](/developers/docs/resources/application#setting-supported-installation-contexts). Details about installation contexts are in the [Application resource documentation](/developers/docs/resources/application#installation-context). + + +## What APIs can apps use? + +There are a handful of different APIs that you can pick and choose from based on your app's functionality and which Discord features you want to access. Below is a quick overview of the main APIs on the Discord developer platform, but you can read more details and information about API usage in the [API reference](/developers/docs/reference). + +### HTTP API + +The **HTTP API** is a REST API that lets you interact and modify core Discord resources like [channels](/developers/docs/resources/channel), [servers (or guilds)](/developers/docs/resources/guild), [users](/developers/docs/resources/user), and [messages](/developers/docs/resources/message#message-object). + +Use the HTTP API to: +- Retrieve information about a resource +- Create, update, or delete a resource + +Read details about using the HTTP API in the [API reference](/developers/docs/reference#http-api). + +### Gateway API + +The **Gateway API** lets you receive event data over a WebSocket anytime an [event](/developers/docs/events/gateway-events) occurs in a server where your app is installed. + +Use the Gateway API to: +- Receive events happening in Discord + +Read details about using the Gateway API in the [API reference](/developers/docs/reference#gateway-websocket-api). + +## Start Building + +Well, would you look at the time? With the basics out of the way, it's time to start building your Discord app! You can explore the reset of the documentation, go to your [Apps](https://discord.com/developers/applications), or explore the beginner resources below. + +import {AppsIcon} from '/snippets/icons/AppsIcon.jsx' +import {ActivitiesIcon} from '/snippets/icons/ActivitiesIcon.jsx' +import {WrenchIcon} from '/snippets/icons/WrenchIcon.jsx' + + + } iconType="solid" horizontal > + Tutorial to develop your first Discord app with interactive components + + } iconType="solid" horizontal > + Tutorial to develop an Activity using the Embedded App SDK + + } iconType="solid" horizontal > + Explore community-built library and tools to speed up and simplify development + + diff --git a/discord/developers/docs/reference.mdx b/discord/developers/docs/reference.mdx new file mode 100644 index 0000000000..7c6741abe6 --- /dev/null +++ b/discord/developers/docs/reference.mdx @@ -0,0 +1,550 @@ +--- +title: API Reference +description: "The Discord API is a REST API that allows you to interact with Discord data from your own applications. It's the primary way to interact with Discord from your own code." +sidebarTitle: API Reference +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Snowflake} from '/snippets/snowflake.jsx' + +**Base URL** + +```bash +https://discord.com/api +``` + +## API Versioning + + + Some API and Gateway versions are now non-functioning, and are labeled as discontinued in the table below for posterity. Trying to use these versions will fail and return 400 Bad Request. + + +Discord exposes different versions of our API[.](https://c.tenor.com/BuZl66EegkgAAAAC/westworld-dolores.gif) You should specify which version to use by including it in the request path like `https://discord.com/api/v{version_number}`. Omitting the version number from the route will route requests to the current default version (marked below). You can find the change log for the newest API version [here](/developers/docs/change-log). + + +**API Versions** + +| Version | Status | Default | +|---------|--------------|----------------------------------------| +| 10 | Available | | +| 9 | Available | | +| 8 | Deprecated | | +| 7 | Deprecated | | +| 6 | Deprecated | | +| 5 | Discontinued | | +| 4 | Discontinued | | +| 3 | Discontinued | | + +## Error Messages + +Starting in API v8, we've improved error formatting in form error responses. The response will tell you which JSON key contains the error, the error code, and a human readable error message. We will be frequently adding new error messages, so a complete list of errors is not feasible and would be almost instantly out of date. Here are some examples instead: + +**Array Error** + +```json +{ + "code": 50035, + "errors": { + "activities": { + "0": { + "platform": { + "_errors": [ + { + "code": "BASE_TYPE_CHOICES", + "message": "Value must be one of ('desktop', 'android', 'ios')." + } + ] + }, + "type": { + "_errors": [ + { + "code": "BASE_TYPE_CHOICES", + "message": "Value must be one of (0, 1, 2, 3, 4, 5)." + } + ] + } + } + } + }, + "message": "Invalid Form Body" +} +``` + +**Object Error** + +```json +{ + "code": 50035, + "errors": { + "access_token": { + "_errors": [ + { + "code": "BASE_TYPE_REQUIRED", + "message": "This field is required" + } + ] + } + }, + "message": "Invalid Form Body" +} +``` + +**Request Error** + +```json +{ + "code": 50035, + "message": "Invalid Form Body", + "errors": { + "_errors": [ + { + "code": "APPLICATION_COMMAND_TOO_LARGE", + "message": "Command exceeds maximum size (8000)" + } + ] + } +} +``` + +## Authentication + +Authenticating with the Discord API can be done in one of two ways: + +1. Using a bot token found on the Bot page within your app's settings. For more information on bots see [bots vs user accounts](/developers/docs/topics/oauth2#bot-vs-user-accounts). +2. Using an OAuth2 bearer token gained through the [OAuth2 API](/developers/docs/topics/oauth2). + +For all authentication types, authentication is performed with the `Authorization` HTTP header in the format `Authorization: TOKEN_TYPE TOKEN`. + +**Example Bot Token Authorization Header** + +```bash +Authorization: Bot MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs +``` + +**Example Bearer Token Authorization Header** + +```bash +Authorization: Bearer CZhtkLDpNYXgPH9Ml6shqh2OwykChw +``` + +## Encryption + +All HTTP-layer services and protocols (e.g. HTTP, WebSocket) within the Discord API are using TLS 1.2. + +## Snowflakes + +Discord utilizes Twitter's [snowflake](https://github.com/twitter-archive/snowflake/tree/snowflake-2010) format for uniquely identifiable descriptors (IDs). These IDs are guaranteed to be unique across all of Discord, except in some unique scenarios in which child objects share their parent's ID. Because Snowflake IDs are up to 64 bits in size (e.g. a uint64), they are always returned as strings in the HTTP API to prevent integer overflows in some languages. See [Gateway ETF/JSON](/developers/docs/events/gateway#encoding-and-compression) for more information regarding Gateway encoding. + +**Snowflake ID Broken Down in Binary** + +``` +111111111111111111111111111111111111111111 11111 11111 111111111111 +64 22 17 12 0 +``` + +**Snowflake ID Format Structure (Left to Right)** + +| Field | Bits | Number of bits | Description | Retrieval | +|---------------------|----------|----------------|------------------------------------------------------------------------------|-------------------------------------| +| Timestamp | 63 to 22 | 42 bits | Milliseconds since Discord Epoch, the first second of 2015 or 1420070400000. | `(snowflake >> 22) + 1420070400000` | +| Internal worker ID | 21 to 17 | 5 bits | | `(snowflake & 0x3E0000) >> 17` | +| Internal process ID | 16 to 12 | 5 bits | | `(snowflake & 0x1F000) >> 12` | +| Increment | 11 to 0 | 12 bits | For every ID that is generated on that process, this number is incremented | `snowflake & 0xFFF` | + + +### Convert Snowflake to DateTime + + + + +### Snowflake IDs in Pagination + +We typically use snowflake IDs in many of our API routes for pagination. The standardized pagination paradigm we utilize is one in which you can specify IDs `before` and `after` in combination with `limit` to retrieve a desired page of results. You will want to refer to the specific endpoint documentation for details. + +It is useful to note that snowflake IDs are just numbers with a timestamp, so when dealing with pagination where you want results from the beginning of time (in Discord Epoch, but `0` works here too) or before/after a specific time you can generate a snowflake ID for that time. + +**Generating a snowflake ID from a Timestamp Example** + +```bash +(timestamp_ms - DISCORD_EPOCH) << 22 +``` + +## ID Serialization + +There are some cases in which our API and Gateway may return IDs in an unexpected format. Internally, Discord stores IDs as integer snowflakes. When we serialize IDs to JSON, we transform `bigints` into strings. Given that all Discord IDs are snowflakes, you should always expect a string. + +However, there are cases in which passing something to our API will instead return IDs serialized as an integer; this is the case when you send our API or Gateway a value in an `id` field that is not `bigint` size. For example, when requesting `GUILD_MEMBERS_CHUNK` from our gateway: + +```bash +// Send +{ + op: 8, + d: { + guild_id: '308994132968210433', + user_ids: [ '123123' ] + } +} + +// Receive +{ + t: 'GUILD_MEMBERS_CHUNK', + s: 3, + op: 0, + d: { + not_found: [ 123123 ], + members: [], + guild_id: '308994132968210433' + } +} +``` + +You can see in this case that the sent `user_id` is not a `bigint`; therefore, when it is serialized back to JSON by Discord, it is not transformed into a string. This will never happen with IDs that come from Discord. But, this can happen if you send malformed data in your requests. + +## ISO8601 Date/Time + +Discord utilizes the [ISO8601 format](https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf) for most Date/Times returned in our models. This format is referred to as type `ISO8601` within tables in this documentation. + +## Nullable and Optional Resource Fields + +Resource fields that may contain a `null` value have types that are prefixed with a question mark. Resource fields that are optional have names that are suffixed with a question mark. + +**Example Nullable and Optional Fields** + +| Field | Type | +|---------------------------------|---------| +| optional\_field? | string | +| nullable\_field | ?string | +| optional\_and\_nullable\_field? | ?string | + +## Consistency + +Discord operates at a scale where true consistency is impossible. Because of this, lots of operations in our API and in-between our services are [eventually consistent](https://en.wikipedia.org/wiki/Eventual_consistency). Due to this, client actions can never be serialized and may be executed in any order (if executed at all). Along with these constraints, events in Discord may: + +- Never be sent to a client +- Be sent exactly one time to the client +- Be sent up to N times per client + +Clients should operate on events and results from the API in as much of an idempotent behavior as possible. + +## HTTP API + +### User Agent + +Clients using the HTTP API must provide a valid [User Agent](https://www.rfc-editor.org/rfc/rfc9110.html#section-10.1.5) which specifies information about the client library and version in the following format: + +**User Agent Example** + +```bash +User-Agent: DiscordBot ($url, $versionNumber) +``` + +Clients may append more information and metadata to the end of this string as they wish. + + + Client requests that do not have a valid User Agent specified may be blocked and return a [Cloudflare error](https://support.cloudflare.com/hc/en-us/articles/360029779472-Troubleshooting-Cloudflare-1XXX-errors). + + +### Content Type + +Clients using the HTTP API must provide a valid `Content-Type` header, either `application/json`, `application/x-www-form-urlencoded`, or `multipart/form-data`, except where specified. Failing to do so will result in a `50035` "Invalid form body" error. + +### Rate Limiting + +The HTTP API implements a process for limiting and preventing excessive requests in accordance with [RFC 6585](https://tools.ietf.org/html/rfc6585#section-4). API users that regularly hit and ignore rate limits will have their API keys revoked, and be blocked from the platform. For more information on rate limiting of requests, please see the [Rate Limits](/developers/docs/topics/rate-limits) section. + +### Boolean Query Strings + +Certain endpoints in the API are documented to accept booleans for their query string parameters. While there is no standard system for boolean representation in query string parameters, Discord represents such cases using `True`, `true`, or `1` for true and `False`, `false` or `0` for false. + +## Gateway (WebSocket) API + +Discord's Gateway API is used for maintaining persistent, stateful websocket connections between your client and our servers. These connections are used for sending and receiving real-time events your client can use to track and update local state. The Gateway API uses secure websocket connections as specified in [RFC 6455](https://tools.ietf.org/html/rfc6455). For information on opening Gateway connections, please see the [Gateway API](/developers/docs/events/gateway#connections) section. + +## Message Formatting + +Discord utilizes a subset of markdown for rendering message content on its clients, while also adding some custom functionality to enable things like mentioning users and channels. This functionality uses the following formats: + + +**Formats** + +| Type | Structure | Example | +|-------------------------|-----------------------|---------------------------------| +| User | `<@USER_ID>` | `<@80351110224678912>` | +| User \* | `<@!USER_ID>` | `<@!80351110224678912>` | +| Channel | `<#CHANNEL_ID>` | `<#103735883630395392>` | +| Role | `<@&ROLE_ID>` | `<@&165511591545143296>` | +| Slash Command \*\* | `` | `` | +| Standard Emoji | Unicode Characters | 🦶 | +| Custom Emoji | `<:NAME:ID>` | `<:mmLol:216154654256398347>` | +| Custom Emoji (Animated) | `` | `` | +| Unix Timestamp | `` | `` | +| Unix Timestamp (Styled) | `` | `` | +| Guild Navigation | `` | `` | + +Using the markdown for either users, roles, or channels will usually mention the target(s) accordingly, but this can be suppressed using the `allowed_mentions` parameter when creating a message. Standard emoji are currently rendered using [Twemoji](https://github.com/jdecked/twemoji) for Desktop/Android and Apple's native emoji on iOS. + +Timestamps are expressed in seconds and display the given timestamp in the user's timezone and locale. + +\* User mentions with an exclamation mark are deprecated and should be handled like any other user mention. + +\*\* Subcommands and subcommand groups can also be mentioned by using respectively `` and ``. + +**Timestamp Styles** + +| Style | Example Output | Description | +|-------|----------------------------------|-------------------------| +| t | 16:20 | Short Time | +| T | 16:20:30 | Medium Time | +| d | 20/04/2021 | Short Date | +| D | April 20, 2021 | Long Date | +| f \* | April 20, 2021 at 16:20 | Long Date, Short Time | +| F | Tuesday, April 20, 2021 at 16:20 | Full Date, Short Time | +| s | 20/04/2021, 16:20 | Short Date, Short Time | +| S | 20/04/2021, 16:20:30 | Short Date, Medium Time | +| R | 2 months ago | Relative Time | + +\*default + +**Guild Navigation Types** + +Guild navigation types link to the corresponding resource in the current server. + +| Type | Description | +|--------------|----------------------------------------------------------------------------------------------------------------| +| customize | Customize tab with the server's [onboarding prompts](/developers/docs/resources/guild#guild-onboarding-object) | +| browse | Browse Channels tab | +| guide | [Server Guide](https://support.discord.com/hc/en-us/articles/13497665141655) | +| linked-roles | [Linked Roles](https://support.discord.com/hc/en-us/articles/10388356626711) | +| linked-roles | Linked Role connection | + +## Image Formatting + +**Image Base Url** + +```bash +https://cdn.discordapp.com/ +``` + +Discord uses ids and hashes to render images in the client. These hashes can be retrieved through various API requests, like [Get User](/developers/docs/resources/user#get-user). Below are the formats, size limitations, and CDN endpoints for images in Discord. The returned format can be changed by changing the [extension name](/developers/docs/reference#image-formatting-image-formats) at the end of the URL. The returned size can be changed by appending a querystring of `?size=desired_size` to the URL. Image size can be any power of two between 16 and 4096. + + + **Image Formats** + +| Name | Extension | +|--------|-------------| +| JPEG | .jpg, .jpeg | +| PNG | .png | +| WebP | .webp | +| GIF | .gif | +| Lottie | .json | + + +**CDN Endpoints** + +| Type | Path | Supports | +|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| +| Custom Emoji | emojis/[emoji\_id](/developers/docs/resources/emoji#emoji-object).png | PNG, JPEG, WebP, GIF | +| Guild Icon | icons/[guild\_id](/developers/docs/resources/guild#guild-object)/[guild\_icon](/developers/docs/resources/guild#guild-object).png \* | PNG, JPEG, WebP, GIF | +| Guild Splash | splashes/[guild\_id](/developers/docs/resources/guild#guild-object)/[guild\_splash](/developers/docs/resources/guild#guild-object).png | PNG, JPEG, WebP | +| Guild Discovery Splash | discovery-splashes/[guild\_id](/developers/docs/resources/guild#guild-object)/[guild\_discovery\_splash](/developers/docs/resources/guild#guild-object).png | PNG, JPEG, WebP | +| Guild Banner | banners/[guild\_id](/developers/docs/resources/guild#guild-object)/[guild\_banner](/developers/docs/resources/guild#guild-object).png \* | PNG, JPEG, WebP, GIF | +| User Banner | banners/[user\_id](/developers/docs/resources/user#user-object)/[user\_banner](/developers/docs/resources/user#user-object).png \* | PNG, JPEG, WebP, GIF | +| Default User Avatar | embed/avatars/[index](/developers/docs/resources/user#user-object).png \*\* \*\*\* | PNG | +| User Avatar | avatars/[user\_id](/developers/docs/resources/user#user-object)/[user\_avatar](/developers/docs/resources/user#user-object).png \* | PNG, JPEG, WebP, GIF | +| Guild Member Avatar | guilds/[guild\_id](/developers/docs/resources/guild#guild-object)/users/[user\_id](/developers/docs/resources/user#user-object)/avatars/[member\_avatar](/developers/docs/resources/guild#guild-member-object).png \* | PNG, JPEG, WebP, GIF | +| Avatar Decoration | avatar-decoration-presets/[avatar\_decoration\_data\_asset](/developers/docs/resources/user#avatar-decoration-data-object).png | PNG | +| Application Icon | app-icons/[application\_id](/developers/docs/resources/application#application-object)/[icon](/developers/docs/resources/application#application-object).png | PNG, JPEG, WebP | +| Application Cover | app-icons/[application\_id](/developers/docs/resources/application#application-object)/[cover\_image](/developers/docs/resources/application#application-object).png | PNG, JPEG, WebP | +| Application Asset | app-assets/[application\_id](/developers/docs/resources/application#application-object)/[asset\_id](/developers/docs/events/gateway-events#activity-object-activity-assets).png | PNG, JPEG, WebP | +| Achievement Icon | app-assets/[application\_id](/developers/docs/resources/application#application-object)/achievements/[achievement\_id](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Achievements.md#user-achievement-struct)/icons/[icon\_hash](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Achievements.md#user-achievement-struct).png | PNG, JPEG, WebP | +| Store Page Asset | app-assets/[application\_id](/developers/docs/resources/application#application-object)/store/asset\_id | PNG, JPEG, WebP | +| Sticker Pack Banner | app-assets/710982414301790216/store/[sticker\_pack\_banner\_asset\_id](/developers/docs/resources/sticker#sticker-pack-object).png | PNG, JPEG, WebP | +| Team Icon | team-icons/[team\_id](/developers/docs/topics/teams#data-models-team-object)/[team\_icon](/developers/docs/topics/teams#data-models-team-object).png | PNG, JPEG, WebP | +| Sticker | stickers/[sticker\_id](/developers/docs/resources/sticker#sticker-object).png \*\*\* \*\*\*\* | PNG, Lottie, GIF | +| Role Icon | role-icons/[role\_id](/developers/docs/topics/permissions#role-object)/[role\_icon](/developers/docs/topics/permissions#role-object).png | PNG, JPEG, WebP | +| Guild Scheduled Event Cover | guild-events/[scheduled\_event\_id](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object)/[scheduled\_event\_cover\_image](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object).png | PNG, JPEG, WebP | +| Guild Member Banner | guilds/[guild\_id](/developers/docs/resources/guild#guild-object)/users/[user\_id](/developers/docs/resources/user#user-object)/banners/[member\_banner](/developers/docs/resources/guild#guild-member-object).png \* | PNG, JPEG, WebP, GIF | + +\* In the case of endpoints that support GIFs, the hash will begin with `a_` if it is available in GIF format. (example: `a_1269e74af4df7417b13759eae50c83dc`) + +\*\* In the case of the Default User Avatar endpoint, the value for `index` depends on whether the user is [migrated to the new username system](/developers/docs/change-log#unique-usernames-on-discord). For users on the new username system, `index` will be `(user_id >> 22) % 6`. For users on the legacy username system, `index` will be `discriminator % 5`. + +\*\*\* In the case of the Default User Avatar and Sticker endpoints, the size of images returned is constant with the "size" querystring parameter being ignored. + +\*\*\*\* In the case of the Sticker endpoint, the sticker will be available as PNG if its [`format_type`](/developers/docs/resources/sticker#sticker-object) is `PNG` or `APNG`, GIF if its `format_type` is `GIF`, and as [Lottie](https://airbnb.io/lottie/#/) if its `format_type` is `LOTTIE`. + +\*\*\*\*\* For Custom Emoji, we highly recommend requesting emojis as WebP for maximum performance and compatibility. Emojis can be uploaded as JPEG, PNG, GIF, WebP, and AVIF formats. WebP and AVIF formats must be requested as WebP since they don't convert well to other formats. The Discord client uses WebP for all emojis displayed in-app. See the [Emoji Resource](/developers/docs/resources/emoji) page for more details. + + +Sticker GIFs do not use the CDN base url, and can be accessed at `https://media.discordapp.net/stickers/.gif`. + + +## Image Data + +Image data is a [Data URI scheme](https://en.wikipedia.org/wiki/Data_URI_scheme) that supports JPG, GIF, and PNG formats. An example Data URI format is: + +``` +data:image/jpeg;base64,BASE64_ENCODED_JPEG_IMAGE_DATA +``` + +Ensure you use the proper content type (`image/jpeg`, `image/png`, `image/gif`) that matches the image data being provided. + +### **Signed Attachment CDN URLs** + +Attachments uploaded to Discord's CDN (like user and bot-uploaded images) have signed URLs with a preset expiry time. Discord automatically refreshes attachment CDN URLs that appear within the client, so when your app receives a payload with a signed URL (like when you [fetch a message](/developers/docs/resources/message#get-channel-message)), it will be valid. + +When passing CDN URLs into API fields, like [`url` in an embed image object](/developers/docs/resources/message#embed-object-embed-image-structure) and [`avatar_url` for webhooks](/developers/docs/resources/webhook#execute-webhook-jsonform-params), your app can pass the CDN URL without any parameters as the value and Discord will automatically render and refresh the URL. + +The [standard CDN endpoints](/developers/docs/reference#image-formatting-cdn-endpoints) listed above are not signed, so they will not expire. + +###### Example Attachment CDN URL + +``` +https://cdn.discordapp.com/attachments/1012345678900020080/1234567891233211234/my_image.png?ex=65d903de&is=65c68ede&hm=2481f30dd67f503f54d020ae3b5533b9987fae4e55f2b4e3926e08a3fa3ee24f& +``` + +###### Attachment CDN URL Parameters + +| Parameter | Description | +|-----------|-----------------------------------------------------------------| +| ex | Hex timestamp indicating when an attachment CDN URL will expire | +| is | Hex timestamp indicating when the URL was issued | +| hm | Unique signature that remains valid until the URL's expiration | + +## Uploading Files + + +The file upload size limit applies to each file in a request. The default limit is `10 MiB` for all users, but may be higher for users depending on their [Nitro](https://support.discord.com/hc/en-us/articles/115000435108-What-are-Nitro-Nitro-Basic) status or by the server's [Boost Tier](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-FAQ-#h_419c3bd5-addd-4989-b7cf-c7957ef92583). The `attachment_size_limit` value provided [when working with interactions](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-structure) is calculated as the maximum of these values. + + +Some endpoints support file attachments, indicated by the `files[n]` parameter. To add file(s), the standard `application/json` body must be replaced by a `multipart/form-data` body. The JSON message body can optionally be provided using the `payload_json` parameter. + +All `files[n]` parameters must include a valid `Content-Disposition` subpart header with a `filename` and unique `name` parameter. Each file parameter must be uniquely named in the format `files[n]` such as `files[0]`, `files[1]`, or `files[42]`. The suffixed index `n` is the snowflake placeholder that can be used in the `attachments` field, which can be passed to the `payload_json` parameter (or [Callback Data Payloads](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-data-structure)). + +Images can also be referenced in embeds using the `attachment://filename` URL. The `filename` for these URLs must be ASCII alphanumeric with underscores, dashes, or dots. An example payload is provided below. + +### Editing Message Attachments + +The `attachments` JSON parameter includes all files that will be appended to the message, including new files and their respective snowflake placeholders (referenced above). When making a `PATCH` request, only files listed in the `attachments` parameter will be appended to the message. Any previously-added files that aren't included will be removed. + +**Example Request Bodies (multipart/form-data)** + +Note that these examples are small sections of an HTTP request to demonstrate behavior of this endpoint - client libraries will set their own form boundaries (`boundary` is just an example). For more information, refer to the [multipart/form-data spec](https://tools.ietf.org/html/rfc7578#section-4). + +This example demonstrates usage of the endpoint without `payload_json`. + +```bash +--boundary +Content-Disposition: form-data; name="content" + +Hello, World! +--boundary +Content-Disposition: form-data; name="tts" + +true +--boundary-- +``` + +This example demonstrates usage of the endpoint with `payload_json` and all content fields (`content`, `embeds`, `files[n]`) set. + +```bash +--boundary +Content-Disposition: form-data; name="payload_json" +Content-Type: application/json + +{ + "content": "Hello, World!", + "embeds": [{ + "title": "Hello, Embed!", + "description": "This is an embedded message.", + "thumbnail": { + "url": "attachment://myfilename.png" + }, + "image": { + "url": "attachment://mygif.gif" + } + }], + "message_reference": { + "message_id": "233648473390448641" + }, + "attachments": [{ + "id": 0, + "description": "Image of a cute little cat", + "filename": "myfilename.png" + }, { + "id": 1, + "description": "Rickroll gif", + "filename": "mygif.gif" + }] +} +--boundary +Content-Disposition: form-data; name="files[0]"; filename="myfilename.png" +Content-Type: image/png + +[image bytes] +--boundary +Content-Disposition: form-data; name="files[1]"; filename="mygif.gif" +Content-Type: image/gif + +[image bytes] +--boundary-- +``` + +**Using Attachments within Embeds** + +You can upload attachments when creating a message and use those attachments within your embed. To do this, you will want to upload files as part of your `multipart/form-data` body. Make sure that you're uploading files which contain a filename, as you will need to reference it in your payload. + + + Only `.jpg`, `.jpeg`, `.png`, `.webp`, and `.gif` may be used at this time. Other file types are not supported. + + +Within an embed object, you can set an image to use an attachment as its URL with the attachment scheme syntax: `attachment://filename.png` + +For example: + +```bash +{ + "embeds": [{ + "image": { + "url": "attachment://screenshot.png" + } + }] +} +``` + +## Locales + +| Locale | Language Name | Native Name | +|--------|-----------------------|---------------------| +| id | Indonesian | Bahasa Indonesia | +| da | Danish | Dansk | +| de | German | Deutsch | +| en-GB | English, UK | English, UK | +| en-US | English, US | English, US | +| es-ES | Spanish | Español | +| es-419 | Spanish, LATAM | Español, LATAM | +| fr | French | Français | +| hr | Croatian | Hrvatski | +| it | Italian | Italiano | +| lt | Lithuanian | Lietuviškai | +| hu | Hungarian | Magyar | +| nl | Dutch | Nederlands | +| no | Norwegian | Norsk | +| pl | Polish | Polski | +| pt-BR | Portuguese, Brazilian | Português do Brasil | +| ro | Romanian, Romania | Română | +| fi | Finnish | Suomi | +| sv-SE | Swedish | Svenska | +| vi | Vietnamese | Tiếng Việt | +| tr | Turkish | Türkçe | +| cs | Czech | Čeština | +| el | Greek | Ελληνικά | +| bg | Bulgarian | български | +| ru | Russian | Pусский | +| uk | Ukrainian | Українська | +| hi | Hindi | हिन्दी | +| th | Thai | ไทย | +| zh-CN | Chinese, China | 中文 | +| ja | Japanese | 日本語 | +| zh-TW | Chinese, Taiwan | 繁體中文 | +| ko | Korean | 한국어 | diff --git a/discord/developers/docs/resources/application-role-connection-metadata.mdx b/discord/developers/docs/resources/application-role-connection-metadata.mdx new file mode 100644 index 0000000000..4b38be6151 --- /dev/null +++ b/discord/developers/docs/resources/application-role-connection-metadata.mdx @@ -0,0 +1,62 @@ +--- +title: Application Role Connection Metadata +description: Complete reference for application role connection metadata objects. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +import {Route} from '/snippets/route.jsx' + +A representation of role connection metadata for an [application](/developers/docs/resources/application). + +When a guild has added a bot and that bot has configured its [`role_connections_verification_url`](/developers/docs/resources/application#application-object) (in the developer portal), the application will render as a potential verification method in the guild's role verification configuration. + +If an application has configured role connection metadata, its metadata will appear in the role verification configuration when the application has been added as a verification method to the role. + +When a user connects their account using the bot's [`role_connections_verification_url`](/developers/docs/resources/application#application-object), the bot will [update a user's role connection with metadata](/developers/docs/resources/user#update-current-user-application-role-connection) using the OAuth2 `role_connections.write` scope. + +### Application Role Connection Metadata Object + + +###### Application Role Connection Metadata Structure + +| Field | Type | Description | +|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------| +| type | [ApplicationRoleConnectionMetadataType](/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object-application-role-connection-metadata-type) | type of metadata value | +| key | string | dictionary key for the metadata field (must be `a-z`, `0-9`, or `_` characters; 1-50 characters) | +| name | string | name of the metadata field (1-100 characters) | +| name_localizations? | dictionary with keys in [available locales](/developers/docs/reference#locales) | translations of the name | +| description | string | description of the metadata field (1-200 characters) | +| description_localizations? | dictionary with keys in [available locales](/developers/docs/reference#locales) | translations of the description | + + +###### Application Role Connection Metadata Type + +| Type | Value | Description | +|--------------------------------|-------|----------------------------------------------------------------------------------------------------------------------------------------| +| INTEGER_LESS_THAN_OR_EQUAL | 1 | the metadata value (`integer`) is less than or equal to the guild's configured value (`integer`) | +| INTEGER_GREATER_THAN_OR_EQUAL | 2 | the metadata value (`integer`) is greater than or equal to the guild's configured value (`integer`) | +| INTEGER_EQUAL | 3 | the metadata value (`integer`) is equal to the guild's configured value (`integer`) | +| INTEGER_NOT_EQUAL | 4 | the metadata value (`integer`) is not equal to the guild's configured value (`integer`) | +| DATETIME_LESS_THAN_OR_EQUAL | 5 | the metadata value (`ISO8601 string`) is less than or equal to the guild's configured value (`integer`; `days before current date`) | +| DATETIME_GREATER_THAN_OR_EQUAL | 6 | the metadata value (`ISO8601 string`) is greater than or equal to the guild's configured value (`integer`; `days before current date`) | +| BOOLEAN_EQUAL | 7 | the metadata value (`integer`) is equal to the guild's configured value (`integer`; `1`) | +| BOOLEAN_NOT_EQUAL | 8 | the metadata value (`integer`) is not equal to the guild's configured value (`integer`; `1`) | + + +Each metadata type offers a comparison operation that allows guilds to configure role requirements based on metadata values stored by the bot. Bots specify a `metadata value` for each user and guilds specify the required `guild's configured value` within the guild role settings. + + +## Get Application Role Connection Metadata Records +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/role-connections/metadata + +Returns a list of [application role connection metadata](/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object) objects for the given application. + +## Update Application Role Connection Metadata Records +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/role-connections/metadata + +Updates and returns a list of [application role connection metadata](/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object) objects for the given application. + + +An application can have a maximum of 5 metadata records. + diff --git a/discord/developers/docs/resources/application.mdx b/discord/developers/docs/resources/application.mdx new file mode 100644 index 0000000000..84e7d25803 --- /dev/null +++ b/discord/developers/docs/resources/application.mdx @@ -0,0 +1,328 @@ +--- +title: Application Resource +sidebarTitle: Application +description: Reference for Discord application objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +[Applications](/developers/docs/quick-start/overview-of-apps) (or "apps") are containers for developer platform features, and can be installed to Discord servers and/or user accounts. + +### Application Object + + +###### Application Structure + +| Field | Type | Description | +|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the app | +| name | string | Name of the app | +| icon | ?string | [Icon hash](/developers/docs/reference#image-formatting) of the app | +| description | string | Description of the app | +| rpc_origins? | array of strings | List of RPC origin URLs, if RPC is enabled | +| bot_public | boolean | When `false`, only the app owner can add the app to guilds | +| bot_require_code_grant | boolean | When `true`, the app's bot will only join upon completion of the full OAuth2 code grant flow | +| bot? | partial [user](/developers/docs/resources/user#user-object) object | Partial user object for the bot user associated with the app | +| terms_of_service_url? | string | URL of the app's Terms of Service | +| privacy_policy_url? | string | URL of the app's Privacy Policy | +| owner? | partial [user](/developers/docs/resources/user#user-object) object | Partial user object for the owner of the app | +| verify_key | string | Hex encoded key for verification in interactions and the GameSDK's [GetTicket](https://github.com/discord/discord-api-docs/blob/legacy-gamesdk/developers/docs/game_sdk/Applications.md#getticket) | +| team | ?[team](/developers/docs/topics/teams#data-models-team-object) object | If the app belongs to a team, this will be a list of the members of that team | +| guild_id? | snowflake | Guild associated with the app. For example, a developer support server. | +| guild? | partial [guild](/developers/docs/resources/guild#guild-object) object | Partial object of the associated guild | +| primary_sku_id? | snowflake | If this app is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists | +| slug? | string | If this app is a game sold on Discord, this field will be the URL slug that links to the store page | +| cover_image? | string | App's default rich presence invite [cover image hash](/developers/docs/reference#image-formatting) | +| flags? | integer | App's public [flags](/developers/docs/resources/application#application-object-application-flags) | +| approximate_guild_count? | integer | Approximate count of guilds the app has been added to | +| approximate_user_install_count? | integer | Approximate count of users that have installed the app (authorized with `application.commands` as a scope) | +| approximate_user_authorization_count? | integer | Approximate count of users that have OAuth2 authorizations for the app | +| redirect_uris? | array of strings | Array of redirect URIs for the app | +| interactions_endpoint_url? | ?string | [Interactions endpoint URL](/developers/docs/interactions/receiving-and-responding#receiving-an-interaction) for the app | +| role_connections_verification_url? | ?string | Role connection verification URL for the app | +| event_webhooks_url? | ?string | [Event webhooks URL](/developers/docs/events/webhook-events#preparing-for-events) for the app to receive webhook events | +| event_webhooks_status | [application event webhook status](/developers/docs/resources/application#application-object-application-event-webhook-status) | If [webhook events](/developers/docs/events/webhook-events) are enabled for the app. `1` (default) means disabled, `2` means enabled, and `3` means disabled by Discord | +| event_webhooks_types? | array of strings | List of [Webhook event types](/developers/docs/events/webhook-events#event-types) the app subscribes to | +| tags? | array of strings | List of tags describing the content and functionality of the app. Max of 5 tags. | +| install_params? | [install params](/developers/docs/resources/application#install-params-object) object | Settings for the app's default in-app authorization link, if enabled | +| integration_types_config? | dictionary with keys of [application integration types](/developers/docs/resources/application#application-object-application-integration-types) | Default scopes and permissions for each supported installation context. Value for each key is an [integration type configuration object](/developers/docs/resources/application#application-object-application-integration-type-configuration-object) | +| custom_install_url? | string | Default custom authorization URL for the app, if enabled | + + +###### Example Application Object + +```json +{ + "bot_public": true, + "bot_require_code_grant": false, + "cover_image": "31deabb7e45b6c8ecfef77d2f99c81a5", + "description": "Test", + "guild_id": "290926798626357260", + "icon": null, + "id": "172150183260323840", + "integration_types_config": { + "0": { + "oauth2_install_params": { + "scopes": [ + "applications.commands", + "bot" + ], + "permissions": "2048" + } + }, + "1": { + "oauth2_install_params": { + "scopes": [ + "applications.commands" + ], + "permissions": "0" + } + } + }, + "name": "Baba O-Riley", + "interactions_endpoint_url": null, + "role_connections_verification_url": null, + "event_webhooks_url": null, + "event_webhooks_status": 1, + "owner": { + "avatar": null, + "discriminator": "1738", + "flags": 1024, + "id": "172150183260323840", + "username": "i own a bot" + }, + "primary_sku_id": "172150183260323840", + "slug": "test", + "team": { + "icon": "dd9b7dcfdf5351b9c3de0fe167bacbe1", + "id": "531992624043786253", + "members": [ + { + "membership_state": 2, + "permissions": ["*"], + "team_id": "531992624043786253", + "user": { + "avatar": "d9e261cd35999608eb7e3de1fae3688b", + "discriminator": "0001", + "id": "511972282709709995", + "username": "Mr Owner" + } + } + ] + }, + "verify_key": "1e0a356058d627ca38a5c8c9648818061d49e49bd9da9e3ab17d98ad4d6bg2u8" +} +``` + + +###### Application Integration Types + +Where an app can be installed, also called its supported [installation contexts](/developers/docs/resources/application#installation-context). + +| Type | ID | Description | +|-----------------|----|-------------------------------| +| `GUILD_INSTALL` | 0 | App is installable to servers | +| `USER_INSTALL` | 1 | App is installable to users | + + +###### Application Integration Type Configuration Object +| Field | Type | Description | +|------------------------|---------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| +| oauth2_install_params? | [install params object](/developers/docs/resources/application#install-params-object) | Install params for each installation context's default in-app authorization link | + + +###### Application Event Webhook Status + +Status indicating whether event webhooks are enabled or disabled for an application. + +| Name | Value | Description | +|-----------------------|-------|-------------------------------------------------------------------| +| `DISABLED` | `1` | Webhook events are disabled by developer | +| `ENABLED` | `2` | Webhook events are enabled by developer | +| `DISABLED_BY_DISCORD` | `3` | Webhook events are disabled by Discord, usually due to inactivity | + + +###### Application Flags + +| Value | Name | Description | +|-----------|-----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `1 << 6` | APPLICATION_AUTO_MODERATION_RULE_CREATE_BADGE | Indicates if an app uses the [Auto Moderation API](/developers/docs/resources/auto-moderation) | +| `1 << 12` | GATEWAY_PRESENCE | Intent required for bots in **100 or more servers** to receive [`presence_update` events](/developers/docs/events/gateway-events#presence-update) | +| `1 << 13` | GATEWAY_PRESENCE_LIMITED | Intent required for bots in under 100 servers to receive [`presence_update` events](/developers/docs/events/gateway-events#presence-update), found on the **Bot** page in your app's settings | +| `1 << 14` | GATEWAY_GUILD_MEMBERS | Intent required for bots in **100 or more servers** to receive member-related events like `guild_member_add`. See the list of member-related events [under `GUILD_MEMBERS`](/developers/docs/events/gateway#list-of-intents) | +| `1 << 15` | GATEWAY_GUILD_MEMBERS_LIMITED | Intent required for bots in under 100 servers to receive member-related events like `guild_member_add`, found on the **Bot** page in your app's settings. See the list of member-related events [under `GUILD_MEMBERS`](/developers/docs/events/gateway#list-of-intents) | +| `1 << 16` | VERIFICATION_PENDING_GUILD_LIMIT | Indicates unusual growth of an app that prevents verification | +| `1 << 17` | EMBEDDED | Indicates if an app is embedded within the Discord client (currently unavailable publicly) | +| `1 << 18` | GATEWAY_MESSAGE_CONTENT | Intent required for bots in **100 or more servers** to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055) | +| `1 << 19` | GATEWAY_MESSAGE_CONTENT_LIMITED | Intent required for bots in under 100 servers to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055), found on the **Bot** page in your app's settings | +| `1 << 23` | APPLICATION_COMMAND_BADGE | Indicates if an app has registered global [application commands](/developers/docs/interactions/application-commands) | + +### Install Params Object + + +###### Install Params Structure + +| Field | Type | Description | +|-------------|------------------|-------------------------------------------------------------------------------------------------------------------| +| scopes | array of strings | [Scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) to add the application to the server with | +| permissions | string | [Permissions](/developers/docs/topics/permissions) to request for the bot role | + +## Installation Context + +An app's installation context defines how it's installed: to a server, to a user, or both. + +The installation context affects how your app can be seen and used within Discord. For example, apps installed only to a user can't take actions in a server, and apps installed only to a server can't be accessed within a user's DMs. + +#### Server Context + +Apps installed in a server context (server-installed apps) must be authorized by a server member with the [`MANAGE_GUILD`](/developers/docs/topics/permissions#permissions-bitwise-permission-flags) permission. Server-installed apps are *visible* to all members of the server, but other factors (like [command permissions](/developers/docs/interactions/application-commands#permissions)) determine where and when specific members can interact with the app. + +During installation, server-installed apps are authorized with a specific set of [OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) and [bot user permissions](/developers/docs/topics/permissions) that determine what resources and data the app can access in that server. + +#### User Context + +Apps installed in a user context (user-installed apps) are visible *only* to the authorizing user, and therefore don't require any server-specific permissions. + +Apps that support the user installation context are visible across all of an authorizing user's servers, DMs, and GDMs, but are forced to respect the user's permissions in the surface where the app is being used. For example, if a user invokes a command for a user-installed app from a server's channel where they don't have permission to send messages, the app won't be able to [respond to an interaction](/developers/docs/interactions/receiving-and-responding#interaction-response-object-interaction-callback-type) with a non-ephemeral message. Details about how the installation context of a command affects interactions is in the [interaction context](/developers/docs/interactions/application-commands#interaction-contexts) documentation. + +### Setting Supported Installation Contexts + +By default, newly-created apps only support installation to guilds. + +You can update which installation contexts your app supports in your [app's settings](https://discord.com/developers/applications). On the **Installation** page under the **Installation Contexts** section, you can select the installation contexts your app supports. + + +If you update your app to support a new installation context, you will need to update your existing [commands](/developers/docs/interactions/application-commands#contexts) if you want them to be supported in the new context. Details are in the [Application Command](/developers/docs/interactions/application-commands#contexts) documentation. + + +## Install Links + +Install links provide an easy way for users to install your app in Discord. If you have an install link configured, an "Add App" button will appear in your app's profile and App Directory page which will guide the user through your app's installation flow. + +### Types of Install Links + +There are three options when configuring an install link for your app: "Discord Provided Link", "Custom URL", and "None". If you don't configure an install link (by selecting "None"), the "Add App" button will not appear for your app, and your app will not be eligible for the App Directory. + + +Note that install links are distinct from OAuth2 flows like the [authorization code grant](/developers/docs/topics/oauth2#authorization-code-grant), which may additionally be required if you need to request user-specific [scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) like `identify` or `role_connections.write`. + + +#### Discord Provided Link + +The default Discord Provided Link is a short link that guides users through the installation flow with your app's [configured installation contexts](/developers/docs/resources/application#setting-supported-installation-contexts). If your app has both **User Install** and **Guild Install** enabled, the user can choose which way to install your app. + +Discord Provided Links don't have scopes or bot user permissions defined in the URL. For example: + +``` +https://discord.com/oauth2/authorize?client_id=1234567895647001626 +``` + +Instead, these links will prompt the user for the scopes and bot user permissions configured in your Default Install Settings. + + +Discord Provided Links are limited to the `application.commands` and `bot` scopes + + +#### Custom URL + +A Custom URL is an alternative to the Discord Provided Link that gives you more control of where users are directed when they click "Add App" on your app's profile or App Directory page. + +A Custom URL doesn't have strict limitations, but is commonly an [OAuth2 `/authorize` URL](/developers/docs/topics/oauth2#shared-resources-oauth2-urls) that has defined scopes, permissions, and an installation context (`integration_type`). + +### Configuring an Install Link and Default Install Settings + +You can configure your app's install link in your [app's settings](https://discord.com/developers/applications). On the **Installation** page, go to the **Install Link** section, and select which type of install link you want for your app. For most apps, we recommend the Discord Provided Link. + +The Default Install Settings will appear on the **Installation** page when you have "Discord Provided Link" selected as your install link type. + +## Get Current Application +/applications/@me + +Returns the [application](/developers/docs/resources/application#application-object) object associated with the requesting bot user. + +## Edit Current Application +/applications/@me + +Edit properties of the app associated with the requesting bot user. Only properties that are passed will be updated. Returns the updated [application](/developers/docs/resources/application#application-object) object on success. + + +All parameters to this endpoint are optional + + + +###### JSON Params + +| Field | Type | Description | +|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| custom_install_url | string | Default custom authorization URL for the app, if enabled | +| description | string | Description of the app | +| role_connections_verification_url | string | Role connection verification URL for the app | +| install_params | [install params](/developers/docs/resources/application#install-params-object) object | Settings for the app's default in-app authorization link, if enabled | +| integration_types_config | dictionary with keys of [application integration types](/developers/docs/resources/application#application-object-application-integration-types) | Default scopes and permissions for each supported installation context. Value for each key is an [integration type configuration object](/developers/docs/resources/application#application-object-application-integration-type-configuration-object) | +| flags \* | integer | App's public [flags](/developers/docs/resources/application#application-object-application-flags) | +| icon | ?[image data](/developers/docs/reference#image-data) | Icon for the app | +| cover_image | ?[image data](/developers/docs/reference#image-data) | Default rich presence invite cover image for the app | +| interactions_endpoint_url \*\* | string | [Interactions endpoint URL](/developers/docs/interactions/receiving-and-responding#receiving-an-interaction) for the app | +| tags | array of strings | List of tags describing the content and functionality of the app (max of 20 characters per tag). Max of 5 tags. | +| event_webhooks_url | string | [Event webhooks URL](/developers/docs/events/webhook-events#preparing-for-events) for the app to receive webhook events | +| event_webhooks_status | [application event webhook status](/developers/docs/resources/application#application-object-application-event-webhook-status) | If [webhook events](/developers/docs/events/webhook-events) are enabled for the app. `1` to disable, and `2` to enable | +| event_webhooks_types | array of strings | List of [Webhook event types](/developers/docs/events/webhook-events#event-types) to subscribe to | + +\* Only limited intent flags (`GATEWAY_PRESENCE_LIMITED`, `GATEWAY_GUILD_MEMBERS_LIMITED`, and `GATEWAY_MESSAGE_CONTENT_LIMITED`) can be updated via the API. + +\*\* To update an Interactions endpoint URL via the API, the URL must be valid according to the [Receiving an Interaction](/developers/docs/interactions/receiving-and-responding#receiving-an-interaction) documentation. + +## Get Application Activity Instance +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/activity-instances/[\{instance_id\}](/developers/docs/resources/application#get-application-activity-instance-activity-instance-object) + +Returns a serialized activity instance, if it exists. Useful for [preventing unwanted activity sessions](/developers/docs/activities/development-guides/multiplayer-experience#preventing-unwanted-activity-sessions). + + +###### Example Activity Instance + +```json +{ + "application_id": "1215413995645968394", + "instance_id": "i-1276580072400224306-gc-912952092627435520-912954213460484116", + "launch_id": "1276580072400224306", + "location": { + "id": "gc-912952092627435520-912954213460484116", + "kind": "gc", + "channel_id": "912954213460484116", + "guild_id": "912952092627435520" + }, + "users": ["205519959982473217"], +} +``` + + +###### Activity Instance Object + +| Field | Type | Description | +|----------------|------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| application_id | snowflake | [Application](/developers/docs/resources/application#application-object) ID | +| instance_id | string | Activity [Instance](/developers/docs/activities/development-guides/multiplayer-experience#activity-instance-management) ID | +| launch_id | snowflake | Unique identifier for the launch | +| location | [Activity Location](/developers/docs/resources/application#get-application-activity-instance-activity-location-object) | Location the instance is running in | +| users | array of snowflakes, [user](/developers/docs/resources/user#user-object) IDs | IDs of the Users currently connected to the instance | + + +###### Activity Location Object + +The Activity Location is an object that describes the location in which an activity instance is running. + +| Field | Type | Description | +|------------|-------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| id | string | Unique identifier for the location | +| kind | [Activity Location Kind Enum](/developers/docs/resources/application#get-application-activity-instance-activity-location-kind-enum) | Enum describing kind of location | +| channel_id | snowflake | ID of the [Channel](/developers/docs/resources/channel#channel-object) | +| guild_id? | ?snowflake | ID of the [Guild](/developers/docs/resources/guild#guild-object) | + + +###### Activity Location Kind Enum + +| Enum | Description | +|------|----------------------------------------------------| +| 'gc' | Location is a Guild Channel | +| 'pc' | Location is a Private Channel, such as a DM or GDM | diff --git a/discord/developers/docs/resources/audit-log.mdx b/discord/developers/docs/resources/audit-log.mdx new file mode 100644 index 0000000000..7659f71f6e --- /dev/null +++ b/discord/developers/docs/resources/audit-log.mdx @@ -0,0 +1,228 @@ +--- +title: Audit Logs Resource +sidebarTitle: Audit Log +description: Reference for Discord audit log objects and endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +import {Route} from '/snippets/route.jsx' + +When an administrative action is performed in a guild, an entry is added to its audit log. Viewing audit logs requires the `VIEW_AUDIT_LOG` permission and can be fetched by apps using the [`GET /guilds/{guild.id}/audit-logs` endpoint](/developers/docs/resources/audit-log#get-guild-audit-log), or seen by users in the guild's **Server Settings**. All audit log entries are stored for 45 days. + +When an app is performing an eligible action using the APIs, it can pass an `X-Audit-Log-Reason` header to indicate why the action was taken. More information is in the [audit log entry](/developers/docs/resources/audit-log#audit-log-entry-object) section. + +### Audit Log Object + + +###### Audit Log Structure + +| Field | Type | Description | +|------------------------|-------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------| +| application_commands | array of [application commands](/developers/docs/interactions/application-commands#application-command-object) objects | List of application commands referenced in the audit log | +| audit_log_entries | array of [audit log entry](/developers/docs/resources/audit-log#audit-log-entry-object) objects | List of audit log entries, sorted from most to least recent | +| auto_moderation_rules | array of [auto moderation rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) objects | List of auto moderation rules referenced in the audit log | +| guild_scheduled_events | array of [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) objects | List of guild scheduled events referenced in the audit log | +| integrations | array of partial [integration](/developers/docs/resources/guild#integration-object) objects | List of partial integration objects | +| threads | array of thread-specific [channel](/developers/docs/resources/channel#channel-object) objects | List of threads referenced in the audit log\* | +| users | array of [user](/developers/docs/resources/user#user-object) objects | List of users referenced in the audit log | +| webhooks | array of [webhook](/developers/docs/resources/webhook#webhook-object) objects | List of webhooks referenced in the audit log | + +\* Threads referenced in `THREAD_CREATE` and `THREAD_UPDATE` events are included in the threads map since archived threads might not be kept in memory by clients. + + +###### Example Partial Integration Object + +```json +{ + "id": "33590653072239123", + "name": "A Name", + "type": "twitch", + "account": { + "name": "twitchusername", + "id": "1234567" + }, + "application_id": "94651234501213162" +} +``` + +### Audit Log Entry Object + +Each audit log entry represents a single administrative action (or [event](/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events)), indicated by `action_type`. Most entries contain one to many changes in the `changes` array that affected an entity in Discord—whether that's a user, channel, guild, emoji, or something else. + +The information (and structure) of an entry's changes will be different depending on its type. For example, in `MEMBER_ROLE_UPDATE` events there is only one change: a member is either added or removed from a specific role. However, in `CHANNEL_CREATE` events there are many changes, including (but not limited to) the channel's name, type, and permission overwrites added. More details are in the [change object](/developers/docs/resources/audit-log#audit-log-change-object) section. + +Apps can specify why an administrative action is being taken by passing an `X-Audit-Log-Reason` request header, which will be stored as the audit log entry's `reason` field. The `X-Audit-Log-Reason` header supports 1-512 URL-encoded UTF-8 characters. Reasons are visible to users in the client and to apps when fetching audit log entries with the API. + + +###### Audit Log Entry Structure + +| Field | Type | Description | +|-------------|--------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------| +| target_id | ?string | ID of the affected entity (webhook, user, role, etc.) | +| changes? | array of [audit log change](/developers/docs/resources/audit-log#audit-log-change-object) objects | Changes made to the target_id | +| user_id | ?snowflake | User or app that made the changes | +| id | snowflake | ID of the entry | +| action_type | [audit log event](/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events) | Type of action that occurred | +| options? | [optional audit entry info](/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info) | Additional info for certain event types | +| reason? | string | Reason for the change (1-512 characters) | + + +For `APPLICATION_COMMAND_PERMISSION_UPDATE` events, the `target_id` is the command ID or the app ID since the `changes` array represents the entire `permissions` property on the [guild permissions](/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object. + + + +###### Audit Log Events + +The table below lists audit log events and values (the `action_type` field) that your app may receive. + +The **Object Changed** column notes which object's values may be included in the entry. Though there are exceptions, possible keys in the `changes` array typically correspond to the object's fields. The descriptions and types for those fields can be found in the linked documentation for the object. + +If no object is noted, there won't be a `changes` array in the entry, though other fields like the `target_id` still exist and many have fields in the [`options` object](/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info). + + +You should assume that your app may run into any field for the changed object, though none are guaranteed to be present. In most cases only a subset of the object's fields will be in the `changes` array. + + +| Event | Value | Description | Object Changed | +|---------------------------------------------|-------|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| GUILD_UPDATE | 1 | Server settings were updated | [Guild](/developers/docs/resources/guild#guild-object) | +| CHANNEL_CREATE | 10 | Channel was created | [Channel](/developers/docs/resources/channel#channel-object) | +| CHANNEL_UPDATE | 11 | Channel settings were updated | [Channel](/developers/docs/resources/channel#channel-object) | +| CHANNEL_DELETE | 12 | Channel was deleted | [Channel](/developers/docs/resources/channel#channel-object) | +| CHANNEL_OVERWRITE_CREATE | 13 | Permission overwrite was added to a channel | [Channel Overwrite](/developers/docs/resources/channel#overwrite-object) | +| CHANNEL_OVERWRITE_UPDATE | 14 | Permission overwrite was updated for a channel | [Channel Overwrite](/developers/docs/resources/channel#overwrite-object) | +| CHANNEL_OVERWRITE_DELETE | 15 | Permission overwrite was deleted from a channel | [Channel Overwrite](/developers/docs/resources/channel#overwrite-object) | +| MEMBER_KICK | 20 | Member was removed from server | | +| MEMBER_PRUNE | 21 | Members were pruned from server | | +| MEMBER_BAN_ADD | 22 | Member was banned from server | | +| MEMBER_BAN_REMOVE | 23 | Server ban was lifted for a member | | +| MEMBER_UPDATE | 24 | Member was updated in server | [Member](/developers/docs/resources/guild#guild-member-object) | +| MEMBER_ROLE_UPDATE | 25 | Member was added or removed from a role | [Partial Role](/developers/docs/topics/permissions#role-object)\* | +| MEMBER_MOVE | 26 | Member was moved to a different voice channel | | +| MEMBER_DISCONNECT | 27 | Member was disconnected from a voice channel | | +| BOT_ADD | 28 | Bot user was added to server | | +| ROLE_CREATE | 30 | Role was created | [Role](/developers/docs/topics/permissions#role-object) | +| ROLE_UPDATE | 31 | Role was edited | [Role](/developers/docs/topics/permissions#role-object) | +| ROLE_DELETE | 32 | Role was deleted | [Role](/developers/docs/topics/permissions#role-object) | +| INVITE_CREATE | 40 | Server invite was created | [Invite](/developers/docs/resources/invite#invite-object) and [Invite Metadata](/developers/docs/resources/invite#invite-metadata-object)* | +| INVITE_UPDATE | 41 | Server invite was updated | [Invite](/developers/docs/resources/invite#invite-object) and [Invite Metadata](/developers/docs/resources/invite#invite-metadata-object)* | +| INVITE_DELETE | 42 | Server invite was deleted | [Invite](/developers/docs/resources/invite#invite-object) and [Invite Metadata](/developers/docs/resources/invite#invite-metadata-object)* | +| WEBHOOK_CREATE | 50 | Webhook was created | [Webhook](/developers/docs/resources/webhook#webhook-object)\* | +| WEBHOOK_UPDATE | 51 | Webhook properties or channel were updated | [Webhook](/developers/docs/resources/webhook#webhook-object)\* | +| WEBHOOK_DELETE | 52 | Webhook was deleted | [Webhook](/developers/docs/resources/webhook#webhook-object)\* | +| EMOJI_CREATE | 60 | Emoji was created | [Emoji](/developers/docs/resources/emoji#emoji-object) | +| EMOJI_UPDATE | 61 | Emoji name was updated | [Emoji](/developers/docs/resources/emoji#emoji-object) | +| EMOJI_DELETE | 62 | Emoji was deleted | [Emoji](/developers/docs/resources/emoji#emoji-object) | +| MESSAGE_DELETE | 72 | Single message was deleted | | +| MESSAGE_BULK_DELETE | 73 | Multiple messages were deleted | | +| MESSAGE_PIN | 74 | Message was pinned to a channel | | +| MESSAGE_UNPIN | 75 | Message was unpinned from a channel | | +| INTEGRATION_CREATE | 80 | App was added to server | [Integration](/developers/docs/resources/guild#integration-object) | +| INTEGRATION_UPDATE | 81 | App was updated (as an example, its scopes were updated) | [Integration](/developers/docs/resources/guild#integration-object) | +| INTEGRATION_DELETE | 82 | App was removed from server | [Integration](/developers/docs/resources/guild#integration-object) | +| STAGE_INSTANCE_CREATE | 83 | Stage instance was created (stage channel becomes live) | [Stage Instance](/developers/docs/resources/stage-instance#stage-instance-object) | +| STAGE_INSTANCE_UPDATE | 84 | Stage instance details were updated | [Stage Instance](/developers/docs/resources/stage-instance#stage-instance-object) | +| STAGE_INSTANCE_DELETE | 85 | Stage instance was deleted (stage channel no longer live) | [Stage Instance](/developers/docs/resources/stage-instance#stage-instance-object) | +| STICKER_CREATE | 90 | Sticker was created | [Sticker](/developers/docs/resources/sticker#sticker-object) | +| STICKER_UPDATE | 91 | Sticker details were updated | [Sticker](/developers/docs/resources/sticker#sticker-object) | +| STICKER_DELETE | 92 | Sticker was deleted | [Sticker](/developers/docs/resources/sticker#sticker-object) | +| GUILD_SCHEDULED_EVENT_CREATE | 100 | Event was created | [Guild Scheduled Event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) | +| GUILD_SCHEDULED_EVENT_UPDATE | 101 | Event was updated | [Guild Scheduled Event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) | +| GUILD_SCHEDULED_EVENT_DELETE | 102 | Event was cancelled | [Guild Scheduled Event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) | +| THREAD_CREATE | 110 | Thread was created in a channel | [Thread](/developers/docs/resources/channel#thread-metadata-object) | +| THREAD_UPDATE | 111 | Thread was updated | [Thread](/developers/docs/resources/channel#thread-metadata-object) | +| THREAD_DELETE | 112 | Thread was deleted | [Thread](/developers/docs/resources/channel#thread-metadata-object) | +| APPLICATION_COMMAND_PERMISSION_UPDATE | 121 | Permissions were updated for a command | [Command Permission](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure)\* | +| SOUNDBOARD_SOUND_CREATE | 130 | Soundboard sound was created | [Soundboard Sound](/developers/docs/resources/soundboard#soundboard-sound-object) | +| SOUNDBOARD_SOUND_UPDATE | 131 | Soundboard sound was updated | [Soundboard Sound](/developers/docs/resources/soundboard#soundboard-sound-object) | +| SOUNDBOARD_SOUND_DELETE | 132 | Soundboard sound was deleted | [Soundboard Sound](/developers/docs/resources/soundboard#soundboard-sound-object) | +| AUTO_MODERATION_RULE_CREATE | 140 | Auto Moderation rule was created | [Auto Moderation Rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) | +| AUTO_MODERATION_RULE_UPDATE | 141 | Auto Moderation rule was updated | [Auto Moderation Rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) | +| AUTO_MODERATION_RULE_DELETE | 142 | Auto Moderation rule was deleted | [Auto Moderation Rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) | +| AUTO_MODERATION_BLOCK_MESSAGE | 143 | Message was blocked by Auto Moderation | | +| AUTO_MODERATION_FLAG_TO_CHANNEL | 144 | Message was flagged by Auto Moderation | | +| AUTO_MODERATION_USER_COMMUNICATION_DISABLED | 145 | Member was timed out by Auto Moderation | | +| AUTO_MODERATION_QUARANTINE_USER | 146 | Member was quarantined by Auto Moderation | | +| CREATOR_MONETIZATION_REQUEST_CREATED | 150 | Creator monetization request was created | | +| CREATOR_MONETIZATION_TERMS_ACCEPTED | 151 | Creator monetization terms were accepted | | +| ONBOARDING_PROMPT_CREATE | 163 | Guild Onboarding Question was created | [Onboarding Prompt Structure](/developers/docs/resources/guild#guild-onboarding-object-onboarding-prompt-structure) | +| ONBOARDING_PROMPT_UPDATE | 164 | Guild Onboarding Question was updated | [Onboarding Prompt Structure](/developers/docs/resources/guild#guild-onboarding-object-onboarding-prompt-structure) | +| ONBOARDING_PROMPT_DELETE | 165 | Guild Onboarding Question was deleted | [Onboarding Prompt Structure](/developers/docs/resources/guild#guild-onboarding-object-onboarding-prompt-structure) | +| ONBOARDING_CREATE | 166 | Guild Onboarding was created | [Guild Onboarding](/developers/docs/resources/guild#guild-onboarding-object) | +| ONBOARDING_UPDATE | 167 | Guild Onboarding was updated | [Guild Onboarding](/developers/docs/resources/guild#guild-onboarding-object) | +| HOME_SETTINGS_CREATE | 190 | Guild Server Guide was created | | +| HOME_SETTINGS_UPDATE | 191 | Guild Server Guide was updated | | + + +\* Object has exception(s) to available keys. See the [exceptions](/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-exceptions) section below for details. + + +###### Optional Audit Entry Info + +| Field | Type | Description | Event Types | +|-----------------------------------|-----------|------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| application_id | snowflake | ID of the app whose permissions were targeted | APPLICATION_COMMAND_PERMISSION_UPDATE | +| auto_moderation_rule_name | string | Name of the Auto Moderation rule that was triggered | AUTO_MODERATION_BLOCK_MESSAGE & AUTO_MODERATION_FLAG_TO_CHANNEL & AUTO_MODERATION_USER_COMMUNICATION_DISABLED & AUTO_MODERATION_QUARANTINE_USER | +| auto_moderation_rule_trigger_type | string | Trigger type of the Auto Moderation rule that was triggered | AUTO_MODERATION_BLOCK_MESSAGE & AUTO_MODERATION_FLAG_TO_CHANNEL & AUTO_MODERATION_USER_COMMUNICATION_DISABLED & AUTO_MODERATION_QUARANTINE_USER | +| channel_id | snowflake | Channel in which the entities were targeted | MEMBER_MOVE & MESSAGE_PIN & MESSAGE_UNPIN & MESSAGE_DELETE & STAGE_INSTANCE_CREATE & STAGE_INSTANCE_UPDATE & STAGE_INSTANCE_DELETE & AUTO_MODERATION_BLOCK_MESSAGE & AUTO_MODERATION_FLAG_TO_CHANNEL & AUTO_MODERATION_USER_COMMUNICATION_DISABLED & AUTO_MODERATION_QUARANTINE_USER | +| count | string | Number of entities that were targeted | MESSAGE_DELETE & MESSAGE_BULK_DELETE & MEMBER_DISCONNECT & MEMBER_MOVE | +| delete_member_days | string | Number of days after which inactive members were kicked | MEMBER_PRUNE | +| id | snowflake | ID of the overwritten entity | CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE | +| members_removed | string | Number of members removed by the prune | MEMBER_PRUNE | +| message_id | snowflake | ID of the message that was targeted | MESSAGE_PIN & MESSAGE_UNPIN | +| role_name | string | Name of the role if type is `"0"` (not present if type is `"1"`) | CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE | +| type | string | Type of overwritten entity - role (`"0"`) or member (`"1"`) | CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE | +| integration_type | string | The type of integration which performed the action | MEMBER_KICK & MEMBER_ROLE_UPDATE | + +### Audit Log Change Object + +Many audit log events include a `changes` array in their [entry object](/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-entry-structure). The [structure for the individual changes](/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-structure) varies based on the event type and its changed objects, so apps shouldn't depend on a single pattern of handling audit log events. + + +###### Audit Log Change Structure + +Some events don't follow the same pattern as other audit log events. Details about these exceptions are explained in [the next section](/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-exceptions). + + +If `new_value` is not present in the change object while `old_value` is, it indicates that the property has been reset or set to `null`. If `old_value` isn't included, it indicated that the property was previously `null`. + + + +| Field | Type | Description | +|------------|-------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| +| new_value? | mixed (matches object field's type) | New value of the key | +| old_value? | mixed (matches object field's type) | Old value of the key | +| key | string | Name of the changed entity, with a few [exceptions](/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-exceptions) | + + +###### Audit Log Change Exceptions + +For most objects, the change keys may be any field on the changed object. The following table details the exceptions to this pattern. + +| Object Changed | Change Key Exceptions | Change Object Exceptions | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Command Permission](/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure) | snowflake as key | The `changes` array contains objects with a `key` field representing the entity whose command was affected (role, channel, or user ID), a previous permissions object (with an `old_value` key), and an updated permissions object (with a `new_value` key) | +| [Invite](/developers/docs/resources/invite#invite-object) and [Invite Metadata](/developers/docs/resources/invite#invite-metadata-object) | Additional `channel_id` key (instead of object's `channel.id`) | | +| [Partial Role](/developers/docs/topics/permissions#role-object) | `$add` and `$remove` as keys | `new_value` is an array of objects that contain the role `id` and `name` | +| [Webhook](/developers/docs/resources/webhook#webhook-object) | `avatar_hash` key (instead of `avatar`) | | + +## Get Guild Audit Log +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/audit-logs + +Returns an [audit log](/developers/docs/resources/audit-log#audit-log-object) object for the guild. Requires the [`VIEW_AUDIT_LOG`](/developers/docs/topics/permissions#permissions-bitwise-permission-flags) permission. + +The returned list of audit log entries is ordered based on whether you use `before` or `after`. When using `before`, the list is ordered by the audit log entry ID **descending** (newer entries first). If `after` is used, the list is reversed and appears in **ascending** order (older entries first). Omitting both `before` and `after` defaults to `before` the current timestamp and will show the most recent entries in descending order by ID, the opposite can be achieved using `after=0` (showing oldest entries). + + +###### Query String Params + +The following parameters can be used to filter which and how many audit log entries are returned. + +| Field | Type | Description | +|--------------|-----------|------------------------------------------------------------------------------------------------------------------------| +| user_id? | snowflake | Entries from a specific user ID | +| action_type? | integer | Entries for a specific [audit log event](/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events) | +| before? | snowflake | Entries with ID less than a specific audit log entry ID | +| after? | snowflake | Entries with ID greater than a specific audit log entry ID | +| limit? | integer | Maximum number of entries (between 1-100) to return, defaults to 50 | diff --git a/discord/developers/docs/resources/auto-moderation.mdx b/discord/developers/docs/resources/auto-moderation.mdx new file mode 100644 index 0000000000..91297f5d06 --- /dev/null +++ b/discord/developers/docs/resources/auto-moderation.mdx @@ -0,0 +1,312 @@ +--- +title: Auto Moderation +description: Reference for Discord's Auto Moderation system and endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +Auto Moderation is a feature which allows each [guild](/developers/docs/resources/guild) to set up rules that trigger based on some criteria. For example, a rule can trigger whenever a message contains a specific keyword. + +Rules can be configured to automatically execute actions whenever they trigger. For example, if a user tries to send a message which contains a certain keyword, a rule can trigger and block the message before it is sent. + +### Auto Moderation Rule Object + + +###### Auto Moderation Rule Structure + +| Field | Type | Description | +|------------------|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------| +| id | snowflake | the id of this rule | +| guild_id | snowflake | the id of the guild which this rule belongs to | +| name | string | the rule name | +| creator_id | snowflake | the user which first created this rule | +| event_type | integer | the rule [event type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types) | +| trigger_type | integer | the rule [trigger type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) | +| trigger_metadata | object | the rule [trigger metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) | +| actions | array of [action](/developers/docs/resources/auto-moderation#auto-moderation-action-object) objects | the actions which will execute when the rule is triggered | +| enabled | boolean | whether the rule is enabled | +| exempt_roles | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) | +| exempt_channels | array of snowflakes | the channel ids that should not be affected by the rule (Maximum of 50) | + + +###### Example Auto Moderation Rule + +```json +{ + "id": "969707018069872670", + "guild_id": "613425648685547541", + "name": "Keyword Filter 1", + "creator_id": "423457898095789043", + "trigger_type": 1, + "event_type": 1, + "actions": [ + { + "type": 1, + "metadata": { "custom_message": "Please keep financial discussions limited to the #finance channel" } + }, + { + "type": 2, + "metadata": { "channel_id": "123456789123456789" } + }, + { + "type": 3, + "metadata": { "duration_seconds": 60 } + } + ], + "trigger_metadata": { + "keyword_filter": ["cat*", "*dog", "*ana*", "i like c++"], + "regex_patterns": ["(b|c)at", "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$"] + }, + "enabled": true, + "exempt_roles": ["323456789123456789", "423456789123456789"], + "exempt_channels": ["523456789123456789"] +} +``` + + +###### Trigger Types +Characterizes the type of content which can trigger the rule. + +| Trigger Type | Value | Description | Max per Guild | +|----------------|-------|-----------------------------------------------------------------------------|---------------| +| KEYWORD | 1 | check if content contains words from a user defined list of keywords | 6 | +| SPAM | 3 | check if content represents generic spam | 1 | +| KEYWORD_PRESET | 4 | check if content contains words from internal pre-defined wordsets | 1 | +| MENTION_SPAM | 5 | check if content contains more unique mentions than allowed | 1 | +| MEMBER_PROFILE | 6 | check if member profile contains words from a user defined list of keywords | 1 | + + +###### Trigger Metadata + +Additional data used to determine whether a rule should be triggered. Different fields are relevant based on the +value of [trigger_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types). + +| Field | Type | Associated Trigger Types | Description | +|---------------------------------|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------|-----------------------------------------------------------------------------------| +| keyword_filter | array of strings * | KEYWORD, MEMBER_PROFILE | substrings which will be searched for in content (Maximum of 1000) | +| regex_patterns | array of strings ** | KEYWORD, MEMBER_PROFILE | regular expression patterns which will be matched against content (Maximum of 10) | +| presets | array of [keyword preset types](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-preset-types) | KEYWORD_PRESET | the internally pre-defined wordsets which will be searched for in content | +| allow_list | array of strings *** | KEYWORD, KEYWORD_PRESET, MEMBER_PROFILE | substrings which should not trigger the rule (Maximum of 100 or 1000) | +| mention_total_limit | integer | MENTION_SPAM | total number of unique role and user mentions allowed per message (Maximum of 50) | +| mention_raid_protection_enabled | boolean | MENTION_SPAM | whether to automatically detect mention raids | + +\* A keyword can be a phrase which contains multiple words. [Wildcard symbols](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-matching-strategies) can be used to customize how each keyword will be matched. Each keyword must be 60 characters or less. + +\** Only Rust flavored regex is currently supported, which can be tested in online editors such as [Rustexp](https://rustexp.lpil.uk/). Each regex pattern must be 260 characters or less. + +\*** Each `allow_list` keyword can be a phrase which contains multiple words. [Wildcard symbols](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-matching-strategies) can be used to customize how each keyword will be matched. Rules with `KEYWORD` [trigger_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) accept a maximum of 100 keywords. Rules with `KEYWORD_PRESET` [trigger_type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) accept a maximum of 1000 keywords. + + +###### Trigger Metadata Field Limits + +| Field | Trigger Types | MAX ARRAY LENGTH | MAX CHARACTERS PER STRING | +|----------------|-------------------------|------------------|---------------------------| +| keyword_filter | KEYWORD, MEMBER_PROFILE | 1000 | 60 | +| regex_patterns | KEYWORD, MEMBER_PROFILE | 10 | 260 | +| allow_list | KEYWORD, MEMBER_PROFILE | 100 | 60 | +| allow_list | KEYWORD_PRESET | 1000 | 60 | + + + +###### Keyword Preset Types + +| Preset Type | Value | Description | +|----------------|-------|--------------------------------------------------------------| +| PROFANITY | 1 | words that may be considered forms of swearing or cursing | +| SEXUAL_CONTENT | 2 | words that refer to sexually explicit behavior or activity | +| SLURS | 3 | personal insults or words that may be considered hate speech | + + + +###### Event Types + +Indicates in what event context a rule should be checked. + +| Event Type | Value | Description | +|---------------|-------|-----------------------------------------------------| +| MESSAGE_SEND | 1 | when a member sends or edits a message in the guild | +| MEMBER_UPDATE | 2 | when a member edits their profile | + + + +###### Keyword Matching Strategies + +Use the wildcard symbol (`*`) at the beginning or end of a keyword to define how it should be matched. All keywords are case insensitive. + +**Prefix** - word must start with the keyword + +| Keyword | Matches | +|-----------|---------------------------------------| +| cat\* | **cat**ch, **Cat**apult, **CAt**tLE | +| tra\* | **tra**in, **tra**de, **TRA**ditional | +| the mat\* | **the mat**rix | + + +**Suffix** - word must end with the keyword + +| Keyword | Matches | +|-----------|-------------------------------------| +| \*cat | wild**cat**, copy**Cat** | +| \*tra | ex**tra**, ul**tra**, orches**TRA** | +| \*the mat | brea**the mat** | + + +**Anywhere** - keyword can appear anywhere in the content + +| Keyword | Matches | +|-------------|-----------------------------| +| \*cat\* | lo**cat**ion, edu**Cat**ion | +| \*tra\* | abs**tra**cted, ou**tra**ge | +| \*the mat\* | brea**the mat**ter | + + +**Whole Word** - keyword is a full word or phrase and must be surrounded by whitespace + +| Keyword | Matches | +|---------|-------------| +| cat | **cat** | +| train | **train** | +| the mat | **the mat** | + + +### Auto Moderation Action Object + +An action which will execute whenever a rule is triggered. + + +###### Auto Moderation Action Structure + +| Field | Type | Description | +|-------------|-------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------| +| type | [action type](/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types) | the type of action | +| metadata? * | [action metadata](/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata) | additional metadata needed during execution for this specific action type | + +\* Can be omitted based on `type`. See the `Associated Action Types` column in [action metadata](/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata) to understand which `type` values require `metadata` to be set. + + +###### Action Types + +| Action Type | Value | Description | +|--------------------------|-------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| BLOCK_MESSAGE | 1 | blocks a member's message and prevents it from being posted. A custom explanation can be specified and shown to members whenever their message is blocked. | +| SEND_ALERT_MESSAGE | 2 | logs user content to a specified channel | +| TIMEOUT | 3 | timeout user for a specified duration * | +| BLOCK_MEMBER_INTERACTION | 4 | prevents a member from using text, voice, or other interactions | + +\* A `TIMEOUT` action can only be set up for `KEYWORD` and `MENTION_SPAM` rules. The `MODERATE_MEMBERS` permission is required to use the `TIMEOUT` action type. + + + +###### Action Metadata + +Additional data used when an action is executed. Different fields are relevant based on the +value of [action type](/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types). + +| Field | Type | Associated Action Types | Description | Constraints | +|------------------|-----------|-------------------------|----------------------------------------------------------------------------------------|--------------------------------------| +| channel_id | snowflake | SEND_ALERT_MESSAGE | channel to which user content should be logged | existing channel | +| duration_seconds | integer | TIMEOUT | timeout duration in seconds | maximum of 2419200 seconds (4 weeks) | +| custom_message? | string | BLOCK_MESSAGE | additional explanation that will be shown to members whenever their message is blocked | maximum of 150 characters | + + +### Auto Moderation Permission Requirements + +Users are required to have the `MANAGE_GUILD` permission to access all Auto Moderation resources. +Some [action types](/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types) require additional permissions, e.g. the `TIMEOUT` action type requires an additional `MODERATE_MEMBERS` permission. + +## List Auto Moderation Rules for Guild +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/auto-moderation/rules + +Get a list of all rules currently configured for the guild. Returns a list of [auto moderation rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) objects for the given guild. + + +This endpoint requires the `MANAGE_GUILD` [permission](/developers/docs/resources/auto-moderation#auto-moderation-permission-requirements). + + +## Get Auto Moderation Rule +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/auto-moderation/rules/[\{auto_moderation_rule.id\}](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) + +Get a single rule. Returns an [auto moderation rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) object. + + +This endpoint requires the `MANAGE_GUILD` [permission](/developers/docs/resources/auto-moderation#auto-moderation-permission-requirements). + + +## Create Auto Moderation Rule +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/auto-moderation/rules + +Create a new rule. Returns an [auto moderation rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) on success. Fires an [Auto Moderation Rule Create](/developers/docs/events/gateway-events#auto-moderation-rule-create) Gateway event. + + +This endpoint requires the `MANAGE_GUILD` [permission](/developers/docs/resources/auto-moderation#auto-moderation-permission-requirements). + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|---------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| name | string | the rule name | +| event_type | integer | the [event type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types) | +| trigger_type | integer | the [trigger type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) | +| trigger_metadata? * | object | the [trigger metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) | +| actions | array of [action](/developers/docs/resources/auto-moderation#auto-moderation-action-object) objects | the actions which will execute when the rule is triggered | +| enabled? | boolean | whether the rule is enabled (False by default) | +| exempt_roles? | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) | + +\* Can be omitted based on `trigger_type`. See the `Associated Trigger Types` column in [trigger metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) to understand which `trigger_type` values require `trigger_metadata` to be set. + + +See [Trigger Types](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types) for limits on how many rules of each trigger type can be created per guild. + + + +## Modify Auto Moderation Rule +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/auto-moderation/rules/[\{auto_moderation_rule.id\}](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) + +Modify an existing rule. Returns an [auto moderation rule](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) on success. Fires an [Auto Moderation Rule Update](/developers/docs/events/gateway-events#auto-moderation-rule-update) Gateway event. + + +Requires `MANAGE_GUILD` [permissions](/developers/docs/resources/auto-moderation#auto-moderation-permission-requirements). + + + +All parameters for this endpoint are optional. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|---------------------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| name | string | the rule name | +| event_type | integer | the [event type](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types) | +| trigger_metadata? * | object | the [trigger metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) | +| actions | array of [action](/developers/docs/resources/auto-moderation#auto-moderation-action-object) objects | the actions which will execute when the rule is triggered | +| enabled | boolean | whether the rule is enabled | +| exempt_roles | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) | +| exempt_channels | array of snowflakes | the channel ids that should not be affected by the rule (Maximum of 50) | + +\* Can be omitted based on `trigger_type`. See the `Associated Trigger Types` column in [trigger metadata](/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata) to understand which `trigger_type` values require `trigger_metadata` to be set. + +## Delete Auto Moderation Rule +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/auto-moderation/rules/[\{auto_moderation_rule.id\}](/developers/docs/resources/auto-moderation#auto-moderation-rule-object) + +Delete a rule. Returns a `204` on success. Fires an [Auto Moderation Rule Delete](/developers/docs/events/gateway-events#auto-moderation-rule-delete) Gateway event. + + +This endpoint requires the `MANAGE_GUILD` [permission](/developers/docs/resources/auto-moderation#auto-moderation-permission-requirements). + + + +This endpoint supports the `X-Audit-Log-Reason` header. + diff --git a/discord/developers/docs/resources/channel.mdx b/discord/developers/docs/resources/channel.mdx new file mode 100644 index 0000000000..c7b05b14fe --- /dev/null +++ b/discord/developers/docs/resources/channel.mdx @@ -0,0 +1,778 @@ +--- +title: Channel Resource +sidebarTitle: Channel +description: Reference for Discord channel objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +import {Route} from '/snippets/route.jsx' + +### Channel Object + +Represents a guild or DM channel within Discord. + + +###### Channel Structure + +| Field | Type | Description | +|-------------------------------------|----------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | the id of this channel | +| type | integer | the [type of channel](/developers/docs/resources/channel#channel-object-channel-types) | +| guild_id? | snowflake | the id of the guild (may be missing for some channel objects received over gateway guild dispatches) | +| position? | integer | sorting position of the channel (channels with the same position are sorted by id) | +| permission_overwrites? | array of [overwrite](/developers/docs/resources/channel#overwrite-object) objects | explicit permission overwrites for members and roles | +| name? | ?string | the name of the channel (1-100 characters) | +| topic? | ?string | the channel topic (0-4096 characters for `GUILD_FORUM` and `GUILD_MEDIA` channels, 0-1024 characters for all others) | +| nsfw? | boolean | whether the channel is nsfw | +| last_message_id? | ?snowflake | the id of the last message sent in this channel (or thread for `GUILD_FORUM` or `GUILD_MEDIA` channels) (may not point to an existing or valid message or thread) | +| bitrate? | integer | the bitrate (in bits) of the voice channel | +| user_limit? | integer | the user limit of the voice channel | +| rate_limit_per_user?\* | integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages` or `manage_channel`, are unaffected | +| recipients? | array of [user](/developers/docs/resources/user#user-object) objects | the recipients of the DM | +| icon? | ?string | icon hash of the group DM | +| owner_id? | snowflake | id of the creator of the group DM or thread | +| application_id? | snowflake | application id of the group DM creator if it is bot-created | +| managed? | boolean | for group DM channels: whether the channel is managed by an application via the `gdm.join` OAuth2 scope | +| parent_id? | ?snowflake | for guild channels: id of the parent category for a channel (each parent category can contain up to 50 channels), for threads: id of the text channel this thread was created | +| last_pin_timestamp? | ?ISO8601 timestamp | when the last pinned message was pinned. This may be `null` in events such as `GUILD_CREATE` when a message is not pinned. | +| rtc_region? | ?string | [voice region](/developers/docs/resources/voice#voice-region-object) id for the voice channel, automatic when set to null | +| video_quality_mode? | integer | the camera [video quality mode](/developers/docs/resources/channel#channel-object-video-quality-modes) of the voice channel, 1 when not present | +| message_count?\*\* | integer | number of messages (not including the initial message or deleted messages) in a thread. | +| member_count? | integer | an approximate count of users in a thread, stops counting at 50 | +| thread_metadata? | a [thread metadata](/developers/docs/resources/channel#thread-metadata-object) object | thread-specific fields not needed by other channels | +| member? | a [thread member](/developers/docs/resources/channel#thread-member-object) object | thread member object for the current user, if they have joined the thread, only included on certain API endpoints | +| default_auto_archive_duration? | integer | default duration, copied onto newly created threads, in minutes, threads will stop showing in the channel list after the specified period of inactivity, can be set to: 60, 1440, 4320, 10080 | +| permissions? | string | computed permissions for the invoking user in the channel, including overwrites, only included when part of the `resolved` data received on an interaction. This does not include [implicit permissions](/developers/docs/topics/permissions#implicit-permissions), which may need to be checked separately | +| flags? | integer | [channel flags](/developers/docs/resources/channel#channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | +| total_message_sent? | integer | number of messages ever sent in a thread, it's similar to `message_count` on message creation, but will not decrement the number when a message is deleted | +| available_tags? | array of [tag](/developers/docs/resources/channel#forum-tag-object) objects | the set of tags that can be used in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | +| applied_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | +| default_reaction_emoji? | ?[default reaction](/developers/docs/resources/channel#default-reaction-object) object | the emoji to show in the add reaction button on a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | +| default_thread_rate_limit_per_user? | integer | the initial `rate_limit_per_user` to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update. | +| default_sort_order? | ?integer | the [default sort order type](/developers/docs/resources/channel#channel-object-sort-order-types) used to order posts in `GUILD_FORUM` and `GUILD_MEDIA` channels. Defaults to `null`, which indicates a preferred sort order hasn't been set by a channel admin | +| default_forum_layout? | integer | the [default forum layout view](/developers/docs/resources/channel#channel-object-forum-layout-types) used to display posts in `GUILD_FORUM` channels. Defaults to `0`, which indicates a layout view has not been set by a channel admin | + +\* `rate_limit_per_user` also applies to thread creation. Users can send one message and create one thread during each `rate_limit_per_user` interval. + +\*\* For threads created before July 1, 2022, the message count is inaccurate when it's greater than 50. + + +###### Channel Types + + +Type 10, 11 and 12 are only available in API v9 and above. + + +| Type | ID | Description | +|---------------------|----|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| GUILD_TEXT | 0 | a text channel within a server | +| DM | 1 | a direct message between users | +| GUILD_VOICE | 2 | a voice channel within a server | +| GROUP_DM | 3 | a direct message between multiple users | +| GUILD_CATEGORY | 4 | an [organizational category](https://support.discord.com/hc/en-us/articles/115001580171-Channel-Categories-101) that contains up to 50 channels | +| GUILD_ANNOUNCEMENT | 5 | a channel that [users can follow and crosspost into their own server](https://support.discord.com/hc/en-us/articles/360032008192) (formerly news channels) | +| ANNOUNCEMENT_THREAD | 10 | a temporary sub-channel within a GUILD_ANNOUNCEMENT channel | +| PUBLIC_THREAD | 11 | a temporary sub-channel within a GUILD_TEXT or GUILD_FORUM channel | +| PRIVATE_THREAD | 12 | a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission | +| GUILD_STAGE_VOICE | 13 | a voice channel for [hosting events with an audience](https://support.discord.com/hc/en-us/articles/1500005513722) | +| GUILD_DIRECTORY | 14 | the channel in a [hub](https://support.discord.com/hc/en-us/articles/4406046651927-Discord-Student-Hubs-FAQ) containing the listed servers | +| GUILD_FORUM | 15 | Channel that can only contain threads | +| GUILD_MEDIA | 16 | Channel that can only contain threads, similar to `GUILD_FORUM` channels | + +\* The `GUILD_MEDIA` channel type is still in active development. Avoid implementing any features that are not documented here, since they are subject to change without notice! + + +###### Video Quality Modes + +| Mode | Value | Description | +|------|-------|-----------------------------------------------------| +| AUTO | 1 | Discord chooses the quality for optimal performance | +| FULL | 2 | 720p | + + +###### Channel Flags + +| Flag | Value | Description | +|-----------------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| PINNED | `1 << 1` | this thread is pinned to the top of its parent `GUILD_FORUM` or `GUILD_MEDIA` channel | +| REQUIRE_TAG | `1 << 4` | whether a tag is required to be specified when creating a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel. Tags are specified in the `applied_tags` field. | +| HIDE_MEDIA_DOWNLOAD_OPTIONS | `1 << 15` | when set hides the embedded media download options. Available only for media channels | + + +###### Sort Order Types + +| Flag | Value | Description | +|-----------------|-------|----------------------------------------------------------------| +| LATEST_ACTIVITY | 0 | Sort forum posts by activity | +| CREATION_DATE | 1 | Sort forum posts by creation time (from most recent to oldest) | + + +###### Forum Layout Types + +| Flag | Value | Description | +|--------------|-------|-------------------------------------------| +| NOT_SET | 0 | No default has been set for forum channel | +| LIST_VIEW | 1 | Display posts as a list | +| GALLERY_VIEW | 2 | Display posts as a collection of tiles | + + +###### Example Guild Text Channel + +```json +{ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "name": "general", + "type": 0, + "position": 6, + "permission_overwrites": [], + "rate_limit_per_user": 2, + "nsfw": true, + "topic": "24/7 chat about how to gank Mike #2", + "last_message_id": "155117677105512449", + "parent_id": "399942396007890945", + "default_auto_archive_duration": 60 +} +``` + + +###### Example Guild Announcement Channel + +Bots can post or publish messages in this type of channel if they have the proper permissions. + +```json +{ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "name": "important-news", + "type": 5, + "position": 6, + "permission_overwrites": [], + "nsfw": true, + "topic": "Rumors about Half Life 3", + "last_message_id": "155117677105512449", + "parent_id": "399942396007890945", + "default_auto_archive_duration": 60 +} +``` + + +###### Example Guild Voice Channel + +```json +{ + "id": "155101607195836416", + "last_message_id": "174629835082649376", + "type": 2, + "name": "ROCKET CHEESE", + "position": 5, + "parent_id": null, + "bitrate": 64000, + "user_limit": 0, + "rtc_region": null, + "guild_id": "41771983423143937", + "permission_overwrites": [], + "rate_limit_per_user": 0, + "nsfw": false, +} +``` + + +###### Example DM Channel + +```json +{ + "last_message_id": "3343820033257021450", + "type": 1, + "id": "319674150115610528", + "recipients": [ + { + "username": "test", + "discriminator": "9999", + "id": "82198898841029460", + "avatar": "33ecab261d4681afa4d85a04691c4a01" + } + ] +} +``` + + +###### Example Group DM Channel + +```json +{ + "name": "Some test channel", + "icon": null, + "recipients": [ + { + "username": "test", + "discriminator": "9999", + "id": "82198898841029460", + "avatar": "33ecab261d4681afa4d85a04691c4a01" + }, + { + "username": "test2", + "discriminator": "9999", + "id": "82198810841029460", + "avatar": "33ecab261d4681afa4d85a10691c4a01" + } + ], + "last_message_id": "3343820033257021450", + "type": 3, + "id": "319674150115710528", + "owner_id": "82198810841029460" +} +``` + + +###### Example Channel Category + +```json +{ + "permission_overwrites": [], + "name": "Test", + "parent_id": null, + "nsfw": false, + "position": 0, + "guild_id": "290926798629997250", + "type": 4, + "id": "399942396007890945" +} +``` + + +###### Example Thread Channel + +[Threads](/developers/docs/topics/threads) can be either `archived` or `active`. Archived threads are generally immutable. To send a message or add a reaction, a thread must first be unarchived. The API will helpfully automatically unarchive a thread when sending a message in that thread. + +Unlike with channels, the API will only sync updates to users about threads the current user can view. When receiving a [guild create](/developers/docs/events/gateway-events#guild-create) payload, the API will only include active threads the current user can view. Threads inside of private channels are completely private to the members of that private channel. As such, when _gaining_ access to a channel the API sends a [thread list sync](/developers/docs/events/gateway-events#thread-list-sync), which includes all active threads in that channel. + +Threads also track membership. Users must be added to a thread before sending messages in them. The API will helpfully automatically add users to a thread when sending a message in that thread. + +Guilds have limits on the number of active threads and members per thread. Once these are reached additional threads cannot be created or unarchived, and users cannot be added. Threads do not count against the per-guild channel limit. + +The [threads](/developers/docs/topics/threads) topic has some more information. + +```json +{ + "id": "41771983423143937", + "guild_id": "41771983423143937", + "parent_id": "41771983423143937", + "owner_id": "41771983423143937", + "name": "don't buy dota-2", + "type": 11, + "last_message_id": "155117677105512449", + "message_count": 1, + "member_count": 5, + "rate_limit_per_user": 2, + "thread_metadata": { + "archived": false, + "auto_archive_duration": 1440, + "archive_timestamp": "2021-04-12T23:40:39.855793+00:00", + "locked": false + }, + "total_message_sent": 1 +} +``` + +### Followed Channel Object + + +###### Followed Channel Structure + +| Field | Type | Description | +|------------|-----------|---------------------------| +| channel_id | snowflake | source channel id | +| webhook_id | snowflake | created target webhook id | + +### Overwrite Object + +See [permissions](/developers/docs/topics/permissions) for more information about the `allow` and `deny` fields. + + +###### Overwrite Structure + +| Field | Type | Description | +|-------|-----------|-------------------------------| +| id | snowflake | role or user id | +| type | int | either 0 (role) or 1 (member) | +| allow | string | permission bit set | +| deny | string | permission bit set | + +### Thread Metadata Object + +The thread metadata object contains a number of thread-specific channel fields that are not needed by other channel types. + + +###### Thread Metadata Structure + +| Field | Type | Description | +|-----------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| archived | boolean | whether the thread is archived | +| auto_archive_duration | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 | +| archive_timestamp | ISO8601 timestamp | timestamp when the thread's archive status was last changed, used for calculating recent activity | +| locked | boolean | whether the thread is locked; when a thread is locked, only users with MANAGE_THREADS can unarchive it | +| invitable? | boolean | whether non-moderators can add other non-moderators to a thread; only available on private threads | +| create_timestamp? | ?ISO8601 timestamp | timestamp when the thread was created; only populated for threads created after 2022-01-09 | + +### Thread Member Object + +A thread member object contains information about a user that has joined a thread. + + +###### Thread Member Structure + +| Field | Type | Description | +|-----------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------| +| id? \* | snowflake | ID of the thread | +| user_id? \* | snowflake | ID of the user | +| join_timestamp | ISO8601 timestamp | Time the user last joined the thread | +| flags | integer | Any user-thread settings, currently only used for notifications | +| member? \* \*\* | [guild member](/developers/docs/resources/guild#guild-member-object) object | Additional information about the user | + +\* These fields are omitted on the member sent within each thread in the [GUILD_CREATE](/developers/docs/events/gateway-events#guild-create) event. + +\*\* The `member` field is only present when `with_member` is set to `true` when calling [List Thread Members](/developers/docs/resources/channel#list-thread-members) or [Get Thread Member](/developers/docs/resources/channel#get-thread-member). + +### Default Reaction Object + +An object that specifies the emoji to use as the default way to react to a forum post. Exactly one of `emoji_id` and `emoji_name` must be set. + + +###### Default Reaction Structure + +| Field | Type | Description | +|------------|------------|------------------------------------| +| emoji_id | ?snowflake | the id of a guild's custom emoji | +| emoji_name | ?string | the unicode character of the emoji | + +### Forum Tag Object + +An object that represents a tag that is able to be applied to a thread in a `GUILD_FORUM` or `GUILD_MEDIA` channel. + + +###### Forum Tag Structure + + +When updating a `GUILD_FORUM` or a `GUILD_MEDIA` channel, tag objects in `available_tags` only require the `name` field. + + +| Field | Type | Description | +|------------|------------|----------------------------------------------------------------------------------------------------------------| +| id | snowflake | the id of the tag | +| name | string | the name of the tag (0-20 characters) | +| moderated | boolean | whether this tag can only be added to or removed from threads by a member with the `MANAGE_THREADS` permission | +| emoji_id | ?snowflake | the id of a guild's custom emoji \* | +| emoji_name | ?string | the unicode character of the emoji \* | + +\* At most one of `emoji_id` and `emoji_name` may be set to a non-null value. + +## Get Channel +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object) + +Get a channel by ID. Returns a [channel](/developers/docs/resources/channel#channel-object) object. If the channel is a thread, a [thread member](/developers/docs/resources/channel#thread-member-object) object is included in the returned result. + +## Modify Channel +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object) + +Update a channel's settings. Returns a [channel](/developers/docs/resources/channel#channel-object) on success, and a 400 BAD REQUEST on invalid parameters. + + +All parameters to this endpoint are optional + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params (Group DM) + +Fires a [Channel Update](/developers/docs/events/gateway-events#channel-update) Gateway event. + +| Field | Type | Description | +|-------|--------|------------------------------| +| name | string | 1-100 character channel name | +| icon | binary | base64 encoded icon | + + +###### JSON Params (Guild channel) + +Requires the `MANAGE_CHANNELS` permission for the guild. Fires a [Channel Update](/developers/docs/events/gateway-events#channel-update) Gateway event. If modifying a category, individual [Channel Update](/developers/docs/events/gateway-events#channel-update) events will fire for each child channel that also changes. If modifying permission overwrites, the `MANAGE_ROLES` permission is required. Only permissions your bot has in the guild or parent channel (if applicable) can be allowed/denied (unless your bot has a `MANAGE_ROLES` overwrite in the channel). + +| Field | Type | Description | Channel Type | +|------------------------------------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------| +| name | string | 1-100 character channel name | All | +| type | integer | the [type of channel](/developers/docs/resources/channel#channel-object-channel-types); only conversion between text and announcement is supported and only in guilds with the "NEWS" feature | Text, Announcement | +| position | ?integer | the position of the channel in the left-hand listing (channels with the same position are sorted by id) | All | +| topic | ?string | 0-1024 character channel topic (0-4096 characters for `GUILD_FORUM` and `GUILD_MEDIA` channels) | Text, Announcement, Forum, Media | +| nsfw | ?boolean | whether the channel is nsfw | Text, Voice, Announcement, Stage, Forum, Media | +| rate_limit_per_user | ?integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages` or `manage_channel`, are unaffected | Text, Voice, Stage, Forum, Media | +| bitrate\* | ?integer | the bitrate (in bits) of the voice or stage channel; min 8000 | Voice, Stage | +| user_limit | ?integer | the user limit of the voice or stage channel, max 99 for voice channels and 10,000 for stage channels (0 refers to no limit) | Voice, Stage | +| permission_overwrites\*\* | ?array of partial [overwrite](/developers/docs/resources/channel#overwrite-object) objects | channel or category-specific permissions | All | +| parent_id | ?snowflake | id of the new parent category for a channel | Text, Voice, Announcement, Stage, Forum, Media | +| rtc_region | ?string | channel [voice region](/developers/docs/resources/voice#voice-region-object) id, automatic when set to null | Voice, Stage | +| video_quality_mode | ?integer | the camera [video quality mode](/developers/docs/resources/channel#channel-object-video-quality-modes) of the voice channel | Voice, Stage | +| default_auto_archive_duration | ?integer | the default duration that the clients use (not the API) for newly created threads in the channel, in minutes, to automatically archive the thread after recent activity | Text, Announcement, Forum, Media | +| flags | integer | [channel flags](/developers/docs/resources/channel#channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field). Currently only `REQUIRE_TAG` (`1 << 4`) is supported by `GUILD_FORUM` and `GUILD_MEDIA` channels. `HIDE_MEDIA_DOWNLOAD_OPTIONS` (`1 << 15`) is supported only by `GUILD_MEDIA` channels | Forum, Media | +| available_tags | array of [tag](/developers/docs/resources/channel#forum-tag-object) objects | the set of tags that can be used in a `GUILD_FORUM` or a `GUILD_MEDIA` channel; limited to 20 | Forum, Media | +| default_reaction_emoji | ?[default reaction](/developers/docs/resources/channel#default-reaction-object) object | the emoji to show in the add reaction button on a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | Forum, Media | +| default_thread_rate_limit_per_user | integer | the initial `rate_limit_per_user` to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update. | Text, Forum, Media | +| default_sort_order | ?integer | the [default sort order type](/developers/docs/resources/channel#channel-object-sort-order-types) used to order posts in `GUILD_FORUM` and `GUILD_MEDIA` channels | Forum, Media | +| default_forum_layout | integer | the [default forum layout type](/developers/docs/resources/channel#channel-object-forum-layout-types) used to display posts in `GUILD_FORUM` channels | Forum | + +\* For voice channels, normal servers can set bitrate up to 96000, servers with Boost level 1 can set up to 128000, servers with Boost level 2 can set up to 256000, and servers with Boost level 3 or the `VIP_REGIONS` [guild feature](/developers/docs/resources/guild#guild-object-guild-features) can set up to 384000. For stage channels, bitrate can be set up to 64000. + +\*\* In each overwrite object, the `allow` and `deny` keys can be omitted or set to `null`, which both default to `"0"`. + + +###### JSON Params (Thread) + +When setting `archived` to `false`, when `locked` is also `false`, only the `SEND_MESSAGES` permission is required. + +Otherwise, requires the `MANAGE_THREADS` permission. Fires a [Thread Update](/developers/docs/events/gateway-events#thread-update) Gateway event. Requires the thread to have `archived` set to `false` or be set to `false` in the request. + +| Field | Type | Description | +|-----------------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | 1-100 character channel name | +| archived | boolean | whether the thread is archived | +| auto_archive_duration | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 | +| locked | boolean | whether the thread is locked; when a thread is locked, only users with MANAGE_THREADS can unarchive it | +| invitable | boolean | whether non-moderators can add other non-moderators to a thread; only available on private threads | +| rate_limit_per_user | ?integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages`, `manage_thread`, or `manage_channel`, are unaffected | +| flags? | integer | [channel flags](/developers/docs/resources/channel#channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field); `PINNED` can only be set for threads in forum and media channels | +| applied_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel; limited to 5 | + +## Delete/Close Channel +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object) + +Delete a channel, or close a private message. Requires the `MANAGE_CHANNELS` permission for the guild, or `MANAGE_THREADS` if the channel is a thread. Deleting a category does not delete its child channels; they will have their `parent_id` removed and a [Channel Update](/developers/docs/events/gateway-events#channel-update) Gateway event will fire for each of them. Returns a [channel](/developers/docs/resources/channel#channel-object) object on success. Fires a [Channel Delete](/developers/docs/events/gateway-events#channel-delete) Gateway event (or [Thread Delete](/developers/docs/events/gateway-events#thread-delete) if the channel was a thread). + + +Deleting a guild channel cannot be undone. Use this with caution, as it is impossible to undo this action when performed on a guild channel. In contrast, when used with a private message, it is possible to undo the action by opening a private message with the recipient again. + + + +For Community guilds, the Rules or Guidelines channel and the Community Updates channel cannot be deleted. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Edit Channel Permissions +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/permissions/[\{overwrite.id\}](/developers/docs/resources/channel#overwrite-object) + +Edit the channel permission overwrites for a user or role in a channel. Only usable for guild channels. Requires the `MANAGE_ROLES` permission. Only permissions your bot has in the guild or parent channel (if applicable) can be allowed/denied (unless your bot has a `MANAGE_ROLES` overwrite in the channel). Returns a 204 empty response on success. Fires a [Channel Update](/developers/docs/events/gateway-events#channel-update) Gateway event. For more information about permissions, see [permissions](/developers/docs/topics/permissions). + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|--------|---------|-----------------------------------------------------------------| +| allow? | string? | the bitwise value of all allowed permissions (default `"0"`) | +| deny? | string? | the bitwise value of all disallowed permissions (default `"0"`) | +| type | integer | 0 for a role or 1 for a member | + +## Get Channel Invites +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/invites + +Returns a list of [invite](/developers/docs/resources/invite#invite-object) objects (with [invite metadata](/developers/docs/resources/invite#invite-metadata-object)) for the channel. Only usable for guild channels. Requires the `MANAGE_CHANNELS` permission. + +## Create Channel Invite +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/invites + +Create a new [invite](/developers/docs/resources/invite#invite-object) object for the channel. Only usable for guild channels. Requires the `CREATE_INSTANT_INVITE` permission. All JSON parameters for this route are optional, however the request body is not. If you are not sending any fields, you still have to send an empty JSON object (`{}`). Returns an [invite](/developers/docs/resources/invite#invite-object) object. Fires an [Invite Create](/developers/docs/events/gateway-events#invite-create) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Default | +|-----------------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------|------------------| +| max_age | integer | duration of invite in seconds before expiry, or 0 for never. between 0 and 604800 (7 days) | 86400 (24 hours) | +| max_uses | integer | max number of uses or 0 for unlimited. between 0 and 100 | 0 | +| temporary | boolean | whether this invite only grants temporary membership | false | +| unique | boolean | if true, don't try to reuse a similar invite (useful for creating many unique one time use invites) | false | +| target_type | integer | the [type of target](/developers/docs/resources/invite#invite-object-invite-target-types) for this voice channel invite | | +| target_user_id | snowflake | the id of the user whose stream to display for this invite, required if `target_type` is 1, the user must be streaming in the channel | | +| target_application_id | snowflake | the id of the embedded application to open for this invite, required if `target_type` is 2, the application must have the `EMBEDDED` flag | | + +## Delete Channel Permission +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/permissions/[\{overwrite.id\}](/developers/docs/resources/channel#overwrite-object) + +Delete a channel permission overwrite for a user or role in a channel. Only usable for guild channels. Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Channel Update](/developers/docs/events/gateway-events#channel-update) Gateway event. For more information about permissions, see [permissions](/developers/docs/topics/permissions) + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Follow Announcement Channel +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/followers + +Follow an Announcement Channel to send messages to a target channel. Requires the `MANAGE_WEBHOOKS` permission in the target channel. Returns a [followed channel](/developers/docs/resources/channel#followed-channel-object) object. Fires a [Webhooks Update](/developers/docs/events/gateway-events#webhooks-update) Gateway event for the target channel. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|--------------------|-----------|----------------------| +| webhook_channel_id | snowflake | id of target channel | + +## Trigger Typing Indicator +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/typing + +Post a typing indicator for the specified channel, which expires after 10 seconds. Returns a 204 empty response on success. Fires a [Typing Start](/developers/docs/events/gateway-events#typing-start) Gateway event. + +Generally bots should **not** use this route. However, if a bot is responding to a command and expects the computation to take a few seconds, this endpoint may be called to let the user know that the bot is processing their message. + +## Group DM Add Recipient +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/recipients/[\{user.id\}](/developers/docs/resources/user#user-object) + +Adds a recipient to a Group DM using their access token. + + +###### JSON Params + +| Field | Type | Description | +|--------------|--------|-----------------------------------------------------------------------| +| access_token | string | access token of a user that has granted your app the `gdm.join` scope | +| nick | string | nickname of the user being added | + +## Group DM Remove Recipient +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/recipients/[\{user.id\}](/developers/docs/resources/user#user-object) + +Removes a recipient from a Group DM. + +## Start Thread from Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object)/threads + +Creates a new thread from an existing message. Returns a [channel](/developers/docs/resources/channel#channel-object) on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](/developers/docs/events/gateway-events#thread-create) and a [Message Update](/developers/docs/events/gateway-events#message-update) Gateway event. + +When called on a `GUILD_TEXT` channel, creates a `PUBLIC_THREAD`. When called on a `GUILD_ANNOUNCEMENT` channel, creates a `ANNOUNCEMENT_THREAD`. Does not work on a [`GUILD_FORUM`](/developers/docs/resources/channel#start-thread-in-forum-or-media-channel) or a `GUILD_MEDIA` channel. The id of the created thread will be the same as the id of the source message, and as such a message can only have a single thread created from it. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | 1-100 character channel name | +| auto_archive_duration? | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 | +| rate_limit_per_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) | + +## Start Thread without Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/threads + +Creates a new thread that is not connected to an existing message. Returns a [channel](/developers/docs/resources/channel#channel-object) on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](/developers/docs/events/gateway-events#thread-create) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|------------------------|----------|--------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | 1-100 character channel name | +| auto_archive_duration? | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 | +| type?\* | integer | the [type of thread](/developers/docs/resources/channel#channel-object-channel-types) to create | +| invitable? | boolean | whether non-moderators can add other non-moderators to a thread; only available when creating a private thread | +| rate_limit_per_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) | + +\* `type` currently defaults to `PRIVATE_THREAD` in order to match the behavior when thread documentation was first published. In a future API version this will be changed to be a required field, with no default. + +## Start Thread in Forum or Media Channel +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/threads + +Creates a new thread in a forum or a media channel, and sends a message within the created thread. Returns a [channel](/developers/docs/resources/channel#channel-object), with a nested [message](/developers/docs/resources/message#message-object) object, on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](/developers/docs/events/gateway-events#thread-create) and [Message Create](/developers/docs/events/gateway-events#message-create) Gateway event. + +- The type of the created thread is `PUBLIC_THREAD`. +- See [message formatting](/developers/docs/reference#message-formatting) for more information on how to properly format messages. +- The current user must have the `SEND_MESSAGES` permission (`CREATE_PUBLIC_THREADS` is ignored). +- The maximum request size when sending a message is **25 MiB**. +- For the embed object, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images. +- Examples for file uploads are available in [Uploading Files](/developers/docs/reference#uploading-files). +- Files must be attached using a `multipart/form-data` body as described in [Uploading Files](/developers/docs/reference#uploading-files). +- Note that when sending a message, you must provide a value for at **least one of** `content`, `embeds`, `sticker_ids`, `components`, or `files[n]`. + + +Discord may strip certain characters from message content, like invalid unicode characters or characters which cause unexpected message formatting. If you are passing user-generated strings into message content, consider sanitizing the data to prevent unexpected behavior and using `allowed_mentions` to prevent unexpected mentions. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON/Form Params + +| Field | Type | Description | +|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | 1-100 character channel name | +| auto_archive_duration?\* | integer | duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 | +| rate_limit_per_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) | +| message | a [forum thread message params](/developers/docs/resources/channel#start-thread-in-forum-or-media-channel-forum-and-media-thread-message-params-object) object | contents of the first message in the forum/media thread | +| applied_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | +| files[n]?\* | file contents | Contents of the file being sent. See [Uploading Files](/developers/docs/reference#uploading-files) | +| payload_json? | string | JSON-encoded body of non-file params, only for `multipart/form-data` requests. See [Uploading Files](/developers/docs/reference#uploading-files) | + + +###### Forum and Media Thread Message Params Object + + +When sending a message, apps must provide a value for **at least one of** `content`, `embeds`, `sticker_ids`, `components`, or `files[n]`. + + +| Field | Type | Description | +|-------------------|----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| content?\* | string | Message contents (up to 2000 characters) | +| embeds?\* | array of [embed](/developers/docs/resources/message#embed-object) objects | Up to 10 `rich` embeds (up to 6000 characters) | +| allowed_mentions? | [allowed mention object](/developers/docs/resources/message#allowed-mentions-object) | Allowed mentions for the message | +| components?\* | array of [message component](/developers/docs/components/reference#component-object) objects | Components to include with the message | +| sticker_ids?\* | array of snowflakes | IDs of up to 3 [stickers](/developers/docs/resources/sticker#sticker-object) in the server to send in the message | +| attachments? | array of partial [attachment](/developers/docs/resources/message#attachment-object) objects | Attachment objects with `filename` and `description`. See [Uploading Files](/developers/docs/reference#uploading-files) | +| flags? | integer | [Message flags](/developers/docs/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS` and `SUPPRESS_NOTIFICATIONS` can be set) | + +\* At least one of `content`, `embeds`, `sticker_ids`, `components`, or `files[n]` is required. + +## Join Thread +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/thread-members/@me + +Adds the current user to a thread. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) and a [Thread Create](/developers/docs/events/gateway-events#thread-create) Gateway event. + +## Add Thread Member +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/thread-members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Adds another member to a thread. Requires the ability to send messages in the thread. Also requires the thread is not archived. Returns a 204 empty response if the member is successfully added or was already a member of the thread. Fires a [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) Gateway event. + +## Leave Thread +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/thread-members/@me + +Removes the current user from a thread. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) Gateway event. + +## Remove Thread Member +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/thread-members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Removes another member from a thread. Requires the `MANAGE_THREADS` permission, or the creator of the thread if it is a `PRIVATE_THREAD`. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) Gateway event. + +## Get Thread Member +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/thread-members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Returns a [thread member](/developers/docs/resources/channel#thread-member-object) object for the specified user if they are a member of the thread, returns a 404 response otherwise. + +When `with_member` is set to `true`, the thread member object will include a `member` field containing a [guild member](/developers/docs/resources/guild#guild-member-object) object. + + +###### Query String Params + +| Field | Type | Description | +|--------------|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| +| with_member? | [boolean](/developers/docs/reference#boolean-query-strings) | Whether to include a [guild member](/developers/docs/resources/guild#guild-member-object) object for the thread member | + +## List Thread Members +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/thread-members + + +Starting in API v11, this endpoint will always return paginated results. Paginated results can be enabled before API v11 by setting `with_member` to `true`. Read [the changelog](/developers/docs/change-log#thread-member-details-and-pagination) for details. + + +Returns array of [thread members](/developers/docs/resources/channel#thread-member-object) objects that are members of the thread. + +When `with_member` is set to `true`, the results will be paginated and each thread member object will include a `member` field containing a [guild member](/developers/docs/resources/guild#guild-member-object) object. + + +This endpoint is restricted according to whether the `GUILD_MEMBERS` [Privileged Intent](/developers/docs/events/gateway#privileged-intents) is enabled for your application. + + + +###### Query String Params + +| Field | Type | Description | +|--------------|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| with_member? | [boolean](/developers/docs/reference#boolean-query-strings) | Whether to include a [guild member](/developers/docs/resources/guild#guild-member-object) object for each thread member | +| after? | snowflake | Get thread members after this user ID | +| limit? | integer | Max number of thread members to return (1-100). Defaults to 100. | + +## List Public Archived Threads +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/threads/archived/public + +Returns archived threads in the channel that are public. When called on a `GUILD_TEXT` channel, returns threads of [type](/developers/docs/resources/channel#channel-object-channel-types) `PUBLIC_THREAD`. When called on a `GUILD_ANNOUNCEMENT` channel returns threads of [type](/developers/docs/resources/channel#channel-object-channel-types) `ANNOUNCEMENT_THREAD`. Threads are ordered by `archive_timestamp`, in descending order. Requires the `READ_MESSAGE_HISTORY` permission. + + +###### Query String Params + +| Field | Type | Description | +|---------|-------------------|------------------------------------------------| +| before? | ISO8601 timestamp | returns threads archived before this timestamp | +| limit? | integer | optional maximum number of threads to return | + + +###### Response Body + +| Field | Type | Description | +|----------|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------| +| threads | array of [channel](/developers/docs/resources/channel#channel-object) objects | the public, archived threads | +| members | array of [thread members](/developers/docs/resources/channel#thread-member-object) objects | a thread member object for each returned thread the current user has joined | +| has_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call | + +## List Private Archived Threads +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/threads/archived/private + +Returns archived threads in the channel that are of [type](/developers/docs/resources/channel#channel-object-channel-types) `PRIVATE_THREAD`. Threads are ordered by `archive_timestamp`, in descending order. Requires both the `READ_MESSAGE_HISTORY` and `MANAGE_THREADS` permissions. + + +###### Query String Params + +| Field | Type | Description | +|---------|-------------------|------------------------------------------------| +| before? | ISO8601 timestamp | returns threads archived before this timestamp | +| limit? | integer | optional maximum number of threads to return | + + +###### Response Body + +| Field | Type | Description | +|----------|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------| +| threads | array of [channel](/developers/docs/resources/channel#channel-object) objects | the private, archived threads | +| members | array of [thread members](/developers/docs/resources/channel#thread-member-object) objects | a thread member object for each returned thread the current user has joined | +| has_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call | + +## List Joined Private Archived Threads +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/users/@me/threads/archived/private + +Returns archived threads in the channel that are of [type](/developers/docs/resources/channel#channel-object-channel-types) `PRIVATE_THREAD`, and the user has joined. Threads are ordered by their `id`, in descending order. Requires the `READ_MESSAGE_HISTORY` permission. + + +###### Query String Params + +| Field | Type | Description | +|---------|-----------|----------------------------------------------| +| before? | snowflake | returns threads before this id | +| limit? | integer | optional maximum number of threads to return | + + +###### Response Body + +| Field | Type | Description | +|----------|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------| +| threads | array of [channel](/developers/docs/resources/channel#channel-object) objects | the private, archived threads the current user has joined | +| members | array of [thread members](/developers/docs/resources/channel#thread-member-object) objects | a thread member object for each returned thread the current user has joined | +| has_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call | diff --git a/discord/developers/docs/resources/emoji.mdx b/discord/developers/docs/resources/emoji.mdx new file mode 100644 index 0000000000..379bc124e9 --- /dev/null +++ b/discord/developers/docs/resources/emoji.mdx @@ -0,0 +1,244 @@ +--- +title: Emoji Resource +sidebarTitle: Emoji +description: Reference for Discord emoji objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + + +Routes for controlling emojis do not follow the normal rate limit conventions. These routes are specifically limited on a per-guild basis to prevent abuse. This means that the quota returned by our APIs may be inaccurate, and you may encounter 429s. + + +### Emoji Object + + +###### Emoji Structure + +| Field | Type | Description | +|-----------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------| +| id | ?snowflake | [emoji id](/developers/docs/reference#image-formatting) | +| name | ?string (can be null only in reaction emoji objects) | emoji name | +| roles? | array of [role](/developers/docs/topics/permissions#role-object) object ids | roles allowed to use this emoji | +| user? | [user](/developers/docs/resources/user#user-object) object | user that created this emoji | +| require_colons? | boolean | whether this emoji must be wrapped in colons | +| managed? | boolean | whether this emoji is managed | +| animated? | boolean | whether this emoji is animated | +| available? | boolean | whether this emoji can be used, may be false due to loss of Server Boosts | + + +###### Premium Emoji + +Roles with the `integration_id` tag being the guild's guild_subscription integration are considered subscription roles. +An emoji cannot have both subscription roles and non-subscription roles. +Emojis with subscription roles are considered premium emoji, and count toward a separate limit of 25. +Emojis cannot be converted between normal and premium after creation. + + +###### Emoji Formats + +Emoji can be uploaded as JPEG, PNG, GIF, WebP, and AVIF formats. All emoji (regardless of original format) can be served as WebP. We highly recommend that developers use the `.webp` extension when fetching emoji so they're rendered as WebP for maximum performance and compatibility. The Discord client uses WebP for all emoji displayed in-app. + +Still WebP emoji can be requested using the `.webp` file extension. For animated WebP emoji, use the `.webp` extension with the `?animated=true` query parameter. + + +###### Application-Owned Emoji + +An application can own up to 2000 emojis that can only be used by that app. +App emojis can be managed using the API with a bot token, or using the app's settings in the portal. +The `USE_EXTERNAL_EMOJIS` permission is not required to use app emojis. +The `user` field of an app emoji object represents the team member that uploaded the emoji from the app's settings, or the bot user if uploaded using the API. + + +###### Emoji Example + +```json +{ + "id": "41771983429993937", + "name": "LUL", + "roles": ["41771983429993000", "41771983429993111"], + "user": { + "username": "Luigi", + "discriminator": "0002", + "id": "96008815106887111", + "avatar": "5500909a3274e1812beb4e8de6631111", + "public_flags": 131328 + }, + "require_colons": true, + "managed": false, + "animated": false +} +``` + + +###### Standard Emoji Example + +```json +{ + "id": null, + "name": "🔥" +} +``` + + +###### Custom Emoji Examples + + +In `MESSAGE_REACTION_ADD`, `MESSAGE_REACTION_REMOVE` and `MESSAGE_REACTION_REMOVE_EMOJI` gateway events `animated` will be returned for animated emoji. + + + +In `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE` gateway events `name` may be `null` when custom emoji data is not available (for example, if it was deleted from the guild). + + +```json +{ + "id": "41771983429993937", + "name": "LUL", + "animated": true +} +``` + +```json +{ + "id": "41771983429993937", + "name": null +} +``` + +## List Guild Emojis +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/emojis + +Returns a list of [emoji](/developers/docs/resources/emoji#emoji-object) objects for the given guild. Includes `user` fields if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + +## Get Guild Emoji +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/emojis/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object) + +Returns an [emoji](/developers/docs/resources/emoji#emoji-object) object for the given guild and emoji IDs. Includes the `user` field if the bot has the `MANAGE_GUILD_EXPRESSIONS` permission, or if the bot created the emoji and has the the `CREATE_GUILD_EXPRESSIONS` permission. + +## Create Guild Emoji +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/emojis + +Create a new emoji for the guild. Requires the `CREATE_GUILD_EXPRESSIONS` permission. Returns the new [emoji](/developers/docs/resources/emoji#emoji-object) object on success. Fires a [Guild Emojis Update](/developers/docs/events/gateway-events#guild-emojis-update) Gateway event. + + +Emojis and animated emojis have a maximum file size of 256 KiB. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a [JSON status code](/developers/docs/topics/opcodes-and-status-codes#json). + + + +We highly recommend that developers use the `.webp` extension when fetching emoji so they're rendered as WebP for maximum performance and compatibility. See the [Emoji Formats](/developers/docs/resources/emoji#emoji-object-emoji-formats) section above for more details. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|-------|-----------------------------------------------------|---------------------------------| +| name | string | name of the emoji | +| image | [image data](/developers/docs/reference#image-data) | the 128x128 emoji image | +| roles | array of snowflakes | roles allowed to use this emoji | + +## Modify Guild Emoji +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/emojis/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object) + +Modify the given emoji. For emojis created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other emojis, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns the updated [emoji](/developers/docs/resources/emoji#emoji-object) object on success. Fires a [Guild Emojis Update](/developers/docs/events/gateway-events#guild-emojis-update) Gateway event. + + +All parameters to this endpoint are optional. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|-------|----------------------|---------------------------------| +| name | string | name of the emoji | +| roles | ?array of snowflakes | roles allowed to use this emoji | + +## Delete Guild Emoji +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/emojis/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object) + +Delete the given emoji. For emojis created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other emojis, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns `204 No Content` on success. Fires a [Guild Emojis Update](/developers/docs/events/gateway-events#guild-emojis-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## List Application Emojis +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/emojis + +Returns an object containing a list of [emoji](/developers/docs/resources/emoji#emoji-object) objects for the given application under the `items` key. Includes a `user` object for the team member that uploaded the emoji from the app's settings, or for the bot user if uploaded using the API. + +```json +{ + "items": [ + { + "id": "41771983429993937", + "name": "LUL", + "roles": [], + "user": { + "username": "Luigi", + "discriminator": "0002", + "id": "96008815106887111", + "avatar": "5500909a3274e1812beb4e8de6631111", + "public_flags": 131328 + }, + "require_colons": true, + "managed": false, + "animated": false + } + ] +} +``` + +## Get Application Emoji +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/emojis/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object) + +Returns an [emoji](/developers/docs/resources/emoji#emoji-object) object for the given application and emoji IDs. Includes the `user` field. + +## Create Application Emoji +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/emojis + +Create a new emoji for the application. Returns the new [emoji](/developers/docs/resources/emoji#emoji-object) object on success. + + +Emojis and animated emojis have a maximum file size of 256 KiB. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a [JSON status code](/developers/docs/topics/opcodes-and-status-codes#json). + + + +We highly recommend that developers use the `.webp` extension when fetching emoji so they're rendered as WebP for maximum performance and compatibility. See the [Emoji Formats](/developers/docs/resources/emoji#emoji-object-emoji-formats) section above for more details. + + + +###### JSON Params + +| Field | Type | Description | +|-------|-----------------------------------------------------|-------------------------| +| name | string | name of the emoji | +| image | [image data](/developers/docs/reference#image-data) | the 128x128 emoji image | + +## Modify Application Emoji +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/emojis/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object) + +Modify the given emoji. Returns the updated [emoji](/developers/docs/resources/emoji#emoji-object) object on success. + + +###### JSON Params + +| Field | Type | Description | +|-------|--------|-------------------| +| name | string | name of the emoji | + +## Delete Application Emoji +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/emojis/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object) + +Delete the given emoji. Returns `204 No Content` on success. diff --git a/discord/developers/docs/resources/entitlement.mdx b/discord/developers/docs/resources/entitlement.mdx new file mode 100644 index 0000000000..8688f56b43 --- /dev/null +++ b/discord/developers/docs/resources/entitlement.mdx @@ -0,0 +1,167 @@ +--- +title: Entitlement Resource +sidebarTitle: Entitlement +description: Reference for Discord entitlement objects that represent user access to premium features. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +Entitlements in Discord represent that a user or guild has access to a premium offering in your application. + +Refer to the [Monetization Overview](/developers/docs/monetization/overview) for more information on how to use Entitlements in your app. + +### Entitlement Object + + +###### Entitlement Structure + +| Field | Type | Description | +|----------------|--------------------|----------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the entitlement | +| sku_id | snowflake | ID of the SKU | +| application_id | snowflake | ID of the parent application | +| user_id? | snowflake | ID of the user that is granted access to the entitlement's sku | +| type | integer | [Type of entitlement](/developers/docs/resources/entitlement#entitlement-object-entitlement-types) | +| deleted | boolean | Entitlement was deleted | +| starts_at | ?ISO8601 timestamp | Start date at which the entitlement is valid. | +| ends_at | ?ISO8601 timestamp | Date at which the entitlement is no longer valid. | +| guild_id? | snowflake | ID of the guild that is granted access to the entitlement's sku | +| consumed? | boolean | For consumable items, whether or not the entitlement has been consumed | + + +###### Entitlement Example + +```json +{ + "id": "1019653849998299136", + "sku_id": "1019475255913222144", + "application_id": "1019370614521200640", + "user_id": "771129655544643584", + "promotion_id": null, + "type": 8, + "deleted": false, + "gift_code_flags": 0, + "consumed": false, + "starts_at": "2022-09-14T17:00:18.704163+00:00", + "ends_at": "2022-10-14T17:00:18.704163+00:00", + "guild_id": "1015034326372454400", + "subscription_id": "1019653835926409216" +} +``` + + +###### Entitlement Types + +| Type | Value | Description | +|--------------------------|-------|----------------------------------------------------------------| +| PURCHASE | 1 | Entitlement was purchased by user | +| PREMIUM_SUBSCRIPTION | 2 | Entitlement for Discord Nitro subscription | +| DEVELOPER_GIFT | 3 | Entitlement was gifted by developer | +| TEST_MODE_PURCHASE | 4 | Entitlement was purchased by a dev in application test mode | +| FREE_PURCHASE | 5 | Entitlement was granted when the SKU was free | +| USER_GIFT | 6 | Entitlement was gifted by another user | +| PREMIUM_PURCHASE | 7 | Entitlement was claimed by user for free as a Nitro Subscriber | +| APPLICATION_SUBSCRIPTION | 8 | Entitlement was purchased as an app subscription | + +## List Entitlements +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/entitlements + +Returns all entitlements for a given app, active and expired. + + +###### Query String Params + +| param | type | description | +|------------------|-------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| user_id? | snowflake | User ID to look up entitlements for | +| sku_ids? | comma-delimited set of snowflakes | Optional list of SKU IDs to check entitlements for | +| before? | snowflake | Retrieve entitlements before this entitlement ID | +| after? | snowflake | Retrieve entitlements after this entitlement ID | +| limit? | integer | Number of entitlements to return, 1-100, default 100 | +| guild_id? | snowflake | Guild ID to look up entitlements for | +| exclude_ended? | [boolean](/developers/docs/reference#boolean-query-strings) | Whether or not ended entitlements should be omitted. Defaults to false, ended entitlements are included by default. | +| exclude_deleted? | [boolean](/developers/docs/reference#boolean-query-strings) | Whether or not deleted entitlements should be omitted. Defaults to true, deleted entitlements are not included by default. | + +```json +[ + { + "id": "1019653849998299136", + "sku_id": "1019475255913222144", + "application_id": "1019370614521200640", + "user_id": "771129655544643584", + "promotion_id": null, + "type": 8, + "deleted": false, + "gift_code_flags": 0, + "consumed": false, + "starts_at": "2022-09-14T17:00:18.704163+00:00", + "ends_at": "2022-10-14T17:00:18.704163+00:00", + "guild_id": "1015034326372454400", + "subscription_id": "1019653835926409216" + } +] +``` + +## Get Entitlement +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/entitlements/[\{entitlement.id\}](/developers/docs/resources/entitlement#entitlement-object) + +Returns an entitlement. + +```json +{ + "id": "1019653849998299136", + "sku_id": "1019475255913222144", + "application_id": "1019370614521200640", + "user_id": "771129655544643584", + "promotion_id": null, + "type": 8, + "deleted": false, + "gift_code_flags": 0, + "consumed": false, + "starts_at": "2022-09-14T17:00:18.704163+00:00", + "ends_at": "2022-10-14T17:00:18.704163+00:00", + "guild_id": "1015034326372454400", + "subscription_id": "1019653835926409216" +} +``` + +## Consume an Entitlement +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/entitlements/[\{entitlement.id\}](/developers/docs/resources/entitlement#entitlement-object)/consume + +For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed. The entitlement will have `consumed: true` when using [List Entitlements](/developers/docs/resources/entitlement#list-entitlements). + +Returns a `204 No Content` on success. + +## Create Test Entitlement +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/entitlements + +Creates a test entitlement to a given SKU for a given guild or user. Discord will act as though that user or guild has entitlement to your premium offering. + +This endpoint returns a partial entitlement object. It will **not** contain `subscription_id`, `starts_at`, or `ends_at`, as it's valid in perpetuity. + +After creating a test entitlement, you'll need to reload your Discord client. After doing so, you'll see that your server or user now has premium access. + + +###### JSON Params + +| param | type | description | +|------------|---------|-----------------------------------------------------------| +| sku_id | string | ID of the SKU to grant the entitlement to | +| owner_id | string | ID of the guild or user to grant the entitlement to | +| owner_type | integer | `1` for a guild subscription, `2` for a user subscription | + +```json +{ + "sku_id": "999184799365857331", + "owner_id": "847184799365857999", + "owner_type": 1 +} +``` + +## Delete Test Entitlement +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/entitlements/[\{entitlement.id\}](/developers/docs/resources/entitlement#entitlement-object) + +Deletes a currently-active test entitlement. Discord will act as though that user or guild _no longer has_ entitlement to your premium offering. + +Returns `204 No Content` on success. diff --git a/discord/developers/docs/resources/guild-scheduled-event.mdx b/discord/developers/docs/resources/guild-scheduled-event.mdx new file mode 100644 index 0000000000..ca5dca476a --- /dev/null +++ b/discord/developers/docs/resources/guild-scheduled-event.mdx @@ -0,0 +1,501 @@ +--- +title: Guild Scheduled Event +sidebarTitle: Guild Scheduled Event +description: Reference for Discord scheduled event objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +A representation of a scheduled event in a [guild](/developers/docs/resources/guild). + +### Guild Scheduled Event Object + + +###### Guild Scheduled Event Structure + +| Field | Type | Description | +|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | the id of the scheduled event | +| guild_id | snowflake | the guild id which the scheduled event belongs to | +| channel_id ** | ?snowflake | the channel id in which the scheduled event will be hosted, or `null` if [scheduled entity type](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types) is `EXTERNAL` | +| creator_id? * | ?snowflake | the id of the user that created the scheduled event * | +| name | string | the name of the scheduled event (1-100 characters) | +| description? | ?string | the description of the scheduled event (1-1000 characters) | +| scheduled_start_time | ISO8601 timestamp | the time the scheduled event will start | +| scheduled_end_time ** | ?ISO8601 timestamp | the time the scheduled event will end, required if entity_type is `EXTERNAL` | +| privacy_level | [privacy level](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event | +| status | [event status](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status) | the status of the scheduled event | +| entity_type | [scheduled entity type](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types) | the type of the scheduled event | +| entity_id | ?snowflake | the id of an entity associated with a guild scheduled event | +| entity_metadata ** | ?[entity metadata](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | additional metadata for the guild scheduled event | +| creator? | [user](/developers/docs/resources/user#user-object) object | the user that created the scheduled event | +| user_count? | integer | the number of users subscribed to the scheduled event | +| image? | ?string | the [cover image hash](/developers/docs/reference#image-formatting) of the scheduled event | +| recurrence_rule | ?[recurrence rule](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object) | the definition for how often this event should recur | + + +\* `creator_id` will be null and `creator` will not be included for events created before October 25th, 2021, when the concept of `creator_id` was introduced and tracked. + +\** See [field requirements by entity type](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-field-requirements-by-entity-type) to understand the relationship between `entity_type` and the following fields: `channel_id`, `entity_metadata`, and `scheduled_end_time` + + +###### Guild Scheduled Event Privacy Level + +| Level | Value | Description | +|------------|-------|---------------------------------------------------------| +| GUILD_ONLY | 2 | the scheduled event is only accessible to guild members | + + +###### Guild Scheduled Event Entity Types + +| Type | Value | +|----------------|-------| +| STAGE_INSTANCE | 1 | +| VOICE | 2 | +| EXTERNAL | 3 | + + +###### Field Requirements By Entity Type + +The following table shows field requirements based on current entity type. + +`value` : This field is required to be a non-null value + +`null` : This field is required to be null + +`-` : No strict requirements + +| Entity Type | channel_id | entity_metadata | scheduled_end_time | +|----------------|------------|-----------------|--------------------| +| STAGE_INSTANCE | value | null | - | +| VOICE | value | null | - | +| EXTERNAL | null | value * | value | + +\* `entity_metadata` with a non-null `location` must be provided + + + +###### Guild Scheduled Event Status + +| Type | Value | +|-------------|-------| +| SCHEDULED | 1 | +| ACTIVE | 2 | +| COMPLETED * | 3 | +| CANCELED * | 4 | + +\* Once `status` is set to `COMPLETED` or `CANCELED`, the `status` can no longer be updated + + +###### Valid Guild Scheduled Event Status Transitions + +SCHEDULED --> ACTIVE + +ACTIVE --------> COMPLETED + +SCHEDULED --> CANCELED + + + +###### Guild Scheduled Event Entity Metadata + +| Field | Type | Description | +|-------------|--------|------------------------------------------| +| location? * | string | location of the event (1-100 characters) | + +\* [required](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata) for events with `'entity_type': EXTERNAL` + +### Guild Scheduled Event User Object + + +###### Guild Scheduled Event User Structure +| Field | Type | Description | +|--------------------------|----------------------------------------------------------------------|-----------------------------------------------------------------------------------| +| guild_scheduled_event_id | snowflake | the scheduled event id which the user subscribed to | +| user | [user](/developers/docs/resources/user#user-object) | user which subscribed to an event | +| member? | [guild member](/developers/docs/resources/guild#guild-member-object) | guild member data for this user for the guild which this event belongs to, if any | + +### Guild Scheduled Event Recurrence Rule Object +Discord's recurrence rule is a subset of the behaviors [defined in the iCalendar RFC](https://datatracker.ietf.org/doc/html/rfc5545) and implemented by [python's dateutil rrule](https://dateutil.readthedocs.io/en/stable/rrule.html) + +There are currently many limitations to this system. Please see "System limitations" below + + + +###### Guild Scheduled Event Recurrence Rule Structure +| Field | Type | Description | +|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| +| start | ISO8601 timestamp | Starting time of the recurrence interval | +| end \[1\] | ?ISO8601 timestamp | Ending time of the recurrence interval | +| frequency | [recurrence rule - frequency](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-frequency) object | How often the event occurs | +| interval | int | The spacing between the events, defined by `frequency`. For example, `frequency` of `WEEKLY` and an `interval` of `2` would be "every-other week" | +| by_weekday | ?array of [recurrence rule - weekday](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-weekday) | Set of specific days within a week for the event to recur on | +| by_n_weekday | ?array of [recurrence rule - n_weekday](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-nweekday-structure) objects | List of specific days within a specific week (1-5) to recur on | +| by_month | ?array of [recurrence rule - month](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-month) | Set of specific months to recur on | +| by_month_day | ?array of int | Set of specific dates within a month to recur on | +| by_year_day \[1\] | ?array of int | Set of days within a year to recur on (1-364) | +| count \[1\] | ?int | The total amount of times that the event is allowed to recur before stopping | + +\[1\] - Cannot be set externally currently. + + +The current system limitations are present due to how reoccurring event data needs to be displayed in the client. +In the future, we would like to open the system up to have fewer / none of these restrictions. + + +###### The following fields cannot be set by the client / application + - `count` + - `end` + - `by_year_day` + + +###### The following combinations are mutually exclusive + - `by_weekday` + - `by_n_weekday` + - `by_month` + `by_month_day` + + +###### `by_weekday` + - Only valid for daily and weekly events (`frequency` of `DAILY` (`0`) or `WEEKLY` (`1`)) + - when used in a daily event (`frequency` is `DAILY` (`0`)) + - The values present in the `by_weekday` event must be a "known set" of weekdays. + - The following are current allowed "sets" + - Monday - Friday (`[0, 1, 2, 3, 4]`) + - Tuesday - Saturday (`[1, 2, 3, 4, 5]`) + - Sunday - Thursday (`[6, 0, 1, 2, 3]`) + - Friday & Saturday (`[4, 5]`) + - Saturday & Sunday (`[5, 6]`) + - Sunday & Monday (`[6, 0]`) + - when used in a weekly event (`frequency` is `WEEKLY` (`1`)) + - `by_weekday` array currently can only be a length of `1` + - i.e: You can only select a single day within a week to have a recurring event on + - If you wish to have multiple days within a week have a recurring event, please use a `frequency` of `DAILY` + - Also, see `interval` below for "every-other" week information + + +###### `by_n_weekday` + - Only valid for monthly events (`frequency` of `MONTHLY` (`1`)) + - `by_n_weekday` array currently can only be a length of `1` + - i.e: You can only select a single day within a month to have a recurring event on + + +###### `by_month` and `by_month_day` + - Only valid for annual event (`frequency` is `YEARLY` (`0`)) + - both `by_month` and `by_month_day` must be provided + - both `by_month` and `by_month_day` arrays must have a length of `1` + - (i.e: You can only set a single date for annual events) + + +###### `interval` can only be set to a value other than `1` when `frequency` is set to `WEEKLY` (`1`) + - In this situation, interval can be set to `2` + - This allowance enables "every-other week" events + - Due to the limitations placed on `by_weekday` this means that if you wish to use "every-other week" functionality + you can only do so for a single day. + + + +**Every weekday** +```js +frequency = 3 // Frequency.DAILY +interval = 1 +by_weekday = [0, 1, 2, 3, 4] // [Weekday.MONDAY, ..., Weekday.FRIDAY] +``` + +**Every Wednesday** +```js +frequency = 2 // Frequency.WEEKLY +interval = 1 +by_weekday = [2] // [Weekday.WEDNESDAY] +``` + +**Every other Wednesday** +```js +frequency = 2 // Frequency.WEEKLY +interval = 2 +by_weekday = [2] // [Weekday.WEDNESDAY] +``` + +**Monthly on the fourth Wednesday** +```js +frequency = 1 // Frequency.MONTHLY +interval = 1 +by_n_weekday = [{ + n: 4, + day: 2 // Weekday.WEDNESDAY +}] +``` + +**Annually on July 24** +```js +frequency = 0 // Frequency.YEARLY +interval = 1 +by_month = [7] // [Month.JULY] +by_month_day = [24] +``` + + + +###### Guild Scheduled Event Recurrence Rule - Frequency + +| Type | Value | +|---------|-------| +| YEARLY | 0 | +| MONTHLY | 1 | +| WEEKLY | 2 | +| DAILY | 3 | + + +###### Guild Scheduled Event Recurrence Rule - Weekday + +| Type | Value | +|-----------|-------| +| MONDAY | 0 | +| TUESDAY | 1 | +| WEDNESDAY | 2 | +| THURSDAY | 3 | +| FRIDAY | 4 | +| SATURDAY | 5 | +| SUNDAY | 6 | + + +###### Guild Scheduled Event Recurrence Rule - N_Weekday Structure + +| Field | Type | Description | +|-------|------|-------------------------------| +| n | int | The week to reoccur on. 1 - 5 | +| day | [recurrence rule - weekday](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object-guild-scheduled-event-recurrence-rule-weekday) | The day within the week to reoccur on | | guild member data for this user for the guild which this event belongs to, if any | + + +###### Guild Scheduled Event Recurrence Rule - Month + +| Type | Value | +|-----------|-------| +| JANUARY | 1 | +| FEBRUARY | 2 | +| MARCH | 3 | +| APRIL | 4 | +| MAY | 5 | +| JUNE | 6 | +| JULY | 7 | +| AUGUST | 8 | +| SEPTEMBER | 9 | +| OCTOBER | 10 | +| NOVEMBER | 11 | +| DECEMBER | 12 | + +## List Scheduled Events for Guild +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/scheduled-events + +Returns a list of [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) objects for the given guild. + + +###### Query String Params + +| Field | Type | Description | +|------------------|-------------------------------------------------------------|--------------------------------------------------| +| with_user_count? | [boolean](/developers/docs/reference#boolean-query-strings) | include number of users subscribed to each event | + +## Create Guild Scheduled Event +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/scheduled-events + +Create a guild scheduled event in the guild. Returns a [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) object on success. Fires a [Guild Scheduled Event Create](/developers/docs/events/gateway-events#guild-scheduled-event-create) Gateway event. + + +A guild can have a maximum of 100 events with `SCHEDULED` or `ACTIVE` status at any time. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|------------------------|----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------| +| channel_id? * | snowflake * | the channel id of the scheduled event. | +| entity_metadata? ** | [entity metadata](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | the entity metadata of the scheduled event | +| name | string | the name of the scheduled event | +| privacy_level | [privacy level](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event | +| scheduled_start_time | ISO8601 timestamp | the time to schedule the scheduled event | +| scheduled_end_time? ** | ISO8601 timestamp | the time when the scheduled event is scheduled to end | +| description? | string | the description of the scheduled event | +| entity_type | [entity type](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types) | the entity type of the scheduled event | +| image? | [image data](/developers/docs/reference#image-data) | the cover image of the scheduled event | +| recurrence_rule? | [recurrence rule](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object) | the definition for how often this event should recur | + + +\* Optional for events with `'entity_type': EXTERNAL` + +\*\* Required for events with `'entity_type': EXTERNAL` + +## Get Guild Scheduled Event +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/scheduled-events/[\{guild_scheduled_event.id\}](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) + +Get a guild scheduled event. Returns a [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) object on success. + + +###### Query String Params + +| Field | Type | Description | +|------------------|-------------------------------------------------------------|--------------------------------------------------| +| with_user_count? | [boolean](/developers/docs/reference#boolean-query-strings) | include number of users subscribed to this event | + +## Modify Guild Scheduled Event +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/scheduled-events/[\{guild_scheduled_event.id\}](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) + +Modify a guild scheduled event. Returns the modified [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) object on success. Fires a [Guild Scheduled Event Update](/developers/docs/events/gateway-events#guild-scheduled-event-update) Gateway event. + + +To start or end an event, use this endpoint to modify the event's [status](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status) field. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +This endpoint silently discards `entity_metadata` for non-`EXTERNAL` events. + + + +All parameters to this endpoint are optional + + + +###### JSON Params + +| Field | Type | Description | +|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| +| channel_id? * | ?snowflake | the channel id of the scheduled event, set to `null` if changing entity type to `EXTERNAL` | +| entity_metadata? | ?[entity metadata](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | the entity metadata of the scheduled event | +| name? | string | the name of the scheduled event | +| privacy_level? | [privacy level](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event | +| scheduled_start_time? | ISO8601 timestamp | the time to schedule the scheduled event | +| scheduled_end_time? * | ISO8601 timestamp | the time when the scheduled event is scheduled to end | +| description? | ?string | the description of the scheduled event | +| entity_type? * | [event entity type](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types) | the entity type of the scheduled event | +| status? | [event status](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status) | the status of the scheduled event | +| image? | [image data](/developers/docs/reference#image-data) | the cover image of the scheduled event | +| recurrence_rule? | ?[recurrence rule](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-recurrence-rule-object) | the definition for how often this event should recur | + + +\* If updating `entity_type` to `EXTERNAL`: + +- `channel_id` is required and [must be set to null](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-field-requirements-by-entity-type) +- `entity_metadata` with a `location` field must be provided +- `scheduled_end_time` must be provided + +## Delete Guild Scheduled Event +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/scheduled-events/[\{guild_scheduled_event.id\}](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) + +Delete a guild scheduled event. Returns a `204` on success. Fires a [Guild Scheduled Event Delete](/developers/docs/events/gateway-events#guild-scheduled-event-delete) Gateway event. + +## Get Guild Scheduled Event Users +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/scheduled-events/[\{guild_scheduled_event.id\}](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object)/users + +Get a list of guild scheduled event users subscribed to a guild scheduled event. Returns a list of [guild scheduled event user](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-user-object) objects on success. Guild member data, if it exists, is included if the `with_member` query parameter is set. + + +###### Query String Params + +| Field | Type | Description | Default | +|--------------|-------------------------------------------------------------|-----------------------------------------------|---------| +| limit? | number | number of users to return (up to maximum 100) | 100 | +| with_member? | [boolean](/developers/docs/reference#boolean-query-strings) | include guild member data if it exists | false | +| before? * | snowflake | consider only users before given user id | null | +| after? * | snowflake | consider only users after given user id | null | + +\* Provide a user id to `before` and `after` for pagination. Users will always be returned in ascending order by `user_id`. If both `before` and `after` are provided, only `before` is respected. Fetching users in-between `before` and `after` is not supported. + + +## Guild Scheduled Event Status Update Automation + +NOTE: `status` and `entity_type` here are expressed by name rather than their value for readability + +### An active scheduled event for a stage channel where all users have left the stage channel will automatically end a few minutes after the last user leaves the channel + +When an event with `'status': ACTIVE` and `'entity_type': STAGE_INSTANCE` has no users connected to the stage channel for a certain period of time (on the order of minutes), the event `status` will be automatically set to `COMPLETED`. + +### An active scheduled event for a voice channel where all users have left the voice channel will automatically end a few minutes after the last user leaves the channel + +When an event with `'status': ACTIVE` and `'entity_type': VOICE` has no users connected to the voice channel for a certain period of time (on the order of minutes), the event `status` will be automatically set to `COMPLETED`. + +### An external event will automatically begin at its scheduled start time + +An event with `'entity_type': EXTERNAL` at its `scheduled_start_time` will automatically have `status` set to `ACTIVE`. + +### An external event will automatically end at its scheduled end time + +An event with `'entity_type': EXTERNAL` at its `scheduled_end_time` will automatically have `status` set to `COMPLETED`. + +### Any scheduled event which has not begun after its scheduled start time will be automatically cancelled after a few hours + +Any event with `'status': SCHEDULED` after a certain time interval (on the order of hours) beyond its `scheduled_start_time` will have its `status` automatically set to `CANCELLED`. + +## Guild Scheduled Event Permissions Requirements + +NOTE: `entity_type` is expressed by name rather than value for readability + + +A user must be a member of the guild in order to access events for that guild unless the guild is lurkable. If a guild is lurkable, events in that guild may be visible to lurkers depending on the event type and the permissions of any channels associated with the event. + + +### Permissions for events with entity_type: STAGE_INSTANCE + +The computed permissions for the user in the `channel_id` associated with the event must include: + +#### Get Permissions + +- `VIEW_CHANNEL` + +#### Create Permissions + +- `CREATE_EVENTS` +- `MANAGE_CHANNELS` +- `MUTE_MEMBERS` +- `MOVE_MEMBERS` + +#### Modify/Delete Permissions + +- If the event was created by the current user, either `CREATE_EVENTS` or `MANAGE_EVENTS`. Otherwise, `MANAGE_EVENTS` +- `MANAGE_CHANNELS` +- `MUTE_MEMBERS` +- `MOVE_MEMBERS` + +### Permissions for events with entity_type: VOICE + +The computed permissions for the user in the `channel_id` associated with the event must include: + +#### Get Permissions + +- `VIEW_CHANNEL` + +#### Create permissions + +- `CREATE_EVENTS` +- `VIEW_CHANNEL` +- `CONNECT` + +#### Modify/Delete Permissions + +- If the event was created by the current user, either `CREATE_EVENTS` or `MANAGE_EVENTS`. Otherwise, `MANAGE_EVENTS` +- `VIEW_CHANNEL` +- `CONNECT` + +### Permissions for events with entity_type: EXTERNAL + +The user's guild-level permissions must include: + +#### Get Permissions + +- *No permissions required* + +#### Create permissions + +- `CREATE_EVENTS` + +#### Modify/Delete Permissions + +- If the event was created by the current user, either `CREATE_EVENTS` or `MANAGE_EVENTS`. Otherwise, `MANAGE_EVENTS` diff --git a/discord/developers/docs/resources/guild-template.mdx b/discord/developers/docs/resources/guild-template.mdx new file mode 100644 index 0000000000..d58954c91f --- /dev/null +++ b/discord/developers/docs/resources/guild-template.mdx @@ -0,0 +1,151 @@ +--- +title: Guild Template Resource +sidebarTitle: Guild Template +description: Reference for Discord Guilt Template objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +### Guild Template Object + +Represents a code that when used, creates a guild based on a snapshot of an existing guild. + + +###### Guild Template Structure + +| Field | Type | Description | +|-------------------------|-----------------------------------------------------------------------|----------------------------------------------------------------------------------| +| code | string | the template code (unique ID) | +| name | string | template name | +| description | ?string | the description for the template | +| usage_count | integer | number of times this template has been used | +| creator_id | snowflake | the ID of the user who created the template | +| creator | [user](/developers/docs/resources/user#user-object) object | the user who created the template | +| created_at | ISO8601 timestamp | when this template was created | +| updated_at | ISO8601 timestamp | when this template was last synced to the source guild | +| source_guild_id | snowflake | the ID of the guild this template is based on | +| serialized_source_guild | partial [guild](/developers/docs/resources/guild#guild-object) object | the guild snapshot this template contains; placeholder IDs are given as integers | +| is_dirty | ?boolean | whether the template has unsynced changes | + + +###### Example Guild Template Object + +```json +{ + "code": "hgM48av5Q69A", + "name": "Friends & Family", + "description": "", + "usage_count": 49605, + "creator_id": "132837293881950208", + "creator": { + "id": "132837293881950208", + "username": "hoges", + "avatar": "79b0d9f8c340f2d43e1f78b09f175b62", + "discriminator": "0001", + "public_flags": 129 + }, + "created_at": "2020-04-02T21:10:38+00:00", + "updated_at": "2020-05-01T17:57:38+00:00", + "source_guild_id": "678070694164299796", + "serialized_source_guild": { + "name": "Friends & Family", + "description": null, + "region": "us-west", + "verification_level": 0, + "default_message_notifications": 0, + "explicit_content_filter": 0, + "preferred_locale": "en-US", + "afk_timeout": 300, + "roles": [ + { + "id": 0, + "name": "@everyone", + "permissions": 104324689, + "color": 0, + "hoist": false, + "mentionable": false + } + ], + "channels": [ + { + "name": "Text Channels", + "position": 1, + "topic": null, + "bitrate": 64000, + "user_limit": 0, + "nsfw": false, + "rate_limit_per_user": 0, + "parent_id": null, + "permission_overwrites": [], + "id": 1, + "type": 4 + }, + { + "name": "general", + "position": 1, + "topic": null, + "bitrate": 64000, + "user_limit": 0, + "nsfw": false, + "rate_limit_per_user": 0, + "parent_id": 1, + "permission_overwrites": [], + "id": 2, + "type": 0 + } + ], + "afk_channel_id": null, + "system_channel_id": 2, + "system_channel_flags": 0, + "icon_hash": null + }, + "is_dirty": null +} +``` + +## Get Guild Template +/guilds/templates/[\{template.code\}](/developers/docs/resources/guild-template#guild-template-object) + +Returns a [guild template](/developers/docs/resources/guild-template#guild-template-object) object for the given code. + +## Get Guild Templates +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/templates + +Returns an array of [guild template](/developers/docs/resources/guild-template#guild-template-object) objects. Requires the `MANAGE_GUILD` permission. + +## Create Guild Template +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/templates + +Creates a template for the guild. Requires the `MANAGE_GUILD` permission. Returns the created [guild template](/developers/docs/resources/guild-template#guild-template-object) object on success. + + +###### JSON Params + +| Field | Type | Description | +|--------------|---------|-------------------------------------------------| +| name | string | name of the template (1-100 characters) | +| description? | ?string | description for the template (0-120 characters) | + +## Sync Guild Template +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/templates/[\{template.code\}](/developers/docs/resources/guild-template#guild-template-object) + +Syncs the template to the guild's current state. Requires the `MANAGE_GUILD` permission. Returns the [guild template](/developers/docs/resources/guild-template#guild-template-object) object on success. + +## Modify Guild Template +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/templates/[\{template.code\}](/developers/docs/resources/guild-template#guild-template-object) + +Modifies the template's metadata. Requires the `MANAGE_GUILD` permission. Returns the [guild template](/developers/docs/resources/guild-template#guild-template-object) object on success. + + +###### JSON Params + +| Field | Type | Description | +|--------------|---------|-------------------------------------------------| +| name? | string | name of the template (1-100 characters) | +| description? | ?string | description for the template (0-120 characters) | + +## Delete Guild Template +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/templates/[\{template.code\}](/developers/docs/resources/guild-template#guild-template-object) + +Deletes the template. Requires the `MANAGE_GUILD` permission. Returns the deleted [guild template](/developers/docs/resources/guild-template#guild-template-object) object on success. diff --git a/discord/developers/docs/resources/guild.mdx b/discord/developers/docs/resources/guild.mdx new file mode 100644 index 0000000000..d5cd0179b5 --- /dev/null +++ b/discord/developers/docs/resources/guild.mdx @@ -0,0 +1,1500 @@ +--- +title: Guild Resource +sidebarTitle: Guild +description: Reference for Discord guild (server) objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +import {Route} from '/snippets/route.jsx' + +Guilds in Discord represent an isolated collection of users and channels, and are often referred to as "servers" in the UI. + +### Guild Object + + +###### Guild Structure + + +Fields specific to the `GUILD_CREATE` event are listed in the [Gateway Events documentation](/developers/docs/events/gateway-events#guild-create). + + +| Field | Type | Description | +|--------------------------------|------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | guild id | +| name | string | guild name (2-100 characters, excluding trailing and leading whitespace) | +| icon | ?string | [icon hash](/developers/docs/reference#image-formatting) | +| icon_hash? | ?string | [icon hash](/developers/docs/reference#image-formatting), returned when in the template object | +| splash | ?string | [splash hash](/developers/docs/reference#image-formatting) | +| discovery_splash | ?string | [discovery splash hash](/developers/docs/reference#image-formatting); only present for guilds with the "DISCOVERABLE" feature | +| owner? \* | boolean | true if [the user](/developers/docs/resources/user#get-current-user-guilds) is the owner of the guild | +| owner_id | snowflake | id of owner | +| permissions? \* | string | total permissions for [the user](/developers/docs/resources/user#get-current-user-guilds) in the guild (excludes overwrites and [implicit permissions](/developers/docs/topics/permissions#implicit-permissions)) | +| region? \*\* | ?string | [voice region](/developers/docs/resources/voice#voice-region-object) id for the guild (deprecated) | +| afk_channel_id | ?snowflake | id of afk channel | +| afk_timeout | integer | afk timeout in seconds | +| widget_enabled? | boolean | true if the server widget is enabled | +| widget_channel_id? | ?snowflake | the channel id that the widget will generate an invite to, or `null` if set to no invite | +| verification_level | integer | [verification level](/developers/docs/resources/guild#guild-object-verification-level) required for the guild | +| default_message_notifications | integer | default [message notifications level](/developers/docs/resources/guild#guild-object-default-message-notification-level) | +| explicit_content_filter | integer | [explicit content filter level](/developers/docs/resources/guild#guild-object-explicit-content-filter-level) | +| roles | array of [role](/developers/docs/topics/permissions#role-object) objects | roles in the guild | +| emojis | array of [emoji](/developers/docs/resources/emoji#emoji-object) objects | custom guild emojis | +| features | array of [guild feature](/developers/docs/resources/guild#guild-object-guild-features) strings | enabled guild features | +| mfa_level | integer | required [MFA level](/developers/docs/resources/guild#guild-object-mfa-level) for the guild | +| application_id | ?snowflake | application id of the guild creator if it is bot-created | +| system_channel_id | ?snowflake | the id of the channel where guild notices such as welcome messages and boost events are posted | +| system_channel_flags | integer | [system channel flags](/developers/docs/resources/guild#guild-object-system-channel-flags) | +| rules_channel_id | ?snowflake | the id of the channel where Community guilds can display rules and/or guidelines | +| max_presences? | ?integer | the maximum number of presences for the guild (`null` is always returned, apart from the largest of guilds) | +| max_members? | integer | the maximum number of members for the guild | +| vanity_url_code | ?string | the vanity url code for the guild | +| description | ?string | the description of a guild | +| banner | ?string | [banner hash](/developers/docs/reference#image-formatting) | +| premium_tier | integer | [premium tier](/developers/docs/resources/guild#guild-object-premium-tier) (Server Boost level) | +| premium_subscription_count? | integer | the number of boosts this guild currently has | +| preferred_locale | string | the preferred [locale](/developers/docs/reference#locales) of a Community guild; used in server discovery and notices from Discord, and sent in interactions; defaults to "en-US" | +| public_updates_channel_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive notices from Discord | +| max_video_channel_users? | integer | the maximum amount of users in a video channel | +| max_stage_video_channel_users? | integer | the maximum amount of users in a stage video channel | +| approximate_member_count? | integer | approximate number of members in this guild, returned from the `GET /guilds/` and `/users/@me/guilds` endpoints when `with_counts` is `true` | +| approximate_presence_count? | integer | approximate number of non-offline members in this guild, returned from the `GET /guilds/` and `/users/@me/guilds` endpoints when `with_counts` is `true` | +| welcome_screen? | [welcome screen](/developers/docs/resources/guild#welcome-screen-object) object | the welcome screen of a Community guild, shown to new members, returned in an [Invite](/developers/docs/resources/invite#invite-object)'s guild object | +| nsfw_level | integer | [guild NSFW level](/developers/docs/resources/guild#guild-object-guild-nsfw-level) | +| stickers? | array of [sticker](/developers/docs/resources/sticker#sticker-object) objects | custom guild stickers | +| premium_progress_bar_enabled | boolean | whether the guild has the boost progress bar enabled | +| safety_alerts_channel_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive safety alerts from Discord | +| incidents_data | ?[incidents data](/developers/docs/resources/guild#incidents-data-object) object | the incidents data for this guild | + +\* These fields are only sent when using the [GET Current User Guilds](/developers/docs/resources/user#get-current-user-guilds) endpoint and are relative to the requested user + +\*\* This field is deprecated and is replaced by [channel.rtc_region](/developers/docs/resources/channel#channel-object-channel-structure) + + +###### Default Message Notification Level + +| Key | Value | Description | +|---------------|-------|------------------------------------------------------------------------------------| +| ALL_MESSAGES | 0 | members will receive notifications for all messages by default | +| ONLY_MENTIONS | 1 | members will receive notifications only for messages that @mention them by default | + + +###### Explicit Content Filter Level + +| Level | Integer | Description | +|-----------------------|---------|-------------------------------------------------------------| +| DISABLED | 0 | media content will not be scanned | +| MEMBERS_WITHOUT_ROLES | 1 | media content sent by members without roles will be scanned | +| ALL_MEMBERS | 2 | media content sent by all members will be scanned | + + +###### MFA Level + +| Level | Integer | Description | +|----------|---------|---------------------------------------------------------| +| NONE | 0 | guild has no MFA/2FA requirement for moderation actions | +| ELEVATED | 1 | guild has a 2FA requirement for moderation actions | + + +###### Verification Level + +| Level | Integer | Description | +|-----------|---------|-----------------------------------------------------------| +| NONE | 0 | unrestricted | +| LOW | 1 | must have verified email on account | +| MEDIUM | 2 | must be registered on Discord for longer than 5 minutes | +| HIGH | 3 | must be a member of the server for longer than 10 minutes | +| VERY_HIGH | 4 | must have a verified phone number | + + +###### Guild NSFW Level + +| Level | Value | +|----------------|-------| +| DEFAULT | 0 | +| EXPLICIT | 1 | +| SAFE | 2 | +| AGE_RESTRICTED | 3 | + + +###### Premium Tier + +| Level | Integer | Description | +|--------|---------|-----------------------------------------------| +| NONE | 0 | guild has not unlocked any Server Boost perks | +| TIER_1 | 1 | guild has unlocked Server Boost level 1 perks | +| TIER_2 | 2 | guild has unlocked Server Boost level 2 perks | +| TIER_3 | 3 | guild has unlocked Server Boost level 3 perks | + + +###### System Channel Flags + +| Flag | Value | Description | +|----------------------------------------------------------|----------|---------------------------------------------------------------| +| SUPPRESS_JOIN_NOTIFICATIONS | `1 << 0` | Suppress member join notifications | +| SUPPRESS_PREMIUM_SUBSCRIPTIONS | `1 << 1` | Suppress server boost notifications | +| SUPPRESS_GUILD_REMINDER_NOTIFICATIONS | `1 << 2` | Suppress server setup tips | +| SUPPRESS_JOIN_NOTIFICATION_REPLIES | `1 << 3` | Hide member join sticker reply buttons | +| SUPPRESS_ROLE_SUBSCRIPTION_PURCHASE_NOTIFICATIONS | `1 << 4` | Suppress role subscription purchase and renewal notifications | +| SUPPRESS_ROLE_SUBSCRIPTION_PURCHASE_NOTIFICATION_REPLIES | `1 << 5` | Hide role subscription sticker reply buttons | + + +###### Guild Features + +| Feature | Description | +|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------| +| ANIMATED_BANNER | guild has access to set an animated guild banner image | +| ANIMATED_ICON | guild has access to set an animated guild icon | +| APPLICATION_COMMAND_PERMISSIONS_V2 | guild is using the [old permissions configuration behavior](/developers/docs/change-log#upcoming-application-command-permission-changes) | +| AUTO_MODERATION | guild has set up auto moderation rules | +| BANNER | guild has access to set a guild banner image | +| COMMUNITY | guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates | +| CREATOR_MONETIZABLE_PROVISIONAL | guild has enabled monetization | +| CREATOR_STORE_PAGE | guild has enabled the role subscription promo page | +| DEVELOPER_SUPPORT_SERVER | guild has been set as a support server on the App Directory | +| DISCOVERABLE | guild is able to be discovered in the directory | +| FEATURABLE | guild is able to be featured in the directory | +| INVITES_DISABLED | guild has paused invites, preventing new users from joining | +| INVITE_SPLASH | guild has access to set an invite splash background | +| MEMBER_VERIFICATION_GATE_ENABLED | guild has enabled [Membership Screening](/developers/docs/resources/guild#membership-screening-object) | +| MORE_SOUNDBOARD | guild has increased custom soundboard sound slots | +| MORE_STICKERS | guild has increased custom sticker slots | +| NEWS | guild has access to create announcement channels | +| PARTNERED | guild is partnered | +| PREVIEW_ENABLED | guild can be previewed before joining via Membership Screening or the directory | +| RAID_ALERTS_DISABLED | guild has disabled alerts for join raids in the configured safety alerts channel | +| ROLE_ICONS | guild is able to set role icons | +| ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE | guild has role subscriptions that can be purchased | +| ROLE_SUBSCRIPTIONS_ENABLED | guild has enabled role subscriptions | +| SOUNDBOARD | guild has created soundboard sounds | +| TICKETED_EVENTS_ENABLED | guild has enabled ticketed events | +| VANITY_URL | guild has access to set a vanity URL | +| VERIFIED | guild is verified | +| VIP_REGIONS | guild has access to set 384kbps bitrate in voice (previously VIP voice servers) | +| WELCOME_SCREEN_ENABLED | guild has enabled the welcome screen | +| GUESTS_ENABLED | guild has access to guest invites | +| GUILD_TAGS | guild has access to set guild tags | +| ENHANCED_ROLE_COLORS | guild is able to set gradient colors to roles | + + +###### Mutable Guild Features + +| Features | Required Permissions | Effects | +|----------------------|----------------------|-----------------------------------------------------------| +| COMMUNITY | Administrator | Enables Community Features in the guild | +| DISCOVERABLE | Administrator* | Enables discovery in the guild, making it publicly listed | +| INVITES_DISABLED | Manage Guild | Pauses all invites/access to the server | +| RAID_ALERTS_DISABLED | Manage Guild | Disables alerts for join raids | + +\* Server also must be passing all discovery requirements + + +###### Example Guild + +```json +{ + "id": "197038439483310086", + "name": "Discord Testers", + "icon": "f64c482b807da4f539cff778d174971c", + "description": "The official place to report Discord Bugs!", + "splash": null, + "discovery_splash": null, + "features": [ + "ANIMATED_ICON", + "VERIFIED", + "NEWS", + "VANITY_URL", + "DISCOVERABLE", + "MORE_EMOJI", + "INVITE_SPLASH", + "BANNER", + "COMMUNITY" + ], + "emojis": [], + "banner": "9b6439a7de04f1d26af92f84ac9e1e4a", + "owner_id": "73193882359173120", + "application_id": null, + "region": null, + "afk_channel_id": null, + "afk_timeout": 300, + "system_channel_id": null, + "widget_enabled": true, + "widget_channel_id": null, + "verification_level": 3, + "roles": [], + "default_message_notifications": 1, + "mfa_level": 1, + "explicit_content_filter": 2, + "max_presences": 40000, + "max_members": 250000, + "vanity_url_code": "discord-testers", + "premium_tier": 3, + "premium_subscription_count": 33, + "system_channel_flags": 0, + "preferred_locale": "en-US", + "rules_channel_id": "441688182833020939", + "public_updates_channel_id": "281283303326089216", + "safety_alerts_channel_id": "281283303326089216" +} +``` + +### Unavailable Guild Object + +A partial [guild](/developers/docs/resources/guild#guild-object) object. Represents an Offline Guild, or a Guild whose information has not been provided through [Guild Create](/developers/docs/events/gateway-events#guild-create) events during the Gateway connect. + + +###### Example Unavailable Guild + +```json +{ + "id": "41771983423143937", + "unavailable": true +} +``` + +### Guild Preview Object + + +###### Guild Preview Structure + +| Field | Type | Description | +|----------------------------|------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| +| id | snowflake | guild id | +| name | string | guild name (2-100 characters) | +| icon | ?string | [icon hash](/developers/docs/reference#image-formatting) | +| splash | ?string | [splash hash](/developers/docs/reference#image-formatting) | +| discovery_splash | ?string | [discovery splash hash](/developers/docs/reference#image-formatting) | +| emojis | array of [emoji](/developers/docs/resources/emoji#emoji-object) objects | custom guild emojis | +| features | array of [guild feature](/developers/docs/resources/guild#guild-object-guild-features) strings | enabled guild features | +| approximate_member_count | integer | approximate number of members in this guild | +| approximate_presence_count | integer | approximate number of online members in this guild | +| description | ?string | the description for the guild | +| stickers | array of [sticker](/developers/docs/resources/sticker#sticker-object) objects | custom guild stickers | + + +###### Example Guild Preview + +```json +{ + "id": "197038439483310086", + "name": "Discord Testers", + "icon": "f64c482b807da4f539cff778d174971c", + "splash": null, + "discovery_splash": null, + "emojis": [], + "features": [ + "DISCOVERABLE", + "VANITY_URL", + "ANIMATED_ICON", + "INVITE_SPLASH", + "NEWS", + "COMMUNITY", + "BANNER", + "VERIFIED", + "MORE_EMOJI" + ], + "approximate_member_count": 60814, + "approximate_presence_count": 20034, + "description": "The official place to report Discord Bugs!", + "stickers": [] +} +``` + +### Guild Widget Settings Object + + +###### Guild Widget Settings Structure + +| Field | Type | Description | +|------------|------------|-------------------------------| +| enabled | boolean | whether the widget is enabled | +| channel_id | ?snowflake | the widget channel id | + + +###### Example Guild Widget Settings + +```json +{ + "enabled": true, + "channel_id": "41771983444115456" +} +``` + +### Guild Widget Object + + +###### Guild Widget Structure + +| Field | Type | Description | +|----------------|---------------------------------------------------------------------------------------|----------------------------------------------------------------------| +| id | snowflake | guild id | +| name | string | guild name (2-100 characters) | +| instant_invite | ?string | instant invite for the guilds specified widget invite channel | +| channels | array of partial [channel](/developers/docs/resources/channel#channel-object) objects | voice and stage channels which are accessible by @everyone | +| members | array of partial [user](/developers/docs/resources/user#user-object) objects | special widget user objects that includes users presence (Limit 100) | +| presence_count | integer | number of online members in this guild | + + +The fields `id`, `discriminator` and `avatar` are anonymized to prevent abuse. + + + +###### Example Guild Widget + +```json +{ + "id": "290926798626999250", + "name": "Test Server", + "instant_invite": "https://discord.com/invite/abcdefg", + "channels": [ + { + "id": "705216630279993882", + "name": "elephant", + "position": 2 + }, + { + "id": "669583461748992190", + "name": "groovy-music", + "position": 1 + } + ], + "members": [ + { + "id": "0", + "username": "1234", + "discriminator": "0000", + "avatar": null, + "status": "online", + "avatar_url": "https://cdn.discordapp.com/widget-avatars/FfvURgcr3Za92K3JtoCppqnYMppMDc5B-Rll74YrGCU/C-1DyBZPQ6t5q2RuATFuMFgq0_uEMZVzd_6LbGN_uJKvZflobA9diAlTjhf6CAESLLeTuu4dLuHFWOb_PNLteooNfhC4C6k5QgAGuxEOP12tVVVCvX6t64k14PMXZrGTDq8pWZhukP40Wg" + } + ], + "presence_count": 1 +} +``` + +### Guild Member Object + + +###### Guild Member Structure + +| Field | Type | Description | +|-------------------------------|-------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| user? | [user](/developers/docs/resources/user#user-object) object | the user this guild member represents | +| nick? | ?string | this user's guild nickname | +| avatar? | ?string | the member's [guild avatar hash](/developers/docs/reference#image-formatting) | +| banner? | ?string | the member's [guild banner hash](/developers/docs/reference#image-formatting) | +| roles | array of snowflakes | array of [role](/developers/docs/topics/permissions#role-object) object ids | +| joined_at | ?ISO8601 timestamp | when the user joined the guild | +| premium_since? | ?ISO8601 timestamp | when the user started [boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-) the guild | +| deaf | boolean | whether the user is deafened in voice channels | +| mute | boolean | whether the user is muted in voice channels | +| flags | integer | [guild member flags](/developers/docs/resources/guild#guild-member-object-guild-member-flags) represented as a bit set, defaults to `0` | +| pending? | boolean | whether the user has not yet passed the guild's [Membership Screening](/developers/docs/resources/guild#membership-screening-object) requirements | +| permissions? | string | total permissions of the member in the channel, including overwrites, returned when in the interaction object | +| communication_disabled_until? | ?ISO8601 timestamp | when the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out | +| avatar_decoration_data? | ?[avatar decoration data](/developers/docs/resources/user#avatar-decoration-data-object) object | data for the member's guild avatar decoration | + + +The field `user` won't be included in the member object attached to `MESSAGE_CREATE` and `MESSAGE_UPDATE` gateway events. + + + +In `GUILD_` events, `pending` will always be included as true or false. In non `GUILD_` events which can only be triggered by non-`pending` users, `pending` will not be included. + + + +Member objects retrieved from `VOICE_STATE_UPDATE` events will have `joined_at` set as `null` if the member was invited as a guest. + + + +###### Example Guild Member + +```json +{ + "user": {}, + "nick": "NOT API SUPPORT", + "avatar": null, + "banner": null, + "roles": [], + "joined_at": "2015-04-26T06:26:56.936000+00:00", + "deaf": false, + "mute": false +} +``` + + +###### Guild Member Flags + +| Flag | Value | Description | Editable | +|---------------------------------|-----------|------------------------------------------------------------------------------|----------| +| DID_REJOIN | `1 << 0` | Member has left and rejoined the guild | false | +| COMPLETED_ONBOARDING | `1 << 1` | Member has completed onboarding | false | +| BYPASSES_VERIFICATION | `1 << 2` | Member is exempt from guild verification requirements | true | +| STARTED_ONBOARDING | `1 << 3` | Member has started onboarding | false | +| IS_GUEST | `1 << 4` | Member is a guest and can only access the voice channel they were invited to | false | +| STARTED_HOME_ACTIONS | `1 << 5` | Member has started Server Guide new member actions | false | +| COMPLETED_HOME_ACTIONS | `1 << 6` | Member has completed Server Guide new member actions | false | +| AUTOMOD_QUARANTINED_USERNAME | `1 << 7` | Member's username, display name, or nickname is blocked by AutoMod | false | +| DM_SETTINGS_UPSELL_ACKNOWLEDGED | `1 << 9` | Member has dismissed the DM settings upsell | false | +| AUTOMOD_QUARANTINED_GUILD_TAG | `1 << 10` | Member's guild tag is blocked by AutoMod | false | + + +BYPASSES_VERIFICATION allows a member who does not meet verification requirements to participate in a server. + + +### Integration Object + + +###### Integration Structure + +| Field | Type | Description | +|-------------------------|-----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| +| id | snowflake | integration id | +| name | string | integration name | +| type | string | integration type (twitch, youtube, discord, or guild_subscription) | +| enabled | boolean | is this integration enabled | +| syncing? \* | boolean | is this integration syncing | +| role_id? \* | snowflake | id that this integration uses for "subscribers" | +| enable_emoticons? \* | boolean | whether emoticons should be synced for this integration (twitch only currently) | +| expire_behavior? \* | [integration expire behavior](/developers/docs/resources/guild#integration-object-integration-expire-behaviors) | the behavior of expiring subscribers | +| expire_grace_period? \* | integer | the grace period (in days) before expiring subscribers | +| user? | [user](/developers/docs/resources/user#user-object) object | user for this integration | +| account | [account](/developers/docs/resources/guild#integration-account-object) object | integration account information | +| synced_at? \* | ISO8601 timestamp | when this integration was last synced | +| subscriber_count? \* | integer | how many subscribers this integration has | +| revoked? \* | boolean | has this integration been revoked | +| application? | [application](/developers/docs/resources/guild#integration-application-object) object | The bot/OAuth2 application for discord integrations | +| scopes? | array of [OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) | the scopes the application has been authorized for | + +\* These fields are not provided for discord bot integrations. + +Some older integrations may not have an attached user. + + + +###### Integration Expire Behaviors + +| Value | Name | +|-------|-------------| +| 0 | Remove role | +| 1 | Kick | + +### Integration Account Object + + +###### Integration Account Structure + +| Field | Type | Description | +|-------|--------|---------------------| +| id | string | id of the account | +| name | string | name of the account | + +### Integration Application Object + + +###### Integration Application Structure + +| Field | Type | Description | +|-------------|------------------------------------------------------------|-------------------------------------------------------------------------| +| id | snowflake | the id of the app | +| name | string | the name of the app | +| icon | ?string | the [icon hash](/developers/docs/reference#image-formatting) of the app | +| description | string | the description of the app | +| bot? | [user](/developers/docs/resources/user#user-object) object | the bot associated with this application | + +### Ban Object + +###### Ban Structure + +| Field | Type | Description | +|--------|------------------------------------------------------------|------------------------| +| reason | ?string | the reason for the ban | +| user | [user](/developers/docs/resources/user#user-object) object | the banned user | + + +###### Example Ban + +```json +{ + "reason": "mentioning b1nzy", + "user": { + "username": "Mason", + "discriminator": "9999", + "id": "53908099506183680", + "avatar": "a_bab14f271d565501444b2ca3be944b25", + "public_flags": 131141 + } +} +``` + +### Welcome Screen Object + + +###### Welcome Screen Structure + +| Field | Type | Description | +|------------------|------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------| +| description | ?string | the server description shown in the welcome screen | +| welcome_channels | array of [welcome screen channel](/developers/docs/resources/guild#welcome-screen-object-welcome-screen-channel-structure) objects | the channels shown in the welcome screen, up to 5 | + + +###### Welcome Screen Channel Structure + +| Field | Type | Description | +|-------------|------------|-------------------------------------------------------------------------------------------| +| channel_id | snowflake | the channel's id | +| description | string | the description shown for the channel | +| emoji_id | ?snowflake | the [emoji id](/developers/docs/reference#image-formatting), if the emoji is custom | +| emoji_name | ?string | the emoji name if custom, the unicode character if standard, or `null` if no emoji is set | + + +###### Example Welcome Screen + +```json +{ + "description": "Discord Developers is a place to learn about Discord's API, bots, and SDKs and integrations. This is NOT a general Discord support server.", + "welcome_channels": [ + { + "channel_id": "697138785317814292", + "description": "Follow for official Discord API updates", + "emoji_id": null, + "emoji_name": "📡" + }, + { + "channel_id": "697236247739105340", + "description": "Get help with Bot Verifications", + "emoji_id": null, + "emoji_name": "📸" + }, + { + "channel_id": "697489244649816084", + "description": "Create amazing things with Discord's API", + "emoji_id": null, + "emoji_name": "🔬" + }, + { + "channel_id": "613425918748131338", + "description": "Integrate Discord into your game", + "emoji_id": null, + "emoji_name": "🎮" + }, + { + "channel_id": "646517734150242346", + "description": "Find more places to help you on your quest", + "emoji_id": null, + "emoji_name": "🔦" + } + ] +} +``` + +### Guild Onboarding Object + +Represents the [onboarding](https://support.discord.com/hc/en-us/articles/11074987197975-Community-Onboarding-FAQ) flow for a guild. + + +###### Guild Onboarding Structure + +| Field | Type | Description | +|---------------------|----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------| +| guild_id | snowflake | ID of the guild this onboarding is part of | +| prompts | array of [onboarding prompt](/developers/docs/resources/guild#guild-onboarding-object-onboarding-prompt-structure) objects | Prompts shown during onboarding and in customize community | +| default_channel_ids | array of snowflakes | Channel IDs that members get opted into automatically | +| enabled | boolean | Whether onboarding is enabled in the guild | +| mode | [onboarding mode](/developers/docs/resources/guild#guild-onboarding-object-onboarding-mode) | Current mode of onboarding | + + +###### Onboarding Prompt Structure + +| Field | Type | Description | +|---------------|--------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the prompt | +| type | [prompt type](/developers/docs/resources/guild#guild-onboarding-object-prompt-types) | Type of prompt | +| options | array of [prompt option](/developers/docs/resources/guild#guild-onboarding-object-prompt-option-structure) objects | Options available within the prompt | +| title | string | Title of the prompt | +| single_select | boolean | Indicates whether users are limited to selecting one option for the prompt | +| required | boolean | Indicates whether the prompt is required before a user completes the onboarding flow | +| in_onboarding | boolean | Indicates whether the prompt is present in the onboarding flow. If `false`, the prompt will only appear in the Channels & Roles tab | + + +###### Prompt Option Structure + +| Field | Type | Description | +|-----------------|---------------------------------------------------------------|-------------------------------------------------------------------| +| id | snowflake | ID of the prompt option | +| channel_ids | array of snowflakes | IDs for channels a member is added to when the option is selected | +| role_ids | array of snowflakes | IDs for roles assigned to a member when the option is selected | +| emoji? | [emoji](/developers/docs/resources/emoji#emoji-object) object | Emoji of the option (see below) | +| emoji_id? | snowflake | Emoji ID of the option (see below) | +| emoji_name? | string | Emoji name of the option (see below) | +| emoji_animated? | boolean | Whether the emoji is animated (see below) | +| title | string | Title of the option | +| description | ?string | Description of the option | + + +When creating or updating a prompt option, the `emoji_id`, `emoji_name`, and `emoji_animated` fields must be used instead of the emoji object. + + + +###### Onboarding Mode + +Defines the criteria used to satisfy Onboarding constraints that are required for enabling. + +| Name | Value | Description | +|---------------------|-------|-----------------------------------------------------------| +| ONBOARDING_DEFAULT | 0 | Counts only Default Channels towards constraints | +| ONBOARDING_ADVANCED | 1 | Counts Default Channels and Questions towards constraints | + + +###### Prompt Types + +| Name | Value | +|-----------------|-------| +| MULTIPLE_CHOICE | 0 | +| DROPDOWN | 1 | + + +###### Example Guild Onboarding + +```json +{ + "guild_id": "960007075288915998", + "prompts": [ + { + "id": "1067461047608422473", + "title": "What do you want to do in this community?", + "options": [ + { + "id": "1067461047608422476", + "title": "Chat with Friends", + "description": "", + "emoji": { + "id": "1070002302032826408", + "name": "chat", + "animated": false + }, + "role_ids": [], + "channel_ids": [ + "962007075288916001" + ] + }, + { + "id": "1070004843541954678", + "title": "Get Gud", + "description": "We have excellent teachers!", + "emoji": { + "id": null, + "name": "😀", + "animated": false + }, + "role_ids": [ + "982014491980083211" + ], + "channel_ids": [] + } + ], + "single_select": false, + "required": false, + "in_onboarding": true, + "type": 0 + } + ], + "default_channel_ids": [ + "998678771706110023", + "998678693058719784", + "1070008122577518632", + "998678764340912138", + "998678704446263309", + "998678683592171602", + "998678699715067986" + ], + "enabled": true +} +``` + +### Membership Screening Object + +In guilds with [Membership Screening](https://support.discord.com/hc/en-us/articles/1500000466882) enabled, when a member joins, [Guild Member Add](/developers/docs/events/gateway-events#guild-member-add) will be emitted but they will initially be restricted from doing any actions in the guild, and `pending` will be true in the [member object](/developers/docs/resources/guild#guild-member-object). When the member completes the screening, [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) will be emitted and `pending` will be false. + + +We are making significant changes to the Membership Screening API specifically related to getting and editing the Membership Screening object. Long story short is that it can be improved. As such, we have removed those documentation. There will **not be** any changes to how pending members work, as outlined above. That behavior will stay the same. + + +### Incidents Data Object + + +###### Incidents Data Structure + +| Field | Type | Description | +|------------------------|--------------------|----------------------------------------| +| invites_disabled_until | ?ISO8601 timestamp | when invites get enabled again | +| dms_disabled_until | ?ISO8601 timestamp | when direct messages get enabled again | +| dm_spam_detected_at? | ?ISO8601 timestamp | when the dm spam was detected | +| raid_detected_at? | ?ISO8601 timestamp | when the raid was detected | + + +###### Example Incidents Data + +```json +{ + "invites_disabled_until": "2023-09-01T14:48:02.222000+00:00", + "dms_disabled_until": null +} +``` + +## Get Guild +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object) + +Returns the [guild](/developers/docs/resources/guild#guild-object) object for the given id. If `with_counts` is set to `true`, this endpoint will also return `approximate_member_count` and `approximate_presence_count` for the guild. + + +###### Query String Params + +| Field | Type | Description | Required | Default | +|--------------|-------------------------------------------------------------|-------------------------------------------------------------------------------|----------|---------| +| with_counts? | [boolean](/developers/docs/reference#boolean-query-strings) | when `true`, will return approximate member and presence counts for the guild | false | false | + + +###### Example Response + +```json +{ + "id": "2909267986263572999", + "name": "Mason's Test Server", + "icon": "389030ec9db118cb5b85a732333b7c98", + "description": null, + "splash": "75610b05a0dd09ec2c3c7df9f6975ea0", + "discovery_splash": null, + "approximate_member_count": 2, + "approximate_presence_count": 2, + "features": [ + "INVITE_SPLASH", + "VANITY_URL", + "COMMERCE", + "BANNER", + "NEWS", + "VERIFIED", + "VIP_REGIONS" + ], + "emojis": [ + { + "name": "ultrafastparrot", + "roles": [], + "id": "393564762228785161", + "require_colons": true, + "managed": false, + "animated": true, + "available": true + } + ], + "banner": "5c3cb8d1bc159937fffe7e641ec96ca7", + "owner_id": "53908232506183680", + "application_id": null, + "region": null, + "afk_channel_id": null, + "afk_timeout": 300, + "system_channel_id": null, + "widget_enabled": true, + "widget_channel_id": "639513352485470208", + "verification_level": 0, + "roles": [ + { + "id": "2909267986263572999", + "name": "@everyone", + "permissions": "49794752", + "position": 0, + "color": 0, + "colors": { + "primary_color": 0, + "secondary_color": null, + "tertiary_color": null + }, + "hoist": false, + "managed": false, + "mentionable": false + } + ], + "default_message_notifications": 1, + "mfa_level": 0, + "explicit_content_filter": 0, + "max_presences": null, + "max_members": 250000, + "max_video_channel_users": 25, + "vanity_url_code": "no", + "premium_tier": 0, + "premium_subscription_count": 0, + "system_channel_flags": 0, + "preferred_locale": "en-US", + "rules_channel_id": null, + "public_updates_channel_id": null, + "safety_alerts_channel_id": null +} +``` + +## Get Guild Preview +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/preview + +Returns the [guild preview](/developers/docs/resources/guild#guild-preview-object) object for the given id. +If the user is not in the guild, then the guild must be [discoverable](/developers/docs/resources/guild#guild-object-guild-features). + +## Modify Guild +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object) + +Modify a guild's settings. Requires the `MANAGE_GUILD` permission. Returns the updated [guild](/developers/docs/resources/guild#guild-object) object on success. Fires a [Guild Update](/developers/docs/events/gateway-events#guild-update) Gateway event. + + +All parameters to this endpoint are optional + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +Attempting to add or remove the `COMMUNITY` [guild feature](/developers/docs/resources/guild#guild-object-guild-features) requires the `ADMINISTRATOR` permission. + + + +###### JSON Params + +| Field | Type | Description | +|-------------------------------|------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | guild name | +| region | ?string | guild [voice region](/developers/docs/resources/voice#voice-region-object) id (deprecated) | +| verification_level | ?integer | [verification level](/developers/docs/resources/guild#guild-object-verification-level) | +| default_message_notifications | ?integer | default [message notification level](/developers/docs/resources/guild#guild-object-default-message-notification-level) | +| explicit_content_filter | ?integer | [explicit content filter level](/developers/docs/resources/guild#guild-object-explicit-content-filter-level) | +| afk_channel_id | ?snowflake | id for afk channel | +| afk_timeout | integer | afk timeout in seconds, can be set to: 60, 300, 900, 1800, 3600 | +| icon | ?[image data](/developers/docs/reference#image-data) | base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has the `ANIMATED_ICON` feature) | +| splash | ?[image data](/developers/docs/reference#image-data) | base64 16:9 png/jpeg image for the guild splash (when the server has the `INVITE_SPLASH` feature) | +| discovery_splash | ?[image data](/developers/docs/reference#image-data) | base64 16:9 png/jpeg image for the guild discovery splash (when the server has the `DISCOVERABLE` feature) | +| banner | ?[image data](/developers/docs/reference#image-data) | base64 16:9 png/jpeg image for the guild banner (when the server has the `BANNER` feature; can be animated gif when the server has the `ANIMATED_BANNER` feature) | +| system_channel_id | ?snowflake | the id of the channel where guild notices such as welcome messages and boost events are posted | +| system_channel_flags | integer | [system channel flags](/developers/docs/resources/guild#guild-object-system-channel-flags) | +| rules_channel_id | ?snowflake | the id of the channel where Community guilds display rules and/or guidelines | +| public_updates_channel_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive notices from Discord | +| preferred_locale | ?string | the preferred [locale](/developers/docs/reference#locales) of a Community guild used in server discovery and notices from Discord; defaults to "en-US" | +| features | array of [guild feature](/developers/docs/resources/guild#guild-object-guild-features) strings | enabled guild features | +| description | ?string | the description for the guild | +| premium_progress_bar_enabled | boolean | whether the guild's boost progress bar should be enabled | +| safety_alerts_channel_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive safety alerts from Discord | + +## Get Guild Channels +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/channels + +Returns a list of guild [channel](/developers/docs/resources/channel#channel-object) objects. Does not include threads. + +## Create Guild Channel +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/channels + +Create a new [channel](/developers/docs/resources/channel#channel-object) object for the guild. Requires the `MANAGE_CHANNELS` permission. If setting permission overwrites, only permissions your bot has in the guild can be allowed/denied. Setting `MANAGE_ROLES` permission in channels is only possible for guild administrators. Returns the new [channel](/developers/docs/resources/channel#channel-object) object on success. Fires a [Channel Create](/developers/docs/events/gateway-events#channel-create) Gateway event. + + +All parameters to this endpoint are optional and nullable excluding `name` + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Channel Type | +|------------------------------------|-------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------| +| name | string | channel name (1-100 characters) | All | +| type | integer | the [type of channel](/developers/docs/resources/channel#channel-object-channel-types) | All | +| topic | string | channel topic (0-1024 characters) | Text, Announcement, Forum, Media | +| bitrate\* | integer | the bitrate (in bits) of the voice or stage channel; min 8000 | Voice, Stage | +| user_limit | integer | the user limit of the voice channel | Voice, Stage | +| rate_limit_per_user | integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages` or `manage_channel`, are unaffected | Text, Voice, Stage, Forum, Media | +| position | integer | sorting position of the channel (channels with the same position are sorted by id) | All | +| permission_overwrites\*\* | array of partial [overwrite](/developers/docs/resources/channel#overwrite-object) objects | the channel's permission overwrites | All | +| parent_id | snowflake | id of the parent category for a channel | Text, Voice, Announcement, Stage, Forum, Media | +| nsfw | boolean | whether the channel is nsfw | Text, Voice, Announcement, Stage, Forum | +| rtc_region | string | channel [voice region](/developers/docs/resources/voice#voice-region-object) id of the voice or stage channel, automatic when set to null | Voice, Stage | +| video_quality_mode | integer | the camera [video quality mode](/developers/docs/resources/channel#channel-object-video-quality-modes) of the voice channel | Voice, Stage | +| default_auto_archive_duration | integer | the default duration that the clients use (not the API) for newly created threads in the channel, in minutes, to automatically archive the thread after recent activity | Text, Announcement, Forum, Media | +| default_reaction_emoji | [default reaction](/developers/docs/resources/channel#default-reaction-object) object | emoji to show in the add reaction button on a thread in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | Forum, Media | +| available_tags | array of [tag](/developers/docs/resources/channel#forum-tag-object) objects | set of tags that can be used in a `GUILD_FORUM` or a `GUILD_MEDIA` channel | Forum, Media | +| default_sort_order | integer | the [default sort order type](/developers/docs/resources/channel#channel-object-sort-order-types) used to order posts in `GUILD_FORUM` and `GUILD_MEDIA` channels | Forum, Media | +| default_forum_layout | integer | the [default forum layout view](/developers/docs/resources/channel#channel-object-forum-layout-types) used to display posts in `GUILD_FORUM` channels | Forum | +| default_thread_rate_limit_per_user | integer | the initial `rate_limit_per_user` to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update. | Text, Announcement, Forum, Media | + +\* For voice channels, normal servers can set bitrate up to 96000, servers with Boost level 1 can set up to 128000, servers with Boost level 2 can set up to 256000, and servers with Boost level 3 or the `VIP_REGIONS` [guild feature](/developers/docs/resources/guild#guild-object-guild-features) can set up to 384000. For stage channels, bitrate can be set up to 64000. + +\*\* In each overwrite object, the `allow` and `deny` keys can be omitted or set to `null`, which both default to `"0"`. + +## Modify Guild Channel Positions +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/channels + +Modify the positions of a set of [channel](/developers/docs/resources/channel#channel-object) objects for the guild. Requires `MANAGE_CHANNELS` permission. Returns a 204 empty response on success. Fires multiple [Channel Update](/developers/docs/events/gateway-events#channel-update) Gateway events. + + +Only channels to be modified are required. + + +This endpoint takes a JSON array of parameters in the following format: + + +###### JSON Params + +| Field | Type | Description | +|-------------------|------------|------------------------------------------------------------------------------------| +| id | snowflake | channel id | +| position? | ?integer | sorting position of the channel (channels with the same position are sorted by id) | +| lock_permissions? | ?boolean | syncs the permission overwrites with the new parent, if moving to a new category | +| parent_id? | ?snowflake | the new parent ID for the channel that is moved | + +## List Active Guild Threads +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/threads/active + +Returns all active threads in the guild, including public and private threads. Threads are ordered by their `id`, in descending order. + + +###### Response Body + +| Field | Type | Description | +|---------|--------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| +| threads | array of [channel](/developers/docs/resources/channel#channel-object) objects | the active threads | +| members | array of [thread members](/developers/docs/resources/channel#thread-member-object) objects | a thread member object for each returned thread the current user has joined | + +## Get Guild Member +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Returns a [guild member](/developers/docs/resources/guild#guild-member-object) object for the specified user. + +## List Guild Members +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members + +Returns a list of [guild member](/developers/docs/resources/guild#guild-member-object) objects that are members of the guild. + + +This endpoint is restricted according to whether the `GUILD_MEMBERS` [Privileged Intent](/developers/docs/events/gateway#privileged-intents) is enabled for your application. + + + +All parameters to this endpoint are optional + + + +###### Query String Params + +| Field | Type | Description | Default | +|-------|-----------|------------------------------------------|---------| +| limit | integer | max number of members to return (1-1000) | 1 | +| after | snowflake | the highest user id in the previous page | 0 | + +## Search Guild Members +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/search + +Returns a list of [guild member](/developers/docs/resources/guild#guild-member-object) objects whose username or nickname starts with a provided string. + + +All parameters to this endpoint except for `query` are optional + + + +###### Query String Params + +| Field | Type | Description | Default | +|-------|---------|------------------------------------------------------------|---------| +| query | string | Query string to match username(s) and nickname(s) against. | | +| limit | integer | max number of members to return (1-1000) | 1 | + +## Add Guild Member +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Adds a user to the guild, provided you have a valid oauth2 access token for the user with the `guilds.join` scope. Returns a 201 Created with the [guild member](/developers/docs/resources/guild#guild-member-object) as the body, or 204 No Content if the user is already a member of the guild. Fires a [Guild Member Add](/developers/docs/events/gateway-events#guild-member-add) Gateway event. + +For guilds with [Membership Screening](/developers/docs/resources/guild#membership-screening-object) enabled, this endpoint will default to adding new members as `pending` in the [guild member object](/developers/docs/resources/guild#guild-member-object). Members that are `pending` will have to complete membership screening before they become full members that can talk. + + +All parameters to this endpoint except for `access_token` are optional. + + + +The Authorization header must be a Bot token (belonging to the same application used for authorization), and the bot must be a member of the guild with `CREATE_INSTANT_INVITE` permission. + + + +###### JSON Params + +| Field | Type | Description | Permission | +|--------------|---------------------|--------------------------------------------------------------------------------------------------------------------------|------------------| +| access_token | string | an oauth2 access token granted with the `guilds.join` to the bot's application for the user you want to add to the guild | | +| nick | string | value to set user's nickname to | MANAGE_NICKNAMES | +| roles | array of snowflakes | array of role ids the member is assigned | MANAGE_ROLES | +| mute | boolean | whether the user is muted in voice channels | MUTE_MEMBERS | +| deaf | boolean | whether the user is deafened in voice channels | DEAFEN_MEMBERS | + + +## Modify Guild Member +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Modify attributes of a [guild member](/developers/docs/resources/guild#guild-member-object). Returns a 200 OK with the [guild member](/developers/docs/resources/guild#guild-member-object) as the body. Fires a [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) Gateway event. If the `channel_id` is set to null, this will force the target user to be disconnected from voice. + + +All parameters to this endpoint are optional and nullable. When moving members to channels, the API user _must_ have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Permission | +|------------------------------|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| +| nick | string | value to set user's nickname to | MANAGE_NICKNAMES | +| roles | array of snowflakes | array of role ids the member is assigned | MANAGE_ROLES | +| mute | boolean | whether the user is muted in voice channels. Will throw a 400 error if the user is not in a voice channel | MUTE_MEMBERS | +| deaf | boolean | whether the user is deafened in voice channels. Will throw a 400 error if the user is not in a voice channel | DEAFEN_MEMBERS | +| channel_id | snowflake | id of channel to move user to (if they are connected to voice) | MOVE_MEMBERS | +| communication_disabled_until | ISO8601 timestamp | when the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again (up to 28 days in the future), set to null to remove timeout. Will throw a 403 error if the user has the ADMINISTRATOR permission or is the owner of the guild | MODERATE_MEMBERS | +| flags | integer | [guild member flags](/developers/docs/resources/guild#guild-member-object-guild-member-flags) | MANAGE_GUILD or MANAGE_ROLES or (MODERATE_MEMBERS and KICK_MEMBERS and BAN_MEMBERS) | + +## Modify Current Member +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/@me + +Modifies the current member in a guild. Returns a 200 with the updated member object on success. Fires a [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Permission | +|---------|---------|-------------------------------------------------------------------------------|-----------------| +| nick? | ?string | value to set user's nickname to | CHANGE_NICKNAME | +| banner? | ?string | [data URI base64 encoded](/developers/docs/reference#image-data) banner image | | +| avatar? | ?string | [data URI base64 encoded](/developers/docs/reference#image-data) avatar image | | +| bio? | ?string | guild member bio | | + +## Modify Current User Nick +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/@me/nick + + +Deprecated in favor of [Modify Current Member](/developers/docs/resources/guild#modify-current-member). + + +Modifies the nickname of the current user in a guild. Returns a 200 with the nickname on success. Fires a [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Permission | +|-------|---------|---------------------------------|-----------------| +| nick? | ?string | value to set user's nickname to | CHANGE_NICKNAME | + +## Add Guild Member Role +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/[\{user.id\}](/developers/docs/resources/user#user-object)/roles/[\{role.id\}](/developers/docs/topics/permissions#role-object) + +Adds a role to a [guild member](/developers/docs/resources/guild#guild-member-object). Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Remove Guild Member Role +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/[\{user.id\}](/developers/docs/resources/user#user-object)/roles/[\{role.id\}](/developers/docs/topics/permissions#role-object) + +Removes a role from a [guild member](/developers/docs/resources/guild#guild-member-object). Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Remove Guild Member +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Remove a member from a guild. Requires `KICK_MEMBERS` permission. Returns a 204 empty response on success. Fires a [Guild Member Remove](/developers/docs/events/gateway-events#guild-member-remove) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Get Guild Bans +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/bans + +Returns a list of [ban](/developers/docs/resources/guild#ban-object) objects for the users banned from this guild. Requires the `BAN_MEMBERS` permission. + + +###### Query String Params + +| Field | Type | Description | Default | +|------------|-----------|------------------------------------------------|---------| +| limit? | number | number of users to return (up to maximum 1000) | 1000 | +| before? \* | snowflake | consider only users before given user id | null | +| after? \* | snowflake | consider only users after given user id | null | + +\* Provide a user id to `before` and `after` for pagination. Users will always be returned in ascending order by `user.id`. If both `before` and `after` are provided, only `before` is respected. + +## Get Guild Ban +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/bans/[\{user.id\}](/developers/docs/resources/user#user-object) + +Returns a [ban](/developers/docs/resources/guild#ban-object) object for the given user or a 404 not found if the ban cannot be found. Requires the `BAN_MEMBERS` permission. + +## Create Guild Ban +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/bans/[\{user.id\}](/developers/docs/resources/user#user-object) + +Create a guild ban, and optionally delete previous messages sent by the banned user. Requires the `BAN_MEMBERS` permission. Returns a 204 empty response on success. Fires a [Guild Ban Add](/developers/docs/events/gateway-events#guild-ban-add) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Default | +|-------------------------|---------|-------------------------------------------------------------------------|---------| +| delete_message_days? | integer | number of days to delete messages for (0-7) (deprecated) | 0 | +| delete_message_seconds? | integer | number of seconds to delete messages for, between 0 and 604800 (7 days) | 0 | + +## Remove Guild Ban +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/bans/[\{user.id\}](/developers/docs/resources/user#user-object) + +Remove the ban for a user. Requires the `BAN_MEMBERS` permissions. Returns a 204 empty response on success. Fires a [Guild Ban Remove](/developers/docs/events/gateway-events#guild-ban-remove) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Bulk Guild Ban +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/bulk-ban + +Ban up to 200 users from a guild, and optionally delete previous messages sent by the banned users. Requires both the `BAN_MEMBERS` and `MANAGE_GUILD` permissions. Returns a 200 response on success, including the fields `banned_users` with the IDs of the banned users and `failed_users` with IDs that could not be banned or were already banned. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Default | +|-------------------------|---------------------|-------------------------------------------------------------------------|---------| +| user_ids | array of snowflakes | list of user ids to ban (max 200) | | +| delete_message_seconds? | integer | number of seconds to delete messages for, between 0 and 604800 (7 days) | 0 | + + +###### Bulk Ban Response + +On success, this endpoint returns a 200 success response with the following body. + +| Field | Type | Description | +|--------------|---------------------|-------------------------------------------------| +| banned_users | array of snowflakes | list of user ids, that were successfully banned | +| failed_users | array of snowflakes | list of user ids, that were not banned | + + +If none of the users could be banned, an error response code `500000: Failed to ban users` is returned instead. + + +## Get Guild Roles +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/roles + +Returns a list of [role](/developers/docs/topics/permissions#role-object) objects for the guild. + +## Get Guild Role +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/roles/[\{role.id\}](/developers/docs/topics/permissions#role-object) + +Returns a [role](/developers/docs/topics/permissions#role-object) object for the specified role. + +## Create Guild Role +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/roles + +Create a new [role](/developers/docs/topics/permissions#role-object) for the guild. Requires the `MANAGE_ROLES` permission. Returns the new [role](/developers/docs/topics/permissions#role-object) object on success. Fires a [Guild Role Create](/developers/docs/events/gateway-events#guild-role-create) Gateway event. All JSON params are optional. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Default | +|---------------|------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| +| name | string | name of the role, max 100 characters | "new role" | +| permissions | string | bitwise value of the enabled/disabled permissions | @everyone permissions in guild | +| color* | integer | **Deprecated** RGB color value | 0 | +| colors | [role colors](/developers/docs/topics/permissions#role-object-role-colors-object) object | the role's colors | [default role colors object](/developers/docs/topics/permissions#role-object-default-role-colors-object) | +| hoist | boolean | whether the role should be displayed separately in the sidebar | false | +| icon | ?[image data](/developers/docs/reference#image-data) | the role's icon image (if the guild has the `ROLE_ICONS` feature) | null | +| unicode_emoji | ?string | the role's unicode emoji as a [standard emoji](/developers/docs/reference#message-formatting) (if the guild has the `ROLE_ICONS` feature) | null | +| mentionable | boolean | whether the role should be mentionable | false | + +\* `color` will still be returned by the API, but using the `colors` field is recommended when doing requests. + +## Modify Guild Role Positions +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/roles + +Modify the positions of a set of [role](/developers/docs/topics/permissions#role-object) objects for the guild. Requires the `MANAGE_ROLES` permission. Returns a list of all of the guild's [role](/developers/docs/topics/permissions#role-object) objects on success. Fires multiple [Guild Role Update](/developers/docs/events/gateway-events#guild-role-update) Gateway events. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +This endpoint takes a JSON array of parameters in the following format: + + +###### JSON Params + +| Field | Type | Description | +|-----------|-----------|------------------------------------------------------------------------------| +| id | snowflake | role | +| position? | ?integer | sorting position of the role (roles with the same position are sorted by id) | + +## Modify Guild Role +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/roles/[\{role.id\}](/developers/docs/topics/permissions#role-object) + +Modify a guild role. Requires the `MANAGE_ROLES` permission. Returns the updated [role](/developers/docs/topics/permissions#role-object) on success. Fires a [Guild Role Update](/developers/docs/events/gateway-events#guild-role-update) Gateway event. + + +All parameters to this endpoint are optional and nullable. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|---------------|------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| +| name | string | name of the role, max 100 characters | +| permissions | string | bitwise value of the enabled/disabled permissions | +| color* | integer | **Deprecated** RGB color value | +| colors | [role colors](/developers/docs/topics/permissions#role-object-role-colors-object) object | the role's colors | +| hoist | boolean | whether the role should be displayed separately in the sidebar | +| icon | [image data](/developers/docs/reference#image-data) | the role's icon image (if the guild has the `ROLE_ICONS` feature) | +| unicode_emoji | string | the role's unicode emoji as a [standard emoji](/developers/docs/reference#message-formatting) (if the guild has the `ROLE_ICONS` feature) | +| mentionable | boolean | whether the role should be mentionable | + +\* `color` will still be returned by the API, but using the `colors` field is recommended when doing requests. + +## Delete Guild Role +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/roles/[\{role.id\}](/developers/docs/topics/permissions#role-object) + +Delete a guild role. Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Role Delete](/developers/docs/events/gateway-events#guild-role-delete) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Get Guild Prune Count +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/prune + +Returns an object with one `pruned` key indicating the number of members that would be removed in a prune operation. Requires the `MANAGE_GUILD` and `KICK_MEMBERS` permissions. + +By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` parameter. Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. + + +###### Query String Params + +| Field | Type | Description | Default | +|---------------|---------------------------------------------|------------------------------------------|---------| +| days | integer | number of days to count prune for (1-30) | 7 | +| include_roles | string; comma-delimited array of snowflakes | role(s) to include | none | + +## Begin Guild Prune +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/prune + +Begin a prune operation. Requires the `MANAGE_GUILD` and `KICK_MEMBERS` permissions. Returns an object with one `pruned` key indicating the number of members that were removed in the prune operation. For large guilds it's recommended to set the `compute_prune_count` option to `false`, forcing `pruned` to `null`. Fires multiple [Guild Member Remove](/developers/docs/events/gateway-events#guild-member-remove) Gateway events. + +By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | Default | +|---------------------|---------------------|------------------------------------------------------------|---------| +| days | integer | number of days to prune (1-30) | 7 | +| compute_prune_count | boolean | whether `pruned` is returned, discouraged for large guilds | true | +| include_roles | array of snowflakes | role(s) to include | none | +| reason? | string | reason for the prune (deprecated) | | + +## Get Guild Voice Regions +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/regions + +Returns a list of [voice region](/developers/docs/resources/voice#voice-region-object) objects for the guild. Unlike the similar `/voice` route, this returns VIP servers when the guild is VIP-enabled. + +## Get Guild Invites +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/invites + +Returns a list of [invite](/developers/docs/resources/invite#invite-object) objects. Requires the `MANAGE_GUILD` or `VIEW_AUDIT_LOG` permission. [Invite Metadata](/developers/docs/resources/invite#invite-metadata-object) is included with the `MANAGE_GUILD` permission. + +## Get Guild Integrations +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/integrations + +Returns a list of [integration](/developers/docs/resources/guild#integration-object) objects for the guild. Requires the `MANAGE_GUILD` permission. + + +This endpoint returns a maximum of 50 integrations. If a guild has more integrations, they cannot be accessed. + + +## Delete Guild Integration +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/integrations/[\{integration.id\}](/developers/docs/resources/guild#integration-object) + +Delete the attached [integration](/developers/docs/resources/guild#integration-object) object for the guild. Deletes any associated webhooks and kicks the associated bot if there is one. Requires the `MANAGE_GUILD` permission. Returns a 204 empty response on success. Fires [Guild Integrations Update](/developers/docs/events/gateway-events#guild-integrations-update) and [Integration Delete](/developers/docs/events/gateway-events#integration-delete) Gateway events. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Get Guild Widget Settings +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/widget + +Returns a [guild widget settings](/developers/docs/resources/guild#guild-widget-settings-object) object. Requires the `MANAGE_GUILD` permission. + +## Modify Guild Widget +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/widget + +Modify a [guild widget settings](/developers/docs/resources/guild#guild-widget-settings-object) object for the guild. All attributes may be passed in with JSON and modified. Requires the `MANAGE_GUILD` permission. Returns the updated [guild widget settings](/developers/docs/resources/guild#guild-widget-settings-object) object. Fires a [Guild Update](/developers/docs/events/gateway-events#guild-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Get Guild Widget +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/widget.json + +Returns the [widget](/developers/docs/resources/guild#guild-widget-object) for the guild. Fires an [Invite Create](/developers/docs/events/gateway-events#invite-create) Gateway event when an invite channel is defined and a new [Invite](/developers/docs/resources/invite#invite-object) is generated. + +## Get Guild Vanity URL +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/vanity-url + +Returns a partial [invite](/developers/docs/resources/invite#invite-object) object for guilds with that feature enabled. Requires the `MANAGE_GUILD` permission. `code` will be null if a vanity url for the guild is not set. + + +This endpoint is required to get the usage count of the vanity invite, but the invite code can be accessed as `vanity_url_code` in the [guild object](/developers/docs/resources/guild#guild-object) without having the `MANAGE_GUILD` permission. + + + +###### Example Partial Invite Object + +```json +{ + "code": "abc", + "uses": 12 +} +``` + +## Get Guild Widget Image +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/widget.png + +Returns a PNG image widget for the guild. Requires no permissions or authentication. + + +All parameters to this endpoint are optional. + + + +###### Query String Params + +| Field | Type | Description | Default | +|-------|--------|------------------------------------------------|---------| +| style | string | style of the widget image returned (see below) | shield | + + +###### Widget Style Options + +| Value | Description | Example | +|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------| +| shield | shield style widget with Discord icon and guild members online count | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=shield) | +| banner1 | large image with guild icon, name and online count. "POWERED BY DISCORD" as the footer of the widget | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner1) | +| banner2 | smaller widget style with guild icon, name and online count. Split on the right with Discord logo | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner2) | +| banner3 | large image with guild icon, name and online count. In the footer, Discord logo on the left and "Chat Now" on the right | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner3) | +| banner4 | large Discord logo at the top of the widget. Guild icon, name and online count in the middle portion of the widget and a "JOIN MY SERVER" button at the bottom | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner4) | + +## Get Guild Welcome Screen +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/welcome-screen + +Returns the [Welcome Screen](/developers/docs/resources/guild#welcome-screen-object) object for the guild. If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. + +## Modify Guild Welcome Screen +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/welcome-screen + +Modify the guild's [Welcome Screen](/developers/docs/resources/guild#welcome-screen-object). Requires the `MANAGE_GUILD` permission. Returns the updated [Welcome Screen](/developers/docs/resources/guild#welcome-screen-object) object. May fire a [Guild Update](/developers/docs/events/gateway-events#guild-update) Gateway event. + + +All parameters to this endpoint are optional and nullable + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|------------------|------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------| +| enabled | boolean | whether the welcome screen is enabled | +| welcome_channels | array of [welcome screen channel](/developers/docs/resources/guild#welcome-screen-object-welcome-screen-channel-structure) objects | channels linked in the welcome screen and their display options | +| description | string | the server description to show in the welcome screen | + +## Get Guild Onboarding +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/onboarding + +Returns the [Onboarding](/developers/docs/resources/guild#guild-onboarding-object) object for the guild. + +## Modify Guild Onboarding +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/onboarding + +Modifies the onboarding configuration of the guild. Returns a 200 with the [Onboarding](/developers/docs/resources/guild#guild-onboarding-object) object for the guild. Requires the `MANAGE_GUILD` and `MANAGE_ROLES` permissions. + + +Onboarding enforces constraints when enabled. These constraints are that there must be at least 7 Default Channels and at least 5 of them must allow sending messages to the @everyone role. The `mode` field modifies what is considered when enforcing these constraints. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +All parameters to this endpoint are optional. + + + +###### JSON Params + +| Field | Type | Description | +|---------------------|----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------| +| prompts | array of [onboarding prompt](/developers/docs/resources/guild#guild-onboarding-object-onboarding-prompt-structure) objects | Prompts shown during onboarding and in customize community | +| default_channel_ids | array of snowflakes | Channel IDs that members get opted into automatically | +| enabled | boolean | Whether onboarding is enabled in the guild | +| mode | [onboarding mode](/developers/docs/resources/guild#guild-onboarding-object-onboarding-mode) | Current mode of onboarding | + +## Modify Guild Incident Actions +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/incident-actions + +Modifies the incident actions of the guild. Returns a 200 with the [Incidents Data](/developers/docs/resources/guild#incidents-data-object) object for the guild. Requires the `MANAGE_GUILD` permission. + + +###### JSON Params + + +Both `invites_disabled_until` and `dms_disabled_until` can be enabled for a maximal timespan of 24 hours in the future. + + +| Field | Type | Description | +|-------------------------|-----------------------|--------------------------------------------| +| invites_disabled_until? | ?ISO8601 timestamp \* | when invites will be enabled again | +| dms_disabled_until? | ?ISO8601 timestamp \* | when direct messages will be enabled again | + +\* Supplying `null` disables the action. diff --git a/discord/developers/docs/resources/invite.mdx b/discord/developers/docs/resources/invite.mdx new file mode 100644 index 0000000000..46e1c1e735 --- /dev/null +++ b/discord/developers/docs/resources/invite.mdx @@ -0,0 +1,184 @@ +--- +title: Invite Resource +sidebarTitle: Invite +description: Reference for Discord Invite objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +### Invite Object + +Represents a code that when used, adds a user to a guild or group DM channel. + + +###### Invite Structure +| Field | Type | Description | +|-----------------------------|---------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| type | integer | the [type of invite](/developers/docs/resources/invite#invite-object-invite-types) | +| code | string | the invite code (unique ID) | +| guild? | partial [guild](/developers/docs/resources/guild#guild-object) object | the guild this invite is for | +| channel | ?partial [channel](/developers/docs/resources/channel#channel-object) object | the channel this invite is for | +| inviter? | [user](/developers/docs/resources/user#user-object) object | the user who created the invite | +| target_type? | integer | the [type of target](/developers/docs/resources/invite#invite-object-invite-target-types) for this voice channel invite | +| target_user? | [user](/developers/docs/resources/user#user-object) object | the user whose stream to display for this voice channel stream invite | +| target_application? | partial [application](/developers/docs/resources/application#application-object) object | the embedded application to open for this voice channel embedded application invite | +| approximate_presence_count? | integer | approximate count of online members, returned from the `GET /invites/` endpoint when `with_counts` is `true` | +| approximate_member_count? | integer | approximate count of total members, returned from the `GET /invites/` endpoint when `with_counts` is `true` | +| expires_at | ?ISO8601 timestamp | the expiration date of this invite | +| guild_scheduled_event? | [guild scheduled event](/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object) object | guild scheduled event data, only included if `guild_scheduled_event_id` contains a valid guild scheduled event id | +| flags? | integer | [guild invite flags](/developers/docs/resources/invite#invite-object-guild-invite-flags) for guild invites | + + +###### Invite Types + +| Type | Value | +|----------|-------| +| GUILD | 0 | +| GROUP_DM | 1 | +| FRIEND | 2 | + + +###### Invite Target Types + +| Type | Value | +|----------------------|-------| +| STREAM | 1 | +| EMBEDDED_APPLICATION | 2 | + + +###### Guild Invite Flags + +| Flag | Value | Description | +|-----------------|----------|---------------------------------------------------| +| IS_GUEST_INVITE | `1 << 0` | this invite is a guest invite for a voice channel | + + +###### Example Invite Object + +```json +{ + "type": 0, + "code": "0vCdhLbwjZZTWZLD", + "guild": { + "id": "165176875973476352", + "name": "CS:GO Fraggers Only", + "splash": null, + "banner": null, + "description": "Very good description", + "icon": null, + "features": ["NEWS", "DISCOVERABLE"], + "verification_level": 2, + "vanity_url_code": null, + "nsfw_level": 0, + "premium_subscription_count": 5 + }, + "channel": { + "id": "165176875973476352", + "name": "illuminati", + "type": 0 + }, + "inviter": { + "id": "115590097100865541", + "username": "speed", + "avatar": "deadbeef", + "discriminator": "7653", + "public_flags": 131328 + }, + "target_type": 1, + "target_user": { + "id": "165176875973476352", + "username": "bob", + "avatar": "deadbeef", + "discriminator": "1234", + "public_flags": 64 + } +} +``` + +### Invite Metadata Object + +Extra information about an invite, will extend the [invite](/developers/docs/resources/invite#invite-object) object. + + +###### Invite Metadata Structure + +| Field | Type | Description | +|------------|-------------------|------------------------------------------------------| +| uses | integer | number of times this invite has been used | +| max_uses | integer | max number of times this invite can be used | +| max_age | integer | duration (in seconds) after which the invite expires | +| temporary | boolean | whether this invite only grants temporary membership | +| created_at | ISO8601 timestamp | when this invite was created | + + +###### Example Invite Metadata + +```json +{ + "uses": 0, + "max_uses": 0, + "max_age": 0, + "temporary": false, + "created_at": "2016-03-31T19:15:39.954000+00:00" +} +``` + +### Invite Stage Instance Object + + +This is deprecated. + + + +###### Invite Stage Instance Structure +| Field | Type | Description | +|-------------------|-----------------------------------------------------------------------------------------------|----------------------------------------------------| +| members | array of partial [guild member](/developers/docs/resources/guild#guild-member-object) objects | the members speaking in the Stage | +| participant_count | integer | the number of users in the Stage | +| speaker_count | integer | the number of users speaking in the Stage | +| topic | string | the topic of the Stage instance (1-120 characters) | + + +###### Example Invite Stage Instance + +```json +{ + "topic": "The debate is over: diet is better than regular", + "participant_count": 200, + "speaker_count": 5 , + "members": [ + { + "roles": [], + "nick": "NOT API SUPPORT", + "avatar": null, + "premium_since": null, + "joined_at": "2015-04-26T06:26:56.936000+00:00", + "pending": false, + "user": {} + } + ] +} +``` + +## Get Invite +/invites/[\{invite.code\}](/developers/docs/resources/invite#invite-object) + +Returns an [invite](/developers/docs/resources/invite#invite-object) object for the given code. + + +###### Query String Params + +| Field | Type | Description | +|---------------------------|-------------------------------------------------------------|-------------------------------------------------------------| +| with_counts? | [boolean](/developers/docs/reference#boolean-query-strings) | whether the invite should contain approximate member counts | +| guild_scheduled_event_id? | snowflake | the guild scheduled event to include with the invite | + +## Delete Invite +/invites/[\{invite.code\}](/developers/docs/resources/invite#invite-object) + +Delete an invite. Requires the `MANAGE_CHANNELS` permission on the channel this invite belongs to, or `MANAGE_GUILD` to remove any invite across the guild. Returns an [invite](/developers/docs/resources/invite#invite-object) object on success. Fires an [Invite Delete](/developers/docs/events/gateway-events#invite-delete) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + diff --git a/discord/developers/docs/resources/lobby.mdx b/discord/developers/docs/resources/lobby.mdx new file mode 100644 index 0000000000..eaba1264e3 --- /dev/null +++ b/discord/developers/docs/resources/lobby.mdx @@ -0,0 +1,180 @@ +--- +title: Lobby Resource +sidebarTitle: Lobby +description: Reference for Discord lobby objects used for matchmaking. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +### Lobby Object + +Represents a lobby within Discord. See [Managing Lobbies](/developers/docs/discord-social-sdk/development-guides/managing-lobbies) for more information. + + +###### Lobby Structure + +| Field | Type | Description | +|-----------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------| +| id | snowflake | the id of this channel | +| application_id | snowflake | application that created the lobby | +| metadata | ?dict\ | dictionary of string key/value pairs. The max total length is 1000. | +| members | array of [lobby member](/developers/docs/resources/lobby#lobby-member-object) objects | members of the lobby | +| linked_channel? | channel object | the guild channel linked to the lobby | + +### Lobby Member Object + +Represents a member of a lobby, including optional metadata and flags. + + +###### Lobby Member Structure + +| Field | Type | Description | +|-----------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | the id of the user | +| metadata? | ?dict\ | dictionary of string key/value pairs. The max total length is 1000. | +| flags? | integer | [lobby member flags](/developers/docs/resources/lobby#lobby-member-object-lobby-member-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | + + +###### Lobby Member Flags + +| Flag | Value | Description | +|--------------|--------|-----------------------------------------| +| CanLinkLobby | `1<<0` | user can link a text channel to a lobby | + + + +###### Example Lobby Object + +```json +{ + "id": "96008815106887111", + "application_id": "41771983429993937", + "metadata": { + "topic": "we need more redstone", + }, + "members": [ + { + "id": "41771983429993000", + "metadata": null, + "flags": 1 + } + ] +} +``` + +## Create Lobby +/lobbies + +Creates a new lobby, adding any of the specified members to it, if provided. + +Returns a [lobby](/developers/docs/resources/lobby#lobby-object) object. + + +[Discord Social SDK](/developers/docs/discord-social-sdk/overview) clients will not be able to join or leave a lobby created using this API, such as [`Client::CreateOrJoinLobby`]. See [Managing Lobbies](/developers/docs/discord-social-sdk/development-guides/managing-lobbies) for more information. + + +### JSON Params + +| Field | Type | Description | +|-----------------------|---------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. | +| members? | array of [lobby member](/developers/docs/resources/lobby#lobby-member-object) objects | optional array of up to 25 users to be added to the lobby | +| idle_timeout_seconds? | integer | seconds to wait before shutting down a lobby after it becomes idle. Value can be between 5 and 604800 (7 days). See [`LobbyHandle`] for more details on this behavior. | + +#### Lobby Member JSON Params + +| Field | Type | Description | +|-----------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | Discord user id of the user to add to the lobby | +| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. | +| flags? | integer | [lobby member flags](/developers/docs/resources/lobby#lobby-member-object-lobby-member-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | + +## Get Lobby +/lobbies/[\{lobby.id\}](/developers/docs/resources/lobby#lobby-object) + +Returns a [lobby](/developers/docs/resources/lobby#lobby-object) object for the specified lobby id, if it exists. + +## Modify Lobby +/lobbies/[\{lobby.id\}](/developers/docs/resources/lobby#lobby-object) + +Modifies the specified lobby with new values, if provided. + +Returns the updated [lobby](/developers/docs/resources/lobby#lobby-object) object. + +### JSON Params + +| Field | Type | Description | +|-----------------------|---------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. Overwrites any existing metadata. | +| members? | array of [lobby member](/developers/docs/resources/lobby#lobby-member-object) objects | optional array of up to 25 users to replace the lobby members with. If provided, lobby members not in this list will be removed from the lobby. | +| idle_timeout_seconds? | integer | seconds to wait before shutting down a lobby after it becomes idle. Value can be between 5 and 604800 (7 days). See [`LobbyHandle`] for more details on this behavior. | + +## Delete Lobby +/lobbies/[\{lobby.id\}](/developers/docs/resources/lobby#lobby-object) + +Deletes the specified lobby if it exists. + +It is safe to call even if the lobby is already deleted as well. + +Returns nothing. + +## Add a Member to a Lobby +/lobbies/[\{lobby.id\}](/developers/docs/resources/lobby#lobby-object)/members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Adds the provided user to the specified lobby. If called when the user is already a member of the lobby will update fields such as metadata on that user instead. + +Returns the [lobby member](/developers/docs/resources/lobby#lobby-member-object) object. + +### JSON Params + +| Field | Type | Description | +|-----------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| metadata? | ?dict\ | optional dictionary of string key/value pairs. The max total length is 1000. | +| flags? | integer | [lobby member flags](/developers/docs/resources/lobby#lobby-member-object-lobby-member-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | + +## Remove a Member from a Lobby +/lobbies/[\{lobby.id\}](/developers/docs/resources/lobby#lobby-object)/members/[\{user.id\}](/developers/docs/resources/user#user-object) + +Removes the provided user from the specified lobby. It is safe to call this even if the user is no longer a member of the lobby, but will fail if the lobby does not exist. + +Returns nothing. + +## Leave Lobby +/lobbies/[\{lobby.id\}](/developers/docs/resources/lobby#lobby-object)/members/@me + +Removes the current user from the specified lobby. It is safe to call this even if the user is no longer a member of the lobby, but will fail if the lobby does not exist. + +Uses `Bearer` token for authorization. + +Returns nothing. + +## Link Channel to Lobby +/lobbies/[\{lobby.id\}](/developers/docs/resources/lobby#lobby-object)/channel-linking + +Links an existing text channel to a lobby. See [Linked Channels](/developers/docs/discord-social-sdk/development-guides/linked-channels) for more information. + +Uses `Bearer` token for authorization and user must be a lobby member with `CanLinkLobby` [lobby member flag](/developers/docs/resources/lobby#lobby-member-object-lobby-member-flags). + +Returns a [lobby](/developers/docs/resources/lobby#lobby-object) object with a linked channel. + +### JSON Params + +| Field | Type | Description | +|-------------|-----------|------------------------------------------------------------------------------------------------------------------------| +| channel_id? | snowflake | the id of the channel to link to the lobby. If not provided, will unlink any currently linked channels from the lobby. | + +## Unlink Channel from Lobby +/lobbies/[\{lobby.id\}](/developers/docs/resources/lobby#lobby-object)/channel-linking + +Unlinks any currently linked channels from the specified lobby. + +Send a request to this endpoint with an empty body to unlink any currently linked channels from the specified lobby. + +Uses `Bearer` token for authorization and user must be a lobby member with `CanLinkLobby` [lobby member flag](/developers/docs/resources/lobby#lobby-member-object-lobby-member-flags). + +Returns a [lobby](/developers/docs/resources/lobby#lobby-object) object without a linked channel. + +{/* Autogenerated Reference Links */} +[`Client::CreateOrJoinLobby`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512 +[`LobbyHandle`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#a04cebab69ab0e7fb930346a14a87e843 \ No newline at end of file diff --git a/discord/developers/docs/resources/message.mdx b/discord/developers/docs/resources/message.mdx new file mode 100644 index 0000000000..6887e250aa --- /dev/null +++ b/discord/developers/docs/resources/message.mdx @@ -0,0 +1,1091 @@ +--- +title: Message Resource +sidebarTitle: Message +description: Reference for Discord message objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +### Message Object + +Represents a message sent in a channel within Discord. + + +###### Message Structure + + +Fields specific to the `MESSAGE_CREATE` and `MESSAGE_UPDATE` events are listed in the [Gateway documentation](/developers/docs/events/gateway-events#message-create). + + + +An app will receive empty values in the `content`, `embeds`, `attachments`, and `components` fields while `poll` will be omitted if they have not configured (or been approved for) the [`MESSAGE_CONTENT` privileged intent (`1 << 15`)](/developers/docs/events/gateway#message-content-intent). + + +| Field | Type | Description | +|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | id of the message | +| channel_id | snowflake | id of the channel the message was sent in | +| author \[1\] | [user](/developers/docs/resources/user#user-object) object | the author of this message (not guaranteed to be a valid user, see below) | +| content \[2\] | string | contents of the message | +| timestamp | ISO8601 timestamp | when this message was sent | +| edited_timestamp | ?ISO8601 timestamp | when this message was edited (or null if never) | +| tts | boolean | whether this was a TTS message | +| mention_everyone | boolean | whether this message mentions everyone | +| mentions | array of [user](/developers/docs/resources/user#user-object) objects | users specifically mentioned in the message | +| mention_roles | array of [role](/developers/docs/topics/permissions#role-object) object ids | roles specifically mentioned in this message | +| mention_channels? \[3\] | array of [channel mention](/developers/docs/resources/message#channel-mention-object) objects | channels specifically mentioned in this message | +| attachments \[2\] | array of [attachment](/developers/docs/resources/message#attachment-object) objects | any attached files | +| embeds \[2\] | array of [embed](/developers/docs/resources/message#embed-object) objects | any embedded content | +| reactions? | array of [reaction](/developers/docs/resources/message#reaction-object) objects | reactions to the message | +| nonce? | integer or string | used for validating a message was sent | +| pinned | boolean | whether this message is pinned | +| webhook_id? | snowflake | if the message is generated by a webhook, this is the webhook's id | +| type | integer | [type of message](/developers/docs/resources/message#message-object-message-types) | +| activity? | [message activity](/developers/docs/resources/message#message-object-message-activity-structure) object | sent with Rich Presence-related chat embeds | +| application? | partial [application](/developers/docs/resources/application#application-object) object | sent with Rich Presence-related chat embeds | +| application_id? | snowflake | if the message is an [Interaction](/developers/docs/interactions/receiving-and-responding) or application-owned webhook, this is the id of the application | +| flags? | integer | [message flags](/developers/docs/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | +| message_reference? | [message reference](/developers/docs/resources/message#message-reference-structure) object | data showing the source of a crosspost, channel follow add, pin, or reply message | +| message_snapshots? \[5\] | array of [message snapshot](/developers/docs/resources/message#message-snapshot-object) objects | the message associated with the `message_reference`. This is a minimal subset of fields in a message (e.g. `author` is excluded.) | +| referenced_message? \[4\] | ?[message object](/developers/docs/resources/message#message-object) | the message associated with the message_reference | +| interaction_metadata? | [message interaction metadata object](/developers/docs/resources/message#message-interaction-metadata-object) | Sent if the message is sent as a result of an [interaction](/developers/docs/interactions/receiving-and-responding) | +| interaction? | [message interaction object](/developers/docs/interactions/receiving-and-responding#message-interaction-object-message-interaction-structure) | **Deprecated in favor of `interaction_metadata`**; sent if the message is a response to an [interaction](/developers/docs/interactions/receiving-and-responding) | +| thread? | [channel](/developers/docs/resources/channel#channel-object) object | the thread that was started from this message, includes [thread member](/developers/docs/resources/channel#thread-member-object) object | +| components? \[2\] | array of [message components](/developers/docs/components/reference#component-object) | sent if the message contains components like buttons, action rows, or other interactive components | +| sticker_items? | array of [message sticker item objects](/developers/docs/resources/sticker#sticker-item-object) | sent if the message contains stickers | +| stickers? | array of [sticker](/developers/docs/resources/sticker#sticker-object) objects | **Deprecated** the stickers sent with the message | +| position? | integer | A generally increasing integer (there may be gaps or duplicates) that represents the approximate position of the message in a thread, it can be used to estimate the relative position of the message in a thread in company with `total_message_sent` on parent thread | +| role_subscription_data? | [role subscription data](/developers/docs/resources/message#role-subscription-data-object) object | data of the role subscription purchase or renewal that prompted this ROLE_SUBSCRIPTION_PURCHASE message | +| resolved? | [resolved](/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure) data | data for users, members, channels, and roles referenced in this message | +| poll? \[2\] | [poll](/developers/docs/resources/poll#poll-object) object | A poll! | +| call? | [message call](/developers/docs/resources/message#message-call-object) object | the call associated with the message | + +\[1\] The author object follows the structure of the user object, but is only a valid user in the case where the message is generated by a user or bot user. If the message is generated by a webhook, the author object corresponds to the webhook's id, username, and avatar. You can tell if a message is generated by a webhook by checking for the `webhook_id` on the message object. + +\[2\] An app will receive empty values in the `content`, `embeds`, `attachments`, and `components` fields while `poll` will be omitted if they have not configured (or been approved for) the [`MESSAGE_CONTENT` privileged intent (`1 << 15`)](/developers/docs/events/gateway#message-content-intent). + +\[3\] Not all channel mentions in a message will appear in `mention_channels`. Only textual channels that are visible to everyone in a lurkable guild will ever be included. Only crossposted messages (via Channel Following) currently include `mention_channels` at all. If no mentions in the message meet these requirements, this field will not be sent. + +\[4\] This field is only returned for messages with a `type` of `19` (REPLY), `21` (THREAD_STARTER_MESSAGE), or `23` (CONTEXT_MENU_COMMAND). If the message is one of these but the `referenced_message` field is not present, the backend did not attempt to fetch the message that was being replied to, so its state is unknown. If the field exists but is null, the referenced message was deleted. + +\[5\] See [message reference types](/developers/docs/resources/message#message-reference-types) + + +###### Message Types + + +Type `19` and `20` are only available in API v8 and above. In v6, they are represented as type `0`. Additionally, type `21` is only available in API v9 and above. + + +| Type | Value | Deletable | +|----------------------------------------------|-------|-----------| +| DEFAULT | 0 | true | +| RECIPIENT_ADD | 1 | false | +| RECIPIENT_REMOVE | 2 | false | +| CALL | 3 | false | +| CHANNEL_NAME_CHANGE | 4 | false | +| CHANNEL_ICON_CHANGE | 5 | false | +| CHANNEL_PINNED_MESSAGE | 6 | true | +| USER_JOIN | 7 | true | +| GUILD_BOOST | 8 | true | +| GUILD_BOOST_TIER_1 | 9 | true | +| GUILD_BOOST_TIER_2 | 10 | true | +| GUILD_BOOST_TIER_3 | 11 | true | +| CHANNEL_FOLLOW_ADD | 12 | true | +| GUILD_DISCOVERY_DISQUALIFIED | 14 | true | +| GUILD_DISCOVERY_REQUALIFIED | 15 | true | +| GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING | 16 | true | +| GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING | 17 | true | +| THREAD_CREATED | 18 | true | +| REPLY | 19 | true | +| CHAT_INPUT_COMMAND | 20 | true | +| THREAD_STARTER_MESSAGE | 21 | false | +| GUILD_INVITE_REMINDER | 22 | true | +| CONTEXT_MENU_COMMAND | 23 | true | +| AUTO_MODERATION_ACTION | 24 | true* | +| ROLE_SUBSCRIPTION_PURCHASE | 25 | true | +| INTERACTION_PREMIUM_UPSELL | 26 | true | +| STAGE_START | 27 | true | +| STAGE_END | 28 | true | +| STAGE_SPEAKER | 29 | true | +| STAGE_TOPIC | 31 | true | +| GUILD_APPLICATION_PREMIUM_SUBSCRIPTION | 32 | true | +| GUILD_INCIDENT_ALERT_MODE_ENABLED | 36 | true | +| GUILD_INCIDENT_ALERT_MODE_DISABLED | 37 | true | +| GUILD_INCIDENT_REPORT_RAID | 38 | true | +| GUILD_INCIDENT_REPORT_FALSE_ALARM | 39 | true | +| PURCHASE_NOTIFICATION | 44 | true | +| POLL_RESULT | 46 | true | + +\* Can only be deleted by members with `MANAGE_MESSAGES` permission + + +###### Message Activity Structure + +| Field | Type | Description | +|-----------|---------|------------------------------------------------------------------------------------------------------| +| type | integer | [type of message activity](/developers/docs/resources/message#message-object-message-activity-types) | +| party_id? | string | party_id from a Rich Presence event | + + +###### Message Activity Types + +| Type | Value | +|--------------|-------| +| JOIN | 1 | +| SPECTATE | 2 | +| LISTEN | 3 | +| JOIN_REQUEST | 5 | + + +###### Message Flags + +| Flag | Value | Description | +|----------------------------------------|-----------|----------------------------------------------------------------------------------------------| +| CROSSPOSTED | `1 << 0` | this message has been published to subscribed channels (via Channel Following) | +| IS_CROSSPOST | `1 << 1` | this message originated from a message in another channel (via Channel Following) | +| SUPPRESS_EMBEDS | `1 << 2` | do not include any embeds when serializing this message | +| SOURCE_MESSAGE_DELETED | `1 << 3` | the source message for this crosspost has been deleted (via Channel Following) | +| URGENT | `1 << 4` | this message came from the urgent message system | +| HAS_THREAD | `1 << 5` | this message has an associated thread, with the same id as the message | +| EPHEMERAL | `1 << 6` | this message is only visible to the user who invoked the Interaction | +| LOADING | `1 << 7` | this message is an Interaction Response and the bot is "thinking" | +| FAILED_TO_MENTION_SOME_ROLES_IN_THREAD | `1 << 8` | this message failed to mention some roles and add their members to the thread | +| SUPPRESS_NOTIFICATIONS | `1 << 12` | this message will not trigger push and desktop notifications | +| IS_VOICE_MESSAGE | `1 << 13` | this message is a voice message | +| HAS_SNAPSHOT | `1 << 14` | this message has a snapshot (via Message Forwarding) | +| IS_COMPONENTS_V2 \* | `1 << 15` | allows you to create fully [component](/developers/docs/components/overview)-driven messages | + +\* Once a message has been sent with this flag, it can't be removed from that message. + + +###### Example Message + +```json +{ + "reactions": [ + { + "count": 1, + "count_details": { + "burst": 0, + "normal": 1 + }, + "me": false, + "me_burst": false, + "emoji": { + "id": null, + "name": "🔥" + }, + "burst_colors": [] + } + ], + "attachments": [], + "tts": false, + "embeds": [], + "timestamp": "2017-07-11T17:27:07.299000+00:00", + "mention_everyone": false, + "id": "334385199974967042", + "pinned": false, + "edited_timestamp": null, + "author": { + "username": "Mason", + "discriminator": "9999", + "id": "53908099506183680", + "avatar": "a_bab14f271d565501444b2ca3be944b25" + }, + "mention_roles": [], + "content": "Supa Hot", + "channel_id": "290926798999357250", + "mentions": [], + "type": 0 +} +``` + + +###### Example Crossposted Message + +```json +{ + "reactions": [ + { + "count": 1, + "count_details": { + "burst": 0, + "normal": 1 + }, + "me": false, + "me_burst": false, + "emoji": { + "id": null, + "name": "🔥" + }, + "burst_colors": [] + } + ], + "attachments": [], + "tts": false, + "embeds": [], + "timestamp": "2017-07-11T17:27:07.299000+00:00", + "mention_everyone": false, + "id": "334385199974967042", + "pinned": false, + "edited_timestamp": null, + "author": { + "username": "Mason", + "discriminator": "9999", + "id": "53908099506183680", + "avatar": "a_bab14f271d565501444b2ca3be944b25" + }, + "mention_roles": [], + "mention_channels": [ + { + "id": "278325129692446722", + "guild_id": "278325129692446720", + "name": "big-news", + "type": 5 + } + ], + "content": "Big news! In this <#278325129692446722> channel!", + "channel_id": "290926798999357250", + "mentions": [], + "type": 0, + "flags": 2, + "message_reference": { + "type": 0, + "channel_id": "278325129692446722", + "guild_id": "278325129692446720", + "message_id": "306588351130107906" + } +} +``` + +### Message Interaction Metadata Object + +Metadata about the interaction, including the source of the interaction and relevant server and user IDs. + +One of [Application Command Interaction Metadata](/developers/docs/resources/message#message-interaction-metadata-object-application-command-interaction-metadata-structure), [Message Component Interaction Metadata](/developers/docs/resources/message#message-interaction-metadata-object-message-component-interaction-metadata-structure), or [Modal Submit Interaction Metadata](/developers/docs/resources/message#message-interaction-metadata-object-modal-submit-interaction-metadata-structure). + + +###### Application Command Interaction Metadata Structure + +| Field | Type | Description | +|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the interaction | +| type | [interaction type](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction | +| user | [user](/developers/docs/resources/user#user-object) object | User who triggered the interaction | +| authorizing_integration_owners | dictionary with keys of [application integration types](/developers/docs/resources/application#application-object-application-integration-types) | IDs for installation context(s) related to an interaction. Details in [Authorizing Integration Owners Object](/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) | +| original_response_message_id? | snowflake | ID of the original response message, present only on [follow-up messages](/developers/docs/interactions/receiving-and-responding) | +| target_user? | [user](/developers/docs/resources/user#user-object) object | The user the command was run on, present only on [user command](/developers/docs/interactions/application-commands#user-commands) interactions | +| target_message_id? | snowflake | The ID of the message the command was run on, present only on [message command](/developers/docs/interactions/application-commands#message-commands) interactions. The original response message will also have `message_reference` and `referenced_message` pointing to this message. | + + + +###### Message Component Interaction Metadata Structure + +| Field | Type | Description | +|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the interaction | +| type | [interaction type](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction | +| user | [user](/developers/docs/resources/user#user-object) object | User who triggered the interaction | +| authorizing_integration_owners | dictionary with keys of [application integration types](/developers/docs/resources/application#application-object-application-integration-types) | IDs for installation context(s) related to an interaction. Details in [Authorizing Integration Owners Object](/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) | +| original_response_message_id? | snowflake | ID of the original response message, present only on [follow-up messages](/developers/docs/interactions/receiving-and-responding) | +| interacted_message_id | snowflake | ID of the message that contained the interactive component | + + + +###### Modal Submit Interaction Metadata Structure + +| Field | Type | Description | +|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the interaction | +| type | [interaction type](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-type) | Type of interaction | +| user | [user](/developers/docs/resources/user#user-object) object | User who triggered the interaction | +| authorizing_integration_owners | dictionary with keys of [application integration types](/developers/docs/resources/application#application-object-application-integration-types) | IDs for installation context(s) related to an interaction. Details in [Authorizing Integration Owners Object](/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) | +| original_response_message_id? | snowflake | ID of the original response message, present only on [follow-up messages](/developers/docs/interactions/receiving-and-responding) | +| triggering_interaction_metadata | [Application Command Interaction Metadata](/developers/docs/resources/message#message-interaction-metadata-object-application-command-interaction-metadata-structure) or [Message Component Interaction Metadata](/developers/docs/resources/message#message-interaction-metadata-object-message-component-interaction-metadata-structure) object | Metadata for the interaction that was used to open the modal | + + +### Message Call Object + +Information about the call in a private channel. + + +###### Message Call Object Structure + +| Field | Type | Description | +|------------------|---------------------|-------------------------------------------------------------------------------------------------------| +| participants | array of snowflakes | array of [user](/developers/docs/resources/user#user-object) object ids that participated in the call | +| ended_timestamp? | ?ISO8601 timestamp | time when call ended | + +### Message Reference Object + +#### Message Reference Structure + +| Field | Type | Description | +|---------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------| +| type? \* | integer | [type of reference](/developers/docs/resources/message#message-reference-types). | +| message_id? | snowflake | id of the originating message | +| channel_id? \*\* | snowflake | id of the originating message's channel | +| guild_id? | snowflake | id of the originating message's guild | +| fail_if_not_exists? | boolean | when sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default true | + +\* If `type` is unset, `DEFAULT` can be assumed in order to match the behavior before message reference had types. +In future API versions this will become a required field. + +\*\* `channel_id` is optional when creating a reply, but will always be present when receiving an event/response that includes this data model. **Required for forwards.** + +#### Message Reference Types + +Determines how associated data is populated. + +| Type | Value | Coupled Message Field | Description | +|---------|-------|-----------------------|----------------------------------------------------------| +| DEFAULT | 0 | `referenced_message` | A standard reference used by replies. | +| FORWARD | 1 | `message_snapshot` | Reference used to point to a message at a point in time. | + +`FORWARD` can only be used for basic messages; i.e. messages which do not have strong bindings to a non global entity. +Thus we support only messages with type `DEFAULT`, `REPLY`, `CHAT_INPUT_COMMAND`, or `CONTEXT_MENU_COMMAND`, and don't support messages with a poll, call, or activity. +This is subject to change in the future. + +#### Message Reference Content Attribution + +Message references are generic attribution on a message. +There are multiple message types that have a `message_reference` object. + + +###### Crosspost messages + +- These are messages that originated from another channel (IS_CROSSPOST flag). +- These messages have all three fields, which point to the original message that was crossposted. + + +###### Channel Follow Add messages + +- These are automatic messages sent when a channel is followed into the current channel (type 12). +- These messages have the `channel_id` and `guild_id` fields, which point to the followed announcement channel. + + +###### Pin messages + +- These are automatic messages sent when a message is pinned (type 6). +- These messages have `message_id` and `channel_id`, and `guild_id` if it is in a guild, which point to the message that was pinned. + + +###### Forwards + +- These are messages which capture a snapshot of a message. +- These messages have an array of [`message_snapshot`](/developers/docs/resources/message#message-snapshot-object) objects containing a copy of the original message. This copy follows the same structure as a message, but has only the minimal set of fields returned required for context/rendering. + - of note: `author` will be excluded +- A forwarded message can be identified by looking at its `message_reference.type` field + - `message_snapshots` will be the message data associated with the forward. Currently we support only 1 snapshot. + - prevents spoofing forwarded data + - `message_snapshots` are taken the moment a forward message is created, and are **immutable**; any mutations to the original message will not be propagated. +- Forwards are created by including a message_reference with `FORWARD` type when sending a message. + - Required fields: `type`, `message_id`, `channel_id` + - the requestor must have `VIEW_CHANNEL` permissions + + +###### Replies + +- These are messages replying to a previous message (type 19). +- These messages have `message_id` and `channel_id`, and `guild_id` if it is in a guild, which point to the message that was replied to. The channel_id and guild_id will be the same as the reply. +- Replies are created by including a message_reference when sending a message. When sending, only `message_id` is required. +- These messages can have the referenced message resolved in the `referenced_message` field. + + +###### Thread Created messages + +- These are automatic messages sent when a public thread is created from an old message or without a message (type 18). +- These messages have the `channel_id` and `guild_id` fields, which point to the created thread channel. + + +###### Thread starter messages + +- These are the first message in public threads created from messages. They point back to the message in the parent channel from which the thread was started. (type 21) +- These messages have `message_id`, `channel_id`, and `guild_id`. +- These messages can have the referenced message resolved in the `referenced_message` field. +- These messages will never have content, embeds, or attachments, mainly just the `message_reference` and `referenced_message` fields. + + +###### Poll result messages + +- These are automatic messages sent after a poll has ended and the results have been finalized. (type 46) +- These messages have `message_id` and `channel_id`, which point to the original poll message. The `channel_id` will be the same as that of the poll. +- The author will be the same as the author of the poll and will be mentioned. +- These messages contain a [`poll_result` embed](/developers/docs/resources/message#embed-fields-by-embed-type-poll-result-embed-fields) + + +###### Context Menu Command messages + +- These are messages sent when a user uses a context menu Application Command. (type 23) +- If the command was a [message command](/developers/docs/interactions/application-commands#message-commands), the message will have have `message_id` and `channel_id`, and `guild_id` if it is in a guild, which point to the message that the command was used on. The channel_id and guild_id will be the same as the new message. +- These messages can have the referenced message resolved in the `referenced_message` field. + +#### Voice Messages + +Voice messages are messages with the `IS_VOICE_MESSAGE` flag. They have the following properties. + +- They cannot be edited. +- Only a single audio attachment is allowed. No content, stickers, etc... +- The [attachment](/developers/docs/resources/message#attachment-object) has additional fields: `duration_secs` and `waveform`. The `Content-Type` of the attachment must begin with `audio/` to respect these fields. + +The `waveform` is intended to be a preview of the entire voice message, with 1 byte per datapoint encoded in base64. Clients sample the recording at most +once per 100 milliseconds, but will downsample so that no more than 256 datapoints are in the waveform. + +As of 2023-04-14, clients upload a 1 channel, 48000 Hz, 32kbps Opus stream in an OGG container. +The encoding, and the waveform details, are an implementation detail and may change without warning or documentation. + + +### Message Snapshot Object + +#### Message Snapshot Structure + +| Field | Type | Description | +|-----------|-----------------------------------------------------------------------------|---------------------------------------------------| +| message\* | partial [message](/developers/docs/resources/message#message-object) object | minimal subset of fields in the forwarded message | + +\* The current subset of message fields consists of: +`type`, `content`, `embeds`, `attachments`, `timestamp`, `edited_timestamp`, `flags`, `mentions`, `mention_roles`, `stickers`, `sticker_items`, and `components`. + + +While message snapshots are able to support nested snapshots, we currently limit the depth of nesting to 1. + + +### Reaction Object + + +###### Reaction Structure + +| Field | Type | Description | +|---------------|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------| +| count | integer | Total number of times this emoji has been used to react (including super reacts) | +| count_details | object | [Reaction count details object](/developers/docs/resources/message#reaction-count-details-object) | +| me | boolean | Whether the current user reacted using this emoji | +| me_burst | boolean | Whether the current user super-reacted using this emoji | +| emoji | partial [emoji](/developers/docs/resources/emoji#emoji-object) object | emoji information | +| burst_colors | array | HEX colors used for super reaction | + +### Reaction Count Details Object + +The reaction count details object contains a breakdown of normal and super reaction counts for the associated emoji. + + +###### Reaction Count Details Structure + +| Field | Type | Description | +|--------|---------|---------------------------| +| burst | integer | Count of super reactions | +| normal | integer | Count of normal reactions | + +### Embed Object + + +###### Embed Structure + +| Field | Type | Description | +|--------------|-------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| title? | string | title of embed | +| type? | string | [type of embed](/developers/docs/resources/message#embed-object-embed-types) (always "rich" for webhook embeds) | +| description? | string | description of embed | +| url? | string | url of embed | +| timestamp? | ISO8601 timestamp | timestamp of embed content | +| color? | integer | color code of the embed | +| footer? | [embed footer](/developers/docs/resources/message#embed-object-embed-footer-structure) object | footer information | +| image? | [embed image](/developers/docs/resources/message#embed-object-embed-image-structure) object | image information | +| thumbnail? | [embed thumbnail](/developers/docs/resources/message#embed-object-embed-thumbnail-structure) object | thumbnail information | +| video? | [embed video](/developers/docs/resources/message#embed-object-embed-video-structure) object | video information | +| provider? | [embed provider](/developers/docs/resources/message#embed-object-embed-provider-structure) object | provider information | +| author? | [embed author](/developers/docs/resources/message#embed-object-embed-author-structure) object | author information | +| fields? | array of [embed field](/developers/docs/resources/message#embed-object-embed-field-structure) objects | fields information, max of 25 | + + +###### Embed Types + +| Type | Description | +|-------------|-------------------------------------------------------------------------------------------------------------| +| rich | generic embed rendered from embed attributes | +| image | image embed | +| video | video embed | +| gifv | animated gif image embed rendered as a video embed | +| article | article embed | +| link | link embed | +| poll_result | [poll result embed](/developers/docs/resources/message#embed-fields-by-embed-type-poll-result-embed-fields) | + + +###### Embed Thumbnail Structure + +| Field | Type | Description | +|------------|---------|-----------------------------------------------------------------| +| url | string | source url of thumbnail (only supports http(s) and attachments) | +| proxy_url? | string | a proxied url of the thumbnail | +| height? | integer | height of thumbnail | +| width? | integer | width of thumbnail | + + +###### Embed Video Structure + +| Field | Type | Description | +|------------|---------|----------------------------| +| url? | string | source url of video | +| proxy_url? | string | a proxied url of the video | +| height? | integer | height of video | +| width? | integer | width of video | + + +###### Embed Image Structure + +| Field | Type | Description | +|------------|---------|-------------------------------------------------------------| +| url | string | source url of image (only supports http(s) and attachments) | +| proxy_url? | string | a proxied url of the image | +| height? | integer | height of image | +| width? | integer | width of image | + + +###### Embed Provider Structure + +| Field | Type | Description | +|-------|--------|------------------| +| name? | string | name of provider | +| url? | string | url of provider | + + +###### Embed Author Structure + +| Field | Type | Description | +|-----------------|--------|------------------------------------------------------------| +| name | string | name of author | +| url? | string | url of author (only supports http(s)) | +| icon_url? | string | url of author icon (only supports http(s) and attachments) | +| proxy_icon_url? | string | a proxied url of author icon | + + +###### Embed Footer Structure + +| Field | Type | Description | +|-----------------|--------|------------------------------------------------------------| +| text | string | footer text | +| icon_url? | string | url of footer icon (only supports http(s) and attachments) | +| proxy_icon_url? | string | a proxied url of footer icon | + + +###### Embed Field Structure + +| Field | Type | Description | +|---------|---------|-------------------------------------------------| +| name | string | name of the field | +| value | string | value of the field | +| inline? | boolean | whether or not this field should display inline | + + +###### Embed Limits + +To facilitate showing rich content, rich embeds do not follow the traditional limits of message content. However, some limits are still in place to prevent excessively large embeds. The following table describes the limits: + +All of the following limits are measured inclusively. Leading and trailing whitespace characters are not included (they are trimmed automatically). + +| Field | Limit | +|---------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| +| title | 256 characters | +| description | 4096 characters | +| fields | Up to 25 [field](/developers/docs/resources/message#embed-object-embed-field-structure) objects | +| [field.name](/developers/docs/resources/message#embed-object-embed-field-structure) | 256 characters | +| [field.value](/developers/docs/resources/message#embed-object-embed-field-structure) | 1024 characters | +| [footer.text](/developers/docs/resources/message#embed-object-embed-footer-structure) | 2048 characters | +| [author.name](/developers/docs/resources/message#embed-object-embed-author-structure) | 256 characters | + +Additionally, the combined sum of characters in all `title`, `description`, `field.name`, `field.value`, `footer.text`, and `author.name` fields across all embeds attached to a message must not exceed 6000 characters. Violating any of these constraints will result in a `Bad Request` response. + +Embeds are deduplicated by URL. If a message contains multiple embeds with the same URL, only the first is shown. + +#### Embed Fields by Embed Type + +Certain embed types are used to power special UIs. These embeds use [fields](/developers/docs/resources/message#embed-object-embed-field-structure) to include additional data in key-value pairs. Below is a reference of possible embed fields for each of the following embed types. + + +###### Poll Result Embed Fields + +| Field | Description | +|-------------------------------|------------------------------------------------------------| +| poll_question_text | question text from the original poll | +| victor_answer_votes | number of votes for the answer(s) with the most votes | +| total_votes | total number of votes in the poll | +| victor_answer_id? | id for the winning answer | +| victor_answer_text? | text for the winning answer | +| victor_answer_emoji_id? | id for an emoji associated with the winning answer | +| victor_answer_emoji_name? | name of an emoji associated with the winning answer | +| victor_answer_emoji_animated? | if an emoji associated with the winning answer is animated | + +### Attachment Object + + +###### Attachment Structure + + +For the `attachments` array in Message Create/Edit requests, only the `id` is required. + + +| Field | Type | Description | +|----------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | attachment id | +| filename | string | name of file attached | +| title? | string | the title of the file | +| description? | string | description for the file (max 1024 characters) | +| content_type? | string | the attachment's [media type](https://en.wikipedia.org/wiki/Media_type) | +| size | integer | size of file in bytes | +| url | string | source url of file | +| proxy_url | string | a proxied url of file | +| height? | ?integer | height of file (if image) | +| width? | ?integer | width of file (if image) | +| ephemeral? \* | boolean | whether this attachment is ephemeral | +| duration_secs? | float | the duration of the audio file (currently for voice messages) | +| waveform? | string | base64 encoded bytearray representing a sampled waveform (currently for voice messages) | +| flags? | integer | [attachment flags](/developers/docs/resources/message#attachment-object-attachment-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | + +\* Ephemeral attachments will automatically be removed after a set period of time. Ephemeral attachments on messages are guaranteed to be available as long as the message itself exists. + + +###### Attachment Flags + +| Flag | Value | Description | +|----------|----------|-------------------------------------------------------------------| +| IS_REMIX | `1 << 2` | this attachment has been edited using the remix feature on mobile | + +### Channel Mention Object + + +###### Channel Mention Structure + +| Field | Type | Description | +|----------|-----------|----------------------------------------------------------------------------------------| +| id | snowflake | id of the channel | +| guild_id | snowflake | id of the guild containing the channel | +| type | integer | the [type of channel](/developers/docs/resources/channel#channel-object-channel-types) | +| name | string | the name of the channel | + +### Allowed Mentions Object + +Setting the `allowed_mentions` field lets you determine whether users will receive notifications when you include mentions in the message content, or the content of components attached to that message. This field is always validated against your permissions and the presence of said mentions in the message, to avoid "phantom" pings where users receive a notification without a visible mention in the message. For example, if you want to ping everyone, including it in the `allowed_mentions` field is not enough, the mention format (`@everyone`) must also be present in the content of the message or its components. It is important to note that setting this field **does not** guarantee a push notification will be sent, as additional factors can influence this: + +- To mention roles and notify their members, the role's `mentionable` field must be set to `true`, or the bot must have the `MENTION_EVERYONE` permission +- To mention `@everyone` and `@here`, the bot must have the `MENTION_EVERYONE` permission +- Setting the `SUPPRESS_NOTIFICATIONS` flag when sending a message will disable push notifications and only cause a notification badge +- Users can customize their notification settings through the Discord app, which might cause them to only receive a notification badge and no push notification + + + +###### Allowed Mention Types + +| Type | Value | Description | +|-------------------|------------|---------------------------------------| +| Role Mentions | "roles" | Controls role mentions | +| User Mentions | "users" | Controls user mentions | +| Everyone Mentions | "everyone" | Controls @everyone and @here mentions | + + +###### Default Settings for Allowed Mentions + +The default value for the `allowed_mentions` field, used when it is not passed in the body, varies depending on the context: + +In **regular messages**, all mention types are parsed, which is equivalent to sending the following data: + +```json +{ + "parse": ["users", "roles", "everyone"] +} +``` + +In **interactions** and **webhooks**, only user mentions are parsed, which corresponds to the following: + +```json +{ + "parse": ["users"] +} +``` + +| Field | Type | Description | +|---------------|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| +| parse? | array of allowed mention types | An array of [allowed mention types](/developers/docs/resources/message#allowed-mentions-object-allowed-mention-types) to parse from the content | +| roles? | array of snowflakes | Array of role ids to mention, max 100 | +| users? | array of snowflakes | Array of user ids to mention, max 100 | +| replied_user? | boolean | For replies, whether to mention the author of the message being replied to, defaults to false | + + +###### Allowed Mentions Examples + +Because the behavior of the `allowed_mentions` field is more complex than it seems, here's a set of examples: + + +In the following case, we are sending a regular message **without** configuring `allowed_mentions`. As a result, all included mentions will be parsed. + +```json +{ + "content": "@here Hello <@&1234> and <@5678> 👋" +} +``` + +If you want to completely suppress all mentions in the message, you can configure the `allowed_mentions` field as we've documented above: + +```json +{ + "content": "@here Hello <@&1234> and <@5678> 👋", + "allowed_mentions": { + "parse": [] + } +} +``` + + +It is important to note that the `parse` field is **mutually exclusive** with the other fields. In the example below, only the `1234` role and the `5678` user mentions would be parsed, but **not** the `@here` at the beginning. Passing a falsy value such as `null` or an empty array into the `users` field does not trigger a validation error. + +```json +{ + "content": "@here Hello <@&1234> and <@5678> 👋", + "allowed_mentions": { + "parse": ["users", "roles"], + "users": [] + } +} +``` + + +In this next example, **only** `@everyone` would be parsed, as well as users `1234` and `5678` in case they suppressed `@everyone` mentions in their settings. + +```json +{ + "content": "@everyone <@1234> <@5678> <@&789> 👋", + "allowed_mentions": { + "parse": ["everyone"], + "users": ["1234", "5678"] + } +} +``` + + +Due to possible ambiguities, not all configurations are accepted. Here's an example of an *invalid* configuration, because it includes both `parse` and `users`, despite those fields being mutually exclusive, causing a validation error. + +```json +{ + "content": "@everyone <@1234> <@5678> <@9012> <@&200>", + "allowed_mentions": { + "parse": ["users"], + "users": ["1234", "5678"] + } +} +``` + +Any entities whose id is included can be mentioned. Do note the API will silently ignore entities whose id are present in the `allowed_mentions` field, but not in the content of the message or its components. For example, in the following configuration, the user 123 mention would be parsed because it is present in the `content`. However, since there is no mention of user 456 in the `content`, they would not be notified. + +```json +{ + "content": "<@123> Time for some memes 🤠", + "allowed_mentions": { + "users": ["123", "456"] + } +} +``` + +### Role Subscription Data Object + + +###### Role Subscription Data Object Structure + +| Field | Type | Description | +|------------------------------|-----------|-----------------------------------------------------------------------| +| role_subscription_listing_id | snowflake | the id of the sku and listing that the user is subscribed to | +| tier_name | string | the name of the tier that the user is subscribed to | +| total_months_subscribed | integer | the cumulative number of months that the user has been subscribed for | +| is_renewal | boolean | whether this notification is for a renewal rather than a new purchase | + +### Message Pin Object + + +###### Message Pin Object Struture + +| Field | Type | Description | +|-----------|---------------------------------------------------------------------|---------------------------------| +| pinned_at | ISO8601 timestamp | the time the message was pinned | +| message | [message](/developers/docs/resources/message#message-object) object | the pinned message | + +## Get Channel Messages +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages + +Retrieves the messages in a channel. Returns an array of [message](/developers/docs/resources/message#message-object) objects from newest to oldest on success. + +If operating on a guild channel, this endpoint requires the current user to have the `VIEW_CHANNEL` permission. If the channel is a voice channel, they must _also_ have the `CONNECT` permission. + +If the current user is missing the `READ_MESSAGE_HISTORY` permission in the channel, then no messages will be returned. + + +The `before`, `after`, and `around` parameters are mutually exclusive, only one may be passed at a time. + + + +###### Query String Params + +| Field | Type | Description | Default | +|---------|-----------|------------------------------------------|---------| +| around? | snowflake | Get messages around this message ID | absent | +| before? | snowflake | Get messages before this message ID | absent | +| after? | snowflake | Get messages after this message ID | absent | +| limit? | integer | Max number of messages to return (1-100) | 50 | + +## Get Channel Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Retrieves a specific message in the channel. Returns a [message](/developers/docs/resources/message#message-object) object on success. + +If operating on a guild channel, this endpoint requires the current user to have the `VIEW_CHANNEL` and `READ_MESSAGE_HISTORY` permissions. If the channel is a voice channel, they must _also_ have the `CONNECT` permission. + +## Create Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages + + +Discord may strip certain characters from message content, like invalid unicode characters or characters which cause unexpected message formatting. If you are passing user-generated strings into message content, consider sanitizing the data to prevent unexpected behavior and using `allowed_mentions` to prevent unexpected mentions. + + +Post a message to a guild text or DM channel. Returns a [message](/developers/docs/resources/message#message-object) object. Fires a [Message Create](/developers/docs/events/gateway-events#message-create) Gateway event. See [message formatting](/developers/docs/reference#message-formatting) for more information on how to properly format messages. + +To create a message as a reply or forward of another message, apps can include a [`message_reference`](/developers/docs/resources/message#message-reference-structure). +Refer to the documentation for required fields. + +Files must be attached using a `multipart/form-data` body as described in [Uploading Files](/developers/docs/reference#uploading-files). + + +###### Limitations + +- When operating on a guild channel, the current user must have the `SEND_MESSAGES` permission. +- When sending a message with `tts` (text-to-speech) set to `true`, the current user must have the `SEND_TTS_MESSAGES` permission. +- When creating a message as a reply to another message, the current user must have the `READ_MESSAGE_HISTORY` permission. + - The referenced message must exist and cannot be a system message. +- The maximum request size when sending a message is **25 MiB** +- For the embed object, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images. + + +###### JSON/Form Params + +| Field | Type | Description | +|----------------------|----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| content?\* | string | Message contents (up to 2000 characters) | +| nonce? | integer or string | Can be used to verify a message was sent (up to 25 characters). Value will appear in the [Message Create event](/developers/docs/events/gateway-events#message-create). | +| tts? | boolean | `true` if this is a TTS message | +| embeds?\* | array of [embed](/developers/docs/resources/message#embed-object) objects | Up to 10 `rich` embeds (up to 6000 characters) | +| allowed_mentions? | [allowed mention object](/developers/docs/resources/message#allowed-mentions-object) | Allowed mentions for the message | +| message_reference?\* | [message reference](/developers/docs/resources/message#message-reference-structure) | Include to make your message a reply or a forward | +| components?\* | array of [message component](/developers/docs/components/reference#component-object) objects | Components to include with the message | +| sticker_ids?\* | array of snowflakes | IDs of up to 3 [stickers](/developers/docs/resources/sticker#sticker-object) in the server to send in the message | +| files[n]?\* | file contents | Contents of the file being sent. See [Uploading Files](/developers/docs/reference#uploading-files) | +| payload_json? | string | JSON-encoded body of non-file params, only for `multipart/form-data` requests. See [Uploading Files](/developers/docs/reference#uploading-files) | +| attachments? | array of partial [attachment](/developers/docs/resources/message#attachment-object) objects | Attachment objects with filename and description. See [Uploading Files](/developers/docs/reference#uploading-files) | +| flags?\*\* | integer | [Message flags](/developers/docs/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS`, `SUPPRESS_NOTIFICATIONS`, `IS_VOICE_MESSAGE`, and `IS_COMPONENTS_V2` can be set) | +| enforce_nonce? | boolean | If true and nonce is present, it will be checked for uniqueness in the past few minutes. If another message was created by the same author with the same nonce, that message will be returned and no new message will be created. | +| poll? | [poll](/developers/docs/resources/poll#poll-create-request-object) request object | A poll! | + +\* At least one of `content`, `embeds`, `sticker_ids`, `components`, `files[n]`, or `poll` is required. When forwarding a message, only `message_reference` is required. + +\*\* When the flag `IS_COMPONENTS_V2` is set, the message can only contain `components`. Providing `content`, `embeds`, `sticker_ids`, `files[n]`, or `poll` will fail with a 400 BAD REQUEST response. + + +###### Example Request Body (application/json) + +```json +{ + "content": "Hello, World!", + "tts": false, + "embeds": [{ + "title": "Hello, Embed!", + "description": "This is an embedded message." + }] +} +``` + +Examples for file uploads are available in [Uploading Files](/developers/docs/reference#uploading-files). + +## Crosspost Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object)/crosspost + +Crosspost a message in an Announcement Channel to following channels. This endpoint requires the `SEND_MESSAGES` permission, if the current user sent the message, or additionally the `MANAGE_MESSAGES` permission, for all other messages, to be present for the current user. + +Returns a [message](/developers/docs/resources/message#message-object) object. Fires a [Message Update](/developers/docs/events/gateway-events#message-update) Gateway event. + +## Create Reaction +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object)/reactions/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object)/@me + +Create a reaction for the message. This endpoint requires the `READ_MESSAGE_HISTORY` permission to be present on the current user. Additionally, if nobody else has reacted to the message using this emoji, this endpoint requires the `ADD_REACTIONS` permission to be present on the current user. Returns a 204 empty response on success. Fires a [Message Reaction Add](/developers/docs/events/gateway-events#message-reaction-add) Gateway event. +The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. + +## Delete Own Reaction +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object)/reactions/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object)/@me + +Delete a reaction the current user has made for the message. Returns a 204 empty response on success. Fires a [Message Reaction Remove](/developers/docs/events/gateway-events#message-reaction-remove) Gateway event. +The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. + +## Delete User Reaction +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object)/reactions/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object)/[\{user.id\}](/developers/docs/resources/user#user-object) + +Deletes another user's reaction. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Returns a 204 empty response on success. Fires a [Message Reaction Remove](/developers/docs/events/gateway-events#message-reaction-remove) Gateway event. +The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. + +## Get Reactions +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object)/reactions/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object) + +Get a list of users that reacted with this emoji. Returns an array of [user](/developers/docs/resources/user#user-object) objects on success. +The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. + + +###### Query String Params + +| Field | Type | Description | Default | +|--------|-----------|-----------------------------------------------------------------------------------------|---------| +| type? | integer | The [type of reaction](/developers/docs/resources/message#get-reactions-reaction-types) | 0 | +| after? | snowflake | Get users after this user ID | absent | +| limit? | integer | Max number of users to return (1-100) | 25 | + + +###### Reaction Types + +| Type | Value | +|--------|-------| +| NORMAL | 0 | +| BURST | 1 | + +## Delete All Reactions +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object)/reactions + +Deletes all reactions on a message. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Fires a [Message Reaction Remove All](/developers/docs/events/gateway-events#message-reaction-remove-all) Gateway event. + +## Delete All Reactions for Emoji +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object)/reactions/[\{emoji.id\}](/developers/docs/resources/emoji#emoji-object) + +Deletes all the reactions for a given emoji on a message. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Fires a [Message Reaction Remove Emoji](/developers/docs/events/gateway-events#message-reaction-remove-emoji) Gateway event. +The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. + +## Edit Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Edit a previously sent message. The fields `content`, `embeds`, `flags` and `components` can be edited by the original message author. Other users can only edit `flags` and only if they have the `MANAGE_MESSAGES` permission in the corresponding channel. When specifying flags, ensure to include all previously set flags/bits in addition to ones that you are modifying. Only `flags` documented in the table below may be modified by users (unsupported flag changes are currently ignored without error). + +When the `content` field is edited, the arrays `mentions` and `mention_roles` and the boolean `mention_everyone` in the message object will be reconstructed from scratch based on the new content. When the message flag `IS_COMPONENTS_V2` is set, the reconstructed arrays and boolean are based on the edited content in the `components` array. The `allowed_mentions` field of the edit request controls how this happens. If there is no explicit `allowed_mentions` in the edit request, the content will be parsed with _default_ allowances, that is, without regard to whether or not an `allowed_mentions` was present in the request that originally created the message. + +Returns a [message](/developers/docs/resources/message#message-object) object. Fires a [Message Update](/developers/docs/events/gateway-events#message-update) Gateway event. + +Refer to [Uploading Files](/developers/docs/reference#uploading-files) for details on attachments and `multipart/form-data` requests. +Any provided files will be **appended** to the message. To remove or replace files you will have to supply the `attachments` field which specifies the files to retain on the message after edit. + + +Starting with API v10, the `attachments` array must contain all attachments that should be present after edit, including **retained and new** attachments provided in the request body. + + + +All parameters to this endpoint are optional and nullable. + + + +###### JSON/Form Params + +| Field | Type | Description | +|------------------|--------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------| +| content | string | Message contents (up to 2000 characters) | +| embeds | array of [embed](/developers/docs/resources/message#embed-object) objects | Up to 10 `rich` embeds (up to 6000 characters) | +| flags\* | integer | Edit the [flags](/developers/docs/resources/message#message-object-message-flags) of a message (`SUPPRESS_EMBEDS` and `IS_COMPONENTS_V2` only) | +| allowed_mentions | [allowed mention object](/developers/docs/resources/message#allowed-mentions-object) | Allowed mentions for the message | +| components | array of [message component](/developers/docs/components/reference#component-object) | Components to include with the message | +| files[n] | file contents | Contents of the file being sent/edited. See [Uploading Files](/developers/docs/reference#uploading-files) | +| payload_json | string | JSON-encoded body of non-file params (multipart/form-data only). See [Uploading Files](/developers/docs/reference#uploading-files) | +| attachments | array of [attachment](/developers/docs/resources/message#attachment-object) objects | Attached files to keep and possible descriptions for new files. See [Uploading Files](/developers/docs/reference#uploading-files) | + +\* The `SUPPRESS_EMBEDS` flag can be both set and unset, while the `IS_COMPONENTS_V2` flag can only be set. When the `IS_COMPONENTS_V2` flag is set, any of the used `content`, `embeds`, `sticker_ids`, or `poll` fields must have their values reset to empty. For `content` and `poll` this is `null`. For `embeds` and `sticker_ids` this is `[]`. Failing to do this will result in a 400 BAD REQUEST response. + +## Delete Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Delete a message. If operating on a guild channel and trying to delete a message that was not sent by the current user, this endpoint requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Message Delete](/developers/docs/events/gateway-events#message-delete) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Bulk Delete Messages +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/bulk-delete + +Delete multiple messages in a single request. This endpoint can only be used on guild channels and requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Message Delete Bulk](/developers/docs/events/gateway-events#message-delete-bulk) Gateway event. + +Any message IDs given that do not exist or are invalid will count towards the minimum and maximum message count (currently 2 and 100 respectively). + + +This endpoint will not delete messages older than 2 weeks, and will fail with a 400 BAD REQUEST if any message provided is older than that or if any duplicate message IDs are provided. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|----------|---------------------|-------------------------------------------| +| messages | array of snowflakes | an array of message ids to delete (2-100) | + +## Get Channel Pins +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/pins + +Retrieves the list of pins in a channel. Requires the `VIEW_CHANNEL` permission. If the user is missing the `READ_MESSAGE_HISTORY` permission in the channel, then no pins will be returned. + + +###### Query String Params + +| Field | Type | Description | Default | +|---------|-------------------|-------------------------------------------|---------| +| before? | ISO8601 timestamp | Get messages pinned before this timestamp | absent | +| limit? | integer | Max number of pins to return (1-50) | 50 | + + +###### Response Structure + +| Field | Type | +|----------|---------------------------------------------------------------------------------------| +| items | array of [message pin](/developers/docs/resources/message#message-pin-object) objects | +| has_more | boolean | + + +###### Example + +If you want to get 100 pins you'd send these two requests: +`GET /channels/:id/messages/pins?limit=50` +`GET /channels/:id/messages/pins?limit=50&before={pins[pins.len() - 1].pinned_at}` + +## Pin Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/pins/[\{message.id\}](/developers/docs/resources/message#message-object) + +Pin a message in a channel. Requires the `PIN_MESSAGES` permission. Fires a [Channel Pins Update](/developers/docs/events/gateway-events#channel-pins-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Unpin Message +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/messages/pins/[\{message.id\}](/developers/docs/resources/message#message-object) + +Unpin a message in a channel. Requires the `PIN_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Channel Pins Update](/developers/docs/events/gateway-events#channel-pins-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Get Pinned Messages (deprecated) +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/pins + +Gets the first 50 pinned messages in a channel, returning an array of [message](/developers/docs/resources/message#message-object) objects on success. +This endpoint is deprecated. Use [Get Channel Pins](/developers/docs/resources/message#get-channel-pins) instead. + +## Pin Message (deprecated) +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/pins/[\{message.id\}](/developers/docs/resources/message#message-object) + +This endpoint is deprecated. Use [Pin Message](/developers/docs/resources/message#pin-message) instead. + +## Unpin Message (deprecated) +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/pins/[\{message.id\}](/developers/docs/resources/message#message-object) + +This endpoint is deprecated. Use [Unpin Message](/developers/docs/resources/message#unpin-message) instead. diff --git a/discord/developers/docs/resources/poll.mdx b/discord/developers/docs/resources/poll.mdx new file mode 100644 index 0000000000..8c955562fe --- /dev/null +++ b/discord/developers/docs/resources/poll.mdx @@ -0,0 +1,158 @@ +--- +title: Poll Resource +sidebarTitle: Poll +description: Reference for Discord poll objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +A poll is... well... a poll! It holds information about a poll! + +![Example message containing a poll](/images/example-poll.png) + +### Poll Object + +The poll object has a lot of levels and nested structures. It was also designed +to support future extensibility, so some fields may appear to be more complex than +necessary. + + +###### Poll Object Structure + +| Field | Type | Description | +|-------------------|----------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------| +| question | [Poll Media Object](/developers/docs/resources/poll#poll-media-object-poll-media-object-structure) | The question of the poll. Only `text` is supported. | +| answers | List of [Poll Answer Objects](/developers/docs/resources/poll#poll-answer-object-poll-answer-object-structure) | Each of the answers available in the poll. | +| expiry | ?IS08601 timestamp | The time when the poll ends. | +| allow_multiselect | boolean | Whether a user can select multiple answers | +| layout_type | integer | The [layout type](/developers/docs/resources/poll#layout-type) of the poll | +| results? | [Poll Results Object](/developers/docs/resources/poll#poll-results-object-poll-results-object-structure) | The results of the poll | + +`expiry` is marked as nullable to support non-expiring polls in the future, but all polls have an expiry currently. + +### Poll Create Request Object + +This is the request object used when creating a poll across the different endpoints. +It is similar but not exactly identical to the main [poll object](/developers/docs/resources/poll#poll-object-poll-object-structure). +The main difference is that the request has `duration` which eventually becomes `expiry`. + + +###### Poll Create Request Object Structure + +| Field | Type | Description | +|--------------------|----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| +| question | [Poll Media Object](/developers/docs/resources/poll#poll-media-object-poll-media-object-structure) | The question of the poll. Only `text` is supported. | +| answers | List of [Poll Answer Objects](/developers/docs/resources/poll#poll-answer-object-poll-answer-object-structure) | Each of the answers available in the poll, up to 10 | +| duration? | integer | Number of hours the poll should be open for, up to 32 days. Defaults to 24 | +| allow_multiselect? | boolean | Whether a user can select multiple answers. Defaults to false | +| layout_type? | integer | The [layout type](/developers/docs/resources/poll#layout-type) of the poll. Defaults to... DEFAULT! | + +### Layout Type + +We might have different layouts for polls in the future. +For now though, this number will be 1. + +| Type | ID | Description | +|---------|----|--------------------------------| +| DEFAULT | 1 | The, uhm, default layout type. | + +### Poll Media Object + +The poll media object is a common object that backs both the question and answers. +The intention is that it allows us to extensibly add new ways to display things in the future. +For now, `question` only supports `text`, while answers can have an optional `emoji`. + + +###### Poll Media Object Structure + +| Field | Type | Description | +|--------|----------------------------------------------------------------|------------------------| +| emoji? | partial [emoji](/developers/docs/resources/emoji#emoji-object) | The emoji of the field | + +`text` should always be non-null for both questions and answers, but please do not depend on that in the future. +The maximum length of `text` is 300 for the question, and 55 for any answer. + +When creating a poll answer with an emoji, one only needs to send either the `id` (custom emoji) or `name` (default emoji) as the only field. + +### Poll Answer Object + +The `answer_id` is a number that labels each answer. +As an implementation detail, it currently starts at 1 for the first answer and goes up sequentially. +We recommend against depending on this sequence. + +Currently, there is a maximum of 10 answers per poll. + + +###### Poll Answer Object Structure + +| Field | Type | Description | +|-------------|----------------------------------------------------------------------------------------------------|------------------------| +| answer_id\* | integer | The ID of the answer | +| poll_media | [Poll Media Object](/developers/docs/resources/poll#poll-media-object-poll-media-object-structure) | The data of the answer | + +\* Only sent as part of responses from Discord's API/Gateway. + +### Poll Results Object + +In a nutshell, this contains the number of votes for each answer. + +The `results` field may be not present in certain responses where, as an implementation detail, we do not fetch the poll results in our backend. +This should be treated as "unknown results", as opposed to "no results". You can keep using the results if you have previously received them through other means. + +Also due to the intricacies of counting at scale, while a poll is in progress the results may not be perfectly accurate. +They usually are accurate, and shouldn't deviate significantly -- it's just difficult to make guarantees. + +To compensate for this, after a poll is finished there is a background job which performs a final, accurate tally of votes. +This tally concludes once `is_finalized` is `true`. Polls that have ended will also always contain results. + +If `answer_counts` does not contain an entry for a particular answer, then there are no votes for that answer. + + +###### Poll Results Object Structure + +| Field | Type | Description | +|---------------|----------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| +| is_finalized | boolean | Whether the votes have been precisely counted | +| answer_counts | List of [Poll Answer Count Object](/developers/docs/resources/poll#poll-results-object-poll-answer-count-object-structure) | The counts for each answer | + +###### Poll Answer Count Object Structure + +| Field | Type | Description | +|----------|---------|------------------------------------------------| +| id | integer | The `answer_id` | +| count | integer | The number of votes for this answer | +| me_voted | boolean | Whether the current user voted for this answer | + +# Poll Endpoints + +For creating a poll, see [Create Message](/developers/docs/resources/message#create-message). After creation, the poll message cannot be edited. + +Apps are not allowed to vote on polls. No rights! :) + +## Get Answer Voters +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/polls/[\{message.id\}](/developers/docs/resources/message#message-object)/answers/[\{answer_id\}](/developers/docs/resources/poll#poll-answer-object) + +Get a list of users that voted for this specific answer. + + +###### Query String Params + +| Field | Type | Description | Default | +|--------|-----------|---------------------------------------|---------| +| after? | snowflake | Get users after this user ID | absent | +| limit? | integer | Max number of users to return (1-100) | 25 | + + +###### Response Body + +| Field | Type | Description | +|-------|--------------------------------------------------------------|---------------------------------| +| users | array of [user](/developers/docs/resources/user#user-object) | Users who voted for this answer | + +## End Poll +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/polls/[\{message.id\}](/developers/docs/resources/message#message-object)/expire + +Immediately ends the poll. You cannot end polls from other users. + +Returns a [message](/developers/docs/resources/message#message-object) object. Fires a [Message Update](/developers/docs/events/gateway-events#message-update) Gateway event. diff --git a/discord/developers/docs/resources/sku.mdx b/discord/developers/docs/resources/sku.mdx new file mode 100644 index 0000000000..268af8cab6 --- /dev/null +++ b/discord/developers/docs/resources/sku.mdx @@ -0,0 +1,117 @@ +--- +title: SKU Resource +sidebarTitle: SKU +description: Reference for Discord SKU objects that represent premium app offerings. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +SKUs (stock-keeping units) in Discord represent premium offerings that can be made available to your application's users or guilds. + +### SKU Object + + +###### SKU Structure + +| Field | Type | Description | +|----------------|-----------|------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of SKU | +| type | integer | [Type of SKU](/developers/docs/resources/sku#sku-object-sku-types) | +| application_id | snowflake | ID of the parent application | +| name | string | Customer-facing name of your premium offering | +| slug | string | System-generated URL slug based on the SKU's name | +| flags | integer | [SKU flags](/developers/docs/resources/sku#sku-object-sku-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | + + +###### SKU Example + +```json +{ + "id": "1088510058284990888", + "type": 5, + "dependent_sku_id": null, + "application_id": "788708323867885999", + "manifest_labels": null, + "access_type": 1, + "name": "Test Premium", + "features": [], + "release_date": null, + "premium": false, + "slug": "test-premium", + "flags": 128, + "show_age_gate": false +} +``` + + +###### SKU Types + +For subscriptions, SKUs will have a type of either `SUBSCRIPTION` represented by `type: 5` or `SUBSCRIPTION_GROUP` represented by `type:6`. For any current implementations, you will want to use the SKU defined by `type: 5`. A `SUBSCRIPTION_GROUP` is automatically created for each `SUBSCRIPTION` SKU and are not used at this time. + +| Type | Value | Description | +|--------------------|-------|----------------------------------------------------------| +| DURABLE | 2 | Durable one-time purchase | +| CONSUMABLE | 3 | Consumable one-time purchase | +| SUBSCRIPTION | 5 | Represents a recurring subscription | +| SUBSCRIPTION_GROUP | 6 | System-generated group for each SUBSCRIPTION SKU created | + + +###### SKU Flags + +For subscriptions, there are two types of access levels you can offer to users: + +- **Guild Subscriptions**: A subscription purchased by a user and applied to a single server. Everyone in that server gets your premium benefits. +- **User Subscriptions**: A subscription purchased by a user for themselves. They get access to your premium benefits in every server. + +The `flags` field can be used to differentiate user and server subscriptions with a bitwise `&` operator. + +| Type | Value | Description | +|--------------------|----------|---------------------------------------------------------------------------------------------------------------------------| +| AVAILABLE | `1 << 2` | SKU is available for purchase | +| GUILD_SUBSCRIPTION | `1 << 7` | Recurring SKU that can be purchased by a user and applied to a single server. Grants access to every user in that server. | +| USER_SUBSCRIPTION | `1 << 8` | Recurring SKU purchased by a user for themselves. Grants access to the purchasing user in every server. | + +## List SKUs +/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/skus + +Returns all SKUs for a given application. + + +Because of how our SKU and subscription systems work, you will see two SKUs for your subscription offering. For integration and testing entitlements for Subscriptions, you should use the SKU with `type: 5`. + + +```json +[ + { + "id": "1088510053843210999", + "type": 6, + "dependent_sku_id": null, + "application_id": "788708323867885999", + "manifest_labels": null, + "access_type": 1, + "name": "Test Premium", + "features": [], + "release_date": null, + "premium": false, + "slug": "test-premium", + "flags": 128, + "show_age_gate": false + }, + { + "id": "1088510058284990888", + "type": 5, + "dependent_sku_id": null, + "application_id": "788708323867885999", + "manifest_labels": null, + "access_type": 1, + "name": "Test Premium", + "features": [], + "release_date": null, + "premium": false, + "slug": "test-premium", + "flags": 128, + "show_age_gate": false + } +] +``` diff --git a/discord/developers/docs/resources/soundboard.mdx b/discord/developers/docs/resources/soundboard.mdx new file mode 100644 index 0000000000..7a8169a2bf --- /dev/null +++ b/discord/developers/docs/resources/soundboard.mdx @@ -0,0 +1,160 @@ +--- +title: Soundboard Resource +sidebarTitle: Soundboard +description: Reference for Discord soundboard objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +Users can play soundboard sounds in voice channels, triggering a [Voice Channel Effect Send](/developers/docs/events/gateway-events#voice-channel-effect-send) Gateway event for users connected to the voice channel. + +There is a set of [default sounds](/developers/docs/resources/soundboard#list-default-soundboard-sounds) available to all users. Soundboard sounds can also be [created in a guild](/developers/docs/resources/soundboard#create-guild-soundboard-sound); users will be able to use the sounds in the guild, and Nitro subscribers can use them in all guilds. + +Soundboard sounds in a set of guilds can be retrieved over the Gateway using [Request Soundboard Sounds](/developers/docs/events/gateway-events#request-soundboard-sounds). + +### Soundboard Sound Object + + +###### Soundboard Sound Structure + +| Field | Type | Description | +|------------|------------------------------------------------------------|---------------------------------------------------------------------------| +| name | string | the name of this sound | +| sound_id | snowflake | the id of this sound | +| volume | double | the volume of this sound, from 0 to 1 | +| emoji_id | ?snowflake | the id of this sound's custom emoji | +| emoji_name | ?string | the unicode character of this sound's standard emoji | +| guild_id? | snowflake | the id of the guild this sound is in | +| available | boolean | whether this sound can be used, may be false due to loss of Server Boosts | +| user? | [user](/developers/docs/resources/user#user-object) object | the user who created this sound | + + +###### Example Default Soundboard Sound + +```json +{ + "name": "quack", + "sound_id": "1", + "volume": 1.0, + "emoji_id": null, + "emoji_name": "🦆", + "available": true +} +``` + + +###### Example Guild Soundboard Sound + +```json +{ + "name": "Yay", + "sound_id": "1106714396018884649", + "volume": 1, + "emoji_id": "989193655938064464", + "emoji_name": null, + "guild_id": "613425648685547541", + "available": true +} +``` + +### Sound Files + +A soundboard sound can be retrieved in MP3 or Ogg format at the URL: + +``` +https://cdn.discordapp.com/soundboard-sounds/{sound_id} +``` + +## Send Soundboard Sound +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/send-soundboard-sound + +Send a soundboard sound to a voice channel the user is connected to. Fires a [Voice Channel Effect Send](/developers/docs/events/gateway-events#voice-channel-effect-send) Gateway event. + +Requires the `SPEAK` and `USE_SOUNDBOARD` permissions, and also the `USE_EXTERNAL_SOUNDS` permission if the sound is from a different server. Additionally, requires the user to be connected to the voice channel, having a [voice state](/developers/docs/resources/voice#voice-state-object) without `deaf`, `self_deaf`, `mute`, or `suppress` enabled. + + +###### JSON Params + +| Field | Type | Description | +|------------------|-----------|--------------------------------------------------------------------------------------------------| +| sound_id | snowflake | the id of the soundboard sound to play | +| source_guild_id? | snowflake | the id of the guild the soundboard sound is from, required to play sounds from different servers | + +## List Default Soundboard Sounds +/soundboard-default-sounds + +Returns an array of [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) objects that can be used by all users. + +## List Guild Soundboard Sounds +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/soundboard-sounds + +Returns a list of the guild's soundboard sounds. Includes `user` fields if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + + +###### Response Structure + +| Field | Type | +|-------|----------------------------------------------------------------------------------------------------| +| items | array of [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) objects | + +## Get Guild Soundboard Sound +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/soundboard-sounds/[\{sound.id\}](/developers/docs/resources/soundboard#soundboard-sound-object) + +Returns a [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) object for the given sound id. Includes the `user` field if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + +## Create Guild Soundboard Sound +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/soundboard-sounds + +Create a new soundboard sound for the guild. Requires the `CREATE_GUILD_EXPRESSIONS` permission. Returns the new [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) object on success. Fires a [Guild Soundboard Sound Create](/developers/docs/events/gateway-events#guild-soundboard-sound-create) Gateway event. + + +Soundboard sounds have a max file size of 512kb and a max duration of 5.2 seconds. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +###### JSON +###### JSON Params + +| Field | Type | Description | +|-------------|------------|-----------------------------------------------------------------------------------------------------------| +| name | string | name of the soundboard sound (2-32 characters) | +| sound | data uri | the mp3 or ogg sound data, base64 encoded, similar to [image data](/developers/docs/reference#image-data) | +| volume? | ?double | the volume of the soundboard sound, from 0 to 1, defaults to 1 | +| emoji_id? | ?snowflake | the id of the custom emoji for the soundboard sound | +| emoji_name? | ?string | the unicode character of a standard emoji for the soundboard sound | + +## Modify Guild Soundboard Sound +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/soundboard-sounds/[\{sound.id\}](/developers/docs/resources/soundboard#soundboard-sound-object) + +Modify the given soundboard sound. For sounds created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other sounds, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns the updated [soundboard sound](/developers/docs/resources/soundboard#soundboard-sound-object) object on success. Fires a [Guild Soundboard Sound Update](/developers/docs/events/gateway-events#guild-soundboard-sound-update) Gateway event. + + +All parameters to this endpoint are optional. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|------------|------------|--------------------------------------------------------------------| +| name | string | name of the soundboard sound (2-32 characters) | +| volume | ?double | the volume of the soundboard sound, from 0 to 1 | +| emoji_id | ?snowflake | the id of the custom emoji for the soundboard sound | +| emoji_name | ?string | the unicode character of a standard emoji for the soundboard sound | + +## Delete Guild Soundboard Sound +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/soundboard-sounds/[\{sound.id\}](/developers/docs/resources/soundboard#soundboard-sound-object) + +Delete the given soundboard sound. For sounds created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other sounds, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns `204 No Content` on success. Fires a [Guild Soundboard Sound Delete](/developers/docs/events/gateway-events#guild-soundboard-sound-delete) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + diff --git a/docs/resources/Stage_Instance.md b/discord/developers/docs/resources/stage-instance.mdx similarity index 50% rename from docs/resources/Stage_Instance.md rename to discord/developers/docs/resources/stage-instance.mdx index 7c6e750ba3..13df4b418c 100644 --- a/docs/resources/Stage_Instance.md +++ b/discord/developers/docs/resources/stage-instance.mdx @@ -1,28 +1,38 @@ -# Stage Instance Resource +--- +title: Stage Instance Resource +sidebarTitle: Stage Instance +description: Reference for Discord stage instance objects for managing live audio events. +--- -A _Stage Instance_ holds information about a live stage. +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +A Stage Instance holds information about a live stage. ### Stage Instance Object + ###### Stage Instance Structure -| Field | Type | Description | -| ------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------- | -| id | snowflake | The id of this Stage instance | -| guild_id | snowflake | The guild id of the associated Stage channel | -| channel_id | snowflake | The id of the associated Stage channel | -| topic | string | The topic of the Stage instance (1-120 characters) | -| privacy_level | integer | The [privacy level](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object-privacy-level) of the Stage instance | -| discoverable_disabled | boolean | Whether or not Stage Discovery is disabled (deprecated) | -| guild_scheduled_event_id | ?snowflake | The id of the scheduled event for this Stage instance | - +| Field | Type | Description | +|--------------------------|------------|--------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | The id of this Stage instance | +| guild_id | snowflake | The guild id of the associated Stage channel | +| channel_id | snowflake | The id of the associated Stage channel | +| topic | string | The topic of the Stage instance (1-120 characters) | +| privacy_level | integer | The [privacy level](/developers/docs/resources/stage-instance#stage-instance-object-privacy-level) of the Stage instance | +| discoverable_disabled | boolean | Whether or not Stage Discovery is disabled (deprecated) | +| guild_scheduled_event_id | ?snowflake | The id of the scheduled event for this Stage instance | + + ###### Privacy Level -| Level | Value | Description | -| ---------- | ----- | ------------------------------------------------------------------- | -| PUBLIC | 1 | The Stage instance is visible publicly. (deprecated) | -| GUILD_ONLY | 2 | The Stage instance is visible to only guild members. | +| Level | Value | Description | +|------------|-------|------------------------------------------------------| +| PUBLIC | 1 | The Stage instance is visible publicly. (deprecated) | +| GUILD_ONLY | 2 | The Stage instance is visible to only guild members. | + ###### Example Stage Instance ```json @@ -42,68 +52,75 @@ A _Stage Instance_ holds information about a live stage. Below are some definitions related to stages. - **Liveness:** A Stage channel is considered _live_ when there is an associated stage instance. Conversely, a Stage channel is _not live_ when there is no associated stage instance. -- **Speakers:** A participant of a Stage channel is a _speaker_ when their [voice state](#DOCS_RESOURCES_VOICE/voice-state-object) +- **Speakers:** A participant of a Stage channel is a _speaker_ when their [voice state](/developers/docs/resources/voice#voice-state-object) is not `suppress`ed, and has no `request_to_speak_timestamp`. -- **Moderators**: A member of the guild is a _moderator_ of a Stage channel if they have all of the following [permissions](#DOCS_TOPICS_PERMISSIONS/permissions): +- **Moderators**: A member of the guild is a _moderator_ of a Stage channel if they have all of the following [permissions](/developers/docs/topics/permissions): - `MANAGE_CHANNELS` - `MUTE_MEMBERS` - `MOVE_MEMBERS` - **Topic**: This is the blurb that gets shown below the channel's name, among other places. - **Public**: A Stage instance is public when it has a `privacy_level` of `PUBLIC`. While a guild has a public Stage instance: - - The guild will be lurkable. - - Lurkers may join any Stage channel with a public Stage instance. - - Users in the Stage can have the Stage show in their [activities](#DOCS_TOPICS_GATEWAY_EVENTS/presence). - - [Invites](#DOCS_RESOURCES_INVITE/invite-object) to the Stage channel will have the `stage_instance` field. + - Users in the Stage can have the Stage show in their [activities](/developers/docs/events/gateway-events#presence). + - [Invites](/developers/docs/resources/invite#invite-object) to the Stage channel will have the `stage_instance` field. ## Auto Closing When a Stage channel has no speakers for a certain period of time (on the order of minutes), the Stage instance will be automatically deleted. -## Create Stage Instance % POST /stage-instances +## Create Stage Instance +/stage-instances -Creates a new Stage instance associated to a Stage channel. Returns that [Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object-stage-instance-structure). Fires a [Stage Instance Create](#DOCS_TOPICS_GATEWAY_EVENTS/stage-instance-create) Gateway event. +Creates a new Stage instance associated to a Stage channel. Returns that [Stage instance](/developers/docs/resources/stage-instance#stage-instance-object-stage-instance-structure). Fires a [Stage Instance Create](/developers/docs/events/gateway-events#stage-instance-create) Gateway event. Requires the user to be a moderator of the Stage channel. -> info -> This endpoint supports the `X-Audit-Log-Reason` header. + +This endpoint supports the `X-Audit-Log-Reason` header. + + ###### JSON Params -| Field | Type | Description | -| --------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| channel_id | snowflake | The id of the Stage channel | -| topic | string | The topic of the Stage instance (1-120 characters) | -| privacy_level? | integer | The [privacy level](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object-privacy-level) of the Stage instance (default GUILD_ONLY) | -| send_start_notification? \* | boolean | Notify @everyone that a Stage instance has started | +| Field | Type | Description | +|-----------------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------| +| channel_id | snowflake | The id of the Stage channel | +| topic | string | The topic of the Stage instance (1-120 characters) | +| privacy_level? | integer | The [privacy level](/developers/docs/resources/stage-instance#stage-instance-object-privacy-level) of the Stage instance (default GUILD_ONLY) | +| send_start_notification? \* | boolean | Notify @everyone that a Stage instance has started | +| guild_scheduled_event_id? | snowflake | The guild scheduled event associated with this Stage instance | \* The stage moderator must have the `MENTION_EVERYONE` permission for this notification to be sent. -## Get Stage Instance % GET /stage-instances/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object} +## Get Stage Instance +/stage-instances/[\{channel.id\}](/developers/docs/resources/channel#channel-object) Gets the stage instance associated with the Stage channel, if it exists. -## Modify Stage Instance % PATCH /stage-instances/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object} +## Modify Stage Instance +/stage-instances/[\{channel.id\}](/developers/docs/resources/channel#channel-object) -Updates fields of an existing Stage instance. Returns the updated [Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object-stage-instance-structure). Fires a [Stage Instance Update](#DOCS_TOPICS_GATEWAY_EVENTS/stage-instance-update) Gateway event. +Updates fields of an existing Stage instance. Returns the updated [Stage instance](/developers/docs/resources/stage-instance#stage-instance-object-stage-instance-structure). Fires a [Stage Instance Update](/developers/docs/events/gateway-events#stage-instance-update) Gateway event. Requires the user to be a moderator of the Stage channel. -> info -> This endpoint supports the `X-Audit-Log-Reason` header. + +This endpoint supports the `X-Audit-Log-Reason` header. + + ###### JSON Params -| Field | Type | Description | -| -------------- | ------- | ------------------------------------------------------------------------------------------------------------- | -| topic? | string | The topic of the Stage instance (1-120 characters) | -| privacy_level? | integer | The [privacy level](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object-privacy-level) of the Stage instance | +| Field | Type | Description | +|----------------|---------|--------------------------------------------------------------------------------------------------------------------------| +| privacy_level? | integer | The [privacy level](/developers/docs/resources/stage-instance#stage-instance-object-privacy-level) of the Stage instance | -## Delete Stage Instance % DELETE /stage-instances/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object} +## Delete Stage Instance +/stage-instances/[\{channel.id\}](/developers/docs/resources/channel#channel-object) -Deletes the Stage instance. Returns `204 No Content`. Fires a [Stage Instance Delete](#DOCS_TOPICS_GATEWAY_EVENTS/stage-instance-delete) Gateway event. +Deletes the Stage instance. Returns `204 No Content`. Fires a [Stage Instance Delete](/developers/docs/events/gateway-events#stage-instance-delete) Gateway event. Requires the user to be a moderator of the Stage channel. -> info -> This endpoint supports the `X-Audit-Log-Reason` header. + +This endpoint supports the `X-Audit-Log-Reason` header. + diff --git a/discord/developers/docs/resources/sticker.mdx b/discord/developers/docs/resources/sticker.mdx new file mode 100644 index 0000000000..ccc025fb49 --- /dev/null +++ b/discord/developers/docs/resources/sticker.mdx @@ -0,0 +1,202 @@ +--- +title: Sticker Resource +sidebarTitle: Sticker +description: Reference for Discord sticker objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +### Sticker Object + +Represents a sticker that can be sent in messages. + + +###### Sticker Structure + +| Field | Type | Description | +|-------------|------------------------------------------------------------|--------------------------------------------------------------------------------------------------| +| id | snowflake | [id of the sticker](/developers/docs/reference#image-formatting) | +| pack_id? | snowflake | for standard stickers, id of the pack the sticker is from | +| name | string | name of the sticker | +| description | ?string | description of the sticker | +| tags\* | string | autocomplete/suggestion tags for the sticker (max 200 characters) | +| type | integer | [type of sticker](/developers/docs/resources/sticker#sticker-object-sticker-types) | +| format_type | integer | [type of sticker format](/developers/docs/resources/sticker#sticker-object-sticker-format-types) | +| available? | boolean | whether this guild sticker can be used, may be false due to loss of Server Boosts | +| guild_id? | snowflake | id of the guild that owns this sticker | +| user? | [user](/developers/docs/resources/user#user-object) object | the user that uploaded the guild sticker | +| sort_value? | integer | the standard sticker's sort order within its pack | + +\* A comma separated list of keywords is the format used in this field by standard stickers, but this is just a convention. +Incidentally the client will always use a name generated from an emoji as the value of this field when creating or modifying a guild sticker. + + +###### Sticker Types + +| Type | Value | Description | +|----------|-------|-------------------------------------------------------| +| STANDARD | 1 | an official sticker in a pack | +| GUILD | 2 | a sticker uploaded to a guild for the guild's members | + + +###### Sticker Format Types + +| Type | Value | +|--------|-------| +| PNG | 1 | +| APNG | 2 | +| LOTTIE | 3 | +| GIF | 4 | + + +###### Example Sticker + +```json +{ + "id": "749054660769218631", + "name": "Wave", + "tags": "wumpus, hello, sup, hi, oi, heyo, heya, yo, greetings, greet, welcome, wave, :wave, :hello, :hi, :hey, hey, \ud83d\udc4b, \ud83d\udc4b\ud83c\udffb, \ud83d\udc4b\ud83c\udffc, \ud83d\udc4b\ud83c\udffd, \ud83d\udc4b\ud83c\udffe, \ud83d\udc4b\ud83c\udfff, goodbye, bye, see ya, later, laterz, cya", + "type": 1, + "format_type": 3, + "description": "Wumpus waves hello", + "pack_id": "847199849233514549", + "sort_value": 12 +} +``` + +### Sticker Item Object + +The smallest amount of data required to render a sticker. A partial sticker object. + + +###### Sticker Item Structure + +| Field | Type | Description | +|-------------|-----------|--------------------------------------------------------------------------------------------------| +| id | snowflake | id of the sticker | +| name | string | name of the sticker | +| format_type | integer | [type of sticker format](/developers/docs/resources/sticker#sticker-object-sticker-format-types) | + +### Sticker Pack Object + +Represents a pack of standard stickers. + + +###### Sticker Pack Structure +| Field | Type | Description | +|-------------------|-------------------------------------------------------------------------------|--------------------------------------------------------------------------------------| +| id | snowflake | id of the sticker pack | +| stickers | array of [sticker](/developers/docs/resources/sticker#sticker-object) objects | the stickers in the pack | +| name | string | name of the sticker pack | +| sku_id | snowflake | id of the pack's SKU | +| cover_sticker_id? | snowflake | id of a sticker in the pack which is shown as the pack's icon | +| description | string | description of the sticker pack | +| banner_asset_id? | snowflake | id of the sticker pack's [banner image](/developers/docs/reference#image-formatting) | + + +###### Example Sticker Pack + +```json +{ + "id": "847199849233514549", + "stickers": [], + "name": "Wumpus Beyond", + "sku_id": "847199849233514547", + "cover_sticker_id": "749053689419006003", + "description": "Say hello to Wumpus!", + "banner_asset_id": "761773777976819732" +} +``` + +## Get Sticker +/stickers/[\{sticker.id\}](/developers/docs/resources/sticker#sticker-object) + +Returns a [sticker](/developers/docs/resources/sticker#sticker-object) object for the given sticker ID. + +## List Sticker Packs +/sticker-packs + +Returns a list of available sticker packs. + + +###### Response Structure + +| Field | Type | +|---------------|-----------------------------------------------------------------------------------------| +| sticker_packs | array of [sticker pack](/developers/docs/resources/sticker#sticker-pack-object) objects | + +## Get Sticker Pack +/sticker-packs/[\{pack.id\}](/developers/docs/resources/sticker#sticker-pack-object) + +Returns a [sticker pack](/developers/docs/resources/sticker#sticker-pack-object) object for the given sticker pack ID. + +## List Guild Stickers +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/stickers + +Returns an array of [sticker](/developers/docs/resources/sticker#sticker-object) objects for the given guild. Includes `user` fields if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + +## Get Guild Sticker +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/stickers/[\{sticker.id\}](/developers/docs/resources/sticker#sticker-object) + +Returns a [sticker](/developers/docs/resources/sticker#sticker-object) object for the given guild and sticker IDs. Includes the `user` field if the bot has the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. + +## Create Guild Sticker +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/stickers + +Create a new sticker for the guild. Send a `multipart/form-data` body. Requires the `CREATE_GUILD_EXPRESSIONS` permission. Returns the new [sticker](/developers/docs/resources/sticker#sticker-object) object on success. Fires a [Guild Stickers Update](/developers/docs/events/gateway-events#guild-stickers-update) Gateway event. + +Every guilds has five free sticker slots by default, and each Boost level will grant access to more slots. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +Lottie stickers can only be uploaded on guilds that have either the `VERIFIED` and/or the `PARTNERED` [guild feature](/developers/docs/resources/guild#guild-object-guild-features). + + + +Uploaded stickers are constrained to 5 seconds in length for animated stickers, and 320 x 320 pixels. + + + +###### Form Params + +| Field | Type | Description | +|-------------|---------------|----------------------------------------------------------------------------------------| +| name | string | name of the sticker (2-30 characters) | +| description | string | description of the sticker (empty or 2-100 characters) | +| tags | string | autocomplete/suggestion tags for the sticker (max 200 characters) | +| file | file contents | the sticker file to upload, must be a PNG, APNG, GIF, or Lottie JSON file, max 512 KiB | + +## Modify Guild Sticker +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/stickers/[\{sticker.id\}](/developers/docs/resources/sticker#sticker-object) + +Modify the given sticker. For stickers created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other stickers, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns the updated [sticker](/developers/docs/resources/sticker#sticker-object) object on success. Fires a [Guild Stickers Update](/developers/docs/events/gateway-events#guild-stickers-update) Gateway event. + + +All parameters to this endpoint are optional. + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|-------------|---------|-------------------------------------------------------------------| +| name | string | name of the sticker (2-30 characters) | +| description | ?string | description of the sticker (2-100 characters) | +| tags | string | autocomplete/suggestion tags for the sticker (max 200 characters) | + +## Delete Guild Sticker +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/stickers/[\{sticker.id\}](/developers/docs/resources/sticker#sticker-object) + +Delete the given sticker. For stickers created by the current user, requires either the `CREATE_GUILD_EXPRESSIONS` or `MANAGE_GUILD_EXPRESSIONS` permission. For other stickers, requires the `MANAGE_GUILD_EXPRESSIONS` permission. Returns `204 No Content` on success. Fires a [Guild Stickers Update](/developers/docs/events/gateway-events#guild-stickers-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + diff --git a/discord/developers/docs/resources/subscription.mdx b/discord/developers/docs/resources/subscription.mdx new file mode 100644 index 0000000000..51d18312d0 --- /dev/null +++ b/discord/developers/docs/resources/subscription.mdx @@ -0,0 +1,82 @@ +--- +title: Subscription Resource +sidebarTitle: Subscription +description: Reference for Discord subscription objects that represent recurring payments. +--- + +import {Route} from '/snippets/route.jsx' + +Subscriptions in Discord represent a user making recurring payments for at least one SKU over an ongoing period. Successful payments grant the user access to entitlements associated with the SKU. + +## Subscription Object + +| Field | Type | Description | +|----------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | ID of the subscription | +| user_id | snowflake | ID of the user who is subscribed | +| sku_ids | array of snowflakes | List of SKUs subscribed to | +| entitlement_ids | array of snowflakes | List of entitlements granted for this subscription | +| renewal_sku_ids | ?array of snowflakes | List of SKUs that this user will be subscribed to at renewal | +| current_period_start | ISO8601 timestamp | Start of the current subscription period | +| current_period_end | ISO8601 timestamp | End of the current subscription period | +| status | SubscriptionStatus | Current status of the subscription | +| canceled_at | ?ISO8601 timestamp | When the subscription was canceled | +| country? | string | ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. | + +The start of a subscription is determined by its ID. When the subscription renews, its current period is updated. + +If the user cancels the subscription, the subscription will enter the `ENDING` status and the `canceled_at` timestamp will reflect the time of the cancellation. + +### Subscription Example + +```json +{ + "id": "1278078770116427839", + "user_id": "1088605110638227537", + "sku_ids": ["1158857122189168803"], + "entitlement_ids": [], + "renewal_sku_ids": null, + "current_period_start": "2024-08-27T19:48:44.406602+00:00", + "current_period_end": "2024-09-27T19:48:44.406602+00:00", + "status": 0, + "canceled_at": null +} +``` + +### Subscription Statuses + +| Type | Value | Description | +|----------|-------|-------------------------------------------------| +| ACTIVE | 0 | Subscription is active and scheduled to renew. | +| ENDING | 1 | Subscription is active but will not renew. | +| INACTIVE | 2 | Subscription is inactive and not being charged. | + + +Subscription status should not be used to grant perks. Use [entitlements](/developers/docs/resources/entitlement#entitlement-object) as an indication of whether a user should have access to a specific SKU. See our guide on [Implementing App Subscriptions](/developers/docs/monetization/implementing-app-subscriptions) for more information. + + +Subscriptions can start and change between any of these statuses within the current period. A subscription can be `ACTIVE` outside its current period or `INACTIVE` within its current period. + +Some examples of this behavior include: + +- While a failed payment is being retried, the subscription would remain `ACTIVE` until it succeeds or our system determines the payment is not recoverable. +- A refund or chargeback during the current period would make the subscription `INACTIVE`. + +## List SKU Subscriptions +/skus/[\{sku.id\}](/developers/docs/resources/sku#sku-object)/subscriptions + +Returns all subscriptions containing the SKU, filtered by user. Returns a list of [subscription](/developers/docs/resources/subscription#subscription-object) objects. + +### Query String Params + +| Field | Type | Description | Default | +|----------|-----------|-------------------------------------------------------------------------------|---------| +| before? | snowflake | List subscriptions before this ID | absent | +| after? | snowflake | List subscriptions after this ID | absent | +| limit? | integer | Number of results to return (1-100) | 50 | +| user_id? | snowflake | User ID for which to return subscriptions. Required except for OAuth queries. | absent | + +## Get SKU Subscription +/skus/[\{sku.id\}](/developers/docs/resources/sku#sku-object)/subscriptions/[\{subscription.id\}](/developers/docs/resources/subscription#subscription-object) + +Get a subscription by its ID. Returns a [subscription](/developers/docs/resources/subscription#subscription-object) object. \ No newline at end of file diff --git a/discord/developers/docs/resources/user.mdx b/discord/developers/docs/resources/user.mdx new file mode 100644 index 0000000000..74a4b002dd --- /dev/null +++ b/discord/developers/docs/resources/user.mdx @@ -0,0 +1,378 @@ +--- +title: User Resource +sidebarTitle: User +description: Reference for Discord user objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +Users in Discord are generally considered the base entity. Users can spawn across the entire platform, be members of +guilds, participate in text and voice chat, and much more. Users are separated by a distinction of "bot" vs "normal." Although they are similar, bot users are automated users that are "owned" by another user. Unlike normal users, bot users do +_not_ have a limitation on the number of Guilds they can be a part of. + +## Usernames and Nicknames + +Discord enforces the following restrictions for usernames and nicknames: + +1. Names can contain most valid unicode characters. We limit some zero-width and non-rendering characters. +2. Usernames must be between 2 and 32 characters long. +3. Nicknames must be between 1 and 32 characters long. +4. Names are sanitized and trimmed of leading, trailing, and excessive internal whitespace. + +The following restrictions are additionally enforced for usernames: + +1. Usernames cannot contain the following substrings: `@`, `#`, `:`, ` ``` `, `discord` +2. Usernames cannot be: `everyone`, `here` + +There are other rules and restrictions not shared here for the sake of spam and abuse mitigation, but the majority of users won't encounter them. It's important to properly handle all error messages returned by Discord when editing or updating names. + +### User Object + + +###### User Structure + +| Field | Type | Description | Required OAuth2 Scope | +|-------------------------|-------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|-----------------------| +| id | snowflake | the user's id | identify | +| username | string | the user's username, not unique across the platform | identify | +| discriminator | string | the user's Discord-tag | identify | +| global_name | ?string | the user's display name, if it is set. For bots, this is the application name | identify | +| avatar | ?string | the user's [avatar hash](/developers/docs/reference#image-formatting) | identify | +| bot? | boolean | whether the user belongs to an OAuth2 application | identify | +| system? | boolean | whether the user is an Official Discord System user (part of the urgent message system) | identify | +| mfa_enabled? | boolean | whether the user has two factor enabled on their account | identify | +| banner? | ?string | the user's [banner hash](/developers/docs/reference#image-formatting) | identify | +| accent_color? | ?integer | the user's banner color encoded as an integer representation of hexadecimal color code | identify | +| locale? | string | the user's chosen [language option](/developers/docs/reference#locales) | identify | +| verified? | boolean | whether the email on this account has been verified | email | +| email? | ?string | the user's email | email | +| flags? | integer | the [flags](/developers/docs/resources/user#user-object-user-flags) on a user's account | identify | +| premium_type? | integer | the [type of Nitro subscription](/developers/docs/resources/user#user-object-premium-types) on a user's account | identify | +| public_flags? | integer | the public [flags](/developers/docs/resources/user#user-object-user-flags) on a user's account | identify | +| avatar_decoration_data? | ?[avatar decoration data](/developers/docs/resources/user#avatar-decoration-data-object) object | data for the user's avatar decoration | identify | +| collectibles? | ?[collectibles](/developers/docs/resources/user#collectibles) object | data for the user's collectibles | identify | +| primary_guild? | ?[user primary guild](/developers/docs/resources/user#user-object-user-primary-guild) object | the user's primary guild | identify | + + +###### Example User + +```json +{ + "id": "80351110224678912", + "username": "Nelly", + "global_name": null, + "discriminator": "1337", + "avatar": "8342729096ea3675442027381ff50dfe", + "verified": true, + "email": "nelly@discord.com", + "flags": 64, + "banner": "06c16474723fe537c283b8efa61a30c8", + "accent_color": 16711680, + "premium_type": 1, + "public_flags": 64, + "avatar_decoration_data": { + "sku_id": "1144058844004233369", + "asset": "a_fed43ab12698df65902ba06727e20c0e" + }, + "collectibles": { + "nameplate": { + "sku_id": "2247558840304243311", + "asset": "nameplates/nameplates/twilight/", + "label": "", + "palette": "cobalt" + } + }, + "primary_guild": { + "identity_guild_id": "1234647491267808778", + "identity_enabled": true, + "tag": "DISC", + "badge": "7d1734ae5a615e82bc7a4033b98fade8" + } +} +``` + + +###### User Flags + +| Value | Name | Description | +|-----------|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `1 << 0` | STAFF | Discord Employee | +| `1 << 1` | PARTNER | Partnered Server Owner | +| `1 << 2` | HYPESQUAD | HypeSquad Events Member | +| `1 << 3` | BUG_HUNTER_LEVEL_1 | Bug Hunter Level 1 | +| `1 << 6` | HYPESQUAD_ONLINE_HOUSE_1 | House Bravery Member | +| `1 << 7` | HYPESQUAD_ONLINE_HOUSE_2 | House Brilliance Member | +| `1 << 8` | HYPESQUAD_ONLINE_HOUSE_3 | House Balance Member | +| `1 << 9` | PREMIUM_EARLY_SUPPORTER | Early Nitro Supporter | +| `1 << 10` | TEAM_PSEUDO_USER | User is a [team](/developers/docs/topics/teams) | +| `1 << 14` | BUG_HUNTER_LEVEL_2 | Bug Hunter Level 2 | +| `1 << 16` | VERIFIED_BOT | Verified Bot | +| `1 << 17` | VERIFIED_DEVELOPER | Early Verified Bot Developer | +| `1 << 18` | CERTIFIED_MODERATOR | Moderator Programs Alumni | +| `1 << 19` | BOT_HTTP_INTERACTIONS | Bot uses only [HTTP interactions](/developers/docs/interactions/receiving-and-responding#receiving-an-interaction) and is shown in the online member list | +| `1 << 22` | ACTIVE_DEVELOPER | User is an [Active Developer](https://support-dev.discord.com/hc/articles/10113997751447) | + + +###### Premium Types + +Premium types denote the level of premium a user has. Visit the [Nitro](https://discord.com/nitro) page to learn more about the premium plans we currently offer. + +| Value | Name | +|-------|---------------| +| 0 | None | +| 1 | Nitro Classic | +| 2 | Nitro | +| 3 | Nitro Basic | + + +###### User Primary Guild + +| Field | Type | Description | +|-------------------|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| identity_guild_id | ?snowflake | the id of the user's primary guild | +| identity_enabled | ?boolean | whether the user is displaying the primary guild's server tag. This can be `null` if the system clears the identity, e.g. the server no longer supports tags. This will be `false` if the user manually removes their tag. | +| tag | ?string | the text of the user's server tag. Limited to 4 characters | +| badge | ?string | the [server tag badge hash](/developers/docs/reference#image-formatting) | + +### Avatar Decoration Data Object + +The data for the user's [avatar decoration](https://support.discord.com/hc/en-us/articles/13410113109911-Avatar-Decorations). + + +###### Avatar Decoration Data Structure + +| Field | Type | Description | +|--------|-----------|---------------------------------------------------------------------------| +| asset | string | the [avatar decoration hash](/developers/docs/reference#image-formatting) | +| sku_id | snowflake | id of the avatar decoration's SKU | + +### Collectibles + +The collectibles the user has, excluding Avatar Decorations and Profile Effects. + + +###### Collectible Structure + +| Field | Type | Description | +|------------|--------|---------------------------------------------------------------------------------------------------| +| nameplate? | object | object mapping of [nameplate data](/developers/docs/resources/user#nameplate-nameplate-structure) | + +### Nameplate + +The nameplate the user has. + + +###### Nameplate Structure + +| Field | Type | Description | +|---------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| sku_id | snowflake | id of the nameplate SKU | +| asset | string | path to the [nameplate asset](/developers/docs/reference#image-formatting) | +| label | string | the label of this nameplate. Currently unused | +| palette | string | background color of the nameplate, one of: `crimson`, `berry`, `sky`, `teal`, `forest`, `bubble_gum`, `violet`, `cobalt`, `clover`, `lemon`, `white` | + +### Connection Object + +The connection object that the user has attached. + + +###### Connection Structure + +| Field | Type | Description | +|---------------|---------|-----------------------------------------------------------------------------------------------------| +| id | string | id of the connection account | +| name | string | the username of the connection account | +| type | string | the [service](/developers/docs/resources/user#connection-object-services) of this connection | +| revoked? | boolean | whether the connection is revoked | +| integrations? | array | an array of partial [server integrations](/developers/docs/resources/guild#integration-object) | +| verified | boolean | whether the connection is verified | +| friend_sync | boolean | whether friend sync is enabled for this connection | +| show_activity | boolean | whether activities related to this connection will be shown in presence updates | +| two_way_link | boolean | whether this connection has a corresponding third party OAuth2 token | +| visibility | integer | [visibility](/developers/docs/resources/user#connection-object-visibility-types) of this connection | + + +###### Services + +| Value | Name | +|-----------------|---------------------| +| amazon-music | Amazon Music | +| battlenet | Battle.net | +| bungie | Bungie.net | +| bluesky | Bluesky | +| crunchyroll | Crunchyroll | +| domain | Domain | +| ebay | eBay | +| epicgames | Epic Games | +| facebook | Facebook | +| github | GitHub | +| instagram * | Instagram | +| leagueoflegends | League of Legends | +| mastodon | Mastodon | +| paypal | PayPal | +| playstation | PlayStation Network | +| reddit | Reddit | +| riotgames | Riot Games | +| roblox | Roblox | +| spotify | Spotify | +| skype * | Skype | +| steam | Steam | +| tiktok | TikTok | +| twitch | Twitch | +| twitter | X (Twitter) | +| xbox | Xbox | +| youtube | YouTube | + +\* Service can no longer be added by users + + +###### Visibility Types + +| Value | Name | Description | +|-------|----------|--------------------------------------------------| +| 0 | None | invisible to everyone except the user themselves | +| 1 | Everyone | visible to everyone | + +### Application Role Connection Object + +The role connection object that an application has attached to a user. + + +###### Application Role Connection Structure + +| Field | Type | Description | +|---------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| platform_name | ?string | the vanity name of the platform a bot has connected (max 50 characters) | +| metadata | object | object mapping [application role connection metadata](/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object) keys to their `string`-ified value (max 100 characters) for the user on the platform a bot has connected | + +## Get Current User +/users/@me + +Returns the [user](/developers/docs/resources/user#user-object) object of the requester's account. For OAuth2, this requires the `identify` scope, which will return the object _without_ an email, and optionally the `email` scope, which returns the object _with_ an email if the user has one. + +## Get User +/users/[\{user.id\}](/developers/docs/resources/user#user-object) + +Returns a [user](/developers/docs/resources/user#user-object) object for a given user ID. + +## Modify Current User +/users/@me + +Modify the requester's user account settings. Returns a [user](/developers/docs/resources/user#user-object) object on success. Fires a [User Update](/developers/docs/events/gateway-events#user-update) Gateway event. + + +All parameters to this endpoint are optional. + + + +###### JSON Params + +| Field | Type | Description | +|----------|------------------------------------------------------|----------------------------------------------------------------------------------| +| username | string | user's username, if changed may cause the user's discriminator to be randomized. | +| avatar | ?[image data](/developers/docs/reference#image-data) | if passed, modifies the user's avatar | +| banner | ?[image data](/developers/docs/reference#image-data) | if passed, modifies the user's banner | + +## Get Current User Guilds +/users/@me/guilds + +Returns a list of partial [guild](/developers/docs/resources/guild#guild-object) objects the current user is a member of. For OAuth2, requires the `guilds` scope. + + +###### Example Partial Guild + +```json +{ + "id": "80351110224678912", + "name": "1337 Krew", + "icon": "8342729096ea3675442027381ff50dfe", + "banner": "bb42bdc37653b7cf58c4c8cc622e76cb", + "owner": true, + "permissions": "36953089", + "features": ["COMMUNITY", "NEWS", "ANIMATED_ICON", "INVITE_SPLASH", "BANNER", "ROLE_ICONS"], + "approximate_member_count": 3268, + "approximate_presence_count": 784 +} +``` + + +This endpoint returns 200 guilds by default, which is the maximum number of guilds a non-bot user can join. Therefore, pagination is **not needed** for integrations that need to get a list of the users' guilds. + + + +###### Query String Params + +| Field | Type | Description | Required | Default | +|-------------|-------------------------------------------------------------|------------------------------------------------------------|----------|---------| +| before | snowflake | get guilds before this guild ID | false | absent | +| after | snowflake | get guilds after this guild ID | false | absent | +| limit | integer | max number of guilds to return (1-200) | false | 200 | +| with_counts | [boolean](/developers/docs/reference#boolean-query-strings) | include approximate member and presence counts in response | false | false | + +## Get Current User Guild Member +/users/@me/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/member + +Returns a [guild member](/developers/docs/resources/guild#guild-member-object) object for the current user. Requires the `guilds.members.read` OAuth2 scope. + +## Leave Guild +/users/@me/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object) + +Leave a guild. Returns a 204 empty response on success. Fires a [Guild Delete](/developers/docs/events/gateway-events#guild-delete) Gateway event and a [Guild Member Remove](/developers/docs/events/gateway-events#guild-member-remove) Gateway event. + +## Create DM +/users/@me/channels + +Create a new DM channel with a user. Returns a [DM channel](/developers/docs/resources/channel#channel-object) object (if one already exists, it will be returned instead). + + +You should not use this endpoint to DM everyone in a server about something. DMs should generally be initiated by a user action. If you open a significant amount of DMs too quickly, your bot may be rate limited or blocked from opening new ones. + + + +###### JSON Params + +| Field | Type | Description | +|--------------|-----------|-----------------------------------------| +| recipient_id | snowflake | the recipient to open a DM channel with | + +## Create Group DM +/users/@me/channels + +Create a new group DM channel with multiple users. Returns a [DM channel](/developers/docs/resources/channel#channel-object) object. This endpoint was intended to be used with the now-deprecated GameBridge SDK. Fires a [Channel Create](/developers/docs/events/gateway-events#channel-create) Gateway event. + + +This endpoint is limited to 10 active group DMs. + + + +###### JSON Params + +| Field | Type | Description | +|---------------|------------------|------------------------------------------------------------------------| +| access_tokens | array of strings | access tokens of users that have granted your app the `gdm.join` scope | +| nicks | dict | a dictionary of user ids to their respective nicknames | + +## Get Current User Connections +/users/@me/connections + +Returns a list of [connection](/developers/docs/resources/user#connection-object) objects. Requires the `connections` OAuth2 scope. + +## Get Current User Application Role Connection +/users/@me/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/role-connection + +Returns the [application role connection](/developers/docs/resources/user#application-role-connection-object) for the user. Requires an OAuth2 access token with `role_connections.write` scope for the application specified in the path. + +## Update Current User Application Role Connection +/users/@me/applications/[\{application.id\}](/developers/docs/resources/application#application-object)/role-connection + +Updates and returns the [application role connection](/developers/docs/resources/user#application-role-connection-object) for the user. Requires an OAuth2 access token with `role_connections.write` scope for the application specified in the path. + + +###### JSON Params + +| Field | Type | Description | +|--------------------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| platform_name? | string | the vanity name of the platform a bot has connected (max 50 characters) | +| platform_username? | string | the username on the platform a bot has connected (max 100 characters) | +| metadata? | object | object mapping [application role connection metadata](/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object) keys to their `string`-ified value (max 100 characters) for the user on the platform a bot has connected | diff --git a/discord/developers/docs/resources/voice.mdx b/discord/developers/docs/resources/voice.mdx new file mode 100644 index 0000000000..cccc23ce95 --- /dev/null +++ b/discord/developers/docs/resources/voice.mdx @@ -0,0 +1,125 @@ +--- +title: Voice Resource +sidebarTitle: Voice +description: Reference for Discord voice objects and connection endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +### Voice State Object + +Used to represent a user's voice connection status. + + +###### Voice State Structure + +| Field | Type | Description | +|----------------------------|-----------------------------------------------------------------------------|---------------------------------------------------| +| guild_id? | snowflake | the guild id this voice state is for | +| channel_id | ?snowflake | the channel id this user is connected to | +| user_id | snowflake | the user id this voice state is for | +| member? | [guild member](/developers/docs/resources/guild#guild-member-object) object | the guild member this voice state is for | +| session_id | string | the session id for this voice state | +| deaf | boolean | whether this user is deafened by the server | +| mute | boolean | whether this user is muted by the server | +| self_deaf | boolean | whether this user is locally deafened | +| self_mute | boolean | whether this user is locally muted | +| self_stream? | boolean | whether this user is streaming using "Go Live" | +| self_video | boolean | whether this user's camera is enabled | +| suppress | boolean | whether this user's permission to speak is denied | +| request_to_speak_timestamp | ?ISO8601 timestamp | the time at which the user requested to speak | + + +###### Example Voice State + +```json +{ + "channel_id": "157733188964188161", + "user_id": "80351110224678912", + "session_id": "90326bd25d71d39b9ef95b299e3872ff", + "deaf": false, + "mute": false, + "self_deaf": false, + "self_mute": true, + "suppress": false, + "request_to_speak_timestamp": "2021-03-31T18:45:31.297561+00:00" +} +``` + +### Voice Region Object + + +###### Voice Region Structure + +| Field | Type | Description | +|------------|---------|-----------------------------------------------------------------------| +| id | string | unique ID for the region | +| name | string | name of the region | +| optimal | boolean | true for a single server that is closest to the current user's client | +| deprecated | boolean | whether this is a deprecated voice region (avoid switching to these) | +| custom | boolean | whether this is a custom voice region (used for events/etc) | + +## List Voice Regions +/voice/regions + +Returns an array of [voice region](/developers/docs/resources/voice#voice-region-object) objects that can be used when setting a voice or stage channel's [`rtc_region`](/developers/docs/resources/channel#channel-object-channel-structure). + +## Get Current User Voice State +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/voice-states/@me + +Returns the current user's [voice state](/developers/docs/resources/voice#voice-state-object) in the guild. + +## Get User Voice State +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/voice-states/[\{user.id\}](/developers/docs/resources/user#user-object) + +Returns the specified user's [voice state](/developers/docs/resources/voice#voice-state-object) in the guild. + +## Modify Current User Voice State +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/voice-states/@me + +Updates the current user's voice state. Returns `204 No Content` on success. Fires a [Voice State Update](/developers/docs/events/gateway-events#voice-state-update) Gateway event. + + +###### JSON Params + +| Field | Type | Description | +|-----------------------------|--------------------|------------------------------------------------| +| channel_id? | snowflake | the id of the channel the user is currently in | +| suppress? | boolean | toggles the user's suppress state | +| request_to_speak_timestamp? | ?ISO8601 timestamp | sets the user's request to speak | + + +###### Caveats + +There are currently several caveats for this endpoint: + +- `channel_id` must currently point to a stage channel. +- current user must already have joined `channel_id`. +- You must have the `MUTE_MEMBERS` permission to unsuppress yourself. You can always suppress yourself. +- You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. +- You are able to set `request_to_speak_timestamp` to any present or future time. + +## Modify User Voice State +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/voice-states/[\{user.id\}](/developers/docs/resources/user#user-object) + +Updates another user's voice state. Returns `204 No Content` on success. Fires a [Voice State Update](/developers/docs/events/gateway-events#voice-state-update) Gateway event. + + +###### JSON Params + +| Field | Type | Description | +|-------------|-----------|------------------------------------------------| +| channel_id? | snowflake | the id of the channel the user is currently in | +| suppress? | boolean | toggles the user's suppress state | + + +###### Caveats + +There are currently several caveats for this endpoint: + +- `channel_id` must currently point to a stage channel. +- User must already have joined `channel_id`. +- You must have the `MUTE_MEMBERS` permission. (Since suppression is the only thing that is available currently.) +- When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. +- When suppressed, the user will have their `request_to_speak_timestamp` removed. diff --git a/discord/developers/docs/resources/webhook.mdx b/discord/developers/docs/resources/webhook.mdx new file mode 100644 index 0000000000..f88d48567c --- /dev/null +++ b/discord/developers/docs/resources/webhook.mdx @@ -0,0 +1,359 @@ +--- +title: Webhook Resource +sidebarTitle: Webhook +description: Reference for Discord webhook objects and management endpoints. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' + +Webhooks are a low-effort way to post messages to channels in Discord. They do not require a bot user or authentication to use. + +Apps can also subscribe to webhook events (i.e. outgoing webhooks) when events happen *in* Discord, which is detailed in the [Webhook Events](/developers/docs/events/webhook-events) documentation. + +### Webhook Object + +Used to represent a webhook. + + +###### Webhook Structure + +| Field | Type | Description | +|-------------------|-----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | the id of the webhook | +| type | integer | the [type](/developers/docs/resources/webhook#webhook-object-webhook-types) of the webhook | +| guild_id? | ?snowflake | the guild id this webhook is for, if any | +| channel_id | ?snowflake | the channel id this webhook is for, if any | +| user? | [user](/developers/docs/resources/user#user-object) object | the user this webhook was created by (not returned when getting a webhook with its token) | +| name | ?string | the default name of the webhook | +| avatar | ?string | the default user avatar [hash](/developers/docs/reference#image-formatting) of the webhook | +| token? | string | the secure token of the webhook (returned for Incoming Webhooks) | +| application_id | ?snowflake | the bot/OAuth2 application that created this webhook | +| source_guild? * | partial [guild](/developers/docs/resources/guild#guild-object) object | the guild of the channel that this webhook is following (returned for Channel Follower Webhooks) | +| source_channel? * | partial [channel](/developers/docs/resources/channel#channel-object) object | the channel that this webhook is following (returned for Channel Follower Webhooks) | +| url? | string | the url used for executing the webhook (returned by the [webhooks](/developers/docs/topics/oauth2#webhooks) OAuth2 flow) | + +\* These fields will be absent if the webhook creator has since lost access to the guild where the followed channel resides + + +###### Webhook Types + + +These types don't include [webhook events](/developers/docs/events/webhook-events), which are outgoing webhooks sent to your app by Discord. See [Webhook Events](/developers/docs/events/webhook-events) for details. + + +| Value | Name | Description | +|-------|------------------|----------------------------------------------------------------------------------------------------------------| +| 1 | Incoming | Incoming Webhooks can post messages to channels with a generated token | +| 2 | Channel Follower | Channel Follower Webhooks are internal webhooks used with Channel Following to post new messages into channels | +| 3 | Application | Application webhooks are webhooks used with Interactions | + + +###### Example Incoming Webhook + +```json +{ + "name": "test webhook", + "type": 1, + "channel_id": "199737254929760256", + "token": "3d89bb7572e0fb30d8128367b3b1b44fecd1726de135cbe28a41f8b2f777c372ba2939e72279b94526ff5d1bd4358d65cf11", + "avatar": null, + "guild_id": "199737254929760256", + "id": "223704706495545344", + "application_id": null, + "user": { + "username": "test", + "discriminator": "7479", + "id": "190320984123768832", + "avatar": "b004ec1740a63ca06ae2e14c5cee11f3", + "public_flags": 131328 + } +} +``` + + +###### Example Channel Follower Webhook + +```json +{ + "type": 2, + "id": "752831914402115456", + "name": "Guildy name", + "avatar": "bb71f469c158984e265093a81b3397fb", + "channel_id": "561885260615255432", + "guild_id": "56188498421443265", + "application_id": null, + "source_guild": { + "id": "56188498421476534", + "name": "Guildy name", + "icon": "bb71f469c158984e265093a81b3397fb" + }, + "source_channel": { + "id": "5618852344134324", + "name": "announcements" + }, + "user": { + "username": "test", + "discriminator": "7479", + "id": "190320984123768832", + "avatar": "b004ec1740a63ca06ae2e14c5cee11f3", + "public_flags": 131328 + } +} +``` + + +###### Example Application Webhook + +```json +{ + "type": 3, + "id": "658822586720976555", + "name": "Clyde", + "avatar": "689161dc90ac261d00f1608694ac6bfd", + "channel_id": null, + "guild_id": null, + "application_id": "658822586720976555" +} +``` + +## Create Webhook +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/webhooks + +Creates a new webhook and returns a [webhook](/developers/docs/resources/webhook#webhook-object) object on success. Requires the `MANAGE_WEBHOOKS` permission. Fires a [Webhooks Update](/developers/docs/events/gateway-events#webhooks-update) Gateway event. + +An error will be returned if a webhook name (`name`) is not valid. A webhook name is valid if: + +- It does not contain the substrings `clyde` or `discord` (case-insensitive) +- It follows the nickname guidelines in the [Usernames and Nicknames](/developers/docs/resources/user#usernames-and-nicknames) documentation, with an exception that webhook names can be up to 80 characters + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|---------|------------------------------------------------------|---------------------------------------| +| name | string | name of the webhook (1-80 characters) | +| avatar? | ?[image data](/developers/docs/reference#image-data) | image for the default webhook avatar | + +## Get Channel Webhooks +/channels/[\{channel.id\}](/developers/docs/resources/channel#channel-object)/webhooks + +Returns a list of channel [webhook](/developers/docs/resources/webhook#webhook-object) objects. Requires the `MANAGE_WEBHOOKS` permission. + +## Get Guild Webhooks +/guilds/[\{guild.id\}](/developers/docs/resources/guild#guild-object)/webhooks + +Returns a list of guild [webhook](/developers/docs/resources/webhook#webhook-object) objects. Requires the `MANAGE_WEBHOOKS` permission. + +## Get Webhook +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object) + +Returns the new [webhook](/developers/docs/resources/webhook#webhook-object) object for the given id. + +This request requires the `MANAGE_WEBHOOKS` permission unless the application making the request owns the +webhook. [(see: webhook.application_id)](/developers/docs/resources/webhook#webhook-object) + +## Get Webhook with Token +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object) + +Same as above, except this call does not require authentication and returns no user in the webhook object. + +## Modify Webhook +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object) + +Modify a webhook. Requires the `MANAGE_WEBHOOKS` permission. Returns the updated [webhook](/developers/docs/resources/webhook#webhook-object) object on success. Fires a [Webhooks Update](/developers/docs/events/gateway-events#webhooks-update) Gateway event. + + +All parameters to this endpoint are optional + + + +This endpoint supports the `X-Audit-Log-Reason` header. + + + +###### JSON Params + +| Field | Type | Description | +|------------|------------------------------------------------------|----------------------------------------------------| +| name | string | the default name of the webhook | +| avatar | ?[image data](/developers/docs/reference#image-data) | image for the default webhook avatar | +| channel_id | snowflake | the new channel id this webhook should be moved to | + +## Modify Webhook with Token +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object) + +Same as above, except this call does not require authentication, does not accept a `channel_id` parameter in the body, and does not return a user in the webhook object. + +## Delete Webhook +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object) + +Delete a webhook permanently. Requires the `MANAGE_WEBHOOKS` permission. Returns a `204 No Content` response on success. Fires a [Webhooks Update](/developers/docs/events/gateway-events#webhooks-update) Gateway event. + + +This endpoint supports the `X-Audit-Log-Reason` header. + + +## Delete Webhook with Token +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object) + +Same as above, except this call does not require authentication. + +## Execute Webhook +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object) + +Refer to [Uploading Files](/developers/docs/reference#uploading-files) for details on attachments and `multipart/form-data` requests. Returns a message or `204 No Content` depending on the `wait` query parameter. + + +Note that when sending a message, you must provide a value for at **least one of** `content`, `embeds`, `components`, `file`, or `poll`. + + + +If the webhook channel is a forum or media channel, you must provide either `thread_id` in the query string params, or `thread_name` in the JSON/form params. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the channel. + + + +Discord may strip certain characters from message content, like invalid unicode characters or characters which cause unexpected message formatting. If you are passing user-generated strings into message content, consider sanitizing the data to prevent unexpected behavior and using `allowed_mentions` to prevent unexpected mentions. + + + +###### Query String Params + +| Field | Type | Description | Required | +|-----------------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| +| wait | [boolean](/developers/docs/reference#boolean-query-strings) | waits for server confirmation of message send before response, and returns the created message body (defaults to `false`; when `false` a message that is not saved does not return an error) | false | +| thread_id | snowflake | Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived. | false | +| with_components | [boolean](/developers/docs/reference#boolean-query-strings) | whether to respect the `components` field of the request. When enabled, allows application-owned webhooks to use all components and non-owned webhooks to use non-interactive components. (defaults to `false`) | false | + + +###### JSON/Form Params + +| Field | Type | Description | Required | +|-------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------| +| content | string | the message contents (up to 2000 characters) | one of content, file, embeds, poll | +| username | string | override the default username of the webhook | false | +| avatar_url | string | override the default avatar of the webhook | false | +| tts | boolean | true if this is a TTS message | false | +| embeds | array of up to 10 [embed](/developers/docs/resources/message#embed-object) objects | embedded `rich` content | one of content, file, embeds, poll | +| allowed_mentions | [allowed mention object](/developers/docs/resources/message#allowed-mentions-object) | allowed mentions for the message | false | +| components \* | array of [message component](/developers/docs/components/reference#component-object) | the components to include with the message | false | +| files[n] \*\* | file contents | the contents of the file being sent | one of content, file, embeds, poll | +| payload_json \*\* | string | JSON encoded body of non-file params | `multipart/form-data` only | +| attachments \*\* | array of partial [attachment](/developers/docs/resources/message#attachment-object) objects | attachment objects with filename and description | false | +| flags \*\*\* | integer | [message flags](/developers/docs/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS`, `SUPPRESS_NOTIFICATIONS` and `IS_COMPONENTS_V2` can be set) | false | +| thread_name | string | name of thread to create (requires the webhook channel to be a forum or media channel) | false | +| applied_tags | array of snowflakes | array of tag ids to apply to the thread (requires the webhook channel to be a forum or media channel) | false | +| poll | [poll](/developers/docs/resources/poll#poll-create-request-object) request object | A poll! | one of content, file, embeds, poll | + +\* Application-owned webhooks can always send components. Non-application-owned webhooks cannot send interactive components, and the `components` field will be ignored unless they set the `with_components` query param. + +\*\* See [Uploading Files](/developers/docs/reference#uploading-files) for details. + +\*\*\* When the flag `IS_COMPONENTS_V2` is set, the webhook message can only contain `components`. Providing `content`, `embeds`, `files[n]` or `poll` will fail with a 400 BAD REQUEST response. + + +For the webhook embed objects, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images. + + +## Execute Slack-Compatible Webhook +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object)/slack + +Refer to [Slack's documentation](https://api.slack.com/incoming-webhooks) for more information. We do not support Slack's `channel`, `icon_emoji`, `mrkdwn`, or `mrkdwn_in` properties. + + +###### Query String Params + +| Field | Type | Description | Required | +|-----------|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|----------| +| thread_id | snowflake | id of the thread to send the message in | false | +| wait | [boolean](/developers/docs/reference#boolean-query-strings) | waits for server confirmation of message send before response (defaults to `true`; when `false` a message that is not saved does not return an error) | false | + +## Execute GitHub-Compatible Webhook +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object)/github + +[Add a new webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to your GitHub repo (in the repo's settings), and use this endpoint as the "Payload URL." You can choose what events your Discord channel receives by choosing the "Let me select individual events" option and selecting individual events for the new webhook you're configuring. The supported [events](https://docs.github.com/en/webhooks/webhook-events-and-payloads) are `commit_comment`, `create`, `delete`, `fork`, `issue_comment`, `issues`, `member`, `public`, `pull_request`, `pull_request_review`, `pull_request_review_comment`, `push`, `release`, `watch`, `check_run`, `check_suite`, `discussion`, and `discussion_comment`. + + +###### Query String Params + +| Field | Type | Description | Required | +|-----------|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|----------| +| thread_id | snowflake | id of the thread to send the message in | false | +| wait | [boolean](/developers/docs/reference#boolean-query-strings) | waits for server confirmation of message send before response (defaults to `true`; when `false` a message that is not saved does not return an error) | false | + +## Get Webhook Message +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Returns a previously-sent webhook message from the same token. Returns a [message](/developers/docs/resources/message#message-object) object on success. + + +###### Query String Params + +| Field | Type | Description | Required | +|-----------|-----------|------------------------------------|----------| +| thread_id | snowflake | id of the thread the message is in | false | + +## Edit Webhook Message +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Edits a previously-sent webhook message from the same token. Returns a [message](/developers/docs/resources/message#message-object) object on success. + +When the `content` field is edited, the arrays `mentions` and `mention_roles` and the boolean `mention_everyone` in the message object will be reconstructed from scratch based on the new content. When the message flag `IS_COMPONENTS_V2` is set, the reconstructed arrays and boolean are based on the edited content in the `components` array. The `allowed_mentions` field of the edit request controls how this happens. If there is no explicit `allowed_mentions` in the edit request, the content will be parsed with _default_ allowances, that is, without regard to whether or not an `allowed_mentions` was present in the request that originally created the message. + +Refer to [Uploading Files](/developers/docs/reference#uploading-files) for details on attachments and `multipart/form-data` requests. +Any provided files will be **appended** to the message. To remove or replace files you will have to supply the `attachments` field which specifies the files to retain on the message after edit. + + +Starting with API v10, the `attachments` array must contain all attachments that should be present after edit, including **retained and new** attachments provided in the request body. + + + +All parameters to this endpoint are optional and nullable. + + + +###### Query String Params + +| Field | Type | Description | Required | +|-----------------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| +| thread_id | snowflake | id of the thread the message is in | false | +| with_components | [boolean](/developers/docs/reference#boolean-query-strings) | whether to respect the `components` field of the request. When enabled, allows application-owned webhooks to use all components and non-owned webhooks to use non-interactive components. (defaults to `false`) | false | + + +###### JSON/Form Params + +| Field | Type | Description | +|---------------------|---------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| content | string | the message contents (up to 2000 characters) | +| embeds | array of up to 10 [embed](/developers/docs/resources/message#embed-object) objects | embedded `rich` content | +| flags \* | integer | [message flags](/developers/docs/resources/message#message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (`SUPPRESS_EMBEDS` and `IS_COMPONENTS_V2` only) | +| allowed_mentions | [allowed mention object](/developers/docs/resources/message#allowed-mentions-object) | allowed mentions for the message | +| components \*\* | array of [message component](/developers/docs/components/reference#component-object) | the components to include with the message | +| files[n] \*\*\* | file contents | the contents of the file being sent/edited | +| payload_json \*\*\* | string | JSON encoded body of non-file params (multipart/form-data only) | +| attachments \*\*\* | array of partial [attachment](/developers/docs/resources/message#attachment-object) objects | attached files to keep and possible descriptions for new files | +| poll \*\*\*\* | [poll](/developers/docs/resources/poll#poll-create-request-object) request object | A poll! | + +\* The `SUPPRESS_EMBEDS` flag can be both set and unset, while the `IS_COMPONENTS_V2` flag can only be set. When the `IS_COMPONENTS_V2` flag is set, any of the used `content`, `embeds`, `files[n]` or `poll` values in the initial message must be set to `null` first, otherwise it will fail with a 400 BAD REQUEST response. + +\*\* Application-owned webhooks can always send components. Non-application-owned webhooks cannot send interactive components, and the `components` field will be ignored unless they set the `with_components` query param. + +\*\*\* See [Uploading Files](/developers/docs/reference#uploading-files) for details. + +\*\*\*\* Polls can only be added when editing a deferred interaction response. + +## Delete Webhook Message +/webhooks/[\{webhook.id\}](/developers/docs/resources/webhook#webhook-object)/[\{webhook.token\}](/developers/docs/resources/webhook#webhook-object)/messages/[\{message.id\}](/developers/docs/resources/message#message-object) + +Deletes a message that was created by the webhook. Returns a `204 No Content` response on success. + + +###### Query String Params + +| Field | Type | Description | Required | +|-----------|-----------|------------------------------------|----------| +| thread_id | snowflake | id of the thread the message is in | false | diff --git a/discord/developers/docs/rich-presence/best-practices.mdx b/discord/developers/docs/rich-presence/best-practices.mdx new file mode 100644 index 0000000000..782539e3ef --- /dev/null +++ b/discord/developers/docs/rich-presence/best-practices.mdx @@ -0,0 +1,104 @@ +--- +title: Best Practices for Rich Presence +sidebarTitle: Best Practices +description: Best practices and guidelines for implementing effective Rich Presence. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +Rich Presence lets you display actionable data in a Discord user's profile about what they're up to in your game or app. This guide is intended to show some best practices on how to make that data the best it can be. + +If you don't already know about Rich Presence, read [the overview](/developers/docs/rich-presence/overview) first. + + +## How should you think about the data you show? + +The data in your players’ profiles is the first thing that others on Discord will see about your game or app, both those familiar with it and those who have never seen it before. It should answer whether someone can play with their friend right now and show data like: + +- What the player is currently doing +- How much time has elapsed or remains (if applicable) +- Their party state +- Your cool artwork! + +## Tips + +### Keep it Short + +- `details` and `state` should be snippets of data, not sentences. +- Make sure your strings stay on one line—especially on the small profile! + + +###### Examples + +![Example of a good rich presence string that is concise and easy to read compared to a bad string that is too long to fit on one line](/images/rich-presence/short-strings.png) + +### Make it Actionable! + +- Always keep party size data up to date. +- Keep accurate track of party state: In Queue, In Game, In Menus, etc. +- Include game modes, ranked vs. unranked, etc. so others can clearly see. + + +###### Examples + +![Examples of good rich presence strings that show a game mode of "Ranked: Control Point" and that the user is "In Queue (2 of 3)" compared to a bad string that reads "Rank 9999"](/images/rich-presence/actionable.png) + +### Use ALL of the fields (where applicable)! + +- Make use of all the fields that are applicable to you. +- Save space by putting map and character names in the tooltips. +- Try not to repeat information. + + +###### Examples + +![Example of a good rich presence string that takes advantage of storing less important information in tooltips compared to a bad string that is hard to read at a glance](/images/rich-presence/all-fields.png) + +### Have interesting, expressive art! + +- The large image should be consistent for all players in a party. +- The small image is where you can customize on a per-player basis. +- Use high resolution artwork so your art looks great on fancy, high DPI screens. +- We strongly recommend image sizes of 1024x1024 pixels. + + +###### Examples + +![Example of a good rich presence icon that is clear and detailed compared to a bad icon that is too dark to see clearly](/images/rich-presence/good-art.png) + +## Launch Checklist + +Ready to launch a Rich Presence integration for your game? If so, we recommend looking over this checklist one last time to ensure that your integration is as great as it can be! + +#### Profile Strings + +- Have you made use of all available fields where appropriate? +- Do your strings fit on their own lines without line wrapping? + - Did you check on the smaller profile pop out? +- Do they clearly communicate: + - What the player is currently doing? + - If the player is in a group or playing alone? + - If the player is in a state where they can party up? + +#### Artwork + +- Is your artwork high resolution? +- Are your images at least 1024x1024 pixels? +- Is it clean, interesting, and descriptive without being too highly detailed? +- Do you have artwork for every different state? Don't forget your default state/main menu! +- Did you make use of tooltips and the small image where appropriate? + +#### Joining + + +Since all Activities presence data has an **Ask to Join** button, Join Invites are only applicable when building with the [Game SDK](/developers/docs/rich-presence/using-with-the-game-sdk) + + +- Have you successfully implemented join invites for your game if applicable? +- Does the state of the invite properly represent the party/group in-game with regards to: + - Size? + - Open slots? + - Discord _and_ non-Discord users in the party? +- Are you able to post invites to Discord without any additional in-game setup or configuration? +- Are you properly removing data from the presence payload when someone can no longer send invites? + - A Join secret should not be sent if the player can't invite anyone! diff --git a/discord/developers/docs/rich-presence/overview.mdx b/discord/developers/docs/rich-presence/overview.mdx new file mode 100644 index 0000000000..fd562c2406 --- /dev/null +++ b/discord/developers/docs/rich-presence/overview.mdx @@ -0,0 +1,109 @@ +--- +title: Overview of Rich Presence +sidebarTitle: Overview +description: Learn how to integrate Rich Presence to display activity information on user profiles. +--- + +![Examples of Rich Presence data on Discord user profiles](/images/rich-presence/examples.png) + +Rich Presence lets you display actionable data in a Discord user's profile about what they're up to in your game or app. The data you choose to display is up to you—whether it's a user's score, the duration they've been playing your game, what they're listening to on your platform, or something else. + +--- + +## Choosing an SDK + +There are three options when integrating Rich Presence. Depending on what you're building, you'll want to choose the right SDK for your project: + +- The **[Discord Social SDK](/developers/docs/rich-presence/overview#discord-social-sdk)** allows you to build social features into your game or app, including friends lists, game invites, and more. +- The **[Embedded App SDK](/developers/docs/rich-presence/overview#embedded-app-sdk)** should be used if you're building an [Activity](/developers/docs/activities/overview) in Discord. +- The **[Game SDK (legacy)](/developers/docs/rich-presence/overview#game-sdk)** can be used if you're building an off-platform game or app and you want to integrate it into Discord. + +All SDKs use similar underlying primitives (like the [`SET_ACTIVITY` RPC command](/developers/docs/topics/rpc#setactivity)), so a lot is the same between them. But there are a few differences, like feature compatibility, which is covered in the sections below. + + +Rich Presence data appears publicly on your Discord profile, so during development you should use a test account that only belongs to your private development server(s). + + +### Discord Social SDK + +The [Discord Social SDK](/developers/docs/discord-social-sdk/overview) is used for building social features directly into your game. When a user is playing your game, you can show what they're up to in their Discord profile and even prompt their friends to join in with game invites. + +When integrating Rich Presence with an off-platform game, data can be shown about what a user is up to in your game. A "Join" button can also be configured to allow a user's friends to jump into their game and enable sending game invites. + +[Read about using Rich Presence with the Discord Social SDK](/developers/docs/rich-presence/using-with-the-discord-social-sdk) + +### Embedded App SDK + +The [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) is used to build Activities, which are multiplayer games and social experiences hosted in an iframe within Discord. The SDK handles communication between Discord and the Activity and helps integrate platform features (like Rich Presence!). + +After a user joins an Activity, Rich Presence can be used to dynamically show data about what that user is doing or playing, as well as prompt others to join in and play along. + +[Read about using Rich Presence with the Embedded App SDK](/developers/docs/rich-presence/using-with-the-embedded-app-sdk) + +### Game SDK + + +**The Game SDK has been archived.** + +We recommend using the [Discord Social SDK](/developers/docs/discord-social-sdk/overview) for new projects. + +Existing projects using the Game SDK will continue to work, but we encourage you to migrate to the [Discord Social SDK](/developers/docs/discord-social-sdk/overview) for new features and updates. + + +The [Game SDK](/developers/docs/developer-tools/game-sdk) makes it easier to build 3rd party games and integrate them with Discord. While many features of the Game SDK are deprecated, it can still be used for a few use cases (like integrating Rich Presence!). + +When integrating Rich Presence with an off-platform game, data can be shown about what a user is up to in your game. A "Join" button can also be configured to allow a user's friends to jump into their game. + +[Read about using Rich Presence with the Game SDK](/developers/docs/rich-presence/using-with-the-game-sdk) + +## Adding Custom Art Assets + +While integrating Rich Presence, you'll likely want to upload custom art assets for your app. **For all Rich Presence assets, it's highly recommended to make them 1024 x 1024**. + +To add custom assets for Rich Presence, navigate to your [app's settings](https://discord.com/developers/applications) and click **Rich Presence** on the left-hand sidebar. On the **Art Assets** page, there are two different types of assets you can upload. + +### Invite Image + +The Rich Presence invite image appears when [invites](/developers/docs/developer-tools/game-sdk#sendinvite) are sent for a 3rd party game or app using the [Game SDK](/developers/docs/rich-presence/overview#game-sdk). After uploading an invite image for your app, you can see a preview of it to the right (under "IRL Invite Image Example"). + + +The invite image can be ignored if you're building using the [Embedded App SDK](/developers/docs/rich-presence/overview#embedded-app-sdk). Invites sent using the Embedded App SDK's[`openInviteDialog()`](/developers/docs/developer-tools/embedded-app-sdk#openinvitedialog) use the Activity's cover art. + + +![Rich Presence invite image in app settings](/images/rich-presence/invite-image.png) + +### Assets + +Up to 300 custom assets can be added for your app to use when setting Rich Presence for a Discord user. These assets can be anything that help orient others to what a user is doing inside of your Activity or 3rd party game. + +If you need more than 300 custom assets or want to use images stored somewhere else, you can also [specify an external URL](/developers/docs/events/gateway-events#activity-object-activity-asset-image) as long it still has the proper dimensions and size. + + +For tips on choosing assets, take a look at the [Rich Presence best practices guide](/developers/docs/rich-presence/best-practices#have-interesting-expressive-art). + + +When uploading Rich Presence assets, **the asset keys will automatically be changed to lowercase**. You can see this reflected in your app's settings after saving a newly-uploaded asset, and should keep it in mind when referencing any asset keys in your code. + +![Rich Presence assets in app settings](/images/rich-presence-asset-images.webp) + +### Using the Visualizer + + +The Rich Presence visualizer currently uses an outdated Discord UI, but can still be helpful as a quick-and-dirty reference for what your Rich Presence data will look like in Discord. + + +In your app's settings, there's a Rich Presence visualizer to make it easier to see what your uploaded [assets](/developers/docs/rich-presence/overview#assets) will look like in the context of a Discord profile. To access the visualizer, navigate to your [app's settings](https://discord.com/developers/applications) and click **Rich Presence** on the left-hand sidebar. On the **Visualizer** page, you can fill out some custom strings, party information, and select your uploaded assets to see a preview. + +## Start Building + + + + Guide on using Rich Presence in the Embedded App SDK while you're building an Activity + + + Guide on using Rich Presence in the Game SDK + + + Best practices and launch checklist for when you're integrating Rich Presence with your game or app + + \ No newline at end of file diff --git a/discord/developers/docs/rich-presence/using-with-the-discord-social-sdk.mdx b/discord/developers/docs/rich-presence/using-with-the-discord-social-sdk.mdx new file mode 100644 index 0000000000..e4d2ea706e --- /dev/null +++ b/discord/developers/docs/rich-presence/using-with-the-discord-social-sdk.mdx @@ -0,0 +1,29 @@ +--- +title: Using Rich Presence with the Discord Social SDK +sidebarTitle: Using with the Discord Social SDK +description: Learn how to implement Rich Presence features using the Discord Social SDK. +--- + +When developing a game, the [Discord Social SDK](/developers/docs/discord-social-sdk/overview) makes it easy to integrate Rich Presence to display details about what a user is up to inside of your game or social experience. + + +Not sure if you should be building with the Discord Social SDK? Read through [Choosing an SDK](/developers/docs/rich-presence/overview#choosing-an-sdk) to understand your options when integrating Rich Presence with your app. + + +## Understanding Rich Presence Data + +Before we dig in, it's helpful to understand what Rich Presence data you can set when updating a user's presence data. Let's just take a look at what we're working with: + +![Graphical representation of the legend for rich presence details](/images/rich-presence/legend.png) + + +For tips on designing Rich Presence, take a look at the [Rich Presence best practices guide](/developers/docs/rich-presence/best-practices). + + +## Setting Rich Presence Data + +Check out our [Setting Rich Presence](/developers/docs/discord-social-sdk/development-guides/setting-rich-presence) guide and our to learn how to set custom presence data for your players using the Discord Social SDK. + +- [Design Guidelines: Status & Rich Presence](/developers/docs/discord-social-sdk/design-guidelines/status-rich-presence) + +![Rich Presence](/images/social-sdk/design-guidelines/StatusPresence-06.png) diff --git a/discord/developers/docs/rich-presence/using-with-the-embedded-app-sdk.mdx b/discord/developers/docs/rich-presence/using-with-the-embedded-app-sdk.mdx new file mode 100644 index 0000000000..8ac2bf7fd2 --- /dev/null +++ b/discord/developers/docs/rich-presence/using-with-the-embedded-app-sdk.mdx @@ -0,0 +1,158 @@ +--- +title: Using Rich Presence with the Embedded App SDK +sidebarTitle: Using with the Embedded App SDK +--- + +When developing an [Activity](/developers/docs/activities/overview), the [Embedded App SDK](/developers/docs/developer-tools/embedded-app-sdk) makes it easy to integrate Rich Presence to display details about what a user is up to inside of your game or social experience. + +Rich Presence data can be thought of as an extension of your Activity—and leveling it up just a *little* makes it more interesting and relevant to the user playing your Activity (and their friends that might want to jump in and play). This guide provides an overview of the platform and technical knowledge you need to integrate Rich Presence with your existing Activity. + + +Not sure if you should be building with the Embedded App SDK? Read through [Choosing an SDK](/developers/docs/rich-presence/overview#choosing-an-sdk) to understand your options when integrating Rich Presence with your app. + + +The rest of the guide assumes you've already developed an [app](/developers/docs/quick-start/overview-of-apps) that can launch an Activity. If you aren't at that point quite yet, you should follow the guide on [building your first Activity](/developers/docs/activities/building-an-activity) before continuing. + +## Understanding Rich Presence Data + +### Default Rich Presence Data + +By default, when a user is connected to your Activity, the app's icon will appear on their profile. If the user viewing the profile has the ability to join, an "Ask to Join" button will be displayed as well. + +![Example of default Rich Presence data for an Activity](/images/rich-presence/default-activities.png) + +While this is okay, it's pretty limited and doesn't provide much context about what a user is actually *doing* inside of the Activity. In the following sections, we'll take a look at what richer and more actionable presence can look like. + +### Custom Rich Presence Data + +Now let's see what custom presence data can look like when a user joins your Activity. The [types for these fields](/developers/docs/rich-presence/using-with-the-embedded-app-sdk#setactivity-fields) and [examples](/developers/docs/rich-presence/using-with-the-embedded-app-sdk#setactivity-example) are in the sections below, but for now let's just get an idea of what we're working with: + +![Image of where Rich Presence data appears in Discord profiles for Activities](/images/rich-presence/annotated-data-activities.png) + +A few small things to note about the above image: +1. `large_image` and `small_image` are both in the `assets` object, which you can see below in the [table below](/developers/docs/rich-presence/using-with-the-embedded-app-sdk#activity-partial-object). They're labeled with the object's keys to make it more clear how they appear in a Discord profile. +2. You can't set App Name when setting presence—it's always the name configured in your [app's settings](https://discord.com/developers/applications). +3. The state `(1 of max_party)` badge will only render when a party field is provided. Otherwise, state will be shown in a line of text below details. + +## Updating Presence + +When updating Rich Presence data using the Embedded App SDK, the only real command you need to use is **[`setActivity()`](/developers/docs/developer-tools/embedded-app-sdk#setactivity)**. Under the hood, `setActivity()` calls the RPC [`SET_ACTIVITY` command](/developers/docs/topics/rpc#setactivity) with the features and fields available when you're building an Activity. + + +As you start exploring the Rich Presence docs, you'll start seeing the word "activity" a *lot*. The "activities" referenced in docs (like the [RPC ones](/developers/docs/topics/rpc#setactivity)) aren't related to the Activities you're building with the Embedded App SDK. + +When Rich Presence was introduced, the underlying object that contains presence data was called an "activity" (long before the Embedded App SDK), which is what the RPC [`SET_ACTIVITY` command](/developers/docs/topics/rpc#setactivity) is referencing. And that's *also* why the Embedded App SDK's wrapper around the RPC command is called `setActivity()` yet isn't really related to setting the state for the kind of Activity that *you're* building. + +We know, it's confusing ¯\\\_(⊙︿⊙)\_/¯ — the naming was logical at the time because it was really about the user's activity in a 3rd party game or service, but now it sorta feels like activity-ception. Understanding the nuances here aren't super important, and it's why we have guides like this one. But as they say...the more you (at least sort of) know. + + +### rpc.activities.write Scope + +To display custom Rich Presence data for a user, your app will need to be authorized with the [`rpc.activities.write` scope](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) for that user. + +To request the scope, your [`authorize()`](/developers/docs/developer-tools/embedded-app-sdk#authorize) call might look something like this: + + + + +The following example only focuses on calling `authorize()`. Follow the [Building an Activity guide](/developers/docs/activities/building-an-activity) for more details on topics like installing and instantiating the Embedded App SDK. + + +```js +// Authorize with Discord Client +const { code } = await discordSdk.commands.authorize({ + client_id: import.meta.env.VITE_DISCORD_CLIENT_ID, + response_type: "code", + state: "", + prompt: "none", + scope: [ + "identify", + "rpc.activities.write" + ], +}); +``` + + +### setActivity Fields + +When calling [`setActivity()`](/developers/docs/developer-tools/embedded-app-sdk#setactivity), you are expected to pass a partial [activity object](/developers/docs/events/gateway-events#activity-object-activity-structure). + +Below is a table with many of the available fields for the activity partial. Some were left out since they don't have an effect for Activities. + +#### Activity partial object + + +All of the fields on the partial object are optional and nullable + + +| field | type | description | +|------------|-------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| type | integer | [Activity](/developers/docs/events/gateway-events#activity-object-activity-types) type, which determines the header text for the Rich Presence data | +| state | string | User's current party status | +| details | string | What the player is currently doing in your Activity | +| timestamps | [timestamps](/developers/docs/events/gateway-events#activity-object-activity-timestamps) object | Unix timestamps to display start and/or end times | +| assets | [assets](/developers/docs/events/gateway-events#activity-object-activity-assets) object | Images used for the Rich Presence data (and their hover texts) | +| party | [party](/developers/docs/events/gateway-events#activity-object-activity-party) object | Information for the current party of the player | + +### setActivity Example + +Now let's take a look at more of a real example. Take a look at the Rich Presence data below that is for an Activity: + +![Example of a fake game's Rich Presence data](/images/rich-presence/activities-example.png) + +To create this sort of Rich Presence, here is what the `setActivity()` code would look like: + + + + +The following example only focuses on using `setActivity()`. Follow the [Building an Activity guide](/developers/docs/activities/building-an-activity) for more details on topics like instantiating the Embedded App SDK and authenticating users. + +```js +await discordSdk.commands.setActivity({ + activity: { + type: 0, + details: 'Traveling with a group', + state: 'In Mainframe', + assets: { + large_image: 'main-game-image', + large_text: 'in a group', + small_image: 'map-mainframe', + small_text: 'in mainframe' + }, + timestamps: { + start: 1723137832 + }, + party: { + size: [2,4] + } + } +}); +``` + + +## Using External Custom Assets + +Typically when building an Activity, you need to be aware of the proxy and [how to use external resources](/developers/docs/activities/development-guides/networking#using-external-resources). However, luckily for you (and the writer of this guide), image URLs in fields for features like Rich Presence don't need to jump through any extra hoops. + +As mentioned in the [Rich Presence overview](/developers/docs/rich-presence/overview#assets), you have more than 300 custom assets or if you want to use your stored images from somewhere else, you can specify an external URL for `large_image` or `small_image` within the `assets` object. + + + + +The following example only focuses on using `setActivity()`. Follow the [Building an Activity guide](/developers/docs/activities/building-an-activity) for more details on topics like instantiating the Embedded App SDK and authenticating users. + +```js +await discordSdk.commands.setActivity({ + activity: { + type: 2, + state: 'Broken Hearts and Code (club edit)', + details: 'DJ Wump', + assets: { + large_image: 'https://example.com/album-covers/dj-wump/broken-code-and-hearts-club-edit.jpg', + large_text: 'Listening to a track', + } + } +}); +``` + + diff --git a/discord/developers/docs/rich-presence/using-with-the-game-sdk.mdx b/discord/developers/docs/rich-presence/using-with-the-game-sdk.mdx new file mode 100644 index 0000000000..175d94d92c --- /dev/null +++ b/discord/developers/docs/rich-presence/using-with-the-game-sdk.mdx @@ -0,0 +1,236 @@ +--- +title: Using Rich Presence with the Game SDK +sidebarTitle: Using with the Game SDK +tag: "LEGACY" +--- + + +**The Game SDK has been archived.** + +We recommend using the [Discord Social SDK](/developers/docs/discord-social-sdk/overview) for new projects. + +Existing projects using the Game SDK will continue to work, but we encourage you to migrate to the [Discord Social SDK](/developers/docs/discord-social-sdk/overview) for new features and updates. + + +The [Game SDK](/developers/docs/developer-tools/game-sdk) helps you build 3rd party games and integrate them with Discord. One of its specialties is making it easy to bring your game's data to Discord using [Rich Presence](/developers/docs/rich-presence/overview), which this guide will cover. + +Before we dig in, make sure you've gone through the guide on [Getting Started with the Game SDK](/developers/docs/developer-tools/game-sdk#getting-started). This guide expects that you've already [downloaded the SDK](/developers/docs/developer-tools/game-sdk#step-1-get-the-sdk), [configured your app](/developers/docs/developer-tools/game-sdk#step-2-create-your-app), and [gotten up and running with a basic example](/developers/docs/developer-tools/game-sdk#step-3-start-coding). + + +Not sure if you should be building with the Game SDK? Read through [Choosing an SDK](/developers/docs/rich-presence/overview#choosing-an-sdk) to understand your options when integrating Rich Presence. + + +## Understanding Rich Presence Data + +Before we dig in, it's helpful to understand what Rich Presence data you can set when updating a user's presence data. We'll explain the specific fields below, but for now let's just take a look at what we're working with: + +![Graphical representation of the legend for rich presence details](/images/rich-presence/legend.png) + + +| location | field name | notes | +|----------------------------------------|----------------|----------------------------------------------------------------------------| +| First row below title | details | | +| Second row below title | state | If provided with no `partySize` or `partyMax` | +| First badge in last row below title | startTimestamp | Converted to a `mm:ss` format such as `12:01` | +| Second badge in last row below title | state | If provided with a `partySize` and `partyMax` | +| Second badge in last row below title | partySize | In parenthesis next to the `state`, first number in the format `(1 of 4)` | +| Second badge in last row below title | partyMax | In parenthesis next to the `state`, second number in the format `(1 of 4)` | +| Button at the bottom | joinSecret | Button has the text **Ask to Join** | +| Large image to the left of any content | largeImageKey | Four rows high, includes the title but not the bottom buttons | +| Small image to the left of any content | smallImageKey | Small icon inset on the bottom right of the `largeImageKey` | + +Note that this layout may be subject to change without warning. This information is only provided to help those with +impaired eyesight to understand the potential layout of this information in a user interface. + + + +For tips on designing Rich Presence, take a look at the [Rich Presence best practices guide](/developers/docs/rich-presence/best-practices). + + +## Activities Manager + +As you likely learned when setting up your app, the Game SDK has a handful of specialized [manager classes](/developers/docs/developer-tools/game-sdk#managers). The **[`ActivitiesManager`](/developers/docs/developer-tools/game-sdk#activities)** is responsible for integrating Rich Presence and sending invites, so that's where we'll spend our focus in this guide. + +Like other Game SDK managers, the `ActivitiesManager` class has a handful of [functions](/developers/docs/developer-tools/game-sdk#functions) and [events](/developers/docs/developer-tools/game-sdk#functions) that are used when building Rich Presence support. We'll touch on the ones important to presence below. + +### Fetching Activity Manager + +In your code, you'll need to fetch the `ActivityManager` to call its functions and listen to events. In your code, you can call the `GetActivityManager()` function: + +```cs +var activityManager = discord.GetActivityManager(); +``` + +## Updating Presence + +The most important function when integrating Rich Presence using the Game SDK will be [`UpdateActivity()`](/developers/docs/developer-tools/game-sdk#updateactivity). This is how you will send your game data to Discord to update a user's presence data. You should call `UpdateActivity()` any time something important in the presence payload changes. + +### UpdateActivity Fields + +When calling `UpdateActivity()`, you'll be expected to pass the [activity](/developers/docs/developer-tools/game-sdk#activity-struct) fields besides your `ApplicationId` and `Name` (which are both read-only). + +#### Partial Activity Struct + +Below are the fields we'll be paying attention to as we're passing presence data for a user. + + +All fields are optional and nullable + + +| Name | Type | Description | +|------------|-------------------------------------------------------------------------------------------|-----------------------------------------------------------------| +| State | string | the player's current party status | +| Details | string | what the player is currently doing | +| Timestamps | [ActivityTimestamps](/developers/docs/developer-tools/game-sdk#activitytimestamps-struct) | helps create elapsed/remaining timestamps on a player's profile | +| Assets | [ActivityAssets](/developers/docs/developer-tools/game-sdk#activityassets-struct) | assets to display on the player's profile | +| Party | [ActivityParty](/developers/docs/developer-tools/game-sdk#activityparty-struct) | information about the player's party | +| Secrets | [ActivitySecrets](/developers/docs/developer-tools/game-sdk#activitysecrets-struct) | secret passwords for joining the player's game | +| Instance | bool | whether this activity is an instanced context, like a match | + +### UpdateActivity Example + +Now let's take a look at a code example for updating presence data. + + +All of the code samples in this guide are in C#, but there are some similar examples in other languages [on GitHub](https://github.com/msciotti/discord-game-sdk-test-apps). + + +Our code sample in this section is based on the data from the example from before: + +![Example of presence data with buttons](/images/rich-presence/game-sdk-example.png) + +The **Ask to Join** button will be covered more in the following sections, but if you don't want it included, you can remove the `Party` and `Secret` fields. + + + + +The following example only focuses on using `UpdateActivity()`. You can read the [Getting Started](/developers/docs/developer-tools/game-sdk#getting-started) and [Using the SDK](/developers/docs/developer-tools/game-sdk#using-the-sdk) sections for more general information about using the Game SDK. + + +```cs +var activity = new Discord.Activity +{ + State = "In Group", + Details = "In 3v3 Arena | 0-2 | Bo5", + Timestamps = + { + Start = 1723218695, + }, + Assets = + { + LargeImage = "map-woods", // using asset from app's settings + LargeText = "Playing in the Misty Woods", + SmallImage = "https://example.com/battlerite/users/example-user.jpg", // you can also use URLs + SmallText = "Example User", + }, + Party = + { + Id = "party-abc123", + Size = { + CurrentSize = 3, + MaxSize = 3, + }, + }, + Secrets = + { + Join = "foo joinSecret", + }, + Instance = true, + +}; + +activityManager.UpdateActivity(activity, (result) => +{ + if (result == Discord.Result.Ok) + { + Console.WriteLine("Success!"); + } + else + { + Console.WriteLine("Failed"); + } +}); +``` + + +## Asking to Join + +In the example above, there was an **Ask to Join** button that a user could click to ask to join a game match directly from Discord. + +### Adding the Ask to Join Button + +To get the **Ask to Join** button to appear under the presence data for a 3rd party game, you should send along a few fields: `ActivityParty.Id`, `ActivityParty.Size.CurrentSize`, `ActivityParty.Size.MaxSize`, and `ActivitySecrets.Join`. + +To see what you need for each Rich Presence feature, you can view the [Activity Action Field Requirements](/developers/docs/developer-tools/game-sdk#activity-action-field-requirements) table. + +### Handling Ask to Join + +To listen for when someone clicks the button, you'll use the [`OnActivityJoinRequest` event](/developers/docs/developer-tools/game-sdk#onactivityjoinrequest). This will include a [user](/developers/docs/developer-tools/game-sdk#user-struct) for the individual that clicked the button. + +As an example, let's say we updated Player A's presence data, and Player B found the **Ask to Join** button on their profile and proceeded to click it. At that point, your app will receive an `OnActivityJoinRequest`. Your game should surface this to Player A to confirm they wish to play with Player B. + +After you confirm that Player A is game, you will call [`SendRequestReply`](/developers/docs/developer-tools/game-sdk#sendrequestreply) with Player B's `userId` and a `reply` field with an [`ActivityJoinRequestReply` value](/developers/docs/developer-tools/game-sdk#activityjoinrequestreply-enum). + + + +```cs +activityManager.OnActivityJoinRequest += user => +{ + Console.WriteLine("Join request from: {0}", user.Id); + activityManager.SendRequestReply(user.Id, Discord.ActivityJoinRequestReply.Yes, (res) => + { + if (res == Discord.Result.Ok) + { + Console.WriteLine("Responded successfully"); + } + }); +} +``` + + +The **Ask to Join** request persists for 30 seconds after the request is received. Therefore, keep these two points in mind: +- Ensure you call [`RunCallbacks()`](/developers/docs/developer-tools/game-sdk#runcallbacks) as frequently as possible to ensure your game client is up to date with any data from Discord +- If the player is in a state in which they cannot interact with an **Ask to Join** request—like in the middle of a match—you should not send `ActivitySecrets.Join` in the presence payload + +## Secrets + +Security is of the utmost importance to us here at Discord, and we know it is for you, too. That's why we want to make sure that you properly understand `ActivitySecrets.Join` so that your game data is safe and secure over the wire. + +To keep security on the up and up, Discord requires that you properly hash/encode/encrypt/put-a-padlock-on-and-swallow-the-key-but-wait-then-how-would-you-open-it your secrets. + +Secrets are obfuscated data of your choosing. They could be match ids, player ids, lobby ids, etc. You should send us data that someone else's game client would need to join their friend. If you can't or don't want to support those actions, you don't need to send us secrets. + +## FAQ + +Below are answers to some common questions about integrating Rich Presence with your 3rd party game. + +#### Q: I see "Playing MyGame", but no Rich Presence data. + +There's a couple things that could be going on: + +- If you're running two instances of the Discord client, check both! +- Double check that your `Discord_Initialize()` function is correct. + +Throughout development, make sure you have your `errored()` and `disconnected()` callbacks hooked up for debugging. You can open up the console in Discord and look for errors pertaining to `SET_ACTIVITY` for more information as well. + +#### Q: What happens if someone has more than one game running that supports Rich Presence? + +The application that connected _first_ is displayed. + +However, invite functionality across multiple connected applications now works no matter which app is display on a user's profile. For example, if you are hosting a Spotify listening party and playing Game A that allows you to send **Ask to Join** invites, you'll be able to send invites to both simultaneously! + +#### Q: What if someone looking at my profile or an invite doesn't own the game? + +Anyone can see your profile data, whether they own the game or not. They'll only be able to interact with chat invites or profile buttons if they own the game and have launched it at least once. Otherwise, the invite/button tooltip will show "Game Not Detected". + +#### Q: Do join invitations allow players to select the number of open slots? + +Currently, the SDK does not support this. Party slot information is determined by the party data you sent in your presence payload. + +#### Q: Can I send images via the payload rather than uploading them to my app's settings? + +Yes! In addition to uploading an asset and specifying its name, you can also specify an external image URL for us to proxy. For more information, see [Activity Asset Image](/developers/docs/events/gateway-events#activity-object-activity-asset-image). + +#### Q: OK—I've got it working! Now, how do I make my integration look _awesome_? + +I'm happy ~~we preempted your question~~ you asked! Check out our [Rich Presence Best Practices](/developers/docs/rich-presence/best-practices) guide for a rundown on how to make your integration the best that it can be! diff --git a/docs/topics/Certified_Devices.md b/discord/developers/docs/topics/certified-devices.mdx similarity index 67% rename from docs/topics/Certified_Devices.md rename to discord/developers/docs/topics/certified-devices.mdx index 0856325ee2..fb1fcddd79 100644 --- a/docs/topics/Certified_Devices.md +++ b/discord/developers/docs/topics/certified-devices.mdx @@ -1,37 +1,46 @@ -# Certified Devices +--- +title: Certified Devices +description: Learn about Discord's Certified Device program for hardware integration. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' Baked into Discord is the ability for hardware manufacturers to tell us a little more about the certified devices that are plugged into a user's computer. Unfortunately, no, you can't show that a user's PUBG Chicken Dinner was all thanks to the amazing TotallyRealHardware RGB Mouse and Keyboard Set Extraordinaire™, but you _can_ give them an amazing experience using your hardware with Discord! + ## How's it work? I'm glad you asked! -1. [Create an application](#APPLICATIONS) for your hardware vendor—save the Client ID! +1. [Create an application](https://discord.com/developers/applications) for your hardware vendor—save the Client ID! 2. Talk to Discord via one simple HTTP or WebSocket call -3. Send us a [`SET_CERTIFIED_DEVICES`](#DOCS_TOPICS_RPC/setcertifieddevices) WebSocket payload or equivalent HTTP POST whenever the state of the device changes +3. Send us a [`SET_CERTIFIED_DEVICES`](/developers/docs/topics/rpc#setcertifieddevices) WebSocket payload or equivalent HTTP POST whenever the state of the device changes Yup, that's it. You give us the real-time info about any connected devices, and we'll handle the rest to make sure that anyone using your device will have an awesome experience. Your device will also have a `CERTIFIED` badge in Discord's audio settings, and really, who doesn't love badges? -![An example of how a certified device may be shown for an example audio input and output device](certified-device.png) +![An example of how a certified device may be shown for an example audio input and output device](/images/certified-device.png) ## Connecting + ###### Query String Params | Name | Value | Required | -| --------- | -------------------- | --------- | +|-----------|----------------------|-----------| | v | `1` | All | | client_id | your app's client id | All | | encoding | `json` | WebSocket | You can send event updates to the following URIs: + ###### HTTP ``` http://127.0.0.1:PORT/rpc?v=1&client_id=YOUR_CLIENT_ID ``` + ###### WebSocket ``` @@ -48,9 +57,11 @@ Each time you update, send a full array of `devices`, sorted by your preferred p For each device in the `SET_CERTIFIED_DEVICES` payload, there is an `id` field. This `id` should be the Windows device's UUID, retrieved through the native Windows API. You'll get back something that looks like `{0.0.1.00000000}.{6cff2b76-44a8-46b9-b528-262ad8609d9f}`. -> info -> On macOS, the `id` will be the name of the device. + +On macOS, the `id` will be the name of the device. + + ###### Microphone Id Example ```cpp @@ -60,6 +71,7 @@ id = waveInMessage((HWAVEIN)IntToPtr(index), cbEndpointId); ``` + ###### Speaker Id Example ```cpp @@ -71,9 +83,10 @@ id = waveOutMessage((HWAVEIN)IntToPtr(index), ## HTTP Example + ###### HTTP Request Example -``` +```bash curl -X POST -H 'Content-Type: application/json' -d ' { "nonce": "9b4e9711-97f3-4f35-b047-32c82a51978e", @@ -105,6 +118,7 @@ curl -X POST -H 'Content-Type: application/json' -d ' The socket will respond with a `200 OK` status code and the following JSON. + ###### HTTP Response Example ```json @@ -118,6 +132,7 @@ The socket will respond with a `200 OK` status code and the following JSON. ## WebSocket Example + ###### RPC Command Example ```json @@ -148,6 +163,7 @@ The socket will respond with a `200 OK` status code and the following JSON. } ``` + ###### RPC Response Example ```json @@ -161,40 +177,44 @@ The socket will respond with a `200 OK` status code and the following JSON. ## Models + ###### Device Object -| Field | Type | Description | -| ------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------- | -| type | [device type](#DOCS_TOPICS_CERTIFIED_DEVICES/models-device-type) | the type of device | -| id | string | the device's Windows UUID | -| vendor | [vendor](#DOCS_TOPICS_CERTIFIED_DEVICES/models-vendor-object) object | the hardware vendor | -| model | [model](#DOCS_TOPICS_CERTIFIED_DEVICES/models-model-object) object | the model of the product | -| related | array of strings | UUIDs of related devices | -| echo_cancellation?\* | boolean | if the device's native echo cancellation is enabled | -| noise_suppression?\* | boolean | if the device's native noise suppression is enabled | -| automatic_gain_control?\* | boolean | if the device's native automatic gain control is enabled | -| hardware_mute?\* | boolean | if the device is hardware muted | +| Field | Type | Description | +|---------------------------|---------------------------------------------------------------------------------|----------------------------------------------------------| +| type | [device type](/developers/docs/topics/certified-devices#models-device-type) | the type of device | +| id | string | the device's Windows UUID | +| vendor | [vendor](/developers/docs/topics/certified-devices#models-vendor-object) object | the hardware vendor | +| model | [model](/developers/docs/topics/certified-devices#models-model-object) object | the model of the product | +| related | array of strings | UUIDs of related devices | +| echo_cancellation?\* | boolean | if the device's native echo cancellation is enabled | +| noise_suppression?\* | boolean | if the device's native noise suppression is enabled | +| automatic_gain_control?\* | boolean | if the device's native automatic gain control is enabled | +| hardware_mute?\* | boolean | if the device is hardware muted | \*These fields are only applicable for `AUDIO_INPUT` device types + ###### Vendor Object | Field | Type | Description | -| ----- | ------ | ------------------ | +|-------|--------|--------------------| | name | string | name of the vendor | | url | string | url for the vendor | + ###### Model Object | Field | Type | Description | -| ----- | ------ | ----------------- | +|-------|--------|-------------------| | name | string | name of the model | | url | string | url for the model | + ###### Device Type | Type | Value | -| ------------ | ------------- | +|--------------|---------------| | AUDIO_INPUT | "audioinput" | | AUDIO_OUTPUT | "audiooutput" | | VIDEO_INPUT | "videoinput" | diff --git a/docs/topics/OAuth2.md b/discord/developers/docs/topics/oauth2.mdx similarity index 55% rename from docs/topics/OAuth2.md rename to discord/developers/docs/topics/oauth2.mdx index 774a1e642d..7ccadc295c 100644 --- a/docs/topics/OAuth2.md +++ b/discord/developers/docs/topics/oauth2.mdx @@ -1,58 +1,69 @@ -# OAuth2 +--- +title: OAuth2 +description: Learn how to implement OAuth2 authentication flows for Discord apps. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' +import {Route} from '/snippets/route.jsx' OAuth2 enables application developers to build applications that utilize authentication and data from the Discord API. Within Discord, there are multiple types of OAuth2 authentication. We support the authorization code grant, the implicit grant, client credentials, and some modified special-for-Discord flows for Bots and Webhooks. ## Shared Resources -The first step in implementing OAuth2 is [registering a developer application](#APPLICATIONS) and retrieving your client ID and client secret. Most people who will be implementing OAuth2 will want to find and utilize a library in the language of their choice. For those implementing OAuth2 from scratch, please see [RFC 6749](https://tools.ietf.org/html/rfc6749) for details. After you create your application with Discord, make sure that you have your `client_id` and `client_secret` handy. The next step is to figure out which OAuth2 flow is right for your purposes. +The first step in implementing OAuth2 is [registering a developer application](https://discord.com/developers/applications) and retrieving your client ID and client secret. Most people who will be implementing OAuth2 will want to find and utilize a library in the language of their choice. For those implementing OAuth2 from scratch, please see [RFC 6749](https://tools.ietf.org/html/rfc6749) for details. After you create your application with Discord, make sure that you have your `client_id` and `client_secret` handy. The next step is to figure out which OAuth2 flow is right for your purposes. + ###### OAuth2 URLs | URL | Description | -| ------------------------------------------- | ----------------------------------------------------------- | +|---------------------------------------------|-------------------------------------------------------------| | https://discord.com/oauth2/authorize | Base authorization URL | | https://discord.com/api/oauth2/token | Token URL | | https://discord.com/api/oauth2/token/revoke | [Token Revocation](https://tools.ietf.org/html/rfc7009) URL | -> warn -> In accordance with the relevant RFCs, the token and token revocation URLs will **only** accept a content type of `application/x-www-form-urlencoded`. JSON content is not permitted and will return an error. + +In accordance with the relevant RFCs, the token and token revocation URLs will **only** accept a content type of `application/x-www-form-urlencoded`. JSON content is not permitted and will return an error. + + ###### OAuth2 Scopes These are a list of all the OAuth2 scopes that Discord supports. Some scopes require approval from Discord to use. Requesting them from a user without approval from Discord may cause errors or undocumented behavior in the OAuth2 flow. -| Name | Description | -| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| activities.read | allows your app to fetch data from a user's "Now Playing/Recently Played" list - requires Discord approval | -| activities.write | allows your app to update a user's activity - requires Discord approval (NOT REQUIRED FOR [GAMESDK ACTIVITY MANAGER](#DOCS_GAME_SDK_ACTIVITIES/)) | -| applications.builds.read | allows your app to read build data for a user's applications | -| applications.builds.upload | allows your app to upload/update builds for a user's applications - requires Discord approval | -| applications.commands | allows your app to use [commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/) in a guild | -| applications.commands.update | allows your app to update its [commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/) using a Bearer token - [client credentials grant](#DOCS_TOPICS_OAUTH2/client-credentials-grant) only | -| applications.commands.permissions.update | allows your app to update [permissions for its commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/permissions) in a guild a user has permissions to | -| applications.entitlements | allows your app to read entitlements for a user's applications | -| applications.store.update | allows your app to read and update store data (SKUs, store listings, achievements, etc.) for a user's applications | -| bot | for oauth2 bots, this puts the bot in the user's selected guild by default | -| connections | allows [/users/@me/connections](#DOCS_RESOURCES_USER/get-user-connections) to return linked third-party accounts | -| dm_channels.read | allows your app to see information about the user's DMs and group DMs - requires Discord approval | -| email | enables [/users/@me](#DOCS_RESOURCES_USER/get-current-user) to return an `email` | -| gdm.join | allows your app to [join users to a group dm](#DOCS_RESOURCES_CHANNEL/group-dm-add-recipient) | -| guilds | allows [/users/@me/guilds](#DOCS_RESOURCES_USER/get-current-user-guilds) to return basic information about all of a user's guilds | -| guilds.join | allows [/guilds/{guild.id}/members/{user.id}](#DOCS_RESOURCES_GUILD/add-guild-member) to be used for joining users to a guild | -| guilds.members.read | allows [/users/@me/guilds/{guild.id}/member](#DOCS_RESOURCES_USER/get-current-user-guild-member) to return a user's member information in a guild | -| identify | allows [/users/@me](#DOCS_RESOURCES_USER/get-current-user) without `email` | -| messages.read | for local rpc server api access, this allows you to read messages from all client channels (otherwise restricted to channels/guilds your app creates) | -| relationships.read | allows your app to know a user's friends and implicit relationships - requires Discord approval | -| rpc | for local rpc server access, this allows you to control a user's local Discord client - requires Discord approval | -| rpc.activities.write | for local rpc server access, this allows you to update a user's activity - requires Discord approval | -| rpc.notifications.read | for local rpc server access, this allows you to receive notifications pushed out to the user - requires Discord approval | -| rpc.voice.read | for local rpc server access, this allows you to read a user's voice settings and listen for voice events - requires Discord approval | -| rpc.voice.write | for local rpc server access, this allows you to update a user's voice settings - requires Discord approval | -| voice | allows your app to connect to voice on user's behalf and see all the voice members - requires Discord approval | -| webhook.incoming | this generates a webhook that is returned in the oauth token response for authorization code grants | - -> info -> `guilds.join` and `bot` require you to have a bot account linked to your application. Also, in order to add a user to a guild, your bot has to already belong to that guild. +| Name | Description | +|------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| activities.read | allows your app to fetch data from a user's "Now Playing/Recently Played" list — not currently available for apps | +| activities.write | allows your app to update a user's activity - not currently available for apps (NOT REQUIRED FOR [GAMESDK ACTIVITY MANAGER](/developers/docs/developer-tools/game-sdk#activities)) | +| applications.builds.read | allows your app to read build data for a user's applications | +| applications.builds.upload | allows your app to upload/update builds for a user's applications - requires Discord approval | +| applications.commands | allows your app to add [commands](/developers/docs/interactions/application-commands) to a guild - included by default with the `bot` scope | +| applications.commands.update | allows your app to update its [commands](/developers/docs/interactions/application-commands) using a Bearer token - [client credentials grant](/developers/docs/topics/oauth2#client-credentials-grant) only | +| applications.commands.permissions.update | allows your app to update [permissions for its commands](/developers/docs/interactions/application-commands#permissions) in a guild a user has permissions to | +| applications.entitlements | allows your app to read entitlements for a user's applications | +| applications.store.update | allows your app to read and update store data (SKUs, store listings, achievements, etc.) for a user's applications | +| bot | for oauth2 bots, this puts the bot in the user's selected guild by default | +| connections | allows [`/users/@me/connections`](/developers/docs/resources/user#get-current-user-connections) to return linked third-party accounts | +| dm_channels.read | allows your app to see information about the user's DMs and group DMs - requires Discord approval | +| email | enables [`/users/@me`](/developers/docs/resources/user#get-current-user) to return an `email` | +| gdm.join | allows your app to [join users to a group dm](/developers/docs/resources/channel#group-dm-add-recipient) | +| guilds | allows [`/users/@me/guilds`](/developers/docs/resources/user#get-current-user-guilds) to return basic information about all of a user's guilds | +| guilds.join | allows [`/guilds/{guild.id}/members/{user.id}`](/developers/docs/resources/guild#add-guild-member) to be used for joining users to a guild | +| guilds.members.read | allows [`/users/@me/guilds/{guild.id}/member`](/developers/docs/resources/user#get-current-user-guild-member) to return a user's member information in a guild | +| identify | allows [`/users/@me`](/developers/docs/resources/user#get-current-user) without `email` | +| messages.read | for local rpc server api access, this allows you to read messages from all client channels (otherwise restricted to channels/guilds your app creates) | +| relationships.read | allows your app to know a user's friends and implicit relationships - requires Discord approval | +| role_connections.write | allows your app to update a user's connection and metadata for the app | +| rpc | for local rpc server access, this allows you to control a user's local Discord client - requires Discord approval | +| rpc.activities.write | for local rpc server access, this allows you to update a user's activity - requires Discord approval | +| rpc.notifications.read | for local rpc server access, this allows you to receive notifications pushed out to the user - requires Discord approval | +| rpc.voice.read | for local rpc server access, this allows you to read a user's voice settings and listen for voice events - requires Discord approval | +| rpc.voice.write | for local rpc server access, this allows you to update a user's voice settings - requires Discord approval | +| voice | allows your app to connect to voice on user's behalf and see all the voice members - requires Discord approval | +| webhook.incoming | this generates a webhook that is returned in the oauth token response for authorization code grants | + + +In order to add a user to a guild, your bot has to already belong to that guild. `role_connections.write` cannot be used with the [Implicit grant type](/developers/docs/topics/oauth2#implicit-grant). + ## State and Security @@ -66,32 +77,37 @@ While Discord does not require the use of the `state` parameter, we support it a The authorization code grant is what most developers will recognize as "standard OAuth2" and involves retrieving an access code and exchanging it for a user's access token. It allows the authorization server to act as an intermediary between the client and the resource owner, so the resource owner's credentials are never shared directly with the client. +All calls to the OAuth2 endpoints require either HTTP Basic authentication or `client_id` and `client_secret` supplied in the form data body. + + ###### Authorization URL Example ``` -https://discord.com/oauth2/authorize?response_type=code&client_id=157730590492196864&scope=identify%20guilds.join&state=15773059ghq9183habn&redirect_uri=https%3A%2F%2Fnicememe.website&prompt=consent +https://discord.com/oauth2/authorize?response_type=code&client_id=157730590492196864&scope=identify%20guilds.join&state=15773059ghq9183habn&redirect_uri=https%3A%2F%2Fnicememe.website&prompt=consent&integration_type=0 ``` -`client_id` is your application's `client_id`. `scope` is a list of [OAuth2 scopes](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes) separated by url encoded spaces (`%20`). `redirect_uri` is whatever URL you registered when creating your application, url-encoded. `state` is the unique string mentioned in [State and Security](#DOCS_TOPICS_OAUTH2/state-and-security). +`client_id` is your application's `client_id`. `scope` is a list of [OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) separated by url encoded spaces (`%20`). `redirect_uri` is whatever URL you registered when creating your application, url-encoded. `state` is the unique string mentioned in [State and Security](/developers/docs/topics/oauth2#state-and-security). When someone navigates to this URL, they will be prompted to authorize your application for the requested scopes. On acceptance, they will be redirected to your `redirect_uri`, which will contain an additional querystring parameter, `code`. `state` will also be returned if previously sent, and should be validated at this point. `prompt` controls how the authorization flow handles existing authorizations. If a user has previously authorized your application with the requested scopes and prompt is set to `consent`, it will request them to reapprove their authorization. If set to `none`, it will skip the authorization screen and redirect them back to your redirect URI without requesting their authorization. For passthrough scopes, like `bot` and `webhook.incoming`, authorization is always required. +The `integration_type` parameter specifies the [installation context](/developers/docs/resources/application#installation-context) for the authorization. The installation context determines where the application will be installed, and is only relevant when `scope` contains `applications.commands`. When set to 0 (GUILD_INSTALL) the application will be authorized for installation to a server, and when set to 1 (USER_INSTALL) the application will be authorized for installation to a user. The application must be configured in the Developer Portal to support the provided `integration_type`. + + ###### Redirect URL Example ``` https://nicememe.website/?code=NhhvTDYsFcdgNLnnLijcl7Ku7bEEeee&state=15773059ghq9183habn ``` -`code` is now exchanged for the user's access token by making a `POST` request to the [token URL](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-urls) with the following parameters: +`code` is now exchanged for the user's access token by making a `POST` request to the [token URL](/developers/docs/topics/oauth2#shared-resources-oauth2-urls) with the following parameters: -- `client_id` - your application's client id -- `client_secret` - your application's client secret - `grant_type` - must be set to `authorization_code` - `code` - the code from the querystring - `redirect_uri` - the `redirect_uri` associated with this authorization, usually from your authorization URL + ###### Access Token Exchange Example ```python @@ -104,8 +120,6 @@ REDIRECT_URI = 'https://nicememe.website' def exchange_code(code): data = { - 'client_id': CLIENT_ID, - 'client_secret': CLIENT_SECRET, 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': REDIRECT_URI @@ -113,13 +127,14 @@ def exchange_code(code): headers = { 'Content-Type': 'application/x-www-form-urlencoded' } - r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers) + r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) r.raise_for_status() return r.json() ``` -You can also pass your `client_id` and `client_secret` as basic authentication with `client_id` as the username and `client_secret` as the password. In response, you will receive: +In response, you will receive: + ###### Access Token Response ```json @@ -132,13 +147,12 @@ You can also pass your `client_id` and `client_secret` as basic authentication w } ``` -Having the user's access token allows your application to make certain requests to the API on their behalf, restricted to whatever scopes were requested. `expires_in` is how long, in seconds, until the returned access token expires, allowing you to anticipate the expiration and refresh the token. To refresh, make another `POST` request to the [token URL](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-urls) with the following parameters: +Having the user's access token allows your application to make certain requests to the API on their behalf, restricted to whatever scopes were requested. `expires_in` is how long, in seconds, until the returned access token expires, allowing you to anticipate the expiration and refresh the token. To refresh, make another `POST` request to the [token URL](/developers/docs/topics/oauth2#shared-resources-oauth2-urls) with the following parameters: -- `client_id` - your application's client id -- `client_secret` - your application's client secret - `grant_type` - must be set to `refresh_token` - `refresh_token` - the user's refresh token + ###### Refresh Token Exchange Example ```python @@ -147,37 +161,68 @@ import requests API_ENDPOINT = 'https://discord.com/api/v10' CLIENT_ID = '332269999912132097' CLIENT_SECRET = '937it3ow87i4ery69876wqire' -REDIRECT_URI = 'https://nicememe.website' def refresh_token(refresh_token): data = { - 'client_id': CLIENT_ID, - 'client_secret': CLIENT_SECRET, 'grant_type': 'refresh_token', 'refresh_token': refresh_token } headers = { 'Content-Type': 'application/x-www-form-urlencoded' } - r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers) + r = requests.post('%s/oauth2/token' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) r.raise_for_status() return r.json() ``` -Boom; fresh [access token response](#DOCS_TOPICS_OAUTH2/authorization-code-grant-access-token-response)! +Boom; fresh [access token response](/developers/docs/topics/oauth2#authorization-code-grant-access-token-response)! + + +###### Token Revocation Example + +To disable an access or refresh token, you can revoke it by making a `POST` request to the [token revocation URL](/developers/docs/topics/oauth2#shared-resources-oauth2-urls) with the following parameters: + +- `token` - the access token or refresh token to revoke +- `token_type_hint` *(optional)* - the `token` parameter's type—either `access_token` or `refresh_token` + + +When you revoke a token, any active access or refresh tokens associated with that authorization will be revoked, regardless of the `token` and `token_type_hint` values you pass in. + + +```python +import requests + +API_ENDPOINT = 'https://discord.com/api/v10' +CLIENT_ID = '332269999912132097' +CLIENT_SECRET = '937it3ow87i4ery69876wqire' + +def revoke_access_token(access_token): + data = { + 'token': access_token, + 'token_type_hint': 'access_token' + } + headers = { + 'Content-Type': 'application/x-www-form-urlencoded' + } + requests.post('%s/oauth2/token/revoke' % API_ENDPOINT, data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET)) +``` + +Boom; the tokens are safely revoked. ## Implicit Grant The implicit OAuth2 grant is a simplified flow optimized for in-browser clients. Instead of issuing the client an authorization code to be exchanged for an access token, the client is directly issued an access token. The URL is formatted as follows: + ###### Authorization URL Example ``` https://discord.com/oauth2/authorize?response_type=token&client_id=290926444748734499&state=15773059ghq9183habn&scope=identify ``` -On redirect, your redirect URI will contain additional **URI fragments**: `access_token`, `token_type`, `expires_in`, `scope`, and [`state`](#DOCS_TOPICS_OAUTH2/state-and-security)(if specified). **These are not querystring parameters.** Be mindful of the "#" character: +On redirect, your redirect URI will contain additional **URI fragments**: `access_token`, `token_type`, `expires_in`, `scope`, and [`state`](/developers/docs/topics/oauth2#state-and-security)(if specified). **These are not querystring parameters.** Be mindful of the "#" character: + ###### Redirect URL Example ``` @@ -188,13 +233,15 @@ There are tradeoffs in using the implicit grant flow. It is both quicker and eas ## Client Credentials Grant -The client credential flow is a quick and easy way for bot developers to get their own bearer tokens for testing purposes. By making a `POST` request to the [token URL](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-urls) with a grant type of `client_credentials`, using Basic authentication with your client id as the username and your client secret as the password, you will be returned an access token for the bot owner. Therefore, always be super-extra-very-we-are-not-kidding-like-really-be-secure-make-sure-your-info-is-not-in-your-source-code careful with your `client_id` and `client_secret`. We don't take kindly to imposters around these parts. +The client credential flow is a quick and easy way for bot developers to get their own bearer tokens for testing purposes. By making a `POST` request to the [token URL](/developers/docs/topics/oauth2#shared-resources-oauth2-urls) with a grant type of `client_credentials`, using Basic authentication with your client id as the username and your client secret as the password, you will be returned an access token for the bot owner. Therefore, always be super-extra-very-we-are-not-kidding-like-really-be-secure-make-sure-your-info-is-not-in-your-source-code careful with your `client_id` and `client_secret`. We don't take kindly to imposters around these parts. -You can specify scopes with the `scope` parameter, which is a list of [OAuth2 scopes](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes) separated by spaces: +You can specify scopes with the `scope` parameter, which is a list of [OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) separated by spaces: -> info -> Team applications are limited to the `identify` and `applications.commands.update` scope, because teams are not bound to a specific user. + +Team applications are limited to the `identify` and `applications.commands.update` scope, because teams are not bound to a specific user. + + ###### Client Credentials Token Request Example ```python @@ -219,6 +266,7 @@ def get_token(): In return, you will receive an access token (without a refresh token): + ###### Client Credentials Access Token Response ```json @@ -230,44 +278,46 @@ In return, you will receive an access token (without a refresh token): } ``` -## Bots +## Bot Users + +Discord's API provides bot users, which are a separate type of user dedicated to automation. Bot users are automatically added to all apps, and are authenticated using the bot token found in your [app's settings](https://discord.com/developers/applications). Unlike the normal OAuth2 flow, bot users have full access to most API routes without using bearer tokens, and can connect to the [Real Time Gateway](/developers/docs/events/gateway). -So, what are bot accounts? ### Bot vs User Accounts -Discord's API provides a separate type of user account dedicated to automation, called a bot account. Bot accounts can be created through the [applications page](#APPLICATIONS), and are authenticated using a token (rather than a username and password). Unlike the normal OAuth2 flow, bot accounts have full access to most API routes without using bearer tokens, and can connect to the [Real Time Gateway](#DOCS_TOPICS_GATEWAY). Automating normal user accounts (generally called "self-bots") outside of the OAuth2/bot API is forbidden, and can result in account termination if found. + +Developers must abide by the [terms of service](https://support-dev.discord.com/hc/articles/8562894815383-Discord-Developer-Terms-of-Service), which includes refraining from automating standard user accounts (generally called "self-bots") outside of the OAuth2/bot API. + -Bot accounts have a few differences in comparison to normal user accounts, namely: +Bot users have a few differences compared to standard Discord users: 1. Bots are added to guilds through the OAuth2 API, and cannot accept normal invites. -2. Bots cannot have friends, nor be added to or join Group DMs. -3. Verified bots do not have a maximum number of Guilds. -4. Bots have an entirely separate set of [Rate Limits](#DOCS_TOPICS_RATE_LIMITS/rate-limits). +2. Bots cannot have friends or be added to or join Group DMs. +3. [Verified bots](https://support-dev.discord.com/hc/en-us/articles/23926564536471-How-Do-I-Get-My-App-Verified) do not have a maximum number of guilds. +4. Bots have an entirely separate set of [rate limits](/developers/docs/topics/rate-limits). ### Bot Authorization Flow Bot authorization is a special server-less and callback-less OAuth2 flow that makes it easy for users to add bots to guilds. The URL you create looks similar to what we use for full stack implementation: + ###### Bot Auth Parameters - -| name | description | -| -------------------- | --------------------------------------------------------------------- | -| client_id | your app's client id | -| scope | needs to include `bot` for the bot flow | -| permissions | the [permissions](#DOCS_TOPICS_PERMISSIONS/) you're requesting | -| guild_id | pre-fills the dropdown picker with a guild for the user | -| disable_guild_select | `true` or `false`—disallows the user from changing the guild dropdown | - +| name | description | +|----------------------|--------------------------------------------------------------------------| +| client_id | your app's client id | +| scope | needs to include `bot` for the bot flow | +| permissions | the [permissions](/developers/docs/topics/permissions) you're requesting | +| guild_id | pre-fills the dropdown picker with a guild for the user | +| disable_guild_select | `true` or `false`—disallows the user from changing the guild dropdown | + + ###### URL Example ``` https://discord.com/oauth2/authorize?client_id=157730590492196864&scope=bot&permissions=1 ``` -In the case of bots, the `scope` parameter should be set to `bot`. There's also a new parameter, `permissions`, which is an integer corresponding to the [permission calculations](#DOCS_TOPICS_PERMISSIONS/permissions-bitwise-permission-flags) for the bot. You'll also notice the absence of `response_type` and `redirect_uri`. Bot authorization does not require these parameters because there is no need to retrieve the user's access token. - -Additionally, if your bot provides [Application Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS), you can add `applications.commands` to the URL's scopes, so that commands will be available in the guild. +In the case of bots, the `scope` parameter should be set to `bot`. There's also a new parameter, `permissions`, which is an integer corresponding to the [permission calculations](/developers/docs/topics/permissions#permissions-bitwise-permission-flags) for the bot. You'll also notice the absence of `response_type` and `redirect_uri`. Bot authorization does not require these parameters because there is no need to retrieve the user's access token. When the user navigates to this page, they'll be prompted to add the bot to a guild in which they have proper permissions. On acceptance, the bot will be added. Super easy! @@ -278,10 +328,11 @@ If your bot is super specific to your private clubhouse, or you just don't like ### Advanced Bot Authorization -Devs can extend the bot authorization functionality. You can request additional scopes outside of `bot` and `applications.commands`, which will prompt a continuation into a complete [authorization code grant flow](#DOCS_TOPICS_OAUTH2/authorization-code-grant) and add the ability to request the user's access token. If you request any scopes outside of `bot` and `applications.commands`, `response_type` is again mandatory; we will also automatically redirect the user to the first URI in your application's registered list unless `redirect_uri` is specified. +Devs can extend the bot authorization functionality. You can request additional scopes outside of `bot` and `applications.commands`, which will prompt a continuation into a complete [authorization code grant flow](/developers/docs/topics/oauth2#authorization-code-grant) and add the ability to request the user's access token. If you request any scopes outside of `bot` and `applications.commands`, `response_type` is again mandatory; we will also automatically redirect the user to the first URI in your application's registered list unless `redirect_uri` is specified. -When receiving the access code on redirect, there will be additional querystring parameters of `guild_id` and `permissions`. The `guild_id` parameter should only be used as a hint as to the relationship between your bot and a guild. To be sure of the relationship between your bot and the guild, consider requiring the Oauth2 code grant in your bot's settings. Enabling it requires anyone adding your bot to a server to go through a full OAuth2 [authorization code grant flow](#DOCS_TOPICS_OAUTH2/authorization-code-grant). When you retrieve the user's access token, you'll also receive information about the guild to which your bot was added: +When receiving the access code on redirect, there will be additional querystring parameters of `guild_id` and `permissions`. The `guild_id` parameter should only be used as a hint as to the relationship between your bot and a guild. To be sure of the relationship between your bot and the guild, consider requiring the Oauth2 code grant in your bot's settings. Enabling it requires anyone adding your bot to a server to go through a full OAuth2 [authorization code grant flow](/developers/docs/topics/oauth2#authorization-code-grant). When you retrieve the user's access token, you'll also receive information about the guild to which your bot was added: + ###### Extended Bot Authorization Access Token Example ```json @@ -321,6 +372,7 @@ When receiving the access code on redirect, there will be additional querystring "icon": null, "description": null, "public_updates_channel_id": null, + "safety_alerts_channel_id": null, "rules_channel_id": null, "max_members": 100000, "vanity_url_code": null, @@ -342,20 +394,22 @@ When receiving the access code on redirect, there will be additional querystring ### Two-Factor Authentication Requirement -For bots with [elevated permissions](#DOCS_TOPICS_PERMISSIONS/permissions-bitwise-permission-flags) (permissions with a `*` next to them), we enforce two-factor authentication on the owner's account when added to guilds that have server-wide 2FA enabled. +For bots with [elevated permissions](/developers/docs/topics/permissions#permissions-bitwise-permission-flags) (permissions with a `*` next to them), we enforce two-factor authentication on the owner's account when added to guilds that have server-wide 2FA enabled. ## Webhooks -Discord's webhook flow is a specialized version of an [authorization code](#DOCS_TOPICS_OAUTH2/authorization-code-grant) implementation. In this case, the `scope` querystring parameter needs to be set to `webhook.incoming`: +Discord's webhook flow is a specialized version of an [authorization code](/developers/docs/topics/oauth2#authorization-code-grant) implementation. In this case, the `scope` querystring parameter needs to be set to `webhook.incoming`: + ###### URL Example ``` https://discord.com/oauth2/authorize?response_type=code&client_id=157730590492196864&scope=webhook.incoming&state=15773059ghq9183habn&redirect_uri=https%3A%2F%2Fnicememe.website ``` -When the user navigates to this URL, they will be prompted to select a channel in which to allow the webhook. When the webhook is [executed](#DOCS_RESOURCES_WEBHOOK/execute-webhook), it will post its message into this channel. On acceptance, the user will be redirected to your `redirect_uri`. The URL will contain the `code` querystring parameter which should be [exchanged for an access token](#DOCS_TOPICS_OAUTH2/authorization-code-grant-access-token-exchange-example). In return, you will receive a slightly modified token response: +When the user navigates to this URL, they will be prompted to select a channel in which to allow the webhook. When the webhook is [executed](/developers/docs/resources/webhook#execute-webhook), it will post its message into this channel. On acceptance, the user will be redirected to your `redirect_uri`. The URL will contain the `code` querystring parameter which should be [exchanged for an access token](/developers/docs/topics/oauth2#authorization-code-grant-access-token-exchange-example). In return, you will receive a slightly modified token response: + ###### Webhook Token Response Example ```json @@ -379,27 +433,30 @@ When the user navigates to this URL, they will be prompted to select a channel i } ``` -From this object, you should store the `webhook.token` and `webhook.id`. See the [execute webhook](#DOCS_RESOURCES_WEBHOOK/execute-webhook) documentation for how to send messages with the webhook. +From this object, you should store the `webhook.token` and `webhook.id`. See the [execute webhook](/developers/docs/resources/webhook#execute-webhook) documentation for how to send messages with the webhook. -Any user that wishes to add your webhook to their channel will need to go through the full OAuth2 flow. A new webhook is created each time, so you will need to save the token and id. If you wish to send a message to all your webhooks, you'll need to iterate over each stored id:token combination and make `POST` requests to each one. Be mindful of our [Rate Limits](#DOCS_TOPICS_RATE_LIMITS/rate-limits)! +Any user that wishes to add your webhook to their channel will need to go through the full OAuth2 flow. A new webhook is created each time, so you will need to save the token and id. If you wish to send a message to all your webhooks, you'll need to iterate over each stored id:token combination and make `POST` requests to each one. Be mindful of our [Rate Limits](/developers/docs/topics/rate-limits)! -## Get Current Bot Application Information % GET /oauth2/applications/@me +## Get Current Bot Application Information +/oauth2/applications/@me -Returns the bot's [application](#DOCS_RESOURCES_APPLICATION/application-object) object. +Returns the bot's [application](/developers/docs/resources/application#application-object) object. -## Get Current Authorization Information % GET /oauth2/@me +## Get Current Authorization Information +/oauth2/@me Returns info about the current authorization. Requires authentication with a bearer token. + ###### Response Structure - -| Field | Type | Description | -| ----------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | -| application | partial [application](#DOCS_RESOURCES_APPLICATION/application-object) object | the current application | -| scopes | array of strings | the scopes the user has authorized the application for | -| expires | ISO8601 timestamp | when the access token expires | -| user? | [user](#DOCS_RESOURCES_USER/user-object) object | the user who has authorized, if the user has authorized with the `identify` scope | - +| Field | Type | Description | +|-------------|-----------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------| +| application | partial [application](/developers/docs/resources/application#application-object) object | the current application | +| scopes | array of strings | the scopes the user has authorized the application for | +| expires | ISO8601 timestamp | when the access token expires | +| user? | [user](/developers/docs/resources/user#user-object) object | the user who has authorized, if the user has authorized with the `identify` scope | + + ###### Example Authorization Information ```json @@ -421,9 +478,10 @@ Returns info about the current authorization. Requires authentication with a bea "expires": "2021-01-23T02:33:17.017000+00:00", "user": { "id": "268473310986240001", - "username": "Discord", + "username": "discord", "avatar": "f749bb0cbeeb26ef21eca719337d20f1", - "discriminator": "0001", + "discriminator": "0", + "global_name": "Discord", "public_flags": 131072 } } diff --git a/docs/topics/Opcodes_and_Status_Codes.md b/discord/developers/docs/topics/opcodes-and-status-codes.mdx similarity index 58% rename from docs/topics/Opcodes_and_Status_Codes.md rename to discord/developers/docs/topics/opcodes-and-status-codes.mdx index ff8043465c..84dfebb9af 100644 --- a/docs/topics/Opcodes_and_Status_Codes.md +++ b/discord/developers/docs/topics/opcodes-and-status-codes.mdx @@ -1,292 +1,366 @@ -# Opcodes and Status Codes +--- +title: Opcodes and Status Codes +description: Reference for Discord Gateway opcodes, voice opcodes, and HTTP response codes. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' ## Gateway All gateway events in Discord are tagged with an opcode that denotes the payload type. Your connection to our gateway may also sometimes close. When it does, you will receive a close code that tells you what happened. + ###### Gateway Opcodes -| Code | Name | Client Action | Description | -|------|-----------------------|---------------|-----------------------------------------------------------------------------------------| -| 0 | Dispatch | Receive | An event was dispatched. | -| 1 | Heartbeat | Send/Receive | Fired periodically by the client to keep the connection alive. | -| 2 | Identify | Send | Starts a new session during the initial handshake. | -| 3 | Presence Update | Send | Update the client's presence. | -| 4 | Voice State Update | Send | Used to join/leave or move between voice channels. | -| 6 | Resume | Send | Resume a previous session that was disconnected. | -| 7 | Reconnect | Receive | You should attempt to reconnect and resume immediately. | -| 8 | Request Guild Members | Send | Request information about offline guild members in a large guild. | -| 9 | Invalid Session | Receive | The session has been invalidated. You should reconnect and identify/resume accordingly. | -| 10 | Hello | Receive | Sent immediately after connecting, contains the `heartbeat_interval` to use. | -| 11 | Heartbeat ACK | Receive | Sent in response to receiving a heartbeat to acknowledge that it has been received. | +| Code | Name | Client Action | Description | +|------|---------------------------|---------------|-----------------------------------------------------------------------------------------| +| 0 | Dispatch | Receive | An event was dispatched. | +| 1 | Heartbeat | Send/Receive | Fired periodically by the client to keep the connection alive. | +| 2 | Identify | Send | Starts a new session during the initial handshake. | +| 3 | Presence Update | Send | Update the client's presence. | +| 4 | Voice State Update | Send | Used to join/leave or move between voice channels. | +| 6 | Resume | Send | Resume a previous session that was disconnected. | +| 7 | Reconnect | Receive | You should attempt to reconnect and resume immediately. | +| 8 | Request Guild Members | Send | Request information about offline guild members in a large guild. | +| 9 | Invalid Session | Receive | The session has been invalidated. You should reconnect and identify/resume accordingly. | +| 10 | Hello | Receive | Sent immediately after connecting, contains the `heartbeat_interval` to use. | +| 11 | Heartbeat ACK | Receive | Sent in response to receiving a heartbeat to acknowledge that it has been received. | +| 31 | Request Soundboard Sounds | Send | Request information about soundboard sounds in a set of guilds. | + ###### Gateway Close Event Codes In order to prevent broken reconnect loops, you should consider some close codes as a signal to stop reconnecting. This can be because your token expired, or your identification is invalid. This table explains what the application defined close codes for the gateway are, and which close codes you should not attempt to reconnect. -| Code | Description | Explanation | Reconnect | -|------|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------| -| 4000 | Unknown error | We're not sure what went wrong. Try reconnecting? | true | -| 4001 | Unknown opcode | You sent an invalid [Gateway opcode](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-opcodes) or an invalid payload for an opcode. Don't do that! | true | -| 4002 | Decode error | You sent an invalid [payload](#DOCS_TOPICS_GATEWAY/sending-events) to Discord. Don't do that! | true | -| 4003 | Not authenticated | You sent us a payload prior to [identifying](#DOCS_TOPICS_GATEWAY/identifying). | true | -| 4004 | Authentication failed | The account token sent with your [identify payload](#DOCS_TOPICS_GATEWAY_EVENTS/identify) is incorrect. | false | -| 4005 | Already authenticated | You sent more than one identify payload. Don't do that! | true | -| 4007 | Invalid `seq` | The sequence sent when [resuming](#DOCS_TOPICS_GATEWAY_EVENTS/resume) the session was invalid. Reconnect and start a new session. | true | -| 4008 | Rate limited | Woah nelly! You're sending payloads to us too quickly. Slow it down! You will be disconnected on receiving this. | true | -| 4009 | Session timed out | Your session timed out. Reconnect and start a new one. | true | -| 4010 | Invalid shard | You sent us an invalid [shard when identifying](#DOCS_TOPICS_GATEWAY/sharding). | false | -| 4011 | Sharding required | The session would have handled too many guilds - you are required to [shard](#DOCS_TOPICS_GATEWAY/sharding) your connection in order to connect. | false | -| 4012 | Invalid API version | You sent an invalid version for the gateway. | false | -| 4013 | Invalid intent(s) | You sent an invalid intent for a [Gateway Intent](#DOCS_TOPICS_GATEWAY/gateway-intents). You may have incorrectly calculated the bitwise value. | false | -| 4014 | Disallowed intent(s) | You sent a disallowed intent for a [Gateway Intent](#DOCS_TOPICS_GATEWAY/gateway-intents). You may have tried to specify an intent that you [have not enabled or are not approved for](#DOCS_TOPICS_GATEWAY/privileged-intents). | false | +| Code | Description | Explanation | Reconnect | +|------|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------| +| 4000 | Unknown error | We're not sure what went wrong. Try reconnecting? | true | +| 4001 | Unknown opcode | You sent an invalid [Gateway opcode](/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes) or an invalid payload for an opcode. Don't do that! | true | +| 4002 | Decode error | You sent an invalid [payload](/developers/docs/events/gateway#sending-events) to Discord. Don't do that! | true | +| 4003 | Not authenticated | You sent us a payload prior to [identifying](/developers/docs/events/gateway#identifying), or this session has been invalidated. | true | +| 4004 | Authentication failed | The account token sent with your [identify payload](/developers/docs/events/gateway-events#identify) is incorrect. | false | +| 4005 | Already authenticated | You sent more than one identify payload. Don't do that! | true | +| 4007 | Invalid `seq` | The sequence sent when [resuming](/developers/docs/events/gateway-events#resume) the session was invalid. Reconnect and start a new session. | true | +| 4008 | Rate limited | Woah nelly! You're sending payloads to us too quickly. Slow it down! You will be disconnected on receiving this. | true | +| 4009 | Session timed out | Your session timed out. Reconnect and start a new one. | true | +| 4010 | Invalid shard | You sent us an invalid [shard when identifying](/developers/docs/events/gateway#sharding). | false | +| 4011 | Sharding required | The session would have handled too many guilds - you are required to [shard](/developers/docs/events/gateway#sharding) your connection in order to connect. | false | +| 4012 | Invalid API version | You sent an invalid version for the gateway. | false | +| 4013 | Invalid intent(s) | You sent an invalid intent for a [Gateway Intent](/developers/docs/events/gateway#gateway-intents). You may have incorrectly calculated the bitwise value. | false | +| 4014 | Disallowed intent(s) | You sent a disallowed intent for a [Gateway Intent](/developers/docs/events/gateway#gateway-intents). You may have tried to specify an intent that you [have not enabled or are not approved for](/developers/docs/events/gateway#privileged-intents). | false | ## Voice Our voice gateways have their own set of opcodes and close codes. + ###### Voice Opcodes -| Code | Name | Sent By | Description | -|------|---------------------|-------------------|----------------------------------------------------------| -| 0 | Identify | client | Begin a voice websocket connection. | -| 1 | Select Protocol | client | Select the voice protocol. | -| 2 | Ready | server | Complete the websocket handshake. | -| 3 | Heartbeat | client | Keep the websocket connection alive. | -| 4 | Session Description | server | Describe the session. | -| 5 | Speaking | client and server | Indicate which users are speaking. | -| 6 | Heartbeat ACK | server | Sent to acknowledge a received client heartbeat. | -| 7 | Resume | client | Resume a connection. | -| 8 | Hello | server | Time to wait between sending heartbeats in milliseconds. | -| 9 | Resumed | server | Acknowledge a successful session resume. | -| 13 | Client Disconnect | server | A client has disconnected from the voice channel | +| Code | Name | Sent By | Description | Binary | +|------|-------------------------------------|-------------------|----------------------------------------------------------|--------| +| 0 | Identify | client | Begin a voice websocket connection. | | +| 1 | Select Protocol | client | Select the voice protocol. | | +| 2 | Ready | server | Complete the websocket handshake. | | +| 3 | Heartbeat | client | Keep the websocket connection alive. | | +| 4 | Session Description | server | Describe the session. | | +| 5 | Speaking | client and server | Indicate which users are speaking. | | +| 6 | Heartbeat ACK | server | Sent to acknowledge a received client heartbeat. | | +| 7 | Resume | client | Resume a connection. | | +| 8 | Hello | server | Time to wait between sending heartbeats in milliseconds. | | +| 9 | Resumed | server | Acknowledge a successful session resume. | | +| 11 | Clients Connect | server | One or more clients have connected to the voice channel | | +| 13 | Client Disconnect | server | A client has disconnected from the voice channel | | +| 21 | DAVE Prepare Transition | server | A downgrade from the DAVE protocol is upcoming | | +| 22 | DAVE Execute Transition | server | Execute a previously announced protocol transition | | +| 23 | DAVE Transition Ready | client | Acknowledge readiness previously announced transition | | +| 24 | DAVE Prepare Epoch | server | A DAVE protocol version or group change is upcoming | | +| 25 | DAVE MLS External Sender | server | Credential and public key for MLS external sender | X | +| 26 | DAVE MLS Key Package | client | MLS Key Package for pending group member | X | +| 27 | DAVE MLS Proposals | server | MLS Proposals to be appended or revoked | X | +| 28 | DAVE MLS Commit Welcome | client | MLS Commit with optional MLS Welcome messages | X | +| 29 | DAVE MLS Announce Commit Transition | server | MLS Commit to be processed for upcoming transition | X | +| 30 | DAVE MLS Welcome | server | MLS Welcome to group for upcoming transition | X | +| 31 | DAVE MLS Invalid Commit Welcome | client | Flag invalid commit or welcome, request re-add | | + ###### Voice Close Event Codes -| Code | Description | Explanation | -|------|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| -| 4001 | Unknown opcode | You sent an invalid [opcode](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice-voice-opcodes). | -| 4002 | Failed to decode payload | You sent an invalid payload in your [identifying](#DOCS_TOPICS_GATEWAY_EVENTS/identify) to the Gateway. | -| 4003 | Not authenticated | You sent a payload before [identifying](#DOCS_TOPICS_GATEWAY_EVENTS/identify) with the Gateway. | -| 4004 | Authentication failed | The token you sent in your [identify](#DOCS_TOPICS_GATEWAY_EVENTS/identify) payload is incorrect. | -| 4005 | Already authenticated | You sent more than one [identify](#DOCS_TOPICS_GATEWAY_EVENTS/identify) payload. Stahp. | -| 4006 | Session no longer valid | Your session is no longer valid. | -| 4009 | Session timeout | Your session has timed out. | -| 4011 | Server not found | We can't find the server you're trying to connect to. | -| 4012 | Unknown protocol | We didn't recognize the [protocol](#DOCS_TOPICS_VOICE_CONNECTIONS/establishing-a-voice-udp-connection-example-select-protocol-payload) you sent. | -| 4014 | Disconnected | Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect. | -| 4015 | Voice server crashed | The server crashed. Our bad! Try [resuming](#DOCS_TOPICS_VOICE_CONNECTIONS/resuming-voice-connection). | -| 4016 | Unknown encryption mode | We didn't recognize your [encryption](#DOCS_TOPICS_VOICE_CONNECTIONS/encrypting-and-sending-voice). | +| Code | Description | Explanation | +|------|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 4001 | Unknown opcode | You sent an invalid [opcode](/developers/docs/topics/opcodes-and-status-codes#voice-voice-opcodes). | +| 4002 | Failed to decode payload | You sent an invalid payload in your [identifying](/developers/docs/events/gateway-events#identify) to the Gateway. | +| 4003 | Not authenticated | You sent a payload before [identifying](/developers/docs/events/gateway-events#identify) with the Gateway. | +| 4004 | Authentication failed | The token you sent in your [identify](/developers/docs/events/gateway-events#identify) payload is incorrect. | +| 4005 | Already authenticated | You sent more than one [identify](/developers/docs/events/gateway-events#identify) payload. Stahp. | +| 4006 | Session no longer valid | Your session is no longer valid. | +| 4009 | Session timeout | Your session has timed out. | +| 4011 | Server not found | We can't find the server you're trying to connect to. | +| 4012 | Unknown protocol | We didn't recognize the [protocol](/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-example-select-protocol-payload) you sent. | +| 4014 | Disconnected | Disconnect individual client (you were kicked, the main gateway session was dropped, etc.). Should not reconnect. | +| 4015 | Voice server crashed | The server crashed. Our bad! Try [resuming](/developers/docs/topics/voice-connections#resuming-voice-connection). | +| 4016 | Unknown encryption mode | We didn't recognize your [encryption](/developers/docs/topics/voice-connections#transport-encryption-and-sending-voice). | +| 4020 | Bad request | You sent a malformed request | +| 4021 | Disconnected: Rate Limited | Disconnect due to rate limit exceeded. Should not reconnect. | +| 4022 | Disconnected: Call Terminated | Disconnect all clients due to call terminated (channel deleted, voice server changed, etc.). Should not reconnect. | + ## HTTP Our API will return semantically valid HTTP response codes based on the success of your request. The following table can be used as a reference for response codes it will return. + ###### HTTP Response Codes -| Code | Meaning | -|---------------------------|----------------------------------------------------------------------------------| -| 200 (OK) | The request completed successfully. | -| 201 (CREATED) | The entity was created successfully. | -| 204 (NO CONTENT) | The request completed successfully but returned no content. | -| 304 (NOT MODIFIED) | The entity was not modified (no action was taken). | -| 400 (BAD REQUEST) | The request was improperly formatted, or the server couldn't understand it. | -| 401 (UNAUTHORIZED) | The `Authorization` header was missing or invalid. | -| 403 (FORBIDDEN) | The `Authorization` token you passed did not have permission to the resource. | -| 404 (NOT FOUND) | The resource at the location specified doesn't exist. | -| 405 (METHOD NOT ALLOWED) | The HTTP method used is not valid for the location specified. | -| 429 (TOO MANY REQUESTS) | You are being rate limited, see [Rate Limits](#DOCS_TOPICS_RATE_LIMITS). | -| 502 (GATEWAY UNAVAILABLE) | There was not a gateway available to process your request. Wait a bit and retry. | -| 5xx (SERVER ERROR) | The server had an error processing your request (these are rare). | +| Code | Meaning | +|---------------------------|-------------------------------------------------------------------------------------| +| 200 (OK) | The request completed successfully. | +| 201 (CREATED) | The entity was created successfully. | +| 204 (NO CONTENT) | The request completed successfully but returned no content. | +| 304 (NOT MODIFIED) | The entity was not modified (no action was taken). | +| 400 (BAD REQUEST) | The request was improperly formatted, or the server couldn't understand it. | +| 401 (UNAUTHORIZED) | The `Authorization` header was missing or invalid. | +| 403 (FORBIDDEN) | The `Authorization` token you passed did not have permission to the resource. | +| 404 (NOT FOUND) | The resource at the location specified doesn't exist. | +| 405 (METHOD NOT ALLOWED) | The HTTP method used is not valid for the location specified. | +| 429 (TOO MANY REQUESTS) | You are being rate limited, see [Rate Limits](/developers/docs/topics/rate-limits). | +| 502 (GATEWAY UNAVAILABLE) | There was not a gateway available to process your request. Wait a bit and retry. | +| 5xx (SERVER ERROR) | The server had an error processing your request (these are rare). | ## JSON -Along with the HTTP error code, our API can also return more detailed error codes through a `code` key in the JSON error response. The response will also contain a `message` key containing a more friendly error string. Some of these errors may include additional details in the form of [Error Messages](#DOCS_REFERENCE/error-messages) provided by an `errors` object. +Along with the HTTP error code, our API can also return more detailed error codes through a `code` key in the JSON error response. The response will also contain a `message` key containing a more friendly error string. Some of these errors may include additional details in the form of [Error Messages](/developers/docs/reference#error-messages) provided by an `errors` object. + ###### JSON Error Codes -| Code | Meaning | -|--------|-------------------------------------------------------------------------------------------------------------------------------| -| 0 | General error (such as a malformed request body, amongst other things) | -| 10001 | Unknown account | -| 10002 | Unknown application | -| 10003 | Unknown channel | -| 10004 | Unknown guild | -| 10005 | Unknown integration | -| 10006 | Unknown invite | -| 10007 | Unknown member | -| 10008 | Unknown message | -| 10009 | Unknown permission overwrite | -| 10010 | Unknown provider | -| 10011 | Unknown role | -| 10012 | Unknown token | -| 10013 | Unknown user | -| 10014 | Unknown emoji | -| 10015 | Unknown webhook | -| 10016 | Unknown webhook service | -| 10020 | Unknown session | -| 10026 | Unknown ban | -| 10027 | Unknown SKU | -| 10028 | Unknown Store Listing | -| 10029 | Unknown entitlement | -| 10030 | Unknown build | -| 10031 | Unknown lobby | -| 10032 | Unknown branch | -| 10033 | Unknown store directory layout | -| 10036 | Unknown redistributable | -| 10038 | Unknown gift code | -| 10049 | Unknown stream | -| 10050 | Unknown premium server subscribe cooldown | -| 10057 | Unknown guild template | -| 10059 | Unknown discoverable server category | -| 10060 | Unknown sticker | -| 10062 | Unknown interaction | -| 10063 | Unknown application command | -| 10065 | Unknown voice state | -| 10066 | Unknown application command permissions | -| 10067 | Unknown Stage Instance | -| 10068 | Unknown Guild Member Verification Form | -| 10069 | Unknown Guild Welcome Screen | -| 10070 | Unknown Guild Scheduled Event | -| 10071 | Unknown Guild Scheduled Event User | -| 10087 | Unknown Tag | -| 20001 | Bots cannot use this endpoint | -| 20002 | Only bots can use this endpoint | -| 20009 | Explicit content cannot be sent to the desired recipient(s) | -| 20012 | You are not authorized to perform this action on this application | -| 20016 | This action cannot be performed due to slowmode rate limit | -| 20018 | Only the owner of this account can perform this action | -| 20022 | This message cannot be edited due to announcement rate limits | -| 20024 | Under minimum age | -| 20028 | The channel you are writing has hit the write rate limit | -| 20029 | The write action you are performing on the server has hit the write rate limit | -| 20031 | Your Stage topic, server name, server description, or channel names contain words that are not allowed | -| 20035 | Guild premium subscription level too low | -| 30001 | Maximum number of guilds reached (100) | -| 30002 | Maximum number of friends reached (1000) | -| 30003 | Maximum number of pins reached for the channel (50) | -| 30004 | Maximum number of recipients reached (10) | -| 30005 | Maximum number of guild roles reached (250) | -| 30007 | Maximum number of webhooks reached (10) | -| 30008 | Maximum number of emojis reached | -| 30010 | Maximum number of reactions reached (20) | -| 30013 | Maximum number of guild channels reached (500) | -| 30015 | Maximum number of attachments in a message reached (10) | -| 30016 | Maximum number of invites reached (1000) | -| 30018 | Maximum number of animated emojis reached | -| 30019 | Maximum number of server members reached | -| 30030 | Maximum number of server categories has been reached (5) | -| 30031 | Guild already has a template | -| 30032 | Maximum number of application commands reached | -| 30033 | Max number of thread participants has been reached (1000) | -| 30034 | Max number of daily application command creates has been reached (200) | -| 30035 | Maximum number of bans for non-guild members have been exceeded | -| 30037 | Maximum number of bans fetches has been reached | -| 30038 | Maximum number of uncompleted guild scheduled events reached (100) | -| 30039 | Maximum number of stickers reached | -| 30040 | Maximum number of prune requests has been reached. Try again later | -| 30042 | Maximum number of guild widget settings updates has been reached. Try again later | -| 30046 | Maximum number of edits to messages older than 1 hour reached. Try again later | -| 30047 | Maximum number of pinned threads in a forum channel has been reached | -| 30048 | Maximum number of tags in a forum channel has been reached | -| 30052 | Bitrate is too high for channel of this type | -| 40001 | Unauthorized. Provide a valid token and try again | -| 40002 | You need to verify your account in order to perform this action | -| 40003 | You are opening direct messages too fast | -| 40004 | Send messages has been temporarily disabled | -| 40005 | Request entity too large. Try sending something smaller in size | -| 40006 | This feature has been temporarily disabled server-side | -| 40007 | The user is banned from this guild | -| 40012 | Connection has been revoked | -| 40032 | Target user is not connected to voice | -| 40033 | This message has already been crossposted | -| 40041 | An application command with that name already exists | -| 40043 | Application interaction failed to send | -| 40058 | Cannot send a message in a forum channel | -| 40060 | Interaction has already been acknowledged | -| 40061 | Tag names must be unique | -| 40066 | There are no tags available that can be set by non-moderators | -| 40067 | A tag is required to create a forum post in this channel | -| 50001 | Missing access | -| 50002 | Invalid account type | -| 50003 | Cannot execute action on a DM channel | -| 50004 | Guild widget disabled | -| 50005 | Cannot edit a message authored by another user | -| 50006 | Cannot send an empty message | -| 50007 | Cannot send messages to this user | -| 50008 | Cannot send messages in a non-text channel | -| 50009 | Channel verification level is too high for you to gain access | -| 50010 | OAuth2 application does not have a bot | -| 50011 | OAuth2 application limit reached | -| 50012 | Invalid OAuth2 state | -| 50013 | You lack permissions to perform that action | -| 50014 | Invalid authentication token provided | -| 50015 | Note was too long | -| 50016 | Provided too few or too many messages to delete. Must provide at least 2 and fewer than 100 messages to delete | -| 50017 | Invalid MFA Level | -| 50019 | A message can only be pinned to the channel it was sent in | -| 50020 | Invite code was either invalid or taken | -| 50021 | Cannot execute action on a system message | -| 50024 | Cannot execute action on this channel type | -| 50025 | Invalid OAuth2 access token provided | -| 50026 | Missing required OAuth2 scope | -| 50027 | Invalid webhook token provided | -| 50028 | Invalid role | -| 50033 | Invalid Recipient(s) | -| 50034 | A message provided was too old to bulk delete | -| 50035 | Invalid form body (returned for both `application/json` and `multipart/form-data` bodies), or invalid `Content-Type` provided | -| 50036 | An invite was accepted to a guild the application's bot is not in | -| 50041 | Invalid API version provided | -| 50045 | File uploaded exceeds the maximum size | -| 50046 | Invalid file uploaded | -| 50054 | Cannot self-redeem this gift | -| 50055 | Invalid Guild | -| 50068 | Invalid message type | -| 50070 | Payment source required to redeem gift | -| 50074 | Cannot delete a channel required for Community guilds | -| 50080 | Cannot edit stickers within a message | -| 50081 | Invalid sticker sent | -| 50083 | Tried to perform an operation on an archived thread, such as editing a message or adding a user to the thread | -| 50084 | Invalid thread notification settings | -| 50085 | `before` value is earlier than the thread creation date | -| 50086 | Community server channels must be text channels | -| 50095 | This server is not available in your location | -| 50097 | This server needs monetization enabled in order to perform this action | -| 50101 | This server needs more boosts to perform this action | -| 50109 | The request body contains invalid JSON. | -| 50132 | Ownership cannot be transferred to a bot user | -| 50138 | Failed to resize asset below the maximum size: 262144 | -| 50146 | Uploaded file not found. | -| 50600 | You do not have permission to send this sticker. | -| 60003 | Two factor is required for this operation | -| 80004 | No users with DiscordTag exist | -| 90001 | Reaction was blocked | -| 110001 | Application not yet available. Try again later | -| 130000 | API resource is currently overloaded. Try again a little later | -| 150006 | The Stage is already open | -| 160002 | Cannot reply without permission to read message history | -| 160004 | A thread has already been created for this message | -| 160005 | Thread is locked | -| 160006 | Maximum number of active threads reached | -| 160007 | Maximum number of active announcement threads reached | -| 170001 | Invalid JSON for uploaded Lottie file | -| 170002 | Uploaded Lotties cannot contain rasterized images such as PNG or JPEG | -| 170003 | Sticker maximum framerate exceeded | -| 170004 | Sticker frame count exceeds maximum of 1000 frames | -| 170005 | Lottie animation maximum dimensions exceeded | -| 170006 | Sticker frame rate is either too small or too large | -| 170007 | Sticker animation duration exceeds maximum of 5 seconds | -| 180000 | Cannot update a finished event | -| 180002 | Failed to create stage needed for stage event | -| 200000 | Message was blocked by automatic moderation | -| 200001 | Title was blocked by automatic moderation | -| 220001 | Webhooks posted to forum channels must have a thread_name or thread_id | -| 220002 | Webhooks posted to forum channels cannot have both a thread_name and thread_id | -| 220003 | Webhooks can only create threads in forum channels | -| 220004 | Webhook services cannot be used in forum channels | -| 240000 | Message blocked by harmful links filter | +| Code | Meaning | +|--------|-----------------------------------------------------------------------------------------------------------------------------------------| +| 0 | General error (such as a malformed request body, amongst other things) | +| 10001 | Unknown account | +| 10002 | Unknown application | +| 10003 | Unknown channel | +| 10004 | Unknown guild | +| 10005 | Unknown integration | +| 10006 | Unknown invite | +| 10007 | Unknown member | +| 10008 | Unknown message | +| 10009 | Unknown permission overwrite | +| 10010 | Unknown provider | +| 10011 | Unknown role | +| 10012 | Unknown token | +| 10013 | Unknown user | +| 10014 | Unknown emoji | +| 10015 | Unknown webhook | +| 10016 | Unknown webhook service | +| 10020 | Unknown session | +| 10021 | Unknown Asset | +| 10026 | Unknown ban | +| 10027 | Unknown SKU | +| 10028 | Unknown Store Listing | +| 10029 | Unknown entitlement | +| 10030 | Unknown build | +| 10031 | Unknown lobby | +| 10032 | Unknown branch | +| 10033 | Unknown store directory layout | +| 10036 | Unknown redistributable | +| 10038 | Unknown gift code | +| 10049 | Unknown stream | +| 10050 | Unknown premium server subscribe cooldown | +| 10057 | Unknown guild template | +| 10059 | Unknown discoverable server category | +| 10060 | Unknown sticker | +| 10061 | Unknown sticker pack | +| 10062 | Unknown interaction | +| 10063 | Unknown application command | +| 10065 | Unknown voice state | +| 10066 | Unknown application command permissions | +| 10067 | Unknown Stage Instance | +| 10068 | Unknown Guild Member Verification Form | +| 10069 | Unknown Guild Welcome Screen | +| 10070 | Unknown Guild Scheduled Event | +| 10071 | Unknown Guild Scheduled Event User | +| 10087 | Unknown Tag | +| 10097 | Unknown sound | +| 20001 | Bots cannot use this endpoint | +| 20002 | Only bots can use this endpoint | +| 20009 | Explicit content cannot be sent to the desired recipient(s) | +| 20012 | You are not authorized to perform this action on this application | +| 20016 | This action cannot be performed due to slowmode rate limit | +| 20018 | Only the owner of this account can perform this action | +| 20022 | This message cannot be edited due to announcement rate limits | +| 20024 | Under minimum age | +| 20028 | The channel you are writing has hit the write rate limit | +| 20029 | The write action you are performing on the server has hit the write rate limit | +| 20031 | Your Stage topic, server name, server description, or channel names contain words that are not allowed | +| 20035 | Guild premium subscription level too low | +| 30001 | Maximum number of guilds reached (100) | +| 30002 | Maximum number of friends reached (1000) | +| 30003 | Maximum number of pins reached for the channel (50) | +| 30004 | Maximum number of recipients reached (10) | +| 30005 | Maximum number of guild roles reached (250) | +| 30007 | Maximum number of webhooks reached (15) | +| 30008 | Maximum number of emojis reached | +| 30010 | Maximum number of reactions reached (20) | +| 30011 | Maximum number of group DMs reached (10) | +| 30013 | Maximum number of guild channels reached (500) | +| 30015 | Maximum number of attachments in a message reached (10) | +| 30016 | Maximum number of invites reached (1000) | +| 30018 | Maximum number of animated emojis reached | +| 30019 | Maximum number of server members reached | +| 30030 | Maximum number of server categories has been reached (5) | +| 30031 | Guild already has a template | +| 30032 | Maximum number of application commands reached | +| 30033 | Maximum number of thread participants has been reached (1000) | +| 30034 | Maximum number of daily application command creates has been reached (200) | +| 30035 | Maximum number of bans for non-guild members have been exceeded | +| 30037 | Maximum number of bans fetches has been reached | +| 30038 | Maximum number of uncompleted guild scheduled events reached (100) | +| 30039 | Maximum number of stickers reached | +| 30040 | Maximum number of prune requests has been reached. Try again later | +| 30042 | Maximum number of guild widget settings updates has been reached. Try again later | +| 30045 | Maximum number of soundboard sounds reached | +| 30046 | Maximum number of edits to messages older than 1 hour reached. Try again later | +| 30047 | Maximum number of pinned threads in a forum channel has been reached | +| 30048 | Maximum number of tags in a forum channel has been reached | +| 30052 | Bitrate is too high for channel of this type | +| 30056 | Maximum number of premium emojis reached (25) | +| 30058 | Maximum number of webhooks per guild reached (1000) | +| 30060 | Maximum number of channel permission overwrites reached (1000) | +| 30061 | The channels for this guild are too large | +| 40001 | Unauthorized. Provide a valid token and try again | +| 40002 | You need to verify your account in order to perform this action | +| 40003 | You are opening direct messages too fast | +| 40004 | Send messages has been temporarily disabled | +| 40005 | Request entity too large. Try sending something smaller in size | +| 40006 | This feature has been temporarily disabled server-side | +| 40007 | The user is banned from this guild | +| 40012 | Connection has been revoked | +| 40018 | Only consumable SKUs can be consumed | +| 40019 | You can only delete sandbox entitlements. | +| 40032 | Target user is not connected to voice | +| 40033 | This message has already been crossposted | +| 40041 | An application command with that name already exists | +| 40043 | Application interaction failed to send | +| 40058 | Cannot send a message in a forum channel | +| 40060 | Interaction has already been acknowledged | +| 40061 | Tag names must be unique | +| 40062 | Service resource is being rate limited | +| 40066 | There are no tags available that can be set by non-moderators | +| 40067 | A tag is required to create a forum post in this channel | +| 40074 | An entitlement has already been granted for this resource | +| 40094 | This interaction has hit the maximum number of follow up messages | +| 40333 | Cloudflare is blocking your request. This can often be resolved by setting a proper [User Agent](/developers/docs/reference#user-agent) | +| 50001 | Missing access | +| 50002 | Invalid account type | +| 50003 | Cannot execute action on a DM channel | +| 50004 | Guild widget disabled | +| 50005 | Cannot edit a message authored by another user | +| 50006 | Cannot send an empty message | +| 50007 | Cannot send messages to this user | +| 50008 | Cannot send messages in a non-text channel | +| 50009 | Channel verification level is too high for you to gain access | +| 50010 | OAuth2 application does not have a bot | +| 50011 | OAuth2 application limit reached | +| 50012 | Invalid OAuth2 state | +| 50013 | You lack permissions to perform that action | +| 50014 | Invalid authentication token provided | +| 50015 | Note was too long | +| 50016 | Provided too few or too many messages to delete. Must provide at least 2 and fewer than 100 messages to delete | +| 50017 | Invalid MFA Level | +| 50019 | A message can only be pinned to the channel it was sent in | +| 50020 | Invite code was either invalid or taken | +| 50021 | Cannot execute action on a system message | +| 50024 | Cannot execute action on this channel type | +| 50025 | Invalid OAuth2 access token provided | +| 50026 | Missing required OAuth2 scope | +| 50027 | Invalid webhook token provided | +| 50028 | Invalid role | +| 50033 | Invalid Recipient(s) | +| 50034 | A message provided was too old to bulk delete | +| 50035 | Invalid form body (returned for both `application/json` and `multipart/form-data` bodies), or invalid `Content-Type` provided | +| 50036 | An invite was accepted to a guild the application's bot is not in | +| 50039 | Invalid Activity Action | +| 50041 | Invalid API version provided | +| 50045 | File uploaded exceeds the maximum size | +| 50046 | Invalid file uploaded | +| 50054 | Cannot self-redeem this gift | +| 50055 | Invalid Guild | +| 50057 | Invalid SKU | +| 50067 | Invalid request origin | +| 50068 | Invalid message type | +| 50070 | Payment source required to redeem gift | +| 50073 | Cannot modify a system webhook | +| 50074 | Cannot delete a channel required for Community guilds | +| 50080 | Cannot edit stickers within a message | +| 50081 | Invalid sticker sent | +| 50083 | Tried to perform an operation on an archived thread, such as editing a message or adding a user to the thread | +| 50084 | Invalid thread notification settings | +| 50085 | `before` value is earlier than the thread creation date | +| 50086 | Community server channels must be text channels | +| 50091 | The entity type of the event is different from the entity you are trying to start the event for | +| 50095 | This server is not available in your location | +| 50097 | This server needs monetization enabled in order to perform this action | +| 50101 | This server needs more boosts to perform this action | +| 50109 | The request body contains invalid JSON. | +| 50110 | The provided file is invalid. | +| 50123 | The provided file type is invalid. | +| 50124 | The provided file duration exceeds maximum of 5.2 seconds. | +| 50131 | Owner cannot be pending member | +| 50132 | Ownership cannot be transferred to a bot user | +| 50138 | Failed to resize asset below the maximum size: 262144 | +| 50144 | Cannot mix subscription and non subscription roles for an emoji | +| 50145 | Cannot convert between premium emoji and normal emoji | +| 50146 | Uploaded file not found. | +| 50151 | The specified emoji is invalid | +| 50159 | Voice messages do not support additional content. | +| 50160 | Voice messages must have a single audio attachment. | +| 50161 | Voice messages must have supporting metadata. | +| 50162 | Voice messages cannot be edited. | +| 50163 | Cannot delete guild subscription integration | +| 50173 | You cannot send voice messages in this channel. | +| 50178 | The user account must first be verified | +| 50192 | The provided file does not have a valid duration. | +| 50600 | You do not have permission to send this sticker. | +| 60003 | Two factor is required for this operation | +| 80004 | No users with DiscordTag exist | +| 90001 | Reaction was blocked | +| 90002 | User cannot use burst reactions | +| 110001 | Application not yet available. Try again later | +| 130000 | API resource is currently overloaded. Try again a little later | +| 150006 | The Stage is already open | +| 160002 | Cannot reply without permission to read message history | +| 160004 | A thread has already been created for this message | +| 160005 | Thread is locked | +| 160006 | Maximum number of active threads reached | +| 160007 | Maximum number of active announcement threads reached | +| 170001 | Invalid JSON for uploaded Lottie file | +| 170002 | Uploaded Lotties cannot contain rasterized images such as PNG or JPEG | +| 170003 | Sticker maximum framerate exceeded | +| 170004 | Sticker frame count exceeds maximum of 1000 frames | +| 170005 | Lottie animation maximum dimensions exceeded | +| 170006 | Sticker frame rate is either too small or too large | +| 170007 | Sticker animation duration exceeds maximum of 5 seconds | +| 180000 | Cannot update a finished event | +| 180002 | Failed to create stage needed for stage event | +| 200000 | Message was blocked by automatic moderation | +| 200001 | Title was blocked by automatic moderation | +| 220001 | Webhooks posted to forum channels must have a thread_name or thread_id | +| 220002 | Webhooks posted to forum channels cannot have both a thread_name and thread_id | +| 220003 | Webhooks can only create threads in forum channels | +| 220004 | Webhook services cannot be used in forum channels | +| 240000 | Message blocked by harmful links filter | +| 350000 | Cannot enable onboarding, requirements are not met | +| 350001 | Cannot update onboarding while below requirements | +| 500000 | Failed to ban users | +| 520000 | Poll voting blocked | +| 520001 | Poll expired | +| 520002 | Invalid channel type for poll creation | +| 520003 | Cannot edit a poll message | +| 520004 | Cannot use an emoji included with the poll | +| 520006 | Cannot expire a non-poll message | + ###### Example JSON Error Response ```json @@ -298,8 +372,9 @@ Along with the HTTP error code, our API can also return more detailed error code ## RPC -RPC is the [local Discord server](#DOCS_TOPICS_RPC/) running on localhost. Access to the RPC server requires approval from Discord. +RPC is the [local Discord server](/developers/docs/topics/rpc) running on localhost. Access to the RPC server requires approval from Discord. + ###### RPC Error Codes | Code | Name | Description | @@ -321,6 +396,7 @@ RPC is the [local Discord server](#DOCS_TOPICS_RPC/) running on localhost. Acces | 5003 | Select voice force required | You tried to join a user to a voice channel but the user was already in one. | | 5004 | Capture shortcut already listening | You tried to capture more than one shortcut key at once. | + ###### RPC Close Event Codes | Code | Name | Description | diff --git a/discord/developers/docs/topics/permissions.mdx b/discord/developers/docs/topics/permissions.mdx new file mode 100644 index 0000000000..fc995419ab --- /dev/null +++ b/discord/developers/docs/topics/permissions.mdx @@ -0,0 +1,308 @@ +--- +title: Permissions +description: Learn how Discord's permission system works including bitwise operations. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +Permissions are a way to limit and grant certain abilities to users in Discord. A set of base permissions can be configured at the guild level for different roles. When these roles are attached to users, they grant or revoke specific privileges within the guild. Along with the guild-level permissions, Discord also supports permission overwrites that can be assigned to individual roles or members on a per-channel basis. + + +[Application command permissions](/developers/docs/interactions/application-commands#permissions) allow you to enable or disable specific commands for entire channels in addition to individual roles or users. + + +Permissions are stored in a variable-length integer serialized into a string, and are calculated using bitwise operations. For example, the permission value `123` will be serialized as `"123"`. For long-term stability, it's recommended to deserialize the permissions using your preferred languages' Big Integer libraries. The total permissions integer can be determined by OR-ing (`|`) together each individual value, and flags can be checked using AND (`&`) operations. + +In API v8 and above, all permissions are serialized as strings, including the `allow` and `deny` fields in overwrites. Any new permissions are rolled back into the base field. + + +In [API v6 (now deprecated)](/developers/docs/reference), the `permissions`, `allow`, and `deny` fields in roles and overwrites are still serialized as a number; however, these numbers shall not grow beyond 31 bits. During the remaining lifetime of API v6, all new permission bits will only be introduced in `permissions_new`, `allow_new`, and `deny_new`. These `_new` fields are just for response serialization; requests with these fields should continue to use the original `permissions`, `allow`, and `deny` fields, which accept both string or number values. + + +```python +# Permissions value that can Send Messages (0x800) and Add Reactions (0x40): +permissions = 0x40 | 0x800 # 2112 + +# Checking for flags that are set: +(permissions & 0x40) == 0x40 # True +(permissions & 0x800) == 0x800 # True + +# Kick Members (0x2) was not set: +(permissions & 0x2) == 0x2 # False +``` + +Additional logic is required when permission overwrites are involved; this is further explained below. For more information about bitwise operations and flags, see [this page](https://en.wikipedia.org/wiki/Bit_field). + +Below is a table of all current permissions, their integer values in hexadecimal, brief descriptions of the privileges that they grant, and the channel type they apply to, if applicable. + + +###### Bitwise Permission Flags + +| Permission | Value | Description | Channel Type (Abbreviated) | +|----------------------------------------|----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------| +| CREATE_INSTANT_INVITE | `0x0000000000000001` `(1 << 0)` | Allows creation of instant invites | T, V, S | +| KICK_MEMBERS \* | `0x0000000000000002` `(1 << 1)` | Allows kicking members | | +| BAN_MEMBERS \* | `0x0000000000000004` `(1 << 2)` | Allows banning members | | +| ADMINISTRATOR \* | `0x0000000000000008` `(1 << 3)` | Allows all permissions and bypasses channel permission overwrites | | +| MANAGE_CHANNELS \* | `0x0000000000000010` `(1 << 4)` | Allows management and editing of channels | T, V, S | +| MANAGE_GUILD \* | `0x0000000000000020` `(1 << 5)` | Allows management and editing of the guild | | +| ADD_REACTIONS | `0x0000000000000040` `(1 << 6)` | Allows for adding new reactions to messages. This permission does not apply to reacting with an existing reaction on a message. | T, V, S | +| VIEW_AUDIT_LOG | `0x0000000000000080` `(1 << 7)` | Allows for viewing of audit logs | | +| PRIORITY_SPEAKER | `0x0000000000000100` `(1 << 8)` | Allows for using priority speaker in a voice channel | V | +| STREAM | `0x0000000000000200` `(1 << 9)` | Allows the user to go live | V, S | +| VIEW_CHANNEL | `0x0000000000000400` `(1 << 10)` | Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels | T, V, S | +| SEND_MESSAGES | `0x0000000000000800` `(1 << 11)` | Allows for sending messages in a channel and creating threads in a forum (does not allow sending messages in threads) | T, V, S | +| SEND_TTS_MESSAGES | `0x0000000000001000` `(1 << 12)` | Allows for sending of `/tts` messages | T, V, S | +| MANAGE_MESSAGES \* | `0x0000000000002000` `(1 << 13)` | Allows for deletion of other users messages | T, V, S | +| EMBED_LINKS | `0x0000000000004000` `(1 << 14)` | Links sent by users with this permission will be auto-embedded | T, V, S | +| ATTACH_FILES | `0x0000000000008000` `(1 << 15)` | Allows for uploading images and files | T, V, S | +| READ_MESSAGE_HISTORY | `0x0000000000010000` `(1 << 16)` | Allows for reading of message history | T, V, S | +| MENTION_EVERYONE | `0x0000000000020000` `(1 << 17)` | Allows for using the `@everyone` tag to notify all users in a channel, and the `@here` tag to notify all online users in a channel | T, V, S | +| USE_EXTERNAL_EMOJIS | `0x0000000000040000` `(1 << 18)` | Allows the usage of custom emojis from other servers | T, V, S | +| VIEW_GUILD_INSIGHTS | `0x0000000000080000` `(1 << 19)` | Allows for viewing guild insights | | +| CONNECT | `0x0000000000100000` `(1 << 20)` | Allows for joining of a voice channel | V, S | +| SPEAK | `0x0000000000200000` `(1 << 21)` | Allows for speaking in a voice channel | V | +| MUTE_MEMBERS | `0x0000000000400000` `(1 << 22)` | Allows for muting members in a voice channel | V, S | +| DEAFEN_MEMBERS | `0x0000000000800000` `(1 << 23)` | Allows for deafening of members in a voice channel | V | +| MOVE_MEMBERS | `0x0000000001000000` `(1 << 24)` | Allows for moving of members between voice channels | V, S | +| USE_VAD | `0x0000000002000000` `(1 << 25)` | Allows for using voice-activity-detection in a voice channel | V | +| CHANGE_NICKNAME | `0x0000000004000000` `(1 << 26)` | Allows for modification of own nickname | | +| MANAGE_NICKNAMES | `0x0000000008000000` `(1 << 27)` | Allows for modification of other users nicknames | | +| MANAGE_ROLES \* | `0x0000000010000000` `(1 << 28)` | Allows management and editing of roles | T, V, S | +| MANAGE_WEBHOOKS \* | `0x0000000020000000` `(1 << 29)` | Allows management and editing of webhooks | T, V, S | +| MANAGE_GUILD_EXPRESSIONS \* | `0x0000000040000000` `(1 << 30)` | Allows for editing and deleting emojis, stickers, and soundboard sounds created by all users | | +| USE_APPLICATION_COMMANDS | `0x0000000080000000` `(1 << 31)` | Allows members to use application commands, including slash commands and context menu commands. | T, V, S | +| REQUEST_TO_SPEAK | `0x0000000100000000` `(1 << 32)` | Allows for requesting to speak in stage channels. (_This permission is under active development and may be changed or removed._) | S | +| MANAGE_EVENTS | `0x0000000200000000` `(1 << 33)` | Allows for editing and deleting scheduled events created by all users | V, S | +| MANAGE_THREADS \* | `0x0000000400000000` `(1 << 34)` | Allows for deleting and archiving threads, and viewing all private threads | T | +| CREATE_PUBLIC_THREADS | `0x0000000800000000` `(1 << 35)` | Allows for creating public and announcement threads | T | +| CREATE_PRIVATE_THREADS | `0x0000001000000000` `(1 << 36)` | Allows for creating private threads | T | +| USE_EXTERNAL_STICKERS | `0x0000002000000000` `(1 << 37)` | Allows the usage of custom stickers from other servers | T, V, S | +| SEND_MESSAGES_IN_THREADS | `0x0000004000000000` `(1 << 38)` | Allows for sending messages in threads | T | +| USE_EMBEDDED_ACTIVITIES | `0x0000008000000000` `(1 << 39)` | Allows for using Activities (applications with the `EMBEDDED` flag) | T, V | +| MODERATE_MEMBERS \*\* | `0x0000010000000000` `(1 << 40)` | Allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels | | +| VIEW_CREATOR_MONETIZATION_ANALYTICS \* | `0x0000020000000000` `(1 << 41)` | Allows for viewing role subscription insights | | +| USE_SOUNDBOARD | `0x0000040000000000` `(1 << 42)` | Allows for using soundboard in a voice channel | V | +| CREATE_GUILD_EXPRESSIONS | `0x0000080000000000` `(1 << 43)` | Allows for creating emojis, stickers, and soundboard sounds, and editing and deleting those created by the current user. Not yet available to developers, [see changelog](/developers/docs/change-log#clarification-on-permission-splits-for-expressions-and-events). | | +| CREATE_EVENTS | `0x0000100000000000` `(1 << 44)` | Allows for creating scheduled events, and editing and deleting those created by the current user. Not yet available to developers, [see changelog](/developers/docs/change-log#clarification-on-permission-splits-for-expressions-and-events). | V, S | +| USE_EXTERNAL_SOUNDS | `0x0000200000000000` `(1 << 45)` | Allows the usage of custom soundboard sounds from other servers | V | +| SEND_VOICE_MESSAGES | `0x0000400000000000` `(1 << 46)` | Allows sending voice messages | T, V, S | +| SEND_POLLS | `0x0002000000000000` `(1 << 49)` | Allows sending polls | T, V, S | +| USE_EXTERNAL_APPS | `0x0004000000000000` `(1 << 50)` | Allows user-installed apps to send public responses. When disabled, users will still be allowed to use their apps but the responses will be ephemeral. This only applies to apps not also installed to the server. | T, V, S | +| PIN_MESSAGES | `0x0008000000000000` `(1 << 51)` | Allows pinning and unpinning messages | T | + +| Channel Type (Abbreviated) | Description | Channel Types | +|----------------------------|-------------|----------------------------------------------------------| +| T | Text | GUILD_TEXT, GUILD_ANNOUNCEMENT, GUILD_FORUM, GUILD_MEDIA | +| V | Voice | GUILD_VOICE | +| S | Stage | GUILD_STAGE_VOICE | + +**\* These permissions require the owner account to use [two-factor authentication](/developers/docs/topics/oauth2#twofactor-authentication-requirement) when used on a guild that has server-wide 2FA enabled.** + +**\*\* See [Permissions for Timed Out Members](/developers/docs/topics/permissions#permissions-for-timed-out-members) to understand how permissions are temporarily modified for timed out users.** + +Note that permission names may be referred to differently in the Discord client. For example, "Manage Permissions" refers to MANAGE_ROLES, "Use Voice Activity" refers to USE_VAD, and "Timeout Members" refers to MODERATE_MEMBERS. + +## Permission Hierarchy + +How permissions apply may at first seem intuitive, but there are some hidden restrictions that prevent bots from performing certain inappropriate actions based on a bot's highest role compared to its target's highest role. A bot's or user's highest role is its role that has the greatest position value in the guild, with the default @everyone role starting at 0. Permissions follow a hierarchy with the following rules: + +- A bot can grant roles to other users that are of a lower position than its own highest role. +- A bot can edit roles of a lower position than its highest role, but it can only grant permissions it has to those roles. +- A bot can only sort roles lower than its highest role. +- A bot can only kick, ban, and edit nicknames for users whose highest role is lower than the bot's highest role. + +Otherwise, permissions do not obey the role hierarchy. For example, a user has two roles: A and B. A denies the `VIEW_CHANNEL` permission on a #coolstuff channel. B allows the `VIEW_CHANNEL` permission on the same #coolstuff channel. The user would ultimately be able to view the #coolstuff channel, regardless of the role positions. + +## Permission Overwrites + +Overwrites can be used to apply certain permissions to roles or members on a channel-level. Applicable permissions are indicated by a **T** for text channels, **V** for voice channels, or **S** for stage channels in the table above. + +When using overwrites, there are cases where permission collisions could occur for a user; that is to say, the user may have certain overwrites with permissions that contradict each other or their guild-level role permissions. With this in mind, permissions are applied to users in the following hierarchy: + +1. Base permissions given to @everyone are applied at a guild level +2. Permissions allowed to a user by their roles are applied at a guild level +3. Overwrites that deny permissions for @everyone are applied at a channel level +4. Overwrites that allow permissions for @everyone are applied at a channel level +5. Overwrites that deny permissions for specific roles are applied at a channel level +6. Overwrites that allow permissions for specific roles are applied at a channel level +7. Member-specific overwrites that deny permissions are applied at a channel level +8. Member-specific overwrites that allow permissions are applied at a channel level + +The following pseudocode demonstrates this process programmatically: + +```python +def compute_base_permissions(member, guild): + if guild.is_owner(member): + return ALL + + role_everyone = guild.get_role(guild.id) # get @everyone role + permissions = role_everyone.permissions + + for role in member.roles: + permissions |= role.permissions + + if permissions & ADMINISTRATOR == ADMINISTRATOR: + return ALL + + return permissions + +def compute_overwrites(base_permissions, member, channel): + # ADMINISTRATOR overrides any potential permission overwrites, so there is nothing to do here. + if base_permissions & ADMINISTRATOR == ADMINISTRATOR: + return ALL + + permissions = base_permissions + overwrite_everyone = overwrites.get(channel.guild_id) # Find (@everyone) role overwrite and apply it. + if overwrite_everyone: + permissions &= ~overwrite_everyone.deny + permissions |= overwrite_everyone.allow + + # Apply role specific overwrites. + overwrites = channel.permission_overwrites + allow = NONE + deny = NONE + for role_id in member.roles: + overwrite_role = overwrites.get(role_id) + if overwrite_role: + allow |= overwrite_role.allow + deny |= overwrite_role.deny + + permissions &= ~deny + permissions |= allow + + # Apply member specific overwrite if it exist. + overwrite_member = overwrites.get(member.user_id) + if overwrite_member: + permissions &= ~overwrite_member.deny + permissions |= overwrite_member.allow + + return permissions + +def compute_permissions(member, channel): + base_permissions = compute_base_permissions(member, channel.guild) + return compute_overwrites(base_permissions, member, channel) +``` + +## Implicit Permissions + +Permissions in Discord are sometimes implicitly denied or allowed based on logical use. The two main cases are `VIEW_CHANNEL` and `SEND_MESSAGES` for text channels. Denying a user or a role `VIEW_CHANNEL` on a channel implicitly denies other permissions on the channel. Though permissions like `SEND_MESSAGES` are not explicitly denied for the user, they are ignored because the user cannot read messages in the channel. + +Denying `SEND_MESSAGES` implicitly denies `MENTION_EVERYONE`, `SEND_TTS_MESSAGES`, `ATTACH_FILES`, and `EMBED_LINKS`. Again, they are not explicitly denied when doing permissions calculations, but they are ignored because the user cannot do the base action of sending messages. + +For voice and stage channels, denying the `CONNECT` permission also implicitly denies other permissions such as `MANAGE_CHANNEL`. + +There may be other cases in which certain permissions implicitly deny or allow other permissions. In all cases, it is based on logical conclusions about how a user with certain permissions should or should not interact with Discord. + +## Inherited Permissions (Threads) + +Threads inherit permissions from the parent channel (the channel they were created in), with one exception: The `SEND_MESSAGES` permission is not inherited; users must have `SEND_MESSAGES_IN_THREADS` to send a message in a thread, which allows for users to participate in threads in places like announcement channels. + +Users must have the `VIEW_CHANNEL` permission to view _any_ threads in the channel, even if they are directly mentioned or added to the thread. + +## Permission Syncing + +Permissions with regards to categories and channels within categories are a bit tricky. Rather than inheritance, permissions are calculated by means of what we call Permission Syncing. If a child channel has the same permissions and overwrites (or lack thereof) as its parent category, the channel is considered "synced" to the category. Any further changes to a **parent category** will be reflected in its synced child channels. Any further changes to a **child channel** will cause it to become de-synced from its parent category, and its permissions will no longer change with changes to its parent category. + +### Role Object + +Roles represent a set of permissions attached to a group of users. Roles have names, colors, and can be "pinned" to the side bar, causing their members to be listed separately. Roles can have separate permission profiles for the global context (guild) and channel context. The `@everyone` role has the same ID as the guild it belongs to. + + +###### Role Structure + +| Field | Type | Description | +|----------------|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| id | snowflake | role id | +| name | string | role name | +| color* | integer | **Deprecated** integer representation of hexadecimal color code | +| colors | [role colors](/developers/docs/topics/permissions#role-object-role-colors-object) object | the role's colors | +| hoist | boolean | if this role is pinned in the user listing | +| icon? | ?string | role [icon hash](/developers/docs/reference#image-formatting) | +| unicode_emoji? | ?string | role unicode emoji | +| position | integer | position of this role (roles with the same position are sorted by id) | +| permissions | string | permission bit set | +| managed | boolean | whether this role is managed by an integration | +| mentionable | boolean | whether this role is mentionable | +| tags? | [role tags](/developers/docs/topics/permissions#role-object-role-tags-structure) object | the tags this role has | +| flags | integer | [role flags](/developers/docs/topics/permissions#role-object-role-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | + +Roles without colors (`colors.primary_color == 0`) do not count towards the final computed color in the user list. + +\* `color` will still be returned by the API, but using the `colors` field is recommended when doing requests. + + +###### Role Tags Structure + +Tags with type `null` represent booleans. They will be present and set to `null` if they are "true", and will be not present if they are "false". + +| Field | Type | Description | +|--------------------------|-----------|----------------------------------------------------| +| bot_id? | snowflake | the id of the bot this role belongs to | +| integration_id? | snowflake | the id of the integration this role belongs to | +| premium_subscriber? | null | whether this is the guild's Booster role | +| subscription_listing_id? | snowflake | the id of this role's subscription sku and listing | +| available_for_purchase? | null | whether this role is available for purchase | +| guild_connections? | null | whether this role is a guild's linked role | + + +###### Role Colors Object + +This object will always be filled with `primary_color` being the role's `color`. Other fields can only be set to a non-null value if the guild has the `ENHANCED_ROLE_COLORS` [guild feature](/developers/docs/resources/guild#guild-object-guild-features). + +| Field | Type | Description | +|-----------------|----------|--------------------------------------------------------------------------------------------------------| +| primary_color | integer | the primary color for the role | +| secondary_color | ?integer | the secondary color for the role, this will make the role a gradient between the other provided colors | +| tertiary_color | ?integer | the tertiary color for the role, this will turn the gradient into a holographic style | + + +When sending `tertiary_color` the API enforces the role color to be a holographic style with values of: +`primary_color = 11127295`, `secondary_color = 16759788`, and `tertiary_color = 16761760`. + + + +###### Default Role Colors Object + +```json +"colors": { + "primary_color": 0, + "secondary_color": null, + "tertiary_color": null +} +``` + + +###### Example Role + +```json +{ + "id": "41771983423143936", + "name": "WE DEM BOYZZ!!!!!!", + "color": 3447003, + "colors": { + "primary_color": 3447003, + "secondary_color": null, + "tertiary_color": null + }, + "hoist": true, + "icon": "cf3ced8600b777c9486c6d8d84fb4327", + "unicode_emoji": null, + "position": 1, + "permissions": "66321471", + "managed": false, + "mentionable": false, + "flags": 0 +} +``` + + +###### Role Flags + +| Flag | Value | Description | +|-----------|----------|---------------------------------------------------------------------------------------------------------------------| +| IN_PROMPT | `1 << 0` | role can be selected by members in an [onboarding](/developers/docs/resources/guild#guild-onboarding-object) prompt | + +## Permissions For Timed Out Members + +Timed out members will temporarily lose all permissions except `VIEW_CHANNEL` and `READ_MESSAGE_HISTORY`. Owners and admin users with `ADMINISTRATOR` permissions are exempt. diff --git a/docs/topics/Rate_Limits.md b/discord/developers/docs/topics/rate-limits.mdx similarity index 62% rename from docs/topics/Rate_Limits.md rename to discord/developers/docs/topics/rate-limits.mdx index 993dd970e8..6f279056e1 100644 --- a/docs/topics/Rate_Limits.md +++ b/discord/developers/docs/topics/rate-limits.mdx @@ -1,23 +1,31 @@ -# Rate Limits +--- +title: Rate Limits +description: Understand Discord's rate limiting system including per-route and global limits. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' Rate limits exist across Discord's APIs to prevent spam, abuse, and service overload. Limits are applied to individual bots and users both on a per-route basis and globally. Individuals are determined using a request's authentication—for example, a bot token for a bot. -> info -> Because rate limits depend on a variety of factors and are subject to change, **rate limits should not be hard coded into your app**. Instead, your app should parse [response headers](#DOCS_TOPICS_RATE_LIMITS/header-format-rate-limit-header-examples) to prevent hitting the limit, and to respond accordingly in case you do. + +Because rate limits depend on a variety of factors and are subject to change, **rate limits should not be hard coded into your app**. Instead, your app should parse [response headers](/developers/docs/topics/rate-limits#header-format-rate-limit-header-examples) to prevent hitting the limit, and to respond accordingly in case you do. + **Per-route rate limits** exist for many individual endpoints, and may include the HTTP method (`GET`, `POST`, `PUT`, or `DELETE`). In some cases, per-route limits will be shared across a set of similar endpoints, indicated in the `X-RateLimit-Bucket` header. It's recommended to use this header as a unique identifier for a rate limit, which will allow you to group shared limits as you encounter them. -During calculation, per-route rate limits often account for top-level resources within the path using an identifier—for example, `guild_id` when calling [`/guilds/{guild.id}/channels`](#DOCS_RESOURCES_GUILD/get-guild-channels). Top-level resources are currently limited to channels (`channel_id`), guilds (`guild_id`), and webhooks (`webhook_id` or `webhook_id + webhook_token`). This means that an endpoint with two different top-level resources may calculate limits independently. As an example, if you exceeded a rate limit when calling one endpoint `/channels/1234`, you could still call another similar endpoint like `/channels/9876` without a problem. +During calculation, per-route rate limits often account for top-level resources within the path using an identifier—for example, `guild_id` when calling [`/guilds/{guild.id}/channels`](/developers/docs/resources/guild#get-guild-channels). Top-level resources are currently limited to channels (`channel_id`), guilds (`guild_id`), and webhooks (`webhook_id` or `webhook_id + webhook_token`). This means that an endpoint with two different top-level resources may calculate limits independently. As an example, if you exceeded a rate limit when calling one endpoint `/channels/1234`, you could still call another similar endpoint like `/channels/9876` without a problem. -**Global rate limits** apply to the total number of requests a bot or user makes, independent of any per-route limits. You can read more on [global rate limits](#DOCS_TOPICS_RATE_LIMITS/global-rate-limit) below. +**Global rate limits** apply to the total number of requests a bot or user makes, independent of any per-route limits. You can read more on [global rate limits](/developers/docs/topics/rate-limits#global-rate-limit) below. -> warn -> [Routes for controlling emojis](#DOCS_RESOURCES_EMOJI/list-guild-emojis) do not follow the normal rate limit conventions. These routes are specifically limited on a per-guild basis to prevent abuse. This means that the quota returned by our APIs may be inaccurate, and you may encounter 429s. + +[Routes for controlling emojis](/developers/docs/resources/emoji#list-guild-emojis) do not follow the normal rate limit conventions. These routes are specifically limited on a per-guild basis to prevent abuse. This means that the quota returned by our APIs may be inaccurate, and you may encounter 429s. + ## Header Format For most API requests made, we return optional HTTP response headers containing the rate limit encountered during your request. + ###### Rate Limit Header Examples ``` @@ -40,16 +48,19 @@ X-RateLimit-Bucket: abcd1234 In the case that a rate limit is exceeded, the API will return a HTTP 429 response code with a JSON body. Your application should rely on the `Retry-After` header or `retry_after` field to determine when to retry the request. + ###### Rate Limit Response Structure -| Field | Type | Description | -|-------------|------------------|------------------------------------------------------------------| -| message | string | A message saying you are being rate limited. | -| retry_after | float | The number of seconds to wait before submitting another request. | -| global | boolean | A value indicating if you are being globally rate limited or not | +| Field | Type | Description | +|-------------|---------|----------------------------------------------------------------------------------------| +| message | string | A message saying you are being rate limited. | +| retry_after | float | The number of seconds to wait before submitting another request. | +| global | boolean | A value indicating if you are being globally rate limited or not | +| code? | integer | An [error code](/developers/docs/topics/opcodes-and-status-codes#json) for some limits | Note that normal route rate-limiting headers will also be sent in this response. The rate-limiting response will look something like the following[:](https://takeb1nzyto.space/) + ###### Example Exceeded User Rate Limit Response ``` @@ -70,6 +81,7 @@ Note that normal route rate-limiting headers will also be sent in this response. ``` + ###### Example Exceeded Resource Rate Limit Response ``` @@ -89,6 +101,7 @@ Note that normal route rate-limiting headers will also be sent in this response. } ``` + ###### Example Exceeded Global Rate Limit Response ``` @@ -106,22 +119,22 @@ Note that normal route rate-limiting headers will also be sent in this response. ## Global Rate Limit -All bots can make up to 50 requests per second to our API. This is independent of any individual rate limit on a route. If your bot gets big enough, based on its functionality, it may be impossible to stay below 50 requests per second during normal operations. +All bots can make up to 50 requests per second to our API. If no authorization header is provided, then the limit is applied to the IP address. This is independent of any individual rate limit on a route. If your bot gets big enough, based on its functionality, it may be impossible to stay below 50 requests per second during normal operations. -Global rate limit issues generally show up as repeatedly getting banned from the Discord API when your bot starts (see below). If your bot gets temporarily CloudFlare banned from the Discord API every once in a while, it is most likely **not** a global rate limit issue. You probably had a spike of errors that was not properly handled and hit our error threshold. +Global rate limit issues generally show up as repeatedly getting banned from the Discord API when your bot starts (see below). If your bot gets temporarily Cloudflare banned from the Discord API every once in a while, it is most likely **not** a global rate limit issue. You probably had a spike of errors that was not properly handled and hit our error threshold. -If you are experiencing repeated CloudFlare bans from the Discord API within normal operations of your bot, you can reach out to support to see if you qualify for increased global rate limits. You can contact Discord support using [https://dis.gd/contact](https://dis.gd/contact). +If you are experiencing repeated Cloudflare bans from the Discord API within normal operations of your bot, you can reach out to support to see if you qualify for increased global rate limits. You can contact Discord support using [https://dis.gd/rate-limit](https://dis.gd/rate-limit). -[Interaction endpoints](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/endpoints) are not bound to the bot's Global Rate Limit. +[Interaction endpoints](/developers/docs/interactions/receiving-and-responding#endpoints) are not bound to the bot's Global Rate Limit. -## Invalid Request Limit aka CloudFlare bans +## Invalid Request Limit aka Cloudflare bans IP addresses that make too many invalid HTTP requests are automatically and temporarily restricted from accessing the Discord API. Currently, this limit is **10,000 per 10 minutes**. An invalid request is one that results in **401**, **403**, or **429** statuses. All applications should make reasonable attempts to avoid making invalid requests. For example: - **401** responses are avoided by providing a valid token in the authorization header when required and by stopping further requests after a token becomes invalid -- **403** responses are avoided by inspecting role or channel permissions and by not making requests that are restricted by such permissions +- **403** responses are avoided by inspecting role or channel permissions and by not making requests that are restricted by such permissions - **429** responses are avoided by inspecting the rate limit headers documented above and by not making requests on exhausted buckets until after they have reset. *429 errors returned with `X-RateLimit-Scope: shared` are not counted against you.* Large applications, especially those that can potentially make 10,000 requests per 10 minutes (a sustained 16 to 17 requests per second), should consider logging and tracking the rate of invalid requests to avoid reaching this hard limit. diff --git a/docs/topics/RPC.md b/discord/developers/docs/topics/rpc.mdx similarity index 51% rename from docs/topics/RPC.md rename to discord/developers/docs/topics/rpc.mdx index 3cf37cdb16..a93353e9bb 100644 --- a/docs/topics/RPC.md +++ b/discord/developers/docs/topics/rpc.mdx @@ -1,33 +1,42 @@ -# RPC +--- +title: RPC +description: Learn about Discord's Rich Presence Client (RPC) for local application integration. +--- -> danger -> For now, RPC is in a private beta. We are not currently accepting any new developers into the program at this time. +import {ManualAnchor} from '/snippets/manualanchor.jsx' + + + +For now, RPC is in a private beta. We are not currently accepting any new developers into the program at this time. + All Discord clients have an RPC server running on localhost that allows control over local Discord clients. + ###### RPC Versions | Version | Out of Service | -| ------- | -------------- | +|---------|----------------| | 1 | no | ## Restrictions -For connections to the RPC server, a [list of approved testers](#DOCS_TOPICS_RPC/authorize) is used to restrict access while you're still developing. You can invite up to 50 people. +For connections to the RPC server, a [list of approved testers](/developers/docs/topics/rpc#authorize) is used to restrict access while you're still developing. You can invite up to 50 people. For applications/games not approved, we limit you to creating 10 guilds and 10 channels. This limit is raised to virtually unlimited after approval. ## Payloads + ###### Payload Structure -| Field | Type | Description | Present | -| ----- | ------ | --------------------------------------------------------------------- | -------------------------------------------------------- | -| cmd | enum | [payload command](#DOCS_TOPICS_RPC/commands-and-events-rpc-commands) | Always | -| nonce | string | unique string used once for replies from the server | In responses to commands (not subscribed events) | -| evt | enum | [subscription event](#DOCS_TOPICS_RPC/commands-and-events-rpc-events) | In subscribed events, errors, and (un)subscribing events | -| data | object | event data | In responses from the server | -| args | object | command arguments | In commands sent to the server | +| Field | Type | Description | Present | +|-------|--------|----------------------------------------------------------------------------------|----------------------------------------------------------| +| cmd | enum | [payload command](/developers/docs/topics/rpc#commands-and-events-rpc-commands) | Always | +| nonce | string | unique string used once for replies from the server | In responses to commands (not subscribed events) | +| evt | enum | [subscription event](/developers/docs/topics/rpc#commands-and-events-rpc-events) | In subscribed events, errors, and (un)subscribing events | +| data | object | event data | In responses from the server | +| args | object | command arguments | In commands sent to the server | ## Connecting @@ -40,7 +49,7 @@ For WebSocket connections, the connection is always `ws://127.0.0.1:PORT/?v=VERS - `PORT` is the port of the RPC Server. - `ENCODING` is the type of encoding for this connection to use. `json` and `etf` are supported. -To begin, you'll need to create an app. Head to [your apps](#APPLICATIONS) and click the big plus button. When you create an app on our Developers site, you must specify an "RPC Origin" and "Redirect URI" from which to permit connections and authorizations. **The origin you send when connecting and the redirect uri you send when exchanging an authorization code for an access token must match one of the ones entered on the Developers site.** +To begin, you'll need to create an app. Head to [your apps](https://discord.com/developers/applications) and click the big plus button. When you create an app on our Developers site, you must specify an "RPC Origin" and "Redirect URI" from which to permit connections and authorizations. **The origin you send when connecting and the redirect uri you send when exchanging an authorization code for an access token must match one of the ones entered on the Developers site.** When establishing a WebSocket connection, we verify the Origin header on connection to prevent client ID spoofing. You will be instantly disconnected if the Origin does not match. @@ -54,8 +63,9 @@ The port range for Discord's local RPC server is [6463, 6472]. Since the RPC ser ## Authenticating -In order to call any commands over RPC, you must be authenticated or you will receive a code `4006` error response. Thankfully, we've removed the oppressive nature of a couple commands that will let you `AUTHORIZE` and `AUTHENTICATE` new users. First, call [AUTHORIZE](#DOCS_TOPICS_RPC/authorize): +In order to call any commands over RPC, you must be authenticated or you will receive a code `4006` error response. Thankfully, we've removed the oppressive nature of a couple commands that will let you `AUTHORIZE` and `AUTHENTICATE` new users. First, call [AUTHORIZE](/developers/docs/topics/rpc#authorize): + ###### RPC Authorize Example ```json @@ -69,8 +79,9 @@ In order to call any commands over RPC, you must be authenticated or you will re } ``` -The user will then be prompted to authorize your app to access RPC on Discord. The `AUTHORIZE` command returns a `code` that you can exchange with a POST to `https://discord.com/api/oauth2/token` containing the [standard OAuth2 body parameters](https://tools.ietf.org/html/rfc6749#section-4.1.3) for the token exchange. The token endpoint on our API will return an `access_token` that can be sent with [AUTHENTICATE](#DOCS_TOPICS_RPC/authenticate): +The user will then be prompted to authorize your app to access RPC on Discord. The `AUTHORIZE` command returns a `code` that you can exchange with a POST to `https://discord.com/api/oauth2/token` containing the [standard OAuth2 body parameters](https://tools.ietf.org/html/rfc6749#section-4.1.3) for the token exchange. The token endpoint on our API will return an `access_token` that can be sent with [AUTHENTICATE](/developers/docs/topics/rpc#authenticate): + ###### RPC Authenticate Example ```json @@ -89,56 +100,58 @@ You can now call RPC commands on behalf of the authorized user! Commands are requests made to the RPC socket by a client. + ###### RPC Commands -| Name | Description | -| ---------------------------------------------------------------------- | --------------------------------------------------------------- | -| [DISPATCH](#DOCS_TOPICS_RPC/commands-and-events-rpc-events) | event dispatch | -| [AUTHORIZE](#DOCS_TOPICS_RPC/authorize) | used to authorize a new client with your app | -| [AUTHENTICATE](#DOCS_TOPICS_RPC/authenticate) | used to authenticate an existing client with your app | -| [GET_GUILD](#DOCS_TOPICS_RPC/getguild) | used to retrieve guild information from the client | -| [GET_GUILDS](#DOCS_TOPICS_RPC/getguilds) | used to retrieve a list of guilds from the client | -| [GET_CHANNEL](#DOCS_TOPICS_RPC/getchannel) | used to retrieve channel information from the client | -| [GET_CHANNELS](#DOCS_TOPICS_RPC/getchannels) | used to retrieve a list of channels for a guild from the client | -| [SUBSCRIBE](#DOCS_TOPICS_RPC/subscribe) | used to subscribe to an RPC event | -| [UNSUBSCRIBE](#DOCS_TOPICS_RPC/unsubscribe) | used to unsubscribe from an RPC event | -| [SET_USER_VOICE_SETTINGS](#DOCS_TOPICS_RPC/setuservoicesettings) | used to change voice settings of users in voice channels | -| [SELECT_VOICE_CHANNEL](#DOCS_TOPICS_RPC/selectvoicechannel) | used to join or leave a voice channel, group dm, or dm | -| [GET_SELECTED_VOICE_CHANNEL](#DOCS_TOPICS_RPC/getselectedvoicechannel) | used to get the current voice channel the client is in | -| [SELECT_TEXT_CHANNEL](#DOCS_TOPICS_RPC/selecttextchannel) | used to join or leave a text channel, group dm, or dm | -| [GET_VOICE_SETTINGS](#DOCS_TOPICS_RPC/getvoicesettings) | used to retrieve the client's voice settings | -| [SET_VOICE_SETTINGS](#DOCS_TOPICS_RPC/setvoicesettings) | used to set the client's voice settings | -| [SET_CERTIFIED_DEVICES](#DOCS_TOPICS_RPC/setcertifieddevices) | used to send info about certified hardware devices | -| [SET_ACTIVITY](#DOCS_TOPICS_RPC/setactivity) | used to update a user's Rich Presence | -| [SEND_ACTIVITY_JOIN_INVITE](#DOCS_TOPICS_RPC/sendactivityjoininvite) | used to consent to a Rich Presence Ask to Join request | -| [CLOSE_ACTIVITY_REQUEST](#DOCS_TOPICS_RPC/closeactivityrequest) | used to reject a Rich Presence Ask to Join request | +| Name | Description | +|-----------------------------------------------------------------------------------|-----------------------------------------------------------------| +| [DISPATCH](/developers/docs/topics/rpc#commands-and-events-rpc-events) | event dispatch | +| [AUTHORIZE](/developers/docs/topics/rpc#authorize) | used to authorize a new client with your app | +| [AUTHENTICATE](/developers/docs/topics/rpc#authenticate) | used to authenticate an existing client with your app | +| [GET_GUILD](/developers/docs/topics/rpc#getguild) | used to retrieve guild information from the client | +| [GET_GUILDS](/developers/docs/topics/rpc#getguilds) | used to retrieve a list of guilds from the client | +| [GET_CHANNEL](/developers/docs/topics/rpc#getchannel) | used to retrieve channel information from the client | +| [GET_CHANNELS](/developers/docs/topics/rpc#getchannels) | used to retrieve a list of channels for a guild from the client | +| [SUBSCRIBE](/developers/docs/topics/rpc#subscribe) | used to subscribe to an RPC event | +| [UNSUBSCRIBE](/developers/docs/topics/rpc#unsubscribe) | used to unsubscribe from an RPC event | +| [SET_USER_VOICE_SETTINGS](/developers/docs/topics/rpc#setuservoicesettings) | used to change voice settings of users in voice channels | +| [SELECT_VOICE_CHANNEL](/developers/docs/topics/rpc#selectvoicechannel) | used to join or leave a voice channel, group dm, or dm | +| [GET_SELECTED_VOICE_CHANNEL](/developers/docs/topics/rpc#getselectedvoicechannel) | used to get the current voice channel the client is in | +| [SELECT_TEXT_CHANNEL](/developers/docs/topics/rpc#selecttextchannel) | used to join or leave a text channel, group dm, or dm | +| [GET_VOICE_SETTINGS](/developers/docs/topics/rpc#getvoicesettings) | used to retrieve the client's voice settings | +| [SET_VOICE_SETTINGS](/developers/docs/topics/rpc#setvoicesettings) | used to set the client's voice settings | +| [SET_CERTIFIED_DEVICES](/developers/docs/topics/rpc#setcertifieddevices) | used to send info about certified hardware devices | +| [SET_ACTIVITY](/developers/docs/topics/rpc#setactivity) | used to update a user's Rich Presence | +| [SEND_ACTIVITY_JOIN_INVITE](/developers/docs/topics/rpc#sendactivityjoininvite) | used to consent to a Rich Presence Ask to Join request | +| [CLOSE_ACTIVITY_REQUEST](/developers/docs/topics/rpc#closeactivityrequest) | used to reject a Rich Presence Ask to Join request | Events are payloads sent over the socket to a client that correspond to events in Discord. + ###### RPC Events -| Name | Description | -| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| [READY](#DOCS_TOPICS_RPC/ready) | non-subscription event sent immediately after connecting, contains server information | -| [ERROR](#DOCS_TOPICS_RPC/error) | non-subscription event sent when there is an error, including command responses | -| [GUILD_STATUS](#DOCS_TOPICS_RPC/guildstatus) | sent when a subscribed server's state changes | -| [GUILD_CREATE](#DOCS_TOPICS_RPC/guildcreate) | sent when a guild is created/joined on the client | -| [CHANNEL_CREATE](#DOCS_TOPICS_RPC/channelcreate) | sent when a channel is created/joined on the client | -| [VOICE_CHANNEL_SELECT](#DOCS_TOPICS_RPC/voicechannelselect) | sent when the client joins a voice channel | -| [VOICE_STATE_CREATE](#DOCS_TOPICS_RPC/voicestatecreatevoicestateupdatevoicestatedelete) | sent when a user joins a subscribed voice channel | -| [VOICE_STATE_UPDATE](#DOCS_TOPICS_RPC/voicestatecreatevoicestateupdatevoicestatedelete) | sent when a user's voice state changes in a subscribed voice channel (mute, volume, etc.) | -| [VOICE_STATE_DELETE](#DOCS_TOPICS_RPC/voicestatecreatevoicestateupdatevoicestatedelete) | sent when a user parts a subscribed voice channel | -| [VOICE_SETTINGS_UPDATE](#DOCS_TOPICS_RPC/voicesettingsupdate) | sent when the client's voice settings update | -| [VOICE_CONNECTION_STATUS](#DOCS_TOPICS_RPC/voiceconnectionstatus) | sent when the client's voice connection status changes | -| [SPEAKING_START](#DOCS_TOPICS_RPC/speakingstartspeakingstop) | sent when a user in a subscribed voice channel speaks | -| [SPEAKING_STOP](#DOCS_TOPICS_RPC/speakingstartspeakingstop) | sent when a user in a subscribed voice channel stops speaking | -| [MESSAGE_CREATE](#DOCS_TOPICS_RPC/messagecreatemessageupdatemessagedelete) | sent when a message is created in a subscribed text channel | -| [MESSAGE_UPDATE](#DOCS_TOPICS_RPC/messagecreatemessageupdatemessagedelete) | sent when a message is updated in a subscribed text channel | -| [MESSAGE_DELETE](#DOCS_TOPICS_RPC/messagecreatemessageupdatemessagedelete) | sent when a message is deleted in a subscribed text channel | -| [NOTIFICATION_CREATE](#DOCS_TOPICS_RPC/notificationcreate) | sent when the client receives a notification (mention or new message in eligible channels) | -| [ACTIVITY_JOIN](#DOCS_TOPICS_RPC/activityjoin) | sent when the user clicks a Rich Presence join invite in chat to join a game | -| [ACTIVITY_SPECTATE](#DOCS_TOPICS_RPC/activityspectate) | sent when the user clicks a Rich Presence spectate invite in chat to spectate a game | -| [ACTIVITY_JOIN_REQUEST](#DOCS_TOPICS_RPC/activityjoinrequest) | sent when the user receives a Rich Presence Ask to Join request | +| Name | Description | +|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| +| [READY](/developers/docs/topics/rpc#ready) | non-subscription event sent immediately after connecting, contains server information | +| [ERROR](/developers/docs/topics/rpc#error) | non-subscription event sent when there is an error, including command responses | +| [GUILD_STATUS](/developers/docs/topics/rpc#guildstatus) | sent when a subscribed server's state changes | +| [GUILD_CREATE](/developers/docs/topics/rpc#guildcreate) | sent when a guild is created/joined on the client | +| [CHANNEL_CREATE](/developers/docs/topics/rpc#channelcreate) | sent when a channel is created/joined on the client | +| [VOICE_CHANNEL_SELECT](/developers/docs/topics/rpc#voicechannelselect) | sent when the client joins a voice channel | +| [VOICE_STATE_CREATE](/developers/docs/topics/rpc#voicestatecreatevoicestateupdatevoicestatedelete) | sent when a user joins a subscribed voice channel | +| [VOICE_STATE_UPDATE](/developers/docs/topics/rpc#voicestatecreatevoicestateupdatevoicestatedelete) | sent when a user's voice state changes in a subscribed voice channel (mute, volume, etc.) | +| [VOICE_STATE_DELETE](/developers/docs/topics/rpc#voicestatecreatevoicestateupdatevoicestatedelete) | sent when a user parts a subscribed voice channel | +| [VOICE_SETTINGS_UPDATE](/developers/docs/topics/rpc#voicesettingsupdate) | sent when the client's voice settings update | +| [VOICE_CONNECTION_STATUS](/developers/docs/topics/rpc#voiceconnectionstatus) | sent when the client's voice connection status changes | +| [SPEAKING_START](/developers/docs/topics/rpc#speakingstartspeakingstop) | sent when a user in a subscribed voice channel speaks | +| [SPEAKING_STOP](/developers/docs/topics/rpc#speakingstartspeakingstop) | sent when a user in a subscribed voice channel stops speaking | +| [MESSAGE_CREATE](/developers/docs/topics/rpc#messagecreatemessageupdatemessagedelete) | sent when a message is created in a subscribed text channel | +| [MESSAGE_UPDATE](/developers/docs/topics/rpc#messagecreatemessageupdatemessagedelete) | sent when a message is updated in a subscribed text channel | +| [MESSAGE_DELETE](/developers/docs/topics/rpc#messagecreatemessageupdatemessagedelete) | sent when a message is deleted in a subscribed text channel | +| [NOTIFICATION_CREATE](/developers/docs/topics/rpc#notificationcreate) | sent when the client receives a notification (mention or new message in eligible channels) | +| [ACTIVITY_JOIN](/developers/docs/topics/rpc#activityjoin) | sent when the user clicks a Rich Presence join invite in chat to join a game | +| [ACTIVITY_SPECTATE](/developers/docs/topics/rpc#activityspectate) | sent when the user clicks a Rich Presence spectate invite in chat to spectate a game | +| [ACTIVITY_JOIN_REQUEST](/developers/docs/topics/rpc#activityjoinrequest) | sent when the user receives a Rich Presence Ask to Join request | #### AUTHORIZE @@ -148,21 +161,24 @@ Used to authenticate a new client with your app. By default this pops up a modal We also have an RPC token system to bypass the user authorization modal. This is usable by approved games as well as by users on a game's list of testers, and also disallows use of the `messages.read` scope. If you have been granted access, you can send a POST request to `https://discord.com/api/oauth2/token/rpc` with your application's `client_id` and `client_secret` in the body (sent as a url-encoded body, **not JSON**). You can then pass the returned `rpc_token` value to the `rpc_token` field in your RPC authorize request (documented below). + ###### Authorize Argument Structure -| Field | Type | Description | -| --------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -| scopes | array of [OAuth2 scopes](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes) | scopes to authorize | -| client_id | string | OAuth2 application id | -| rpc_token | string | one-time use RPC token | -| username | string | username to create a guest account with if the user does not have Discord | +| Field | Type | Description | +|-----------|-----------------------------------------------------------------------------------------|---------------------------------------------------------------------------| +| scopes | array of [OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) | scopes to authorize | +| client_id | string | OAuth2 application id | +| rpc_token | string | one-time use RPC token | +| username | string | username to create a guest account with if the user does not have Discord | + ###### Authorize Response Structure | Field | Type | Description | -| ----- | ------ | ------------------------- | +|-------|--------|---------------------------| | code | string | OAuth2 authorization code | + ###### Example Authorize Command Payload ```json @@ -176,6 +192,7 @@ We also have an RPC token system to bypass the user authorization modal. This is } ``` + ###### Example Authorize Response Payload ```json @@ -192,31 +209,35 @@ We also have an RPC token system to bypass the user authorization modal. This is Used to authenticate an existing client with your app. + ###### Authenticate Argument Structure | Field | Type | Description | -| ------------ | ------ | ------------------- | +|--------------|--------|---------------------| | access_token | string | OAuth2 access token | + ###### Authenticate Response Structure -| Field | Type | Description | -| ----------- | --------------------------------------------------------------------------------------- | ------------------------------- | -| user | partial [user](#DOCS_RESOURCES_USER/user-object) object | the authed user | -| scopes | array of [OAuth2 scopes](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes) | authorized scopes | -| expires | date | expiration date of OAuth2 token | -| application | [OAuth2 application](#DOCS_TOPICS_RPC/authenticate-oauth2-application-structure) object | application the user authorized | +| Field | Type | Description | +|-------------|----------------------------------------------------------------------------------------------------|---------------------------------| +| user | partial [user](/developers/docs/resources/user#user-object) object | the authed user | +| scopes | array of [OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) | authorized scopes | +| expires | date | expiration date of OAuth2 token | +| application | [OAuth2 application](/developers/docs/topics/rpc#authenticate-oauth2-application-structure) object | application the user authorized | + ###### OAuth2 Application Structure | Field | Type | Description | -| ----------- | ---------------- | ------------------------ | +|-------------|------------------|--------------------------| | description | string | application description | | icon | string | hash of the icon | | id | snowflake | application client id | | rpc_origins | array of strings | array of rpc origin urls | | name | string | application name | + ###### Example Authenticate Command Payload ```json @@ -229,6 +250,7 @@ Used to authenticate an existing client with your app. } ``` + ###### Example Authenticate Response Payload ```json @@ -255,16 +277,19 @@ Used to authenticate an existing client with your app. } ``` + #### GET_GUILDS Used to get a list of guilds the client is in. + ###### Get Guilds Response Structure -| Field | Type | Description | -| ------ | -------------------------------------------------------------------- | ------------------------- | -| guilds | array of partial [guild](#DOCS_RESOURCES_GUILD/guild-object) objects | the guilds the user is in | +| Field | Type | Description | +|--------|---------------------------------------------------------------------------------|---------------------------| +| guilds | array of partial [guild](/developers/docs/resources/guild#guild-object) objects | the guilds the user is in | + ###### Example Get Guilds Command Payload ```json @@ -275,6 +300,7 @@ Used to get a list of guilds the client is in. } ``` + ###### Example Get Guilds Response Payload ```json @@ -292,26 +318,30 @@ Used to get a list of guilds the client is in. } ``` + #### GET_GUILD Used to get a guild the client is in. + ###### Get Guild Argument Structure | Field | Type | Description | -| -------- | ------- | ------------------------------------------------------------ | +|----------|---------|--------------------------------------------------------------| | guild_id | string | id of the guild to get | | timeout | integer | asynchronously get guild with time to wait before timing out | + ###### Get Guild Response Structure -| Field | Type | Description | -| -------- | -------------------------------------------------------------------------- | ----------------------------------------------------- | -| id | string | guild id | -| name | string | guild name | -| icon_url | string | guild icon url | -| members | array of [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) objects | members of the guild (deprecated; always empty array) | +| Field | Type | Description | +|----------|---------------------------------------------------------------------------------------|-------------------------------------------------------| +| id | string | guild id | +| name | string | guild name | +| icon_url | string | guild icon url | +| members | array of [guild member](/developers/docs/resources/guild#guild-member-object) objects | members of the guild (deprecated; always empty array) | + ###### Example Get Guild Command Payload ```json @@ -324,6 +354,7 @@ Used to get a guild the client is in. } ``` + ###### Example Get Guild Response Payload ```json @@ -339,31 +370,35 @@ Used to get a guild the client is in. } ``` + #### GET_CHANNEL Used to get a channel the client is in. + ###### Get Channel Argument Structure | Field | Type | Description | -| ---------- | ------ | ------------------------ | +|------------|--------|--------------------------| | channel_id | string | id of the channel to get | + ###### Get Channel Response Structure -| Field | Type | Description | -| ------------ | ------------------------------------------------------------------------ | ---------------------------------------------------------------- | -| id | string | channel id | -| guild_id | string | channel's guild id | -| name | string | channel name | -| type | integer | channel type (guild text: 0, guild voice: 2, dm: 1, group dm: 3) | -| topic | string | (text) channel topic | -| bitrate | integer | (voice) bitrate of voice channel | -| user_limit | integer | (voice) user limit of voice channel (0 for none) | -| position | integer | position of channel in channel list | -| voice_states | array of [voice state](#DOCS_RESOURCES_VOICE/voice-state-object) objects | (voice) channel's voice states | -| messages | array of [message](#DOCS_RESOURCES_CHANNEL/message-object) objects | (text) channel's messages | - +| Field | Type | Description | +|--------------|-------------------------------------------------------------------------------------|------------------------------------------------------------------| +| id | string | channel id | +| guild_id | string | channel's guild id | +| name | string | channel name | +| type | integer | channel type (guild text: 0, guild voice: 2, dm: 1, group dm: 3) | +| topic | string | (text) channel topic | +| bitrate | integer | (voice) bitrate of voice channel | +| user_limit | integer | (voice) user limit of voice channel (0 for none) | +| position | integer | position of channel in channel list | +| voice_states | array of [voice state](/developers/docs/resources/voice#voice-state-object) objects | (voice) channel's voice states | +| messages | array of [message](/developers/docs/resources/message#message-object) objects | (text) channel's messages | + + ###### Example Get Channel Command Payload ```json @@ -376,6 +411,7 @@ Used to get a channel the client is in. } ``` + ###### Example Get Channel Response Payload ```json @@ -419,22 +455,26 @@ Used to get a channel the client is in. } ``` + #### GET_CHANNELS Used to get a guild's channels the client is in. + ###### Get Channels Argument Structure | Field | Type | Description | -| -------- | ------ | ----------------------------------- | +|----------|--------|-------------------------------------| | guild_id | string | id of the guild to get channels for | + ###### Get Channels Response Structure -| Field | Type | Description | -| -------- | -------------------------------------------------------------------------- | ----------------------------- | -| channels | array of partial [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | guild channels the user is in | +| Field | Type | Description | +|----------|---------------------------------------------------------------------------------------|-------------------------------| +| channels | array of partial [channel](/developers/docs/resources/channel#channel-object) objects | guild channels the user is in | + ###### Example Get Channels Command Payload ```json @@ -447,6 +487,7 @@ Used to get a guild's channels the client is in. } ``` + ###### Example Get Channels Response Payload ```json @@ -470,34 +511,34 @@ Used to get a guild's channels the client is in. } ``` + #### SET_USER_VOICE_SETTINGS Used to change voice settings of users in voice channels + ###### Set User Voice Settings Argument and Response Structure -| Field | Type | Description | -| ------- | -------------------------------------------------------------- | -------------------------------------------------------- | -| user_id | string | user id | -| pan? | [pan](#DOCS_TOPICS_RPC/setuservoicesettings-pan-object) object | set the pan of the user | -| volume? | integer | set the volume of user (defaults to 100, min 0, max 200) | -| mute? | boolean | set the mute state of the user | - -> info -> In the current release, we only support a single modifier of voice settings at a time over RPC. -> If an app changes voice settings, it will lock voice settings so that other apps connected simultaneously -> lose the ability to change voice settings. Settings reset to what they were before being changed after the -> controlling app disconnects. When an app that has previously set voice settings connects, the client will swap -> to that app's configured voice settings and lock voice settings again. This is a temporary situation that will -> be changed in the future. +| Field | Type | Description | +|---------|---------------------------------------------------------------------------|----------------------------------------------------------| +| user_id | string | user id | +| pan? | [pan](/developers/docs/topics/rpc#setuservoicesettings-pan-object) object | set the pan of the user | +| volume? | integer | set the volume of user (defaults to 100, min 0, max 200) | +| mute? | boolean | set the mute state of the user | + + +In the current release, we only support a single modifier of voice settings at a time over RPC. If an app changes voice settings, it will lock voice settings so that other apps connected simultaneously lose the ability to change voice settings. Settings reset to what they were before being changed after the controlling app disconnects. When an app that has previously set voice settings connects, the client will swap to that app's configured voice settings and lock voice settings again. This is a temporary situation that will be changed in the future. + + ###### Pan Object | Field | Type | Description | -| ----- | ----- | -------------------------------------- | +|-------|-------|----------------------------------------| | left | float | left pan of user (min: 0.0, max: 1.0) | | right | float | right pan of user (min: 0.0, max: 1.0) | + ###### Example Set User Voice Settings Command Payload ```json @@ -516,6 +557,7 @@ Used to change voice settings of users in voice channels } ``` + ###### Example Set User Voice Settings Response Payload ```json @@ -534,21 +576,26 @@ Used to change voice settings of users in voice channels } ``` + #### SELECT_VOICE_CHANNEL -Used to join and leave voice channels, group dms, or dms. Returns the [Get Channel](#DOCS_TOPICS_RPC/getchannel) response, `null` if none. +Used to join and leave voice channels, group dms, or dms. Returns the [Get Channel](/developers/docs/topics/rpc#getchannel) response, `null` if none. + ###### Select Voice Channel Argument Structure | Field | Type | Description | -| ---------- | ------- | --------------------------------------------------------------- | +|------------|---------|-----------------------------------------------------------------| | channel_id | string | channel id to join (or `null` to leave) | | timeout | integer | asynchronously join channel with time to wait before timing out | | force | boolean | forces a user to join a voice channel | +| navigate | boolean | after joining the voice channel, navigate to it in the client | -> warn -> When trying to join the user to a voice channel, you will receive a `5003` error coded response if the user is already in a voice channel. The `force` parameter should only be specified in response to the case where a user is already in a voice channel and they have **approved** to be moved by your app to a new voice channel. + +When trying to join the user to a voice channel, you will receive a `5003` error coded response if the user is already in a voice channel. The `force` parameter should only be specified in response to the case where a user is already in a voice channel and they have **approved** to be moved by your app to a new voice channel. + + ###### Example Select Voice Channel Command Payload ```json @@ -561,6 +608,7 @@ Used to join and leave voice channels, group dms, or dms. Returns the [Get Chann } ``` + ###### Example Select Voice Channel Response Payload ```json @@ -604,81 +652,92 @@ Used to join and leave voice channels, group dms, or dms. Returns the [Get Chann } ``` + #### GET_SELECTED_VOICE_CHANNEL -Used to get the client's current voice channel. There are no arguments for this command. Returns the [Get Channel](#DOCS_TOPICS_RPC/getchannel) response, or `null` if none. +Used to get the client's current voice channel. There are no arguments for this command. Returns the [Get Channel](/developers/docs/topics/rpc#getchannel) response, or `null` if none. + #### SELECT_TEXT_CHANNEL -Used to join and leave text channels, group dms, or dms. Returns the [Get Channel](#DOCS_TOPICS_RPC/getchannel) response, or `null` if none. +Used to join and leave text channels, group dms, or dms. Returns the [Get Channel](/developers/docs/topics/rpc#getchannel) response, or `null` if none. + ###### Select Text Channel Argument Structure | Field | Type | Description | -| ---------- | ------- | --------------------------------------------------------------- | +|------------|---------|-----------------------------------------------------------------| | channel_id | string | channel id to join (or `null` to leave) | | timeout | integer | asynchronously join channel with time to wait before timing out | + #### GET_VOICE_SETTINGS + ###### Get Voice Settings Response Structure -| Field | Type | Description | -| ---------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------- | -| input | [voice settings input](#DOCS_TOPICS_RPC/getvoicesettings-voice-settings-input-object) object | input settings | -| output | [voice settings output](#DOCS_TOPICS_RPC/getvoicesettings-voice-settings-output-object) object | output settings | -| mode | [voice settings mode](#DOCS_TOPICS_RPC/getvoicesettings-voice-settings-mode-object) object | voice mode settings | -| automatic_gain_control | boolean | state of automatic gain control | -| echo_cancellation | boolean | state of echo cancellation | -| noise_suppression | boolean | state of noise suppression | -| qos | boolean | state of voice quality of service | -| silence_warning | boolean | state of silence warning notice | -| deaf | boolean | state of self-deafen | -| mute | boolean | state of self-mute | - +| Field | Type | Description | +|------------------------|-----------------------------------------------------------------------------------------------------------|-----------------------------------| +| input | [voice settings input](/developers/docs/topics/rpc#getvoicesettings-voice-settings-input-object) object | input settings | +| output | [voice settings output](/developers/docs/topics/rpc#getvoicesettings-voice-settings-output-object) object | output settings | +| mode | [voice settings mode](/developers/docs/topics/rpc#getvoicesettings-voice-settings-mode-object) object | voice mode settings | +| automatic_gain_control | boolean | state of automatic gain control | +| echo_cancellation | boolean | state of echo cancellation | +| noise_suppression | boolean | state of noise suppression | +| qos | boolean | state of voice quality of service | +| silence_warning | boolean | state of silence warning notice | +| deaf | boolean | state of self-deafen | +| mute | boolean | state of self-mute | + + ###### Voice Settings Input Object | Field | Type | Description | -| ----------------- | ---------------- | -------------------------------------------------------------------------- | +|-------------------|------------------|----------------------------------------------------------------------------| | device_id | string | device id | | volume | float | input voice level (min: 0, max: 100) | | available_devices | array of objects | array of _read-only_ device objects containing `id` and `name` string keys | + ###### Voice Settings Output Object | Field | Type | Description | -| ----------------- | ---------------- | -------------------------------------------------------------------------- | +|-------------------|------------------|----------------------------------------------------------------------------| | device_id | string | device id | | volume | float | output voice level (min: 0, max: 200) | | available_devices | array of objects | array of _read-only_ device objects containing `id` and `name` string keys | + ###### Voice Settings Mode Object -| Field | Type | Description | -| -------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | -| type | string | voice setting mode type (can be `PUSH_TO_TALK` or `VOICE_ACTIVITY`) | -| auto_threshold | boolean | voice activity threshold automatically sets its threshold | -| threshold | float | threshold for voice activity (in dB) (min: -100, max: 0) | -| shortcut | [shortcut key combo](#DOCS_TOPICS_RPC/getvoicesettings-shortcut-key-combo-object) object | shortcut key combos for PTT | -| delay | float | the PTT release delay (in ms) (min: 0, max: 2000) | +| Field | Type | Description | +|----------------|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------| +| type | string | voice setting mode type (can be `PUSH_TO_TALK` or `VOICE_ACTIVITY`) | +| auto_threshold | boolean | voice activity threshold automatically sets its threshold | +| threshold | float | threshold for voice activity (in dB) (min: -100, max: 0) | +| shortcut | [shortcut key combo](/developers/docs/topics/rpc#getvoicesettings-shortcut-key-combo-object) object | shortcut key combos for PTT | +| delay | float | the PTT release delay (in ms) (min: 0, max: 2000) | + ###### Shortcut Key Combo Object -| Field | Type | Description | -| ----- | ------- | ------------------------------------------------------------ | -| type | integer | see [key types](#DOCS_TOPICS_RPC/getvoicesettings-key-types) | -| code | integer | key code | -| name | string | key name | +| Field | Type | Description | +|-------|---------|-------------------------------------------------------------------------| +| type | integer | see [key types](/developers/docs/topics/rpc#getvoicesettings-key-types) | +| code | integer | key code | +| name | string | key name | + ###### Key Types -| Type | Id | -| --------------------- | --- | -| KEYBOARD_KEY | 0 | -| MOUSE_BUTTON | 1 | -| KEYBOARD_MODIFIER_KEY | 2 | -| GAMEPAD_BUTTON | 3 | +| Type | Id | +|-----------------------|----| +| KEYBOARD_KEY | 0 | +| MOUSE_BUTTON | 1 | +| KEYBOARD_MODIFIER_KEY | 2 | +| GAMEPAD_BUTTON | 3 | + ###### Example Get Voice Settings Response Payload ```json @@ -732,33 +791,32 @@ Used to join and leave text channels, group dms, or dms. Returns the [Get Channe } ``` + #### SET_VOICE_SETTINGS -> info -> In the current release, we only support a single modifier of voice settings at a time over RPC. -> If an app changes voice settings, it will lock voice settings so that other apps connected simultaneously -> lose the ability to change voice settings. Settings reset to what they were before being changed after the -> controlling app disconnects. When an app that has previously set voice settings connects, the client will swap -> to that app's configured voice settings and lock voice settings again. This is a temporary situation that will -> be changed in the future. + +In the current release, we only support a single modifier of voice settings at a time over RPC. If an app changes voice settings, it will lock voice settings so that other apps connected simultaneously lose the ability to change voice settings. Settings reset to what they were before being changed after the controlling app disconnects. When an app that has previously set voice settings connects, the client will swap to that app's configured voice settings and lock voice settings again. This is a temporary situation that will be changed in the future. + When setting voice settings, all fields are optional. Only passed fields are updated. + ###### Set Voice Settings Argument and Response Structure -| Field | Type | Description | -| ---------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------- | -| input | [voice settings input](#DOCS_TOPICS_RPC/getvoicesettings-voice-settings-input-object) object | input settings | -| output | [voice settings output](#DOCS_TOPICS_RPC/getvoicesettings-voice-settings-output-object) object | output settings | -| mode | [voice settings mode](#DOCS_TOPICS_RPC/getvoicesettings-voice-settings-mode-object) object | voice mode settings | -| automatic_gain_control | boolean | state of automatic gain control | -| echo_cancellation | boolean | state of echo cancellation | -| noise_suppression | boolean | state of noise suppression | -| qos | boolean | state of voice quality of service | -| silence_warning | boolean | state of silence warning notice | -| deaf | boolean | state of self-deafen | -| mute | boolean | state of self-mute | - +| Field | Type | Description | +|------------------------|-----------------------------------------------------------------------------------------------------------|-----------------------------------| +| input | [voice settings input](/developers/docs/topics/rpc#getvoicesettings-voice-settings-input-object) object | input settings | +| output | [voice settings output](/developers/docs/topics/rpc#getvoicesettings-voice-settings-output-object) object | output settings | +| mode | [voice settings mode](/developers/docs/topics/rpc#getvoicesettings-voice-settings-mode-object) object | voice mode settings | +| automatic_gain_control | boolean | state of automatic gain control | +| echo_cancellation | boolean | state of echo cancellation | +| noise_suppression | boolean | state of noise suppression | +| qos | boolean | state of voice quality of service | +| silence_warning | boolean | state of silence warning notice | +| deaf | boolean | state of self-deafen | +| mute | boolean | state of self-mute | + + ###### Example Set Voice Settings Command Payload ```json @@ -773,6 +831,7 @@ When setting voice settings, all fields are optional. Only passed fields are upd } ``` + ###### Example Set Voice Settings Response Payload ```json @@ -830,12 +889,14 @@ When setting voice settings, all fields are optional. Only passed fields are upd Used to subscribe to events. `evt` of the payload should be set to the event being subscribed to. `args` of the payload should be set to the args needed for the event. + ###### Subscribe Response Structure | Field | Type | Description | -| ----- | ------ | ---------------------------- | +|-------|--------|------------------------------| | evt | string | event name now subscribed to | + ###### Example Subscribe Command Payload ```json @@ -849,6 +910,7 @@ Used to subscribe to events. `evt` of the payload should be set to the event bei } ``` + ###### Example Subscribe Response Payload ```json @@ -865,12 +927,14 @@ Used to subscribe to events. `evt` of the payload should be set to the event bei Used to unsubscribe from events. `evt` of the payload should be set to the event that was subscribed to. `args` of the payload should be set to the args needed for the previously subscribed event. + ###### Unsubscribe Response Structure | Field | Type | Description | -| ----- | ------ | -------------------------------- | +|-------|--------|----------------------------------| | evt | string | event name now unsubscribed from | + ###### Example Unsubscribe Command Payload ```json @@ -884,6 +948,7 @@ Used to unsubscribe from events. `evt` of the payload should be set to the event } ``` + ###### Example Unsubscribe Response Payload ```json @@ -896,54 +961,61 @@ Used to unsubscribe from events. `evt` of the payload should be set to the event } ``` + #### SET_CERTIFIED_DEVICES Used by hardware manufacturers to send information about the current state of their certified devices that are connected to Discord. + ###### Set Certified Devices Argument Structure -| Field | Type | Description | -| ------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -| devices | array of [certified device](#DOCS_TOPICS_RPC/setcertifieddevices-device-object) objects | a list of devices for your manufacturer, in order of priority | +| Field | Type | Description | +|---------|----------------------------------------------------------------------------------------------------|---------------------------------------------------------------| +| devices | array of [certified device](/developers/docs/topics/rpc#setcertifieddevices-device-object) objects | a list of devices for your manufacturer, in order of priority | + ###### Device Object -| Field | Type | Description | -| ------------------------- | --------------------------------------------------------------------- | -------------------------------------------------------- | -| type | [device type](#DOCS_TOPICS_RPC/setcertifieddevices-device-type) | the type of device | -| id | string | the device's Windows UUID | -| vendor | [vendor](#DOCS_TOPICS_RPC/setcertifieddevices-vendor-object) object | the hardware vendor | -| model | [model](#DOCS_TOPICS_RPC/setcertifieddevices-model-object) object | the model of the product | -| related | array of strings | UUIDs of related devices | -| echo_cancellation?\* | boolean | if the device's native echo cancellation is enabled | -| noise_suppression?\* | boolean | if the device's native noise suppression is enabled | -| automatic_gain_control?\* | boolean | if the device's native automatic gain control is enabled | -| hardware_mute?\* | boolean | if the device is hardware muted | +| Field | Type | Description | +|---------------------------|--------------------------------------------------------------------------------|----------------------------------------------------------| +| type | [device type](/developers/docs/topics/rpc#setcertifieddevices-device-type) | the type of device | +| id | string | the device's Windows UUID | +| vendor | [vendor](/developers/docs/topics/rpc#setcertifieddevices-vendor-object) object | the hardware vendor | +| model | [model](/developers/docs/topics/rpc#setcertifieddevices-model-object) object | the model of the product | +| related | array of strings | UUIDs of related devices | +| echo_cancellation?\* | boolean | if the device's native echo cancellation is enabled | +| noise_suppression?\* | boolean | if the device's native noise suppression is enabled | +| automatic_gain_control?\* | boolean | if the device's native automatic gain control is enabled | +| hardware_mute?\* | boolean | if the device is hardware muted | \*These fields are only applicable for `AUDIO_INPUT` device types + ###### Vendor Object | Field | Type | Description | -| ----- | ------ | ------------------ | +|-------|--------|--------------------| | name | string | name of the vendor | | url | string | url for the vendor | + ###### Model Object | Field | Type | Description | -| ----- | ------ | ----------------- | +|-------|--------|-------------------| | name | string | name of the model | | url | string | url for the model | + ###### Device Type | Type | Value | -| ------------ | ------------- | +|--------------|---------------| | AUDIO_INPUT | "audioinput" | | AUDIO_OUTPUT | "audiooutput" | | VIDEO_INPUT | "videoinput" | + ###### Example Set Certified Devices Command Payload ```json @@ -974,6 +1046,7 @@ Used by hardware manufacturers to send information about the current state of th } ``` + ###### Example Set Certified Devices Response Payload ```json @@ -985,17 +1058,24 @@ Used by hardware manufacturers to send information about the current state of th } ``` + #### SET_ACTIVITY Used to update a user's Rich Presence. + ###### Set Activity Argument Structure -| Field | Type | Description | -| -------- | ------------------------------------------------------- | --------------------------------------- | -| pid | integer | the application's process id | -| activity | [activity](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object) object | the rich presence to assign to the user | + +When using `SET_ACTIVITY`, the `activity` object is limited to a `type` of Playing (`0`), Listening (`2`), Watching (`3`), or Competing (`5`). + + +| Field | Type | Description | +|----------|---------------------------------------------------------------------------|-----------------------------------------| +| pid | integer | the application's process id | +| activity | [activity](/developers/docs/events/gateway-events#activity-object) object | the rich presence to assign to the user | + ###### Example Set Activity Payload ```json @@ -1005,7 +1085,9 @@ Used to update a user's Rich Presence. "pid": 9999, "activity": { "state": "In a Group", + "state_url": "https://example.com/groups/50335231-9d9d-4ebd-873b-984787ee4d1d", "details": "Competitive | In a Match", + "details_url": "https://example.com/matches/42340203-2f25-4534-8ff6-2a6509e81207", "timestamps": { "start": time(nullptr), "end": time(nullptr) + (60 * 5 + 23) @@ -1013,8 +1095,10 @@ Used to update a user's Rich Presence. "assets": { "large_image": "numbani_map", "large_text": "Numbani", + "large_url": "https://example.wiki/maps/Numbani", "small_image": "pharah_profile", - "small_text": "Pharah" + "small_text": "Pharah", + "small_url": "https://example.wiki/characters/Pharah" }, "party": { "id": GameEngine.GetPartyId(), @@ -1032,16 +1116,19 @@ Used to update a user's Rich Presence. } ``` + #### SEND_ACTIVITY_JOIN_INVITE Used to accept an Ask to Join request. + ###### Send Activity Join Invite Argument Structure | Field | Type | Description | -| ------- | --------- | ----------------------------- | +|---------|-----------|-------------------------------| | user_id | snowflake | the id of the requesting user | + ###### Example Send Activity Join Invite Payload ```json @@ -1054,16 +1141,19 @@ Used to accept an Ask to Join request. } ``` + #### CLOSE_ACTIVITY_REQUEST Used to reject an Ask to Join request. + ###### Close Activity Request Argument Structure | Field | Type | Description | -| ------- | --------- | ----------------------------- | +|---------|-----------|-------------------------------| | user_id | snowflake | the id of the requesting user | + ###### Example Close Activity Request Payload ```json @@ -1078,22 +1168,25 @@ Used to reject an Ask to Join request. #### READY + ###### Ready Dispatch Data Structure -| Field | Type | Description | -| ------ | ----------------------------------------------------------------------------------------- | ---------------------------------- | -| v | integer | RPC version | -| config | [rpc server configuration](#DOCS_TOPICS_RPC/ready-rpc-server-configuration-object) object | server configuration | -| user | partial [user](#DOCS_RESOURCES_USER/user-object) object | the user to whom you are connected | +| Field | Type | Description | +|--------|------------------------------------------------------------------------------------------------------|------------------------------------| +| v | integer | RPC version | +| config | [rpc server configuration](/developers/docs/topics/rpc#ready-rpc-server-configuration-object) object | server configuration | +| user | partial [user](/developers/docs/resources/user#user-object) object | the user to whom you are connected | + ###### RPC Server Configuration Object | Field | Type | Description | -| ------------ | ------ | --------------------- | +|--------------|--------|-----------------------| | cdn_host | string | server's cdn | | api_endpoint | string | server's api endpoint | | environment | string | server's environment | + ###### Example Ready Dispatch Payload ```json @@ -1119,13 +1212,15 @@ Used to reject an Ask to Join request. #### ERROR + ###### Error Data Structure | Field | Type | Description | -| ------- | ------- | ----------------- | +|---------|---------|-------------------| | code | integer | RPC Error Code | | message | string | Error description | + ###### Example Error Payload ```json @@ -1140,21 +1235,25 @@ Used to reject an Ask to Join request. } ``` + #### GUILD_STATUS + ###### Guild Status Argument Structure | Field | Type | Description | -| -------- | ------ | ----------------------------------- | +|----------|--------|-------------------------------------| | guild_id | string | id of guild to listen to updates of | + ###### Guild Status Dispatch Data Structure -| Field | Type | Description | -| ------ | ---------------------------------------------------------- | ------------------------------------------------------ | -| guild | partial [guild](#DOCS_RESOURCES_GUILD/guild-object) object | guild with requested id | -| online | integer | number of online users in guild (deprecated; always 0) | +| Field | Type | Description | +|--------|-----------------------------------------------------------------------|--------------------------------------------------------| +| guild | partial [guild](/developers/docs/resources/guild#guild-object) object | guild with requested id | +| online | integer | number of online users in guild (deprecated; always 0) | + ###### Example Guild Status Dispatch Payload ```json @@ -1172,17 +1271,20 @@ Used to reject an Ask to Join request. } ``` + #### GUILD_CREATE No arguments + ###### Guild Create Dispatch Data Structure | Field | Type | Description | -| ----- | ------ | ----------------- | +|-------|--------|-------------------| | id | string | guild id | | name | string | name of the guild | + ###### Example Guild Create Dispatch Payload ```json @@ -1196,18 +1298,21 @@ No arguments } ``` + #### CHANNEL_CREATE No arguments + ###### Channel Create Dispatch Data Structure | Field | Type | Description | -| ----- | ------- | ---------------------------------------------------------------- | +|-------|---------|------------------------------------------------------------------| | id | string | channel id | | name | string | name of the channel | | type | integer | channel type (guild text: 0, guild voice: 2, dm: 1, group dm: 3) | + ###### Example Channel Create Dispatch Payload ```json @@ -1222,17 +1327,20 @@ No arguments } ``` + #### VOICE_CHANNEL_SELECT No arguments + ###### Voice Channel Select Dispatch Data Structure | Field | Type | Description | -| ---------- | ------ | ------------------------------ | +|------------|--------|--------------------------------| | channel_id | string | id of channel (`null` if none) | | guild_id | string | id of guild (`null` if none) | + ###### Example Voice Channel Select Dispatch Payload ```json @@ -1246,12 +1354,15 @@ No arguments } ``` + #### VOICE_SETTINGS_UPDATE + ###### Voice Settings Argument Structure -No arguments. Dispatches the [Get Voice Settings](#DOCS_TOPICS_RPC/getvoicesettings) response. +No arguments. Dispatches the [Get Voice Settings](/developers/docs/topics/rpc#getvoicesettings) response. + ###### Example Voice Settings Dispatch Payload ```json @@ -1303,16 +1414,19 @@ No arguments. Dispatches the [Get Voice Settings](#DOCS_TOPICS_RPC/getvoicesetti } ``` + #### VOICE_STATE_CREATE/VOICE_STATE_UPDATE/VOICE_STATE_DELETE Dispatches channel voice state objects + ###### Voice State Argument Structure | Field | Type | Description | -| ---------- | ------ | ------------------------------------- | +|------------|--------|---------------------------------------| | channel_id | string | id of channel to listen to updates of | + ###### Example Voice State Dispatch Payload ```json @@ -1345,24 +1459,27 @@ Dispatches channel voice state objects } ``` + #### VOICE_CONNECTION_STATUS No arguments + ###### Voice Connection Status Dispatch Data Structure | Field | Type | Description | -| ------------ | ----------------- | ----------------------------------------------- | +|--------------|-------------------|-------------------------------------------------| | state | string | one of the voice connection states listed below | | hostname | string | hostname of the connected voice server | | pings | array of integers | last 20 pings (in ms) | | average_ping | integer | average ping (in ms) | | last_ping | integer | last ping (in ms) | + ###### Voice Connection States | Field | Description | -| ------------------ | --------------------------------- | +|--------------------|-----------------------------------| | DISCONNECTED | TCP disconnected | | AWAITING_ENDPOINT | Waiting for voice endpoint | | AUTHENTICATING | TCP authenticating | @@ -1374,6 +1491,7 @@ No arguments | NO_ROUTE | No route to host | | ICE_CHECKING | WebRTC ice checking | + ###### Example Voice Connection Status Dispatch Payload ```json @@ -1390,16 +1508,19 @@ No arguments } ``` + #### MESSAGE_CREATE/MESSAGE_UPDATE/MESSAGE_DELETE Dispatches message objects, with the exception of deletions, which only contains the id in the message object. + ###### Message Argument Structure | Field | Type | Description | -| ---------- | ------ | ------------------------------------- | +|------------|--------|---------------------------------------| | channel_id | string | id of channel to listen to updates of | + ###### Example Message Dispatch Payload ```json @@ -1441,20 +1562,24 @@ Dispatches message objects, with the exception of deletions, which only contains } ``` + #### SPEAKING_START/SPEAKING_STOP + ###### Speaking Argument Structure | Field | Type | Description | -| ---------- | ------ | ------------------------------------- | +|------------|--------|---------------------------------------| | channel_id | string | id of channel to listen to updates of | + ###### Speaking Dispatch Data Structure | Field | Type | Description | -| ------- | ------ | --------------------------------------- | +|---------|--------|-----------------------------------------| | user_id | string | id of user who started/stopped speaking | + ###### Example Speaking Dispatch Payload ```json @@ -1467,20 +1592,23 @@ Dispatches message objects, with the exception of deletions, which only contains } ``` + #### NOTIFICATION_CREATE -No arguments. This event requires the `rpc.notifications.read` [OAuth2 scope](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes). +No arguments. This event requires the `rpc.notifications.read` [OAuth2 scope](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes). + ###### Notification Create Dispatch Data Structure -| Field | Type | Description | -| ---------- | -------------------------------------------------------- | ----------------------------------------- | -| channel_id | string | id of channel where notification occurred | -| message | [message](#DOCS_RESOURCES_CHANNEL/message-object) object | message that generated this notification | -| icon_url | string | icon url of the notification | -| title | string | title of the notification | -| body | string | body of the notification | +| Field | Type | Description | +|------------|---------------------------------------------------------------------|-------------------------------------------| +| channel_id | string | id of channel where notification occurred | +| message | [message](/developers/docs/resources/message#message-object) object | message that generated this notification | +| icon_url | string | icon url of the notification | +| title | string | title of the notification | +| body | string | body of the notification | + ###### Example Notification Create Dispatch Payload ```json @@ -1525,16 +1653,19 @@ No arguments. This event requires the `rpc.notifications.read` [OAuth2 scope](#D } ``` + #### ACTIVITY_JOIN No arguments + ###### Activity Join Dispatch Data Structure -| Field | Type | Description | -| ------ | ------ | --------------------------------------------------------------------------------------------------------------------- | -| secret | string | the [`join_secret`](#DOCS_RICH_PRESENCE_HOW_TO/updating-presence-update-presence-payload-fields) for the given invite | +| Field | Type | Description | +|--------|--------|------------------------------------------------------------------------------------------------------------| +| secret | string | the [`join_secret`](/developers/docs/developer-tools/game-sdk#activitysecrets-struct) for the given invite | + ###### Example Activity Join Dispatch Payload ```json @@ -1547,16 +1678,19 @@ No arguments } ``` + #### ACTIVITY_SPECTATE No arguments + ###### Activity Spectate Dispatch Data Structure -| Field | Type | Description | -| ------ | ------ | ------------------------------------------------------------------------------------------------------------------------- | -| secret | string | the [`spectate_secret`](#DOCS_RICH_PRESENCE_HOW_TO/updating-presence-update-presence-payload-fields) for the given invite | +| Field | Type | Description | +|--------|--------|----------------------------------------------------------------------------------------------------------------| +| secret | string | the [`spectate_secret`](/developers/docs/developer-tools/game-sdk#activitysecrets-struct) for the given invite | + ###### Example Activity Spectate Dispatch Payload ```json @@ -1569,16 +1703,19 @@ No arguments } ``` + #### ACTIVITY_JOIN_REQUEST No arguments + ###### Activity Join Request Data Structure -| Field | Type | Description | -| ----- | ------------------------------------------------------- | --------------------------------------------- | -| user | partial [user](#DOCS_RESOURCES_USER/user-object) object | information about the user requesting to join | +| Field | Type | Description | +|-------|--------------------------------------------------------------------|-----------------------------------------------| +| user | partial [user](/developers/docs/resources/user#user-object) object | information about the user requesting to join | + ###### Example Activity Join Request Dispatch Payload ```json diff --git a/discord/developers/docs/topics/teams.mdx b/discord/developers/docs/topics/teams.mdx new file mode 100644 index 0000000000..765c31f282 --- /dev/null +++ b/discord/developers/docs/topics/teams.mdx @@ -0,0 +1,87 @@ +--- +title: Teams +description: Learn how to create and manage developer teams for collaborative development. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +Teams are groups of developers (or other Discord users) who want to collaborate and share access to an app's configuration, management, and payout settings. Go team(s)! + +## Creating a Team + +To create or be a member on a team, you must [enable 2FA for your Discord account](https://support.discord.com/hc/en-us/articles/219576828-Setting-up-Two-Factor-Authentication). After you have 2FA enabled, create a team by navigating to the [Teams page](https://discord.com/developers/teams) then clicking the "New Team" button. + +![Screenshot of the Teams page](/images/team-page.png) + +Once you create a team, you'll land on the **Team Information** page where you can fill out details and start inviting other Discord users to join your team. Since users added to a team have access to any apps that team owns, use caution when adding new team members. + + +Only the team Owner and team Admins can invite or remove additional users. + + +## Adding Apps to a Team + +Once your team is set up, you can create or transfer apps that will be owned by the team. Teams can have a maximum of 25 apps. + +### Creating an App + +To create a new app that belongs to a team, select the team from the **Team** dropdown in the app creation modal. If you want to keep the app under your own account's ownership, choose `Personal`: + +![Screenshot of the create application modal with a Team selected](/images/create-team-owned-app.png) + +### Transferring an App + +To transfer an existing app to a team, navigate to the [Application](https://discord.com/developers/applications) that you want to transfer. At the bottom of the app's **General Information** page, click "Transfer App to Team". + + +Once an app has been transferred to a team, it _cannot_ be transferred back. + + +![Screenshot of where to find the button to transfer an Application to a team](/images/transfer-app-to-team.png) + +## Team Member Roles + +Team members can be one of four roles (owner, admin, developer, and read-only), and each role inherits the access of those below it. Roles for team members can be configured under **Team Members** in a team's settings. + + +###### Team Member Role Types + +| Role Name | Value | Description | +|-----------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Owner\* | | Owners are the most permissible role, and can take destructive, irreversible actions like deleting team-owned apps or the team itself. Teams are limited to 1 owner. | +| Admin | admin | Admins have similar access as owners, except they cannot take destructive actions on the team or team-owned apps. | +| Developer | developer | Developers can access information about team-owned apps, like the client secret or public key. They can also take limited actions on team-owned apps, like configuring interaction endpoints or resetting the bot token. Members with the Developer role *cannot* manage the team or its members, or take destructive actions on team-owned apps. | +| Read-only | read_only | Read-only members can access information about a team and any team-owned apps. Some examples include getting the IDs of applications and exporting payout records. Members can also invite bots associated with team-owned apps that are marked private. | + +\* The owner role is not represented in the `role` field on the [team member object](/developers/docs/topics/teams#data-models-team-member-object). Instead, the `owner_user_id` field on the [team object](/developers/docs/topics/teams#data-models-team-object) should be used to identify which user has the owner role for the team. + +## Data Models + + +###### Team Object + +| field | type | description | +|---------------|----------------------------------------------------------------------------------------------|--------------------------------------| +| icon | ?string | Hash of the image of the team's icon | +| id | snowflake | Unique ID of the team | +| members | array of [team member](/developers/docs/topics/teams#data-models-team-member-object) objects | Members of the team | +| name | string | Name of the team | +| owner_user_id | snowflake | User ID of the current team owner | + + +###### Team Member Object + +| field | type | description | +|------------------|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| membership_state | integer | User's [membership state](/developers/docs/topics/teams#data-models-membership-state-enum) on the team | +| team_id | snowflake | ID of the parent team of which they are a member | +| user | partial [user](/developers/docs/resources/user#user-object) object | Avatar, discriminator, ID, and username of the user | +| role | string | [Role](/developers/docs/topics/teams#team-member-roles-team-member-role-types) of the team member | + + +###### Membership State Enum + +| name | value | +|----------|-------| +| INVITED | 1 | +| ACCEPTED | 2 | diff --git a/discord/developers/docs/topics/threads.mdx b/discord/developers/docs/topics/threads.mdx new file mode 100644 index 0000000000..324754b66a --- /dev/null +++ b/discord/developers/docs/topics/threads.mdx @@ -0,0 +1,250 @@ +--- +title: Threads +description: Learn about Discord threads - temporary sub-channels for organizing conversations. +--- + +[Threads](/developers/docs/resources/channel#channel-object) can be thought of as temporary sub-channels inside an existing channel, to help better organize conversation in a busy channel. + +Threads have been designed to be very similar to [channel](/developers/docs/resources/channel#channel-object) objects, and this topic aggregates all of the information about threads, which should all help to make migrating very straightforward. + +## Backwards Compatibility + +Threads are only available in API v9 and above. Bots that do not update to API v9 or above will not receive most gateway events for threads, or things that happen in threads (such as [Message Create](/developers/docs/events/gateway-events#message-create)). Bots on API v8 will still receive gateway events for Interactions. + +The list of gateway events that may be dropped includes, but is not limited to: + +- MESSAGE_CREATE +- MESSAGE_DELETE +- MESSAGE_DELETE_BULK +- MESSAGE_REACTION_ADD +- MESSAGE_REACTION_REMOVE +- MESSAGE_REACTION_REMOVE_ALL +- MESSAGE_REACTION_REMOVE_EMOJI +- MESSAGE_UPDATE +- THREAD_CREATE +- THREAD_UPDATE +- THREAD_DELETE +- THREAD_MEMBER_UPDATE +- THREAD_MEMBERS_UPDATE + +## Thread Fields + +Threads share and repurpose a number of the existing fields from the [channel object](/developers/docs/resources/channel#channel-object): + +- `id`, `guild_id`, `type`, `name`, `last_message_id`, `last_pin_timestamp`, `rate_limit_per_user` are being re-used +- `owner_id` has been repurposed to store the id of the user that started the thread +- `parent_id` has been repurposed to store the id of the `GUILD_TEXT` or `GUILD_ANNOUNCEMENT` channel the thread was created in + +Additionally, there are a few fields that are only available on threads: + +- `member_count` stores an approximate member count, but it stops counting at 50 (this is only used in our UI, so it is not valuable to bots) +- `message_count` and `total_message_sent` store the number of messages in a thread. The difference is that when a message is deleted, `message_count` is decremented, but `total_message_sent` will not be. (threads created before July 1, 2022 stop counting both values at 50). +- `thread_metadata` contains a few thread specific fields, `archived`, `archive_timestamp`, `auto_archive_duration`, `locked`. `archive_timestamp` is changed when creating, archiving, or unarchiving a thread, and when changing the `auto_archive_duration` field. + +## Public & Private Threads + +Public threads are viewable by everyone who can view the parent channel of the thread. Public threads must be created from an existing message, but can be "orphaned" if that message is deleted. The created thread and the message it was started from will share the same id. The [type](/developers/docs/resources/channel#channel-object-channel-types) of thread created matches the [type](/developers/docs/resources/channel#channel-object-channel-types) of the parent channel. `GUILD_TEXT` channels [create](/developers/docs/resources/channel#start-thread-from-message) `PUBLIC_THREAD` and `GUILD_ANNOUNCEMENT` channels [create](/developers/docs/resources/channel#start-thread-from-message) `ANNOUNCEMENT_THREAD`. + +Private threads behave similar to Group DMs, but in a Guild. Private threads are always [created](/developers/docs/resources/channel#start-thread-without-message) with the `GUILD_PRIVATE_THREAD` [type](/developers/docs/resources/channel#channel-object-channel-types) and can only be created in `GUILD_TEXT` channels. + +## Locked Threads +Users (including bot users) without the `MANAGE_THREADS` permission are more restricted in locked threads. Users won't be able to create or update messages in locked threads, or update properties like its title or tags. Additionally, some user activity like deleting messages and adding or removing reactions will *only* be allowed in locked threads if that thread is also active (or un-archived). + +If a user or bot user has the `MANAGE_THREADS` permission, they will still be able to make changes to the thread and messages. + +## Active & Archived Threads + +Every thread can be either active or archived. Changing a thread from archived -> active is referred to as unarchiving the thread. Threads that have `locked` set to true can only be unarchived by a user with the `MANAGE_THREADS` permission. + +Besides helping to de-clutter the UI for users, archiving exists to limit the working set of threads that need to be kept around. Since the number of archived threads can be quite large, keeping all of them in memory may be quite prohibitive. Therefore guilds are capped at a certain number of active threads, and only active threads can be manipulated. Users cannot edit messages, add reactions, use application commands, or join archived threads. The only operation that should happen within an archived thread is messages being deleted. Sending a message will automatically unarchive the thread, unless the thread has been locked by a moderator. + +Because of this constraint, the gateway protocol is designed to ensure that bots are able to have an accurate view of the full set of active threads, but archived threads are not synced up-front via the gateway. + +Threads do not count against the max-channels limit in a guild, but there is a limit on the maximum number of active threads in a guild. + +Threads automatically archive after a period of inactivity. As a server approaches the max thread limit this timer will automatically lower, usually not below the `auto_archive_duration`. In very busy channels, threads set to a 7 day auto archive may archive earlier to help avoid the server becoming "full"). "Activity" is defined as sending a message, unarchiving a thread, or changing the auto-archive time. The `auto_archive_duration` field previously controlled how long a thread could stay active, but is now repurposed to control how long the thread stays in the channel list. Channels can also set `default_auto_archive_duration`, which is used by our clients to pre-select a different `auto_archive_duration` value when a user creates a thread. + +## Permissions + +Threads generally inherit permissions from the parent channel (e.g. if you can add reactions in the parent channel, you can do that in a thread as well). + +Three permission bits are specific to threads: `CREATE_PUBLIC_THREADS`, `CREATE_PRIVATE_THREADS`, and `SEND_MESSAGES_IN_THREADS`. + + +The `SEND_MESSAGES` permission has no effect in threads; users must have `SEND_MESSAGES_IN_THREADS` to talk in a thread. + + +Private threads are similar to Group DMs, but in a guild: You must be invited to the thread to be able to view or participate in it, or be a moderator (`MANAGE_THREADS` permission). + +Finally, threads are treated slightly differently from channels in the gateway protocol. Clients will not be informed of a thread through the gateway if they do not have permission to view that thread. + +## Gateway Events + +- [Guild Create](/developers/docs/events/gateway-events#guild-create) contains a field, `threads`, which is an array of channel objects. This represents all active threads in the guild that the current user is able to view. +- When a thread is created, updated, or deleted, a [Thread Create](/developers/docs/events/gateway-events#thread-create), [Thread Update](/developers/docs/events/gateway-events#thread-update), or [Thread Delete](/developers/docs/events/gateway-events#thread-delete) event is sent. Like their channel counterparts, these just contain a thread. +- Since the gateway only syncs active threads that the user can see, if a user _gains_ access to a channel, then the gateway may need to sync the active threads in that channel to the user. It will send a [Thread List Sync](/developers/docs/events/gateway-events#thread-list-sync) event for this. + +## Thread Membership + +Each thread tracks explicit membership. There are two primary use cases for this data: + +- Clients use _their own_ [thread member](/developers/docs/resources/channel#thread-member-object) to calculate read states and notification settings. This is largely irrelevant for bots though, but is the reason for some of the syncing complexity detailed here. +- Knowing everyone that is in a thread. + +Membership is tracked in an array of [thread member](/developers/docs/resources/channel#thread-member-object) objects. These have four fields, `id` (the thread id), `user_id`, `join_timestamp`, and `flags`. Currently the only `flags` are for notification settings, but others may be added in future updates. + +### Syncing for the current user + +- A [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) Gateway Event is always sent when the current user is added to or removed from a thread. +- A [Thread Member Update](/developers/docs/events/gateway-events#thread-member-update) Gateway Event is sent whenever the current user's [thread member](/developers/docs/resources/channel#thread-member-object) object is updated. +- Certain API calls, such as listing archived threads and search will return an array of [thread member](/developers/docs/resources/channel#thread-member-object) objects for any returned threads the current user is a member of. Other API calls, such as getting a channel will return the [thread member](/developers/docs/resources/channel#thread-member-object) object for the current user as a property on the channel, if the current user is a member of the thread. +- The [Guild Create](/developers/docs/events/gateway-events#guild-create) Gateway Event will contain a [thread member](/developers/docs/resources/channel#thread-member-object) object as a property on any returned threads the current is a member of. +- The [Thread Create](/developers/docs/events/gateway-events#thread-create) Gateway Event will contain a [thread member](/developers/docs/resources/channel#thread-member-object) object as a property of the thread if the current user is a member of, and the user has recently gained access to view the parent channel. +- The [Thread List Sync](/developers/docs/events/gateway-events#thread-list-sync) Gateway Event will contain an array of [thread member](/developers/docs/resources/channel#thread-member-object) objects for any returned threads the current user is a member of. + +### Syncing for other users + + +These require the `GUILD_MEMBERS` [Gateway Intent](/developers/docs/events/gateway#gateway-intents) + + +- An API `GET` call to [`/channels//thread-members`](/developers/docs/resources/channel#list-thread-members) which returns an array of [thread member](/developers/docs/resources/channel#thread-member-object) objects. +- The [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) Gateway Event which will include all users who were added to or removed from a thread by an action. + +## Editing & Deleting Threads + +Threads can be edited and deleted with the existing `PATCH` and `DELETE` endpoints to edit a channel. + +- Deleting a thread requires the `MANAGE_THREADS` permission. +- Editing a thread to set `archived` to `false` only requires the current user has already been added to the thread. If `locked` is true, then the user must have created the thread or have `MANAGE_THREADS` +- Editing a thread to change the `name`, `archived`, `auto_archive_duration` fields requires `MANAGE_THREADS` or that the current user is the thread creator +- Editing a thread to change `rate_limit_per_user` or `locked` requires `MANAGE_THREADS` + +## NSFW Threads + +Threads do not explicitly set the `nsfw` field. All threads in a channel marked as `nsfw` inherit that setting though. + +## New Message Types + +Threads introduce a few [message types](/developers/docs/resources/message#message-object-message-types), and repurpose some others: + +- `RECIPIENT_ADD` and `RECIPIENT_REMOVE` have been repurposed to also send when a user is added to or removed from a thread by someone else +- `CHANNEL_NAME_CHANGE` has been repurposed and is sent when the thread's name is changed +- `THREAD_CREATED` is a new message sent to the parent `GUILD_TEXT` channel, used to inform users that a thread has been created. It is currently only sent in one case: when a `PUBLIC_THREAD` is created from an older message (older is still TBD, but is currently set to a very small value). The message contains a [message reference](/developers/docs/resources/message#message-reference-structure) with the `guild_id` and `channel_id` of the thread. The `content` of the message is the `name` of the thread. +- `THREAD_STARTER_MESSAGE` is a new message sent as the first message in threads that are started from an existing message in the parent channel. It _only_ contains a [message reference](/developers/docs/resources/message#message-reference-structure) field that points to the message from which the thread was started. + +## Enumerating threads + +There are four `GET` routes for enumerating threads in a specific channel: + +- [`/guilds//threads/active`](/developers/docs/resources/guild#list-active-guild-threads) returns all active threads in a guild that the current user can access, includes public & private threads +- [`/channels//users/@me/threads/archived/private`](/developers/docs/resources/channel#list-joined-private-archived-threads) returns all archived, private threads in a channel, that the current user has is a member of, sorted by thread id descending +- [`/channels//threads/archived/public`](/developers/docs/resources/channel#list-public-archived-threads) returns all archived, public threads in a channel, sorted by archive timestamp descending +- [`/channels//threads/archived/private`](/developers/docs/resources/channel#list-private-archived-threads) returns all archived, private threads in a channel, sorted by archive timestamp descending + +## Webhooks + +Webhooks can send messages to threads by using the `thread_id` query parameter. See the [execute webhook](/developers/docs/resources/webhook#execute-webhook) docs for more details. + +## Details about Thread Access and Syncing + +While the syncing of threads is similar to channels, there are two important differences that are relevant for [Thread List Sync](/developers/docs/events/gateway-events#thread-list-sync) and [Thread Create](/developers/docs/events/gateway-events#thread-create) events: + +1. The [Gateway](/developers/docs/events/gateway) will only sync threads that the app has permission to view +2. The Gateway will only sync threads once the app has "subscribed" to the guild. For context, in Discord's official clients, a subscription happens when the user visits a channel in the guild. + +These differences mean there is some unique behavior that is worth going into. + +### Thread Access + +#### Gaining Access to Private Threads + +When an app is added to a private thread, it likely doesn't have that thread in memory yet since it doesn't have permission to view it. + +Private threads are only synced to you if you are a member or a moderator. Whenever a user is added to a private thread, the Gateway also sends a [Thread Create](/developers/docs/events/gateway-events#thread-create) event. This ensures the client always has a non-null value for that thread. + + +The `THREAD_CREATE` event is also sent when the user is a moderator (and thus would already have the channel in memory). + + +#### Gaining Access to Public Threads + +Upon connecting to the Gateway, apps will be automatically subscribed to thread events and active threads. + +However, when a non-app is added to a public thread but hasn't subscribed to threads, it may not have that thread in memory yet (which is a requirement for Discord's clients). Because of this, the Gateway will send a [Thread Create](/developers/docs/events/gateway-events#thread-create) event when a user is added to _any_ thread, even if the event is not necessary for apps. + +### Channel Access + +#### Gaining Access to Channels + +When an app gains access to a channel (for example, they're given the moderator role), they likely won't have the threads in memory for that channel since the Gateway only syncs threads that the client has permission to view. To account for this, a [Thread List Sync](/developers/docs/events/gateway-events#thread-list-sync) event is sent. + +The [Thread List Sync](/developers/docs/events/gateway-events#thread-list-sync) event contains a `channel_ids` array, which is the IDs of all channels whose threads are being synced. This field can be used to first clear out any active threads whose `parent_id` is in the `channel_ids` array, and then ingest any threads that were in the event. + +#### Losing Access to Channels + +When an app loses access to a channel, the Gateway does **not** send it [Thread Delete](/developers/docs/events/gateway-events#thread-delete) event (or any equivalent thread-specific event). Instead, the app will receive the event that caused its permissions on the channel to change. + +If an app wanted to track when it lost access to any thread, it's possible but difficult as it would need to handle all cases correctly. Usually, events that cause permission changes are a [Guild Role Update](/developers/docs/events/gateway-events#guild-role-update), [Guild Member Update](/developers/docs/events/gateway-events#guild-member-update) or [Channel Update](/developers/docs/events/gateway-events#channel-update) event. + + +Discord's clients check their permissions *first* when performing an action. That way, even if it has some stale data, it does not end up acting on it. + + +Additionally, when a user or app loses access to a channel, they are not removed from the thread and will continue to be reported as a member of that thread. However, they will **not** receive any new Gateway events unless they are removed from the thread, in which case they will receive a [Thread Members Update](/developers/docs/events/gateway-events#thread-members-update) event. + +### Unarchiving a Thread + +When a thread is unarchived, there is no guarantee that an app has the thread or its member status in memory. To account for this, the Gateway will send two events (in the listed order): + +1. A [Thread Update](/developers/docs/events/gateway-events#thread-update) event, which contains the full channel object. +2. A [Thread Member Update](/developers/docs/events/gateway-events#thread-member-update) event, which is sent to all members of the unarchived thread. Discord's clients only load active threads into memory on start, so this event is sent even if it may not be relevant to most apps. + +## Forums + +A `GUILD_FORUM` (type `15`) channel is similar to a `GUILD_TEXT` channel, except *only* threads can be created in them. Unless otherwise noted, threads in forum channels behave in the same way as in text channels—meaning they use the same endpoints and receive the same Gateway events. + +Messages cannot be sent directly in forum channels. + + +More information about forum channels and how they appear in Discord can be found in the [Forum Channels FAQ](https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ#h_01G69FJQWTWN88HFEHK7Z6X79N) + + +## Media Channels + +A `GUILD_MEDIA` (type `16`) channel is similar to a `GUILD_FORUM` channel in that only threads can be created in them. Unless otherwise noted, threads in media channels behave in the same way as in forum channels - meaning they use the same endpoints and receive the same Gateway events. More information about media channels and how they appear in Discord can be found in the [media channels Help Center Article](https://creator-support.discord.com/hc/en-us/articles/14346342766743). + + +`GUILD_MEDIA` channels are in beta and still being actively developed. The API and other technical details are subject to change. + + + +### Creating Threads in Forum and Media Channels + +Within thread-only channels, threads appear as posts. Threads can be created using the [`POST /channels//threads`](/developers/docs/resources/channel#start-thread-in-forum-or-media-channel) endpoint with [slightly different parameters](/developers/docs/resources/channel#start-thread-in-forum-or-media-channel-jsonform-params) than threads in text channels. For example, when creating threads in a threads-only channel, a message is created that has the same ID as the thread which requires you to pass parameters for both a thread *and* a message. + +Threads in thread-only channels have the same permissions behavior as threads in a text channel, inheriting all permissions from the parent channel, with one exception: creating a thread in a thread-only channel only requires the `SEND_MESSAGES` permission. + +### Forum and Media Channel Fields + +It's worth calling out a few details about fields specific to thread-only channels that may be important to keep in mind: + +- The `last_message_id` field is the ID of the most recently created thread in that channel. As with messages, your app will not receive a `CHANNEL_UPDATE` event when the field is changed. Instead clients should update the value when receiving [Thread Create](/developers/docs/events/gateway-events#thread-create) events. +- The `topic` field is what is shown in the "Guidelines" section within the Discord client. +- The `rate_limit_per_user` field limits how frequently threads can be created. There is a new `default_thread_rate_limit_per_user` field on thread-only channels as well, which limits how often messages can be sent _in a thread_. This field is copied into `rate_limit_per_user` on the thread at creation time. +- The `available_tags` field can be set when creating or updating a channel, which determines which tags can be set on individual threads within the thread's `applied_tags` field. +- The `flags` field indicates any [channel flags](/developers/docs/resources/channel#channel-object-channel-flags) set for a thread-only channel. Currently only `REQUIRE_TAG` can be used, which requires that a tag from `available_tags` be specified when creating a thread in that channel. + +All fields for channels, including thread-only channels, can be found in the [Channel Object](/developers/docs/resources/channel#channel-object). + +### Forum and Media Channel Thread Fields + +A thread can be pinned within a thread-only, which will be represented as the [`PINNED` flag](/developers/docs/resources/channel#channel-object-channel-flags) in the `flags` field within a thread-only channel. A thread that is pinned will have the `(1 << 1)` flag set, and archiving that thread will unset the flag. A pinned thread will *not* auto-archive. + +The `message_count` and `total_message_sent` fields on threads in thread-only channels will increment on `MESSAGE_CREATE` events, and decrement on `MESSAGE_DELETE` and `MESSAGE_DELETE_BULK` events. There will be no specific `CHANNEL_UPDATE` event that notifies your app of changes to those fields—instead, apps should update those values when receiving corresponding events. + +All fields for threads in thread-only channels can be found in the [channel resources documentation](/developers/docs/resources/channel#start-thread-in-forum-or-media-channel-jsonform-params). + +### Forum and Media Channel Formatting + +In thread-only channels, the first message in a thread and the channel topic can both contain markdown for bulleted lists and headings (unlike text channels). diff --git a/discord/developers/docs/topics/voice-connections.mdx b/discord/developers/docs/topics/voice-connections.mdx new file mode 100644 index 0000000000..e96655046c --- /dev/null +++ b/discord/developers/docs/topics/voice-connections.mdx @@ -0,0 +1,592 @@ +--- +title: Voice +description: Technical guide to implementing voice connections with Discord. +--- + +import {ManualAnchor} from '/snippets/manualanchor.jsx' + +Voice connections operate in a similar fashion to the [Gateway](/developers/docs/events/gateway) connection. However, they use a different set of payloads and a separate UDP-based connection for voice data transmission. Because UDP is used for both receiving and transmitting voice data, your client _must_ be able to receive UDP packets, even through a firewall or NAT (see [UDP Hole Punching](https://en.wikipedia.org/wiki/UDP_hole_punching) for more information). The Discord Voice servers implement functionality (see [IP Discovery](/developers/docs/topics/voice-connections#ip-discovery)) for discovering the local machines remote UDP IP/Port, which can assist in some network configurations. + +## Voice Gateway Versioning + +To ensure that you have the most up-to-date information, please use [version 8](/developers/docs/topics/voice-connections#voice-gateway-versioning-gateway-versions). Otherwise, we cannot guarantee that the [Opcodes](/developers/docs/topics/opcodes-and-status-codes#voice) documented here will reflect what you receive over the socket. + +Previously if the version query string `v` was omitted the voice gateway would default to version 1. That behavior is being deprecated, in the future you must present a voice gateway version or your voice connection will be rejected. + + +Versions below 4 as well as the default version behavior will be discontinued as of November 18th, 2024. Connections without a version or with a version less than version 4 will be rejected as of this date. + + + +###### Gateway Versions + +| Version | Status | WebSocket URL Append | Change | +|---------|-------------|----------------------|------------------------------------------------------------------------| +| 8 | recommended | ?v=8 | Added server message buffering, missed messages re-delivered on resume | +| 7 | available | ?v=7 | Added channel options opcode | +| 6 | available | ?v=6 | Added code version opcode | +| 5 | available | ?v=5 | Added video sink wants opcode | +| 4 | available | ?v=4 | Changed speaking status to bitmask from boolean | +| 3 | deprecated | ?v=3 | Added video | +| 2 | deprecated | ?v=2 | Changed heartbeat reply from server to heartbeat ACK opcode | +| 1 | deprecated | ?v=1 | Initial version | + +## Connecting to Voice + +### Retrieving Voice Server Information + +The first step in connecting to a voice server (and in turn, a guild's voice channel) is formulating a request that can be sent to the [Gateway](/developers/docs/events/gateway), which will return information about the voice server we will connect to. Because Discord's voice platform is widely distributed, users **should never** cache or save the results of this call. To inform the gateway of our intent to establish voice connectivity, we first send an [Opcode 4 Gateway Voice State Update](/developers/docs/topics/opcodes-and-status-codes#gateway): + + +###### Gateway Voice State Update Example + +```json +{ + "op": 4, + "d": { + "guild_id": "41771983423143937", + "channel_id": "127121515262115840", + "self_mute": false, + "self_deaf": false + } +} +``` + +If our request succeeded, the gateway will respond with _two_ events—a [Voice State Update](/developers/docs/events/gateway-events#voice-state-update) event and a [Voice Server Update](/developers/docs/events/gateway-events#voice-server-update) event—meaning your library must properly wait for both events before continuing. The first will contain a new key, `session_id`, and the second will provide voice server information we can use to establish a new voice connection: + + +###### Example Voice Server Update Payload + +```json +{ + "t": "VOICE_SERVER_UPDATE", + "s": 2, + "op": 0, + "d": { + "token": "my_token", + "guild_id": "41771983423143937", + "endpoint": "sweetwater-12345.discord.media:2048" + } +} +``` + +With this information, we can move on to establishing a voice WebSocket connection. + +When sending a voice state update to change channels within the same guild, it is possible to receive a VOICE_SERVER_UPDATE with the same `endpoint` as the previous VOICE_SERVER_UPDATE. The `token` will be changed and you cannot re-use the previous session during a channel change, even if the endpoint remains the same. + + +Bot users respect the voice channel's user limit, if set. When the voice channel is full, you will not receive the Voice State Update or Voice Server Update events in response to your own Voice State Update. Having `MANAGE_CHANNELS` permission bypasses this limit and allows you to join regardless of the channel being full or not. + + + +## Establishing a Voice Websocket Connection + +Once we retrieve a session_id, token, and endpoint information, we can connect and handshake with the voice server over another secure WebSocket. Unlike the gateway endpoint we receive in an HTTP [Get Gateway](/developers/docs/events/gateway#get-gateway) request, the endpoint received from our [Voice Server Update](/developers/docs/events/gateway-events#voice-server-update) payload does not contain a URL protocol, so some libraries may require manually prepending it with "wss://" before connecting. Once connected to the voice WebSocket endpoint, we can send an [Opcode 0 Identify](/developers/docs/topics/opcodes-and-status-codes#voice) payload with our server_id, user_id, session_id, and token: + + +###### Example Voice Identify Payload + +```json +{ + "op": 0, + "d": { + "server_id": "41771983423143937", + "user_id": "104694319306248192", + "session_id": "my_session_id", + "token": "my_token", + "max_dave_protocol_version": 1 + } +} +``` + +The voice server should respond with an [Opcode 2 Ready](/developers/docs/topics/opcodes-and-status-codes#voice) payload, which informs us of the `SSRC`, UDP IP/port, and supported encryption modes the voice server expects: + + +###### Example Voice Ready Payload + +```json +{ + "op": 2, + "d": { + "ssrc": 1, + "ip": "127.0.0.1", + "port": 1234, + "modes": ["xsalsa20_poly1305", "xsalsa20_poly1305_suffix", "xsalsa20_poly1305_lite"], + "heartbeat_interval": 1 + } +} +``` + + +`heartbeat_interval` here is an erroneous field and should be ignored. The correct `heartbeat_interval` value comes from the Hello payload. + + +## Heartbeating + +In order to maintain your WebSocket connection, you need to continuously send heartbeats at the interval determined in [Opcode 8 Hello](/developers/docs/topics/opcodes-and-status-codes#voice): + + +###### Example Hello Payload + +```json +{ + "op": 8, + "d": { + "heartbeat_interval": 41250, + } +} +``` + +After receiving [Opcode 8 Hello](/developers/docs/topics/opcodes-and-status-codes#voice), you should send [Opcode 3 Heartbeat](/developers/docs/topics/opcodes-and-status-codes#voice)—which contains an integer nonce—every elapsed interval: + + +###### Example Heartbeat Payload since V8 + +```json +{ + "op": 3, + "d": { + "t": 1501184119561, + "seq_ack": 10 + } +} +``` + +Since voice gateway version 8, heartbeat messages must include `seq_ack` which contains the sequence number of last numbered message received from the gateway. See [Buffered Resume](/developers/docs/topics/voice-connections#buffered-resume) for more information. + + +###### Example Heartbeat Payload below V8 + +```json +{ + "op": 3, + "d": 1501184119561 +} +``` + +In return, you will be sent back an [Opcode 6 Heartbeat ACK](/developers/docs/topics/opcodes-and-status-codes#voice) that contains the previously sent nonce: + + +###### Example Heartbeat ACK Payload since V8 + +```json +{ + "op": 6, + "d": { + "t": 1501184119561 + } +} +``` + + +###### Example Heartbeat ACK Payload below V8 + +```json +{ + "op": 6, + "d": 1501184119561 +} +``` + +## Establishing a Voice UDP Connection + +Once we receive the properties of a UDP voice server from our [Opcode 2 Ready](/developers/docs/topics/opcodes-and-status-codes#voice) payload, we can proceed to the final step of voice connections, which entails establishing and handshaking a UDP connection for voice data. + + +###### Example Ready Payload + +```json +{ + "op": 2, + "d": { + ..., + "modes": ["aead_aes256_gcm_rtpsize", "aead_xchacha20_poly1305_rtpsize", "xsalsa20_poly1305_lite_rtpsize"] + } +} +``` + +First, we open a UDP connection to the IP and port provided in the Ready payload. If required, we can now perform an [IP Discovery](/developers/docs/topics/voice-connections#ip-discovery) using this connection. Once we've fully discovered our external IP and UDP port, we can then tell the voice WebSocket what it is, and start receiving/sending data. We do this using [Opcode 1 Select Protocol](/developers/docs/topics/opcodes-and-status-codes#voice): + + +###### Example Select Protocol Payload + +```json +{ + "op": 1, + "d": { + "protocol": "udp", + "data": { + "address": "127.0.0.1", + "port": 1337, + "mode": "aead_aes256_gcm_rtpsize" + } + } +} +``` + +## End-to-End Encryption (DAVE Protocol) + +Since September 2024, Discord is migrating voice and video in DMs, Group DMs, voice channels, and Go Live streams to use end-to-end encryption (E2EE). + + +To support our long-term privacy goals, we will **only support E2EE calls starting on March 1st, 2026** for all audio +and video conversations in direct messages (DMs), group messages (GDMs), voice channels, and Go Live streams on +Discord.

+We highly recommend you implement DAVE support as soon as possible to ensure a smooth transition. +
+ +This section is a high-level overview of how to support Discord's audio & video end-to-end encryption (DAVE) protocol, centered around the voice gateway opcodes necessary for the protocol. The most thorough documentation on the DAVE protocol is found in the [Protocol Whitepaper](https://daveprotocol.com). You may additionally be able to leverage or refer to our open-source library [libdave](https://github.com/discord/libdave) to assist your implementation. The exact format of the DAVE protocol opcodes is detailed in the [Voice Gateway Opcodes section of the protocol whitepaper](https://daveprotocol.com/#voice-gateway-opcodes). + +When a call is E2EE, all members of the call exchange keys via a [Messaging Layer Security](https://www.rfc-editor.org/rfc/rfc9420.html) (MLS) group. This group is used to derive per-sender ratcheted media keys (known only to the participants of the group) to encrypt/decrypt media frames sent in the call. + +### Binary Websocket Messages + +To reduce overhead, some of the new DAVE protocol opcodes are sent as binary instead of JSON text. See the binary column in the [Voice Opcodes Table](/developers/docs/topics/opcodes-and-status-codes#voice) to identify these opcodes. Binary websocket messages have the following format: + +| Field | Description | Size | +|-----------------|--------------------------------------------------------------------|----------------| +| Sequence Number | OPTIONAL (server -> client only) big-endian uint16 sequence number | 2 bytes | +| Opcode | Unsigned integer opcode value | 1 bytes | +| Payload | Binary message payload (format defined by opcode) | Variable bytes | + +Sequence numbers are only sent from the server to the client, and all server-sent binary opcodes require the sequence number. See [Resuming Voice Connection](/developers/docs/topics/voice-connections#resuming-voice-connection) for further details on how sequence numbers are used when present. + +### Indicating DAVE Protocol Support + +Include the highest DAVE protocol version you support in [Opcode 0 Identify](/developers/docs/topics/opcodes-and-status-codes#voice) as `max_dave_protocol_version`. Sending version 0, or omitting the `max_dave_protocol_version` field, indicates no DAVE protocol support. + +The voice gateway specifies the initial protocol version in [Opcode 4 Session Description](/developers/docs/topics/opcodes-and-status-codes#voice) under `dave_protocol_version`. This may be any non-discontinued protocol version equal to or less than your supported protocol version. + + +Clients must retain backwards-compatibility of any non-discontinued DAVE protocol versions. The voice gateway selects the lowest shared protocol version for the call. + + +### Protocol Transitions + +The voice gateway negotiates protocol version and MLS group transitions, to ensure the continuity of media being sent for the call. This can occur when the call is upgrading/downgrading to/from E2EE (in the initial transition phase), changing protocol versions, or when the MLS group is changing. + +Some opcodes include a transition ID. After preparing local state necessary to perform the transition, send [Opcode 23 DAVE Protocol Transition Ready](/developers/docs/topics/opcodes-and-status-codes#voice) to indicate to the voice gateway that you are ready to execute the transition. When all participants are ready or when a timeout has been reached, the voice gateway dispatches [Opcode 22 DAVE Protocol Execute Transition](/developers/docs/topics/opcodes-and-status-codes#voice) to confirm execution of the transition. The transition execution is what indicates to media senders that they can begin sending media with the new protocol context (e.g. without E2EE after a downgrade, with a new protocol version after a protocol version change, or using a new key ratchet after a group participant change). + +#### Downgrade + +Downgrades to protocol version 0 are announced via [Opcode 21 DAVE Protocol Prepare Transition](/developers/docs/topics/opcodes-and-status-codes#voice). This can occur during the transition phase when a client that does not support the protocol joins the call. When this transition is executed, senders should stop sending media using the protocol format. + +#### Protocol Version Change & Upgrade + +Protocol version transitions (including upgrades from protocol version 0) are announced via [Opcode 24 DAVE Protocol Prepare Epoch](/developers/docs/topics/opcodes-and-status-codes#voice). In addition to the `transition_id`, this opcode includes the `epoch_id` for the upcoming MLS epoch. + +Receiving [Opcode 24 DAVE Protocol Prepare Epoch](/developers/docs/topics/opcodes-and-status-codes#voice) with `epoch_id = 1` indicates that a new MLS group is being created. Participants must: +- prepare a local MLS group with the parameters appropriate for the DAVE protocol version +- generate and send [Opcode 26 DAVE MLS Key Package](/developers/docs/topics/opcodes-and-status-codes#voice) to deliver a new MLS key package to the voice gateway + +When the `epoch_id` is greater than 1, the protocol version of the existing MLS group is changing. + +When the transition is executed, senders must start sending media using the new protocol context (e.g. formatted for the new protocol version or using a new key ratchet). + +#### MLS Group Changes + +When the participants of the MLS group must change, existing participants receive an [Opcode 29 DAVE MLS Announce Commit Transition](/developers/docs/topics/opcodes-and-status-codes#voice) whereas new members being added to the group receive [Opcode 30 DAVE MLS Welcome](/developers/docs/topics/opcodes-and-status-codes#voice). Both opcodes include the transition ID and binary MLS Commit or MLS Welcome message. + +To prepare for the protocol transition, existing group members must apply the commit to progress their local MLS group to the correct next state. [Opcode 23 DAVE Protocol Transition Ready](/developers/docs/topics/opcodes-and-status-codes#voice) is sent when the MLS commit has been processed. + +Welcomed members send [Opcode 23 DAVE Protocol Transition Ready](/developers/docs/topics/opcodes-and-status-codes#voice) after successfully joining the group received in the MLS Welcome message. + +### External Sender + +The voice gateway must be an external sender of the MLS group, so that it can send external MLS proposals to add and remove call participants when appropriate (i.e. proposing the addition of new members when they connect and the removal of previous members when they disconnect). + +DAVE protocol participants only process proposals which arrive from the external sender, and not from any other group members. The external sender only sends Add or Remove proposals. + +The voice gateway uses [Opcode 25 DAVE MLS External Sender Package](/developers/docs/topics/opcodes-and-status-codes#voice) to provide the external sender public key and credential to MLS group participants. This message may be sent immediately on voice gateway connection or at a later time when the call is upgrading to use the DAVE protocol. + +Group creators must include the external sender they receive from the voice gateway in their MLS group extensions when creating the group. Welcomed group members ensure that the expected external sender extension is present in the group they are about to join. + +### Joining the MLS Group + +Except for the initial creation of the first group for the call, joining the MLS group always occurs after receiving [Opcode 30 DAVE MLS Welcome](/developers/docs/topics/opcodes-and-status-codes#voice). + +#### Key Packages + +To be proposed to be added to the MLS group, pending members must send an MLS key package via [Opcode 26 DAVE MLS Key Package](/developers/docs/topics/opcodes-and-status-codes#voice). Key packages are only used one time, and a new key package must be generated each time pending member is waiting to be added or re-added to the group. + +##### Identity Public Key + +MLS participants use an asymmetric keypair for MLS message signatures and authentication. The public key of this keypair is included in the key package and MLS tree. It is known to other participants in the call and is leveraged for out-of-band identity verification. + +You can choose to generate a new ephemeral keypair for every protocol call or use the same persistent keypair at all times. + +#### Initial Group + +When there is not yet an MLS group (e.g. a transport-only encrypted call is upgrading or two members have just joined a new call) all pending group members create a local group using the MLS parameters defined by the DAVE protocol version and including the voice gateway external sender received via [Opcode 25 DAVE MLS External Sender Package](/developers/docs/topics/opcodes-and-status-codes#voice). Every pending member of the group has the chance to produce the initial commit that creates the MLS group with `epoch = 1`. + +Pending group members receive add proposals for every other pending group member from the voice gateway. If an additional pending member joins while there is not yet an MLS group, they receive all in-flight proposal messages. + +Proposal and commit handling follows the same process whether or not there is an established group. See [Proposals and Commits](/developers/docs/topics/voice-connections#proposals-and-commits) + +#### Welcome + +Pending group members receive a welcome message from another group member which adds them to the MLS group. This is dispatched from the voice gateway via [Opcode 30 DAVE MLS Welcome](/developers/docs/topics/opcodes-and-status-codes#voice). + +#### Invalid Group + +If the group received in an [Opcode 30 DAVE MLS Welcome](/developers/docs/topics/opcodes-and-status-codes#voice) or [Opcode 29 DAVE MLS Announce Commit Transition](/developers/docs/topics/opcodes-and-status-codes#voice) is unprocessable, the member receiving the unprocessable message sends [Opcode 31 DAVE MLS Invalid Commit Welcome](/developers/docs/topics/opcodes-and-status-codes#voice) to the voice gateway. Additionally, the local group state is reset and a new key package is generated and sent to the voice gateway via [Opcode 26 DAVE MLS Key Package](/developers/docs/topics/opcodes-and-status-codes#voice). + +This causes the voice gateway to propose the removal and re-addition of the requesting member. + +### Proposals and Commits + +The voice gateway dispatches proposals which must be appended or revoked via [Opcode 27 DAVE MLS Proposals](/developers/docs/topics/opcodes-and-status-codes#voice). All members of the established or pending MLS group must append or revoke the proposals they receive, and then produce an MLS commit message and optionally an MLS welcome message (when committing add proposals which add new members) which they send to the voice gateway via [Opcode 28 DAVE MLS Commit Welcome](/developers/docs/topics/opcodes-and-status-codes#voice). + +In each epoch, the voice gateway dispatches the "winning" commit via [Opcode 29 DAVE MLS Announce Commit Transition](/developers/docs/topics/opcodes-and-status-codes#voice) and optionally the associated welcome messages via [Opcode 30 DAVE MLS Welcome](/developers/docs/topics/opcodes-and-status-codes#voice). The voice gateway broadcasts the first valid commit and welcome(s) it sees in the given epoch, and drops any commits later received for the out-of-date epoch. All dispatched unrevoked proposals in the epoch must be included in the commit for it to be valid. All members added in the epoch must be welcomed for the welcome to be valid. + +### Audio Frame E2EE + +While transport-encryption operates at the packet level, E2EE enabled by the DAVE protocol operates at the frame level. + +When any DAVE protocol is enabled for a call, the full contents of OPUS frames sent and received by call participants are end-to-end encrypted. + +#### ULEB128 Encoding + +Some fields in the protocol frame payload use [ULEB128 encoding](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128). This is a variable-length code compression to represent arbitrarily large unsigned integers in a small number of bytes. + +#### Payload Format + +| Field | Description | Size | +|----------------------------|------------------------------------------------------------|----------------| +| E2EE OPUS Frame | Ciphertext for E2EE OPUS frame | Variable bytes | +| AES-GCM Auth. Tag | Truncated AES128-GCM AEAD Authentication Tag | 8 bytes | +| ULEB128 Nonce | ULEB128 synchronization nonce | Variable bytes | +| ULEB128 Unencrypted Ranges | ULEB128 offset/length pairs of E2EE frame unencrypted data | Variable bytes | +| Supplemental Data Size | Unsigned integer bytes size of supplemental data | 1 byte | +| Magic Marker | 0xFAFA marker to assist with protocol frame identification | 2 bytes | + +The E2EE OPUS frame is the full ciphertext resulting from the AES128-GCM AEAD encryption described below. The authentication tag is an 8-byte truncated version of the authentication tag resulting from the AEAD encryption. + +The ULEB128 nonce is a variable length representation of the nonce used for encryption/decryption. + +The ULEB128 unencrypted ranges field is empty (0 bytes) for OPUS frames, because the full contents of the frame are encrypted. + +The supplemental data size is the sum of bytes required for: + - 8-byte authentication tag + - Variable length ULEB128 nonce + - Variable length ULEB128 unencrypted ranges + - 1 byte supplemental data size + - 2 byte magic marker + +The magic marker is a constant 2-byte value 0xFAFA. This is used by media receivers to detect protocol frames as well as by the SFU to avoid sending protocol frames to non-protocol-supporting receivers during transition periods. + +#### Frame Encryption + +Media frames are encrypted for E2EE using AES128-GCM. + +##### Sender Key Derivation + +Each media sender has a ratcheted per-sender key. There is a new per-sender ratchet created in each MLS group epoch. The initial secret for each sender's ratchet is an exported 16-byte secret from the MLS group. Keys are retrieved from the ratchet via a generation counter derived from the most-significant byte of the 4-byte nonce. + +For very long lived epochs, the nonce wrap-around must be handled so the generation does not also wrap back around to 0. + +See the [Sender Key Derivation section of the Protocol Whitepaper](https://daveprotocol.com/#sender-key-derivation) for the detailed process. + +##### Nonce + +The nonce passed to the AES128-GCM encryption and decryption functions is a full 12-byte nonce, but the protocol only uses at most 4-bytes. The 12-byte nonce can be expanded from a 4-byte truncated nonce by setting the 8 most significant bytes of the nonce to zero, with the 4 least significant bytes carrying the value of the truncated nonce. + +The generation used for the sender's key ratchet is retrieved from the most-significant byte of the 4-byte nonce (i.e., the 4th least significant byte of the full 12-byte nonce). + +##### Authentication Tag + +The authentication tag resulting from the AES128-GCM encryption is truncated to 8 bytes. Some implementations may provide the desired tag length as a parameter whereas some may always return the full 12-byte tag from which the 4 least significant bytes should be removed. + +##### AEAD Additional Data + +OPUS frames are fully encrypted and no additional data is passed to the AEAD. + +## Transport Encryption and Sending Voice + +Transport encryption between the client and the selective forwarding unit (SFU) is still used even in E2EE calls. + +Voice data sent to discord should be encoded with [Opus](https://www.opus-codec.org/), using two channels (stereo) and a sample rate of 48kHz. Voice Data is sent using a [RTP Header](https://www.rfcreader.com/#rfc3550_line548), followed by encrypted Opus audio data. Voice encryption uses the key passed in [Opcode 4 Session Description](/developers/docs/topics/opcodes-and-status-codes#voice) and the nonce formed with the 12 byte header appended with 12 null bytes to achieve the 24 required by xsalsa20_poly1305. Discord encrypts with the [libsodium](https://download.libsodium.org/doc/) encryption library. + +### Transport Encryption Modes + +| Mode | Key | Nonce | Status | +|------------------------------------|---------------------------------|-------------------------------------------------------|-----------------------| +| AEAD AES256-GCM (RTP Size) | aead_aes256_gcm_rtpsize | 32-bit incremental integer value, appended to payload | Available (Preferred) | +| AEAD XChaCha20 Poly1305 (RTP Size) | aead_xchacha20_poly1305_rtpsize | 32-bit incremental integer value, appended to payload | Available (Required) | +| XSalsa20 Poly1305 Lite (RTP Size) | xsalsa20_poly1305_lite_rtpsize | 32-bit incremental integer value, appended to payload | Deprecated | +| AEAD AES256-GCM | aead_aes256_gcm | 32-bit incremental integer value, appended to payload | Deprecated | +| XSalsa20 Poly1305 | xsalsa20_poly1305 | Copy of RTP header | Deprecated | +| XSalsa20 Poly1305 Suffix | xsalsa20_poly1305_suffix | 24 random bytes | Deprecated | +| XSalsa20 Poly1305 Lite | xsalsa20_poly1305_lite | 32-bit incremental integer value, appended to payload | Deprecated | + + +The deprecated encryption modes above will be discontinued as of November 18th, 2024. As of this date the voice gateway will not allow you to connect with one of the deprecated encryption modes. + + + +The nonce has to be stripped from the payload before encrypting and before decrypting the audio data + + +The RTP size variants determine the unencrypted size of the RTP header in [the same way as SRTP](https://tools.ietf.org/html/rfc3711#section-3.1), which considers CSRCs and (optionally) the extension preamble to be part of the unencrypted header. The deprecated variants use a fixed size unencrypted header for RTP. + +The voice gateway will report what encryption modes are available in [Opcode 2 Ready](/developers/docs/topics/opcodes-and-status-codes#voice). + +Voice gateway compatible modes will always include `aead_xchacha20_poly1305_rtpsize` but may not include `aead_aes256_gcm_rtpsize` depending on the underlying hardware. You must support `aead_xchacha20_poly1305_rtpsize`. You should prefer to use `aead_aes256_gcm_rtpsize` when it is available. + +Include your selected mode in [Opcode 1 Select Protocol](/developers/docs/topics/opcodes-and-status-codes#voice). + +Finally, the voice server will respond with a [Opcode 4 Session Description](/developers/docs/topics/opcodes-and-status-codes#voice) that includes the `mode` and `secret_key`, a 32 byte array used for [transport encryption and sending](/developers/docs/topics/voice-connections#transport-encryption-and-sending-voice) voice data: + + +###### Example Session Description Payload + +```json +{ + "op": 4, + "d": { + "mode": "aead_aes256_gcm_rtpsize", + "secret_key": [ ...251, 100, 11...], + "dave_protocol_version": 1 + } +} +``` + +We can now start encrypting and sending voice data over the previously established UDP connection. + + +###### Voice Packet Structure + +| Field | Type | Size | +|-----------------|-------------------------------|---------| +| Version + Flags | Single byte value of `0x80` | 1 byte | +| Payload Type | Single byte value of `0x78` | 1 byte | +| Sequence | Unsigned short (big endian) | 2 bytes | +| Timestamp | Unsigned integer (big endian) | 4 bytes | +| SSRC | Unsigned integer (big endian) | 4 bytes | +| Encrypted audio | Binary data | n bytes | + +## Speaking + +Speaking updates are used to update the **speaking modes** used by the client. This speaking mode is set using a [Opcode 5 Speaking](/developers/docs/topics/opcodes-and-status-codes#voice) payload. The client must send this at least once before sending audio to update the SSRC and set the initial speaking mode. + +The following flags can be used as a bitwise mask. For example `5` would be priority and voice. The speaking mode should not be 0 to allow sending audio. + +| Flag | Meaning | Value | +|------------|----------------------------------------------------------------|----------| +| Microphone | Normal transmission of voice audio | `1 << 0` | +| Soundshare | Transmission of context audio for video, no speaking indicator | `1 << 1` | +| Priority | Priority speaker, lowering audio of other speakers | `1 << 2` | + + +###### Example Speaking Payload + +```json +{ + "op": 5, + "d": { + "speaking": 5, + "delay": 0, + "ssrc": 1 + } +} +``` + + +You must send at least one [Opcode 5 Speaking](/developers/docs/topics/opcodes-and-status-codes#voice) payload before sending voice data, or you will be disconnected with an invalid SSRC error. + + + +The `delay` property should be set to `0` for bots that use the voice gateway. + + +### Voice Data Interpolation + +When there's a break in the sent data, the packet transmission shouldn't simply stop. Instead, send five frames of silence (`0xF8, 0xFF, 0xFE`) before stopping to avoid unintended Opus interpolation with subsequent transmissions. + +## Resuming Voice Connection + +When your client detects that its connection has been severed, it should open a new WebSocket connection. Once the new connection has been opened, your client should send an [Opcode 7 Resume](/developers/docs/topics/opcodes-and-status-codes#voice) payload: + + +###### Example Resume Connection Payload Since V8 + +```json +{ + "op": 7, + "d": { + "server_id": "41771983423143937", + "session_id": "my_session_id", + "token": "my_token", + "seq_ack": 10 + } +} +``` + + +###### Example Resume Connection Payload Before V8 + +```json +{ + "op": 7, + "d": { + "server_id": "41771983423143937", + "session_id": "my_session_id", + "token": "my_token" + } +} +``` + +If successful, the Voice server will respond with an [Opcode 9 Resumed](/developers/docs/topics/opcodes-and-status-codes#voice) to signal that your client is now resumed: + + +###### Example Resumed Payload + +```json +{ + "op": 9, + "d": null +} +``` + +If the resume is unsuccessful—for example, due to an invalid session—the WebSocket connection will close with the appropriate [close event code](/developers/docs/topics/opcodes-and-status-codes#voice-voice-close-event-codes). You should then follow the [Connecting](/developers/docs/topics/voice-connections#connecting-to-voice) flow to reconnect. + +### Buffered Resume + +Since voice gateway version 8, the gateway can resend buffered messages that have been lost upon resume. To support this, the gateway includes a sequence number with all messages that may need to be re-sent. + + +###### Example Message With Sequence Number + +```json +{ + "op": 5, + "d": { + "speaking": 0, + "delay": 0, + "ssrc": 110 + }, + "seq": 10 +} +``` + +A client using voice gateway version 8 must include the last sequence number they received under the data `d` key as `seq_ack` in both the [Opcode 3 Heartbeat](/developers/docs/topics/opcodes-and-status-codes#voice) and [Opcode 7 Resume](/developers/docs/topics/opcodes-and-status-codes#voice) payloads. + +If no sequence numbered messages have been received, `seq_ack` can be omitted or included with a value of -1. + +The gateway server uses a fixed bit length sequence number and handles wrapping the sequence number around. Since voice gateway messages will always arrive in order, a client only needs to retain the last sequence number they have seen. + +If the session is successfully resumed the voice gateway will respond with an [Opcode 9 Resumed](/developers/docs/topics/opcodes-and-status-codes#voice) and will re-send any messages that the client did not receive. + +The resume may be unsuccessful if the voice gateway buffer for the session no longer contains a message that has been missed. In this case the session will be closed and you should then follow the [Connecting](/developers/docs/topics/voice-connections#connecting-to-voice) flow to reconnect. + +## IP Discovery + +Generally routers on the Internet mask or obfuscate UDP ports through a process called NAT. Most users who implement voice will want to utilize IP discovery to find their external IP and port which will then be used for receiving voice communications. To retrieve your external IP and port, send the following UDP packet to your voice port (all numeric are big endian): + +| Field | Description | Size | +|---------|----------------------------------------------------------------|----------| +| Type | Values 0x1 and 0x2 indicate request and response, respectively | 2 bytes | +| Length | Message length excluding Type and Length fields (value 70) | 2 bytes | +| SSRC | Unsigned integer | 4 bytes | +| Address | Null-terminated string in response | 64 bytes | +| Port | Unsigned short | 2 bytes | diff --git a/discord/developers/docs/tutorials/configuring-app-metadata-for-linked-roles.mdx b/discord/developers/docs/tutorials/configuring-app-metadata-for-linked-roles.mdx new file mode 100644 index 0000000000..801b3a6b3f --- /dev/null +++ b/discord/developers/docs/tutorials/configuring-app-metadata-for-linked-roles.mdx @@ -0,0 +1,187 @@ +--- +title: Configuring App Metadata for Linked Roles +description: Tutorial for setting up role connection metadata to link external app data with Discord roles. +--- + +Linked roles are a type of role in Discord that requires a user to connect to 3rd-party services and meet defined criteria. A role's criteria could just include the user connecting to that service, but it's often more narrow—like having a verified account, having certain stats, or having more than a certain number of followers. + +Apps can define their own [role connection metadata](/developers/docs/resources/application-role-connection-metadata), which admins can use to configure linked roles in servers where that app is installed. Apps must also set up an [OAuth2 flow](/developers/docs/topics/oauth2) to allow users to authenticate and grant the required `role_connections.write` scope. + +This tutorial walks through building a Discord app in JavaScript with linked roles support. + + +All of the sample code used in this tutorial can be found in the [`linked-roles-sample` GitHub repo](https://github.com/discord/linked-roles-sample) + + +--- + +## Creating an app + +The first thing we’ll do is create an app through the [developer dashboard](https://discord.com/developers/applications). If you already have an app created, you can jump right to the [Running your app](/developers/docs/tutorials/configuring-app-metadata-for-linked-roles#running-your-app) section. + + +Basic steps to create an app are outlined below, but a more detailed walkthrough is in the [Getting Started guide](/developers/docs/quick-start/getting-started). + + +- Navigate to the [developer dashboard](https://discord.com/developers/applications) +- Click **New Application** in the upper right corner, then select a name and create your app +- Click on the **Bot** tab on the left sidebar. On that page, click **Reset Token** and store the token somewhere safe (like in a password manager) + + +Bot tokens are used to authorize API requests and carry your bot's permissions, making them highly sensitive. Never share your token or check it into any kind of version control. + + +### Adding scopes + +Apps need approval from installing users to perform actions inside of Discord. So before installing your app, let's add some scopes to request during installation. + +- Click on `OAuth2` in the left sidebar, then `URL generator` +- Check the `bot` scope +- After the scope is selected, you should see a **Generated URL** which can be used to install your app + + +See a list of all [OAuth2 scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes), or read more on [user permissions](/developers/docs/topics/permissions) in the documentation. + + +### Installing your app + +Copy the **Generated URL** from above, and paste it into your browser. You’ll be guided through the installation flow, where you should make sure you’re installing the app on a server where you can develop and test. + +After installing your app, you can head over to your server and see that it has joined ✨ + +## Running your app + +All of the code used in the example app can be found in the [GitHub repository](https://github.com/discord/linked-roles-sample). + +### Remix the project + +This guide uses Glitch, which allows you to quickly clone and develop an app from within your browser. There are also instructions on developing locally using ngrok in the README if you'd prefer. + + +While Glitch is great for development and testing, [it has technical limitations](https://help.glitch.com/kb/article/17-technical-restrictions/) so other hosting providers should be considered for production apps. + + +To start, [remix (or clone) the Glitch project 🎏](https://glitch.com/edit/#!/remix/linked-role-discord-bot) + +When you remix the project, you'll see a new Glitch project with a unique name similar to this: + +![Glitch Remix](/images/tutorials/linked-roles-glitch.png) + +#### Project structure + +All of the files for the project are on the left-hand side. Here's a quick glimpse at the structure: + +``` +├── assets -> images used in this tutorial +├── src +├──├── config.js -> Parsing of local configuration +├──├── discord.js -> Discord specific auth & API wrapper +├──├── register.js -> Tool to register the metadata schema +├──├── server.js -> Main entry point for the application +├──├── storage.js -> Provider for storing OAuth2 tokens +├── .env -> your credentials and IDs +├── .gitignore +├── package.json +└── README.md +``` + +### Configure your app + +There's already some code in your `server.js` file, but you’ll need your app’s token and ID to make requests. All of your credentials can be stored directly in the `.env` file. + + +It bears repeating that you should never check any credentials or secrets into source control. The getting started project's `.gitignore` comes pre-loaded with `.env` to prevent it. + + +First, copy your bot user’s token from earlier and paste it in the `DISCORD_TOKEN` variable in your `.env` file. + +Next, navigate to your app settings in the developer portal, and navigate to OAuth2 -> General. Copy the Client ID and Client Secret for your application, and paste the values as `DISCORD_CLIENT_ID` and `DISCORD_CLIENT_SECRET` in your `.env`. + +![Configure OAuth2](/images/tutorials/linked-roles-oauth-config.png) + +Now, we need to set the Redirect URL that will be used for our OAuth2 flow. Go back to Glitch, and click the `Share` button for your project. Copy the public live URL for your app: + +![Glitch Share](/images/tutorials/linked-roles-glitch-shared-url.png) + +Go back to the OAuth2 -> General tab in the Discord developer portal, and add a new redirect for your app using the Glitch URL and the `/discord-oauth-callback` route. Copy this URL, then paste it as `DISCORD_REDIRECT_URI` in your `.env`. + +Go to the General Information tab in the developer portal, and scroll down to the `Linked Roles Verification Url` field. Paste the base URL to your Glitch app, add the `/linked-role` route, then save. + + +For the Glitch project used in the screenshots, the verification URL would be `https://adjoining-crawling-yamamomo.glitch.me/linked-role` + + +![Verify endpoint](/images/tutorials/linked-roles-verify-endpoint.png) + +Finally, to generate a unique cookie secret, go back to Glitch, and click on the `Terminal` tab. Run the following commands: + +``` +$ node +crypto.randomUUID() +``` + +Copy the randomly generated UUID, and paste it into your `.env` as `COOKIE_SECRET`. Your `.env` should look something like this: + +``` +DISCORD_CLIENT_ID: +DISCORD_CLIENT_SECRET: +DISCORD_TOKEN: +DISCORD_REDIRECT_URI: https://.glitch.me/discord-oauth-callback +COOKIE_SECRET: +``` + +## Registering your metadata schema + +As a one-time step, you must tell Discord which metadata fields you are going to allow admins to use for linked roles associated with your app. + +To configure connection metadata for your app, you'll call the [PUT /users/@me/applications/<application_id>/role-connection](/developers/docs/resources/application-role-connection-metadata#update-application-role-connection-metadata-records) method with [application connection role metadata](/developers/docs/resources/application-role-connection-metadata#application-role-connection-metadata-object). In the sample app, this is handled in [`src/register.js`](https://github.com/discord/linked-roles-sample/blob/main/src/register.js), and can be run via the command line. + +Go back to Glitch, click the **terminal** tab, and run the following command: + +``` +$ node src/register.js +``` + +![Register Metadata Schema](/images/tutorials/linked-roles-register.png) + +## Trying it out + +Now that you've built your app, let's give it a try both from the server owner and the user's perspective. + +### Creating the linked role + +To try out the app, we'll create a linked role in a server where you have admin permissions. Open up the **Server Settings**, select **Roles**, and click on `Create Role`. + +Give the role a name, save it, then click on `Links`. Click the `Add requirement` button, and you should see your bot in the list of available Apps. Click on it, and you will see a setup screen where you can configure specific criteria for your role. + +![Verification Setup](/images/tutorials/linked-roles-verification-setup.png) + +### Acquiring the role + +To acquire your newly created role, click the server name in the upper left corner of the screen, and select `Linked Roles`. Click on your role, and it will present the opportunity to connect your account. + + +When you connect your account, one of the scopes requested in the OAuth flow is `role_connections.write`, which is required for an app to update a user's role connection information. + + +![Connect accounts](/images/tutorials/linked-roles-connect-account.png) + +Click on the linked role criteria. This should lead to the Discord OAuth2 consent screen. Click `Authorize`, and then return to Discord. + +![Consent Dialog](/images/tutorials/linked-roles-consent-dialog.png) + +After returning to Discord, you should see your account granted the linked role. + +![Connected](/images/tutorials/linked-roles-connected.png) + +Finally, create a new private channel, and add the new linked role. + +## Tips & Tricks + +### Token storage + +This app largely relies on Discord's [OAuth2](/developers/docs/topics/oauth2) implementation to obtain access tokens. This model of user based authentication relies on storing refresh tokens, and using them to acquire access tokens. The example code in [`src/storage.js`](https://github.com/discord/linked-roles-sample/blob/main/src/storage.js) uses in-memory storage to manage these tokens, but for any production deployment a database with persistent storage should be used. + +### Advanced examples + +For a more complex example using the Fitbit API, see https://github.com/JustinBeckwith/fitbit-discord-bot/. diff --git a/discord/developers/docs/tutorials/developing-a-user-installable-app.mdx b/discord/developers/docs/tutorials/developing-a-user-installable-app.mdx new file mode 100644 index 0000000000..9ffa7f51ed --- /dev/null +++ b/discord/developers/docs/tutorials/developing-a-user-installable-app.mdx @@ -0,0 +1,389 @@ +--- +title: Developing A User-Installable App +description: Tutorial for creating Discord apps that can be installed to user accounts. +--- + +import {LinkButton} from '/snippets/linkbutton.jsx' + +Discord apps can be installed to servers, users, or both. This guide will walk you through building a basic game integration app that is installable to both Discord users and servers. + +While the tutorial will focus on supporting different [installation contexts](/developers/docs/resources/application#installation-context), we'll be building a basic game integration along the way with a wiki lookup with user-specific bookmarking and a server leaderboard. The app has four commands (`/link`, `/profile`, `/leaderboard`, and `/wiki`) that can be run in different installation and interaction contexts (which are concepts we'll dig into later in the tutorial). + + + +- **[GitHub repository](https://github.com/discord/user-install-example)** where the code from this guide lives. +- **[discord-interactions](https://github.com/discord/discord-interactions-js)**, a library that provides types and helper functions for Discord apps. +- **[Express](https://expressjs.com/)**, a popular JavaScript web framework we'll use to create a server where Discord can send requests. +- **[ngrok](https://ngrok.com/)**, a tool that lets us tunnel our local server to a public URL where Discord can send requests. + + +--- + +## Step 0: Project Setup + +Before we dig in, you'll need the project code from the [sample app repository](https://github.com/discord/user-install-example). + + +``` +├── .env.sample -> sample .env file +├── app.js -> main entrypoint for the app +├── commands.js -> slash command payloads + helpers +├── game.js -> logic specific to the fake game +├── utils.js -> utility functions and enums +├── package.json +├── README.md +└── .gitignore +``` + + + +We'll be developing our app locally with a little help from [ngrok](https://ngrok.com/), but you can use your preferred development environment. + + +If you don't have [NodeJS](https://nodejs.org/en/download/) installed, install that first. + +Now, clone the project code to your machine using the command line: + +``` +git clone https://github.com/discord/user-install-example.git +``` + +Then navigate to the directory and install the project's dependencies: + +``` +# navigate to directory +cd user-install-example + +# install dependencies +npm install +``` + +With that out of the way, open your new project in the code editor of your choice, then we'll move ahead to setting up your Discord app. + +## Step 1: Creating an App + +First, you'll need to create an app in the developer portal if you don't have one already: + +Create App + +Enter a name for your app, then press **Create**. + +After you create your app, you'll land on the **General Overview** page of the app's settings where you can update basic information about your app like its description and icon. + +### Fetching app credentials + +While we're in your app's settings, we'll want to get a few sensitive values for your app, like its token and ID. + + +Your token is used to authorize API requests and carry your app's permissions, so they are *highly* sensitive. Make sure to never share your token or check it into any kind of version control. + + +Back in your project folder, rename the `.env.sample` file to `.env`. `.env` is where we'll store all of your app's credentials. + +We'll need three values from your app's settings for your `.env` file: + +- On the **General Information** page, copy the value for **Application ID**. In `.env`, replace `` with the ID you copied. +- Back on the **General Information** page, copy the value for **Public Key**, which is used to ensure HTTP requests are coming from Discord. In `.env`, replace `` with the value you copied. +- On the **Bot** page under **Token**, click "Reset Token" to generate a new bot token. In `.env`, replace `` with your new token. + +Now that you have the credentials you need, we'll configure your app to support different installation contexts. + +### Add Guild Members intent + +The sample app fetches members in the server when constructing a fake game leaderboard. Getting server members requires a special permission called a [privileged intent](/developers/docs/events/gateway#privileged-intents), so we'll add that to our app. + +Go to the **Bot** page and find the **Privileged Gateway Intents** section. Toggle "Server Member Intent" to be active. + +### Choosing Supported Installation Contexts + +An app's **[installation context](/developers/docs/resources/application#installation-context)** defines how it's installed: to a server, to a user, or both. + +We're going to configure our app to support both installation contexts, and while that's a good default for most apps, some apps may only make sense in one context or the other. + +In your app's settings, go to the **Installation** page from the sidebar. Under **Installation Contexts**, check both **User Install** and **Guild Install**, then press **Save Changes**. + +### Configuring Default Install Settings + +The default install settings of your app determines the default [scopes](/developers/docs/topics/oauth2#shared-resources-oauth2-scopes) and [bot user permissions](/developers/docs/topics/permissions) for each supported installation context. At the moment, apps installed to a user context only support the `applications.commands` scope (which allows your app to install [commands](/developers/docs/interactions/application-commands)) in the default install settings. + +###### Update Install Link + +Before adding default install settings, we need to select Discord Provided Link for the app's [install link](/developers/docs/resources/application#install-links). Under the **Install Link** section, select `Discord Provided Link` from the dropdown if it isn't already selected (it should be by default). Once its selected, the **Default Install Settings** will appear. + +###### Adding Default Install Settings + +Under the **Default Install Settings** section: +- For **User Install**, add the `applications.commands` scope +- For **Guild Install**, add the `applications.commands` scope and `bot` scope. When you select `bot`, a new **Permissions** menu will appear to select the bot user's permissions. Select any permissions that you may want for your app—for now, I'll just select `Send Messages`. + + +Permissions for a bot user are very similar to permissions for other Discord users. Details about permissions, and a list of available permissions is on the [Permissions](/developers/docs/topics/permissions#permissions-bitwise-permission-flags) page. + + +After you've selected the scopes and permissions for your app, click **Save Changes**. + +![Installation settings in App Settings](/images/tutorials/user-install-settings.png) + +### Installing your app + +Finally, we'll install your new app to both a test server and your user account so that we can test in both installation contexts. + +###### Install to server + +To install your app to your test server, copy the default Install Link for your app from the **Installation** page. Paste the link in your browser and hit enter, then select "Add to server" in the installation prompt. + +Select your test server, and follow the installation prompt. Once your app is added to your test server, you should see it appear in the member list. + +###### Install to user account + +Next, install your app to your user account. Paste the same Install Link in your browser and hit enter. This time, select "Add to my apps" in the installation prompt. + +Follow the installation prompt to install your app to your user account. Once it's installed you can open a DM with it. + +## Step 2: Setting Up Commands + +Next, we'll register the [application commands](/developers/docs/interactions/application-commands) for our app. But before touching code, it's important to understand the concept of command contexts: + + + +Commands have two context fields that can be set when creating or updating a command which let you limit the supported install methods and surfaces in Discord for that command: + +- **`integration_types`** lets you control which **[installation contexts](/developers/docs/interactions/application-commands#installation-context)** a command is supported (user, guild, or both). For example, the `/link` and `/profile` commands we'll be creating are only available when the app is installed to a user. +- **`contexts`** lets you set the **[interaction contexts](/developers/docs/interactions/application-commands#interaction-contexts)**, or the surfaces in Discord, where a command can be used (in a guild channel, in your bot user's DM, and within other DMs or GDMs). For example, the `/leaderboard` command we'll be creating is only available when the command is run from a guild channel. + +More information and details about command contexts are in the [contexts](/developers/docs/interactions/application-commands#contexts) documentation, but for now we'll get a better understanding of contexts by using them in our sample app. + + +### Commands in the sample project + +We'll be setting up four commands for our sample app that all have *slightly* different contexts, which are included in the table below: + +| Name | Description | Installation Contexts (`integration_types`) | Interaction Contexts (`contexts`) | +|----------------|--------------------------------------------------------|---------------------------------------------|--------------------------------------| +| `/leaderboard` | View game leaderboard for the current server | `GUILD_INSTALL` | `GUILD` | +| `/wiki` | Find information about game items and characters | `GUILD_INSTALL`, `USER_INSTALL` | `GUILD`, `BOT_DM`, `PRIVATE_CHANNEL` | +| `/profile` | Get information about your game inventory and progress | `USER_INSTALL` | `GUILD`, `BOT_DM`, `PRIVATE_CHANNEL` | +| `/link` | Link your game account to Discord | `USER_INSTALL` | `BOT_DM` | + + +The supported installation contexts for a command affects which interaction contexts you can set. Specifically, the `PRIVATE_CHANNEL` interaction context can only be included in `contexts` if `USER_INSTALL` is included in `integration_types` for the command. Read details in the [documentation](/developers/docs/interactions/application-commands#contexts). + + +The payloads for our app's commands are in `commands.js` in the project folder in case you want to change any values or see what the command's context fields (`integration_types` and `contexts`) look like for each of the commands in the table above. + +### Registering the commands + +Now let's register your app's commands so you can see them in Discord. In your project folder run: + +``` +npm run register +``` + +The register command will call the [Create Global Application Command](/developers/docs/interactions/application-commands#create-global-application-command) endpoint for each of the command payloads in `commands.js`. + +After your new commands have been created, you can go into Discord and look for the commands in the surfaces where we made them available: +- In **channels within the guild you installed your app**, you should see `/leaderboard`, `/wiki`, and `/profile` +- In **channels within any of your guilds**, you should see `/wiki` and `/profile` +- In **your app's DM**, you should see `/wiki`, `/profile`, and `link` +- And finally, **in DMs or GDMs with other users**, you should see `/wiki` and `/profile` + +However, if you try to run any of the commands, you'll get an error :( + +...let's fix that. + +## Step 3: Handling Interactivity + +To receive and handle interactive requests, we'll set up an **Interactions Endpoint URL**, which is a public URL where Discord sends your app's interactions. + +### Set up a public endpoint + +To set up a public endpoint we'll start our app, which runs an [Express](https://expressjs.com/) server, then use [ngrok](https://ngrok.com/) to expose our server publicly. + +First, go to your project's folder and run the following to start your app: + +``` +npm run start +``` + +There should be some output indicating your app is running on port 3000. Behind the scenes, our app is ready to handle interactions from Discord, which includes verifying security request headers and responding to `PING` requests. We're skipping over a lot of the details in this tutorial, but details about preparing apps for interactions is in the [Interactions Overview](/developers/docs/interactions/overview#preparing-for-interactions) documentation. + + +By default, the server will listen to requests sent to port 3000, but if you want to change the port, you can specify a `PORT` variable in your `.env` file. + + +Next, we'll start our ngrok tunnel. If you don't have ngrok installed locally, you can install it by following the instructions on the [ngrok download page](https://ngrok.com/download). + +After ngrok is installed locally, open a new terminal and create a public endpoint that will forward requests to your Express server: + + +``` +ngrok http 3000 +``` + +The output will include a **Forwarding** URL, which is the publicly-accessible URL we'll use for our Interactions Endpoint URL in the next step. + +### Configuring an interaction endpoint URL + +Let's configure our app's **Interaction Endpoint URL**. + +Go to your [app's settings](https://discord.com/developers/applications) and on the **General Information** page under **Interaction Endpoint URL**, paste your new ngrok URL and append `/interactions` (it'll be something like `https://84c5df474.ngrok-free.dev/interactions`). + +Click **Save Changes** and if all is well, your Interactions Endpoint URL should be verified by Discord. + + +If you have troubles verifying your endpoint, make sure both ngrok and your app is running on the same port, and that you've copied the ngrok URL correctly + + +### Understanding metadata for interactions + +Now that our Interactions Endpoint URL is set up, we should now be able to run our app's commands. Go to your app's DM and run `/profile`, and your app should respond with a sample game profile. + +Back on the command line, our app is logging incoming requests from Discord, so you can see what the request body for your command invocation looked like. + + +The payload below is condensed to be more readable, but your interaction request body should look something like this: + +```json +{ + "app_permissions": "442368", + "application_id": "234248956100616262", + "authorizing_integration_owners": { "1": "1090372582781497424" }, + "channel": { + // Partial channel object corresponding to channel_id + }, + "channel_id": "1234563982236504123", + "context": 1, + "data": { "id": "1234358421659193405", "name": "link", "type": 1 }, + "entitlements": [], + "id": "1234968734674853908", + "locale": "en-US", + "token": "a really long interactions token that your app can use to respond to the interactions", + "type": 2, + "user": { + // Partial user object + }, + "version": 1 +} +``` + + + +To see which command was run, you can look at the [`data` object](/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-data). + +However, for this tutorial, we're going to focus more on the metadata related to installation and interaction contexts. There are a few metadata fields you'll want to pay attention to when building an app that can be installed to multiple interaction contexts— + +###### `context` + +`context` tells you which [interaction context](/developers/docs/interactions/application-commands#interaction-contexts) the command was invoked from. Since I triggered the command from my app's DM the `context` is `1` (or `BOT_DM`). + +With interaction context, something to keep in mind in `BOT_DM` is only the *DM with your bot user*. If you run the same command in a DM with your bestie, or in a group DM, the interaction context will be `PRIVATE_CHANNEL` (`2`). + +###### `authorizing_integration_owners` + +`authorizing_integration_owners` provides data about any ID relevant to the installation context(s) associated with the interaction. + +The keys in the object are the relevant installation context(s) (`GUILD_INSTALL`/`"0"` and/or `USER_INSTALL`/`"1"`). The values depend on the key, but for `USER_INSTALL` the key will always be the ID of the user that authorized your app. + + +`authorizing_integration_owners` is not the same as the user that triggered the interaction. Information about the user that triggered the interaction is in the `user` object. + + +Understanding the authorization owner can be helpful when handling interactions from message components for apps installed to a user, which is discussed more in the [message component interactions](/developers/docs/tutorials/developing-a-user-installable-app#using-metadata-for-message-component-interactions) section. Or you can find technical details in the [Authorizing Integration Owners](/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object) documentation. + +###### `app_permissions` + +`app_permissions` are the bitwise set of permissions your app has in the place where the interaction was triggered. The permissions your app has will be different for DMs with your app, in servers, and G(DM)s with other users. + +In the sample payload, the value is `"442368"`. + +These values can be helpful when deciding how you want your app to [respond to the interaction](/developers/docs/interactions/receiving-and-responding#responding-to-an-interaction). For example, perhaps you want your app to respond ephemerally when a specific command is invoked from a server, which the sample app does for the `/profile` command. + +### Using metadata for command interactions + +As mentioned above, the `/profile` command for our app will respond ephemerally, meaning only the invoking user will see the response, when invoked from a server. If it's invoked within a DM with the bot user, it'll respond with a non-ephemeral message. In the project, you can see this logic in `app.js` when handling the `/profile` command: + + + +Below is the logic for handling the `/profile` command. When the context for the interaction is in a server, we'll make our interaction response ephemeral and add a button so the user can share their profile if they want. + +```javascript +// "profile" command +if (name === 'profile') { + const profile = getFakeProfile(0); + const profileEmbed = createPlayerEmbed(profile); + + // Use interaction context that the interaction was triggered from + const interactionContext = req.body.context; + + // Construct `data` for our interaction response. The profile embed will be included regardless of interaction context + let profilePayloadData = { + embeds: [profileEmbed], + }; + + // If profile isn't run in a DM with the app, we'll make the response ephemeral and add a share button + if (interactionContext !== 1) { + // Make message ephemeral + profilePayloadData['flags'] = 64; + // Add button to components + profilePayloadData['components'] = [ + { + type: 1, + components: [ + { + type: 2, + label: 'Share Profile', + custom_id: 'share_profile', + style: 2, + }, + ], + }, + ]; + } + + // Send response + return res.send({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + data: profilePayloadData, + }); +} +``` + + +In the sample app code, we assign the value of `context` in the request body to a new `interactionContext` variable. Based on the context, we modify how we respond to the command interaction. If it was run in a guild or within a G(DM) other than the DM with the app's bot user, `flags` is set to `64` to make the response ephemeral, and a new button component is added so that the user can share their profile if they want. + +### Using metadata for message component interactions + +Message component interactions can be triggered by any user the component is visible to, regardless of the installation context. Since potentially any user can trigger the component, it can be helpful to use metadata to understand context about the installation. + +Consider we added a new `/game` command supported in the `USER_INSTALL` installation context that a user could trigger to send a message to whatever guild or group DM they're in to ask others if they're interested in joining a game match: + +![Sample game command response](/images/tutorials/user-install-game-message.png) + +When someone clicks on the button, our app would care about two users when handling the interaction: +1. User B, who clicked the "Join" button so our app can track who is interested in joining the match +2. User A, who ran the `/game` command so our app can tell them who is interested in joining the match + +There are two fields additional fields to know about that can be helpful in this scenario— + +###### `interaction_metadata` + +Messages created in response to an interaction will include an [`interaction_metadata` object](/developers/docs/resources/message#message-interaction-metadata-object) which includes metadata related to the interaction. + +###### `authorizing_integration_owners` + +`authorizing_integration_owners` was touched on above, but it's worth highlighting again since it's most helpful when handling message component interactions for user-installed apps. + +For user-installed apps, it can be used to differentiate between the user that installed an app and the user that triggered an app's interaction since messages sent in response to interactions (either an interaction response or a follow-up message) can be visible to users that don't have the app installed to their account. + +## Next Steps + +*Yay~!* At this point, you have an app that supports both installation contexts and understand the basics of using metadata to support different contexts. Now you can go explore the documentation for details, or play with the sample app to develop more complex features. + + + + Explore the Interactions documentation to learn more about receiving and responding to commands and message components + + + GitHub repository with sample project + + diff --git a/docs/tutorials/Hosting_on_Cloudflare_Workers.md b/discord/developers/docs/tutorials/hosting-on-cloudflare-workers.mdx similarity index 78% rename from docs/tutorials/Hosting_on_Cloudflare_Workers.md rename to discord/developers/docs/tutorials/hosting-on-cloudflare-workers.mdx index e37563732e..928d986c74 100644 --- a/docs/tutorials/Hosting_on_Cloudflare_Workers.md +++ b/discord/developers/docs/tutorials/hosting-on-cloudflare-workers.mdx @@ -1,18 +1,20 @@ -# Hosting a Reddit API Discord app on Cloudflare Workers +--- +title: Hosting a Reddit API Discord app on Cloudflare Workers +sidebarTitle: Hosting on Cloudflare Workers +description: Tutorial for deploying Discord apps on Cloudflare Workers. +--- -When building Discord apps, your app can receive common events from the client as [webhooks](#DOCS_RESOURCES_WEBHOOK) when users interact with your app through interactions like [application commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS) or [message components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS). +When building Discord apps, your app can receive common events from the client as [webhooks](/developers/docs/resources/webhook) when users interact with your app through interactions like [application commands](/developers/docs/interactions/application-commands) or [message components](/developers/docs/components/reference). Discord will send these events to a pre-configured HTTPS endpoint (called an Interactions Endpoint URL in an app's configuration) as a JSON payload with details about the event. -This tutorial walks through building a Discord app powered by [`r/aww`](https://www.reddit.com/r/aww) using JavaScript: - -![Demo of Reddit API app](cloudflare-tutorial-demo.gif) +This tutorial walks through building a Discord app powered by [`r/aww`](https://www.reddit.com/r/aww) using JavaScript. -All of the code for this app can be found **[on Github](https://github.com/discord/cloudflare-sample-app)**. +All of the code for this app can be found **[on GitHub](https://github.com/discord/cloudflare-sample-app)**. ### Features and technologies used -- [Discord Interactions API](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING) (specifically slash commands) +- [Discord Interactions API](/developers/docs/interactions/receiving-and-responding) (specifically slash commands) - [Cloudflare Workers](https://workers.cloudflare.com/) for hosting - [Reddit API](https://www.reddit.com/dev/api/) to send messages back to the user @@ -26,22 +28,23 @@ To start, we'll create the app through the [Discord Developer Dashboard](https:/ - Click `New Application`, and choose a name - Copy your **Public Key** and **Application ID**, and put them somewhere locally (we'll need these later) -![IDs found in app settings](cloudflare-general-overview.png) +![IDs found in app settings](/images/tutorials/cloudflare-general-overview.png) -- Now click on the `Bot` tab on the left sidebar, and create a bot! You can choose the same name as your app. +- Now click on the **Bot** tab on the left sidebar. - Grab the `token` for your bot, and store it somewhere safe (I like to put these tokens in a password manager like [1password](https://1password.com/) or [lastpass](https://www.lastpass.com/)). -> warn -> For security reasons, you can only view your bot token once. If you misplace your token, you'll have to generate a new one. + +For security reasons, you can only view your bot token once. If you misplace your token, you'll have to generate a new one. + ## Adding bot permissions -Now we'll configure the bot with [permissions](#DOCS_TOPICS_PERMISSIONS) required to create and use slash commands, as well as send messages in channels. +Now we'll configure the bot with [permissions](/developers/docs/topics/permissions) required to create and use slash commands, as well as send messages in channels. - Click on the `OAuth2` tab, and choose the `URL Generator`. Click the `bot` and `applications.commands` scopes. - Check the boxes next to `Send Messages` and `Use Slash Commands`, then copy the `Generated URL`. -![Configuring bot permissions in app settings](cloudflare-url-generator.png) +![Configuring bot permissions in app settings](/images/tutorials/cloudflare-url-generator.png) - Paste the URL into the browser and follow the OAuth flow, selecting the server where you'd like to develop and test your bot. @@ -49,8 +52,9 @@ Now we'll configure the bot with [permissions](#DOCS_TOPICS_PERMISSIONS) require Cloudflare Workers are a convenient way to host Discord apps due to the free tier, simple development model, and automatically managed environment (no VMs!). -> warn -> When using Cloudflare Workers, your app won't be able to access non-ephemeral CDN media. For example, trying to fetch an image like `https://cdn.discordapp.com/attachments/1234/56789/my_image.png` would result in a `403` error. Cloudflare Workers are still able to access ephemeral CDN media. + +When using Cloudflare Workers, your app won't be able to access non-ephemeral CDN media. For example, trying to fetch an image like `https://cdn.discordapp.com/attachments/1234/56789/my_image.png` would result in a `403` error. Cloudflare Workers are still able to access ephemeral CDN media. + - Visit the [Cloudflare Dashboard](https://dash.cloudflare.com/) - Click on the `Workers` tab, and create a new service using the same name as your Discord bot @@ -68,8 +72,9 @@ $ wrangler secret put DISCORD_APPLICATION_ID You'll also need the Guild ID for the server where your app is installed. This can be found in the URL when you visit any channel in that server. -> info -> For example, if my URL was `https://discord.com/channels/123456/789101112`, the Guild ID is the first number—in this case **`123456`**. + +For example, if my URL was `https://discord.com/channels/123456/789101112`, the Guild ID is the first number—in this case **`123456`**. + Once you know your Guild ID, set that variable as well: @@ -79,8 +84,9 @@ $ wrangler secret put DISCORD_TEST_GUILD_ID ## Running locally -> info -> This depends on the beta version of the `wrangler` package, which better supports ESM on Cloudflare Workers. + +This depends on the beta version of the `wrangler` package, which better supports ESM on Cloudflare Workers. + Let's start by cloning the repository and installing dependencies. This requires at least v16 of [Node.js](https://nodejs.org/en/): @@ -93,14 +99,14 @@ $ npm install A brief look at the cloned app's project structure: ``` -├── .github/workflows/ci.yaml -> Github Action configuration +├── .github/workflows/ci.yaml -> GitHub Action configuration ├── src -│ ├── commands.js -> JSON payloads for commands -│ ├── reddit.js -> Interactions with the Reddit API -│ ├── register.js -> Sets up commands with the Discord API -│ ├── server.js -> Discord app logic and routing +├── ├── commands.js -> JSON payloads for commands +├── ├── reddit.js -> Interactions with the Reddit API +├── ├── register.js -> Sets up commands with the Discord API +├── ├── server.js -> Discord app logic and routing ├── test -| ├── test.js -> Tests for app +├── ├── test.js -> Tests for app ├── wrangler.toml -> Configuration for Cloudflare Workers ├── package.json ├── README.md @@ -110,6 +116,7 @@ A brief look at the cloned app's project structure: ├── .prettierrc.json └── .gitignore ``` + ### Registering commands Before testing our app, we need to register our desired slash commands. For this app, we'll have a `/awwww` command, and a `/invite` command. The name and description for these are kept separate in `commands.js`: @@ -126,7 +133,7 @@ export const INVITE_COMMAND = { }; ``` -The code to register commands lives in `register.js`. Commands can be [registered globally](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/create-global-application-command), making them available for all servers with the app installed, or they can be [registered on a single server](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/create-guild-application-command). +The code to register commands lives in `register.js`. Commands can be [registered globally](/developers/docs/interactions/application-commands#create-global-application-command), making them available for all servers with the app installed, or they can be [registered on a single server](/developers/docs/interactions/application-commands#create-guild-application-command). In this example - we'll just focus on global commands: @@ -206,13 +213,13 @@ When a user types a slash command, Discord will send an HTTP request to a public $ npm run ngrok ``` -![ngrok forwarding address](cloudflare-ngrok.png) +![ngrok forwarding address](/images/tutorials/cloudflare-ngrok.png) This is going to bounce requests off of an external endpoint, and forward them to your machine. Copy the HTTPS link provided by the tool. It should look something like `https://8098-24-22-245-250.ngrok.io`. Now head back to the Discord Developer Dashboard, and update the `Interactions Endpoint URL` for your app: -![Interactions Endpoint URL](cloudflare-interactions-endpoint.png) +![Interactions Endpoint URL](/images/tutorials/cloudflare-interactions-endpoint.png) This is the process we'll use for local testing and development. When you've published your app to Cloudflare, you will **want to update this field to use your Cloudflare Worker URL.** @@ -284,7 +291,7 @@ All of the API calls from Discord in this example will be POSTed to `/`. From he /** * Main route for all requests sent from Discord. All incoming messages will * include a JSON payload described here: - * https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object + * /developers/docs/interactions/receiving-and-responding#interaction-object */ router.post('/', async (request, env) => { const message = await request.json(); @@ -335,10 +342,12 @@ router.post('/', async (request, env) => { ## Next steps -> info -> In case you need to reference any of the code, you can find the repo [on Github](https://github.com/discord/cloudflare-sample-app) + +In case you need to reference any of the code, you can find the repo [on GitHub](https://github.com/discord/cloudflare-sample-app) + With your app built and deployed, you can start customizing it to be your own: -- Use **[message components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS)** in your app to add more interactivity (like buttons and select menus). -- Take a look at different **[public APIs](https://github.com/public-apis/public-apis)** on Github. + +- Use **[message components](/developers/docs/components/reference)** in your app to add more interactivity (like buttons and select menus). +- Take a look at different **[public APIs](https://github.com/public-apis/public-apis)** on GitHub. - Join the **[Discord Developers server](https://discord.gg/discord-developers)** to ask questions about the API, attend events hosted by the Discord API team, and interact with other developers. diff --git a/docs/tutorials/Upgrading_to_Application_Commands.md b/discord/developers/docs/tutorials/upgrading-to-application-commands.mdx similarity index 57% rename from docs/tutorials/Upgrading_to_Application_Commands.md rename to discord/developers/docs/tutorials/upgrading-to-application-commands.mdx index c46e9b466b..e62a1c13e6 100644 --- a/docs/tutorials/Upgrading_to_Application_Commands.md +++ b/discord/developers/docs/tutorials/upgrading-to-application-commands.mdx @@ -1,13 +1,18 @@ -# Upgrading Apps to Use Application Commands +--- +title: Upgrading Apps to Use Application Commands +sidebarTitle: Upgrading to Application Commands +description: Migration guide for upgrading from legacy text commands to application commands. +--- -As [message content has become a privileged intent](https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Privileged-Intent-FAQ) for verified apps, [application commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS) are the primary way Discord users interact with apps. The three types of commands (slash commands, user commands, and message commands) act as entry points into apps, and can be registered globally or for a subset of guilds. +As [message content has become a privileged intent](https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Privileged-Intent-FAQ) for verified apps, [application commands](/developers/docs/interactions/application-commands) are the primary way Discord users interact with apps. The three types of commands (slash commands, user commands, and message commands) act as entry points into apps, and can be registered globally or for a subset of guilds. This guide is intended to provide developers with apps currently using message content with a resource to walk through implementing and designing commands. Throughout the guide, the terms "application commands" and "commands" are used interchangeably. -![Client interfaces showing the different types of application commands](command-types.png) +![Client interfaces showing the different types of application commands](/images/tutorials/command-types.png) -> info -> If you are developing an app for the first time, the [commands documentation](#DOCS_INTERACTIONS_APPLICATION_COMMANDS) may be a more helpful resource for you. + +If you are developing an app for the first time, the [commands documentation](/developers/docs/interactions/application-commands) may be a more helpful resource for you. + Before diving in, it’s worth noting that with the message content changes, apps can still access message content in their DMs with users, as well as when messages are sent that directly `@mentions` their bot user (since there is clear user intent that the bot can read those messages). @@ -21,52 +26,55 @@ There are three types of application commands: slash commands, user commands, an ### Slash Commands -[Slash commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/slash-commands) are the most common type of command. They are accessed by typing a forward slash (`/`) followed by the command’s name, or by using the plus button (+) to the left of the message input. +[Slash commands](/developers/docs/interactions/application-commands#slash-commands) are the most common type of command. They are accessed by typing a forward slash (`/`) followed by the command’s name, or by using the plus button (+) to the left of the message input. Slash commands can appear in channels and DMs, so they’re helpful when an action is tied to a channel, a server, or nothing at all. ### User Commands -[User commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/user-commands) are in the context menu for users, which is accessed by right-clicking or tapping on any user. They’re helpful when an action is tied to an individual user, like a moderation app adding a timeout to someone. +[User commands](/developers/docs/interactions/application-commands#user-commands) are in the context menu for users, which is accessed by right-clicking or tapping on any user. They’re helpful when an action is tied to an individual user, like a moderation app adding a timeout to someone. -> info -> Within the context menus for users and messages, the `Apps` submenu will only appear if an app in that server has installed a corresponding command (whether or not an individual user can use the installed command). + +Within the context menus for users and messages, the `Apps` submenu will only appear if an app in that server has installed a corresponding command (whether or not an individual user can use the installed command). + ### Message Commands -[Message commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/message-commands) are in the context menu for messages, which is accessed by right-clicking on a message, or by clicking on the ellipses at the top-right of a message. They’re helpful when an action is tied to a message, like creating a reminder to reply to a message the following day. +[Message commands](/developers/docs/interactions/application-commands#message-commands) are in the context menu for messages, which is accessed by right-clicking on a message, or by clicking on the ellipses at the top-right of a message. They’re helpful when an action is tied to a message, like creating a reminder to reply to a message the following day. ### After This Section - Start thinking about how different app features might map to the different types of application commands. ## Registering Commands -Commands can be registered via HTTP requests after an app is authorized with the `applications.commands` scope. Since commands aren’t tied to bot users, being authorized with the `bot` scope isn’t sufficient. +Commands can be registered via HTTP requests after an app is authorized with the `applications.commands` scope. The `applications.commands` scope is also automatically included when an app requests the `bot` scope. -> info -> There is a section on [designing commands](#DOCS_TUTORIALS_UPGRADING_TO_APPLICATION_COMMANDS/designing-for-commands) below implementation details that may be helpful as you start mapping out different commands + +There is a section on [designing commands](/developers/docs/tutorials/upgrading-to-application-commands#designing-for-commands) below implementation details that may be helpful as you start mapping out different commands + The API endpoint to register (or create) commands is different depending on its scope: -- **[Global commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/making-a-global-command)** are available in all of the servers where your app is installed, and can be created using the [`/applications/{YOUR APP ID}/commands` endpoint](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/create-global-application-command). All of your app's global commands will automatically be added to the servers your app is installed in, regardless of whether they were registered before or after installation. -- **[Guild commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/making-a-guild-command)** are only available in the servers you explicitly add them to via the API, making them useful for features available only to a subset of guilds. They can be created using the [`/applications/{YOUR APP ID}/guilds/{A GUILD ID}/commands` endpoint](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/create-guild-application-command). -While most apps won’t need to register more than a handful of commands, apps can have up to 100 global slash commands and 100 guild slash commands with unique names. They can also have 5 global user commands and 5 global message commands. Different limitations apply for global and guild commands, which can be found [in the documentation](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/registering-a-command). +- **[Global commands](/developers/docs/interactions/application-commands#making-a-global-command)** are available in all of the servers where your app is installed, and can be created using the [`/applications/{YOUR APP ID}/commands` endpoint](/developers/docs/interactions/application-commands#create-global-application-command). All of your app's global commands will automatically be added to the servers your app is installed in, regardless of whether they were registered before or after installation. +- **[Guild commands](/developers/docs/interactions/application-commands#making-a-guild-command)** are only available in the servers you explicitly add them to via the API, making them useful for features available only to a subset of guilds. They can be created using the [`/applications/{YOUR APP ID}/guilds/{A GUILD ID}/commands` endpoint](/developers/docs/interactions/application-commands#create-guild-application-command). + +While most apps won’t need to register more than a handful of commands, apps can have up to 100 global slash commands and 100 guild slash commands with unique names. They can also have 5 global user commands and 5 global message commands. Different limitations apply for global and guild commands, which can be found [in the documentation](/developers/docs/interactions/application-commands#registering-a-command). ### Using Options as Parameters -Command options is an optional field (`options`) that can be defined when creating commands. When used, options will display for the user to fill out during invocation. You can also provide dynamic option suggestions using the `autocomplete` field. Read more about options [in the documentation](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure). +Command options is an optional field (`options`) that can be defined when creating commands. When used, options will display for the user to fill out during invocation. You can also provide dynamic option suggestions using the `autocomplete` field. Read more about options [in the documentation](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure). -![Slash command using options](slash-command-options.png) +![Slash command using options](/images/tutorials/slash-command-options.png) ### Using Subcommands to Group Actions -[Subcommands and subcommand groups](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/subcommands-and-subcommand-groups) help organize commands that are related to a shared resource or action. Instead of several top-level commands (like `/add-resource` and `/delete-resource`), you can have one top-level command with several subcommands (like `/resource add` and `/resource delete`). +[Subcommands and subcommand groups](/developers/docs/interactions/application-commands#subcommands-and-subcommand-groups) help organize commands that are related to a shared resource or action. Instead of several top-level commands (like `/add-resource` and `/delete-resource`), you can have one top-level command with several subcommands (like `/resource add` and `/resource delete`). Subcommands use the same `options` field as passing parameters, but with a type of `2`. ### Restricting Command Use -[Application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/permissions) allow users and apps to restrict command usage in a server. When commands are restricted for a user, they won't appear for them in the client. +[Application command permissions](/developers/docs/interactions/application-commands#permissions) allow users and apps to restrict command usage in a server. When commands are restricted for a user, they won't appear for them in the client. If your app currently relies on permissioning, using command permissions can be a way to port them. It also cleans up the command picker UI for users, making it easier for them to find your app’s commands that are most relevant to them. @@ -74,7 +82,7 @@ If your app currently relies on permissioning, using command permissions can be Below is a sample payload to create a global slash command with an optional parameter: -``` +```json { "name": "rock", "type": 1, @@ -106,13 +114,13 @@ Below is a sample payload to create a global slash command with an optional para #### Default Permissions -When [creating](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/create-global-application-command) or [updating](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/edit-global-application-command) a command, you can use the `default_member_permissions` field to restrict the command to a set of bitwise permissions that reflect the default permission flags a user must be granted in order to use the command. +When [creating](/developers/docs/interactions/application-commands#create-global-application-command) or [updating](/developers/docs/interactions/application-commands#edit-global-application-command) a command, you can use the `default_member_permissions` field to restrict the command to a set of bitwise permissions that reflect the default permission flags a user must be granted in order to use the command. In addition, the `dm_permission` flag can be used to control whether a global command is available in DMs (not available for guild commands). #### Updating Permissions -Permissions for specific roles, users, and channels can be updated by your app if you have the `applications.commands.permissions.update` scope and a [Bearer token](#DOCS_TOPICS_OAUTH2) that’s authenticated by a user with the necessary user permissions. Details about updating a command’s permissions are [in the documentation](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/permissions). +Permissions for specific roles, users, and channels can be updated by your app if you have the `applications.commands.permissions.update` scope and a [Bearer token](/developers/docs/topics/oauth2) that’s authenticated by a user with the necessary user permissions. Details about updating a command’s permissions are [in the documentation](/developers/docs/interactions/application-commands#permissions). ### After This Section @@ -123,31 +131,34 @@ Permissions for specific roles, users, and channels can be updated by your app i ## Handling Commands -Commands use the [interactions model](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING) through HTTP-based outgoing webhooks or the WebSocket-based [Interaction Create gateway event](#DOCS_TOPICS_GATEWAY_EVENTS/interaction-create). Regardless of the transit method used to arrive, your app will receive relevant information when a Discord user triggers one of your app’s interactions. +Commands use the [interactions model](/developers/docs/interactions/receiving-and-responding) through HTTP-based outgoing webhooks or the WebSocket-based [Interaction Create gateway event](/developers/docs/events/gateway-events#interaction-create). Regardless of the transit method used to arrive, your app will receive relevant information when a Discord user triggers one of your app’s interactions. -> warn -> If you continue using Gateway events, you’ll still receive message events but the payloads will have empty string or array data for message content-related fields like `content`, `embeds`, `attachments`, and `components`. You can read more in the [message content intent](#DOCS_TOPICS_GATEWAY/message-content-intent) section. + +If you continue using Gateway events, you’ll still receive message events but the payloads will have empty string or array data for message content-related fields like `content`, `embeds`, `attachments`, and `components` while `poll` will be omitted. You can read more in the [message content intent](/developers/docs/events/gateway#message-content-intent) section. + -For commands, this means that each time one of your commands is used, your app will receive information like the command name and the user who triggered it. More information about this information is below in the section on [parsing command payloads](#DOCS_TUTORIALS_UPGRADING_TO_APPLICATION_COMMANDS/parsing-command-payloads). +For commands, this means that each time one of your commands is used, your app will receive information like the command name and the user who triggered it. More information about this information is below in the section on [parsing command payloads](/developers/docs/tutorials/upgrading-to-application-commands#parsing-command-payloads). ### Adding an Interactions Endpoint URL -> info -> This step is specific to receiving interactions over HTTP. If you prefer to use the [Gateway](#DOCS_TOPICS_GATEWAY), this step won't be applicable to your app. + +This step is specific to receiving interactions over HTTP. If you prefer to use the [Gateway](/developers/docs/events/gateway), this step won't be applicable to your app. + Before your app can receive interactions, you’ll need to set up an **Interaction Endpoint URL** in your app settings. This endpoint should be a public URL where Discord can send all interactions-related requests. However, before adding your URL to your app settings, your endpoint must be set up to handle two things: -1. **Responding to `PING` events**: When you save a URL in your settings, Discord will send a `POST` request with `type: 1`. To acknowledge this request (and thus verify your endpoint), you should return a `200` response with a payload containing `type: 1`. More information can be found in the [Receiving an Interaction documentation](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/receiving-an-interaction). -2. **Verifying request signatures**: To ensure that requests are coming from Discord, your endpoint must verify each request using the included headers, specifically `X-Signature-Ed25519` and `X-Signature-Timestamp`. If the signature fails validating, your app should return a `401` response. More information and example code can be found in the [Security and Authorization documentation](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/security-and-authorization). +1. **Responding to `PING` events**: When you save a URL in your settings, Discord will send a `POST` request with `type: 1`. To acknowledge this request (and thus verify your endpoint), you should return a `200` response with a payload containing `type: 1`. More information can be found in the [Receiving an Interaction documentation](/developers/docs/interactions/receiving-and-responding#receiving-an-interaction). +2. **Verifying request signatures**: To ensure that requests are coming from Discord, your endpoint must verify each request using the included headers, specifically `X-Signature-Ed25519` and `X-Signature-Timestamp`. If the signature fails validating, your app should return a `401` response. More information and example code can be found in the [Security and Authorization documentation](/developers/docs/interactions/overview#setting-up-an-endpoint-validating-security-request-headers). -> info -> Many libraries on the [Community Resources page](#DOCS_TOPICS_COMMUNITY_RESOURCES/interactions) simplify verification and interaction request handling by exporting reusable functions and/or handling it automatically. + +Many libraries on the [Community Resources page](/developers/docs/developer-tools/community-resources#interactions) simplify verification and interaction request handling by exporting reusable functions and/or handling it automatically. + After your URL is set up to handle signature verification and `PING` requests, you can add your Interaction Endpoint URL by navigating to your app settings from the [developer portal](https://discord.com/developers/applications). On the **General Information** page, you’ll see a field for your **Interactions Endpoint URL**. -![Interactions endpoint URL in app settings](interactions-url.png) +![Interactions endpoint URL in app settings](/images/tutorials/interactions-url.png) After you paste your URL and click **Save Changes**, Discord will attempt to verify your endpoint. @@ -157,16 +168,17 @@ If all goes well, your endpoint will successfully save. And if not, you should d Once your app has a verified endpoint, you should start being able to receive command requests from Discord. -As mentioned above, these include information relevant to handling the command like its name, the user who invoked it, and the guild and channel it was invoked from. It also includes additional details that could be relevant, like the [command options](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) or [locale information](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/localization). +As mentioned above, these include information relevant to handling the command like its name, the user who invoked it, and the guild and channel it was invoked from. It also includes additional details that could be relevant, like the [command options](/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) or [locale information](/developers/docs/interactions/application-commands#localization). -Since slash commands (`CHAT_INPUT` commands) are run in the context of a channel, you’ll notice that their payloads don’t contain any information about specific messages. If your app needs the message content, you can use [message commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/message-commands) which *do* include the message content. +Since slash commands (`CHAT_INPUT` commands) are run in the context of a channel, you’ll notice that their payloads don’t contain any information about specific messages. If your app needs the message content, you can use [message commands](/developers/docs/interactions/application-commands#message-commands) which *do* include the message content. -> info -> In the getting started guide’s repository, there’s a code sample showing [how to create and handle commands using JavaScript](https://github.com/discord/discord-example-app/blob/main/examples/command.js). + +In the getting started guide’s repository, there’s a code sample showing [how to create and handle commands using JavaScript](https://github.com/discord/discord-example-app/blob/main/examples/command.js). + Below is an example payload your app would receive when a user invoked a global command with an option: -``` +```json { "type": 2, "token": "A_UNIQUE_TOKEN", @@ -206,7 +218,7 @@ Below is an example payload your app would receive when a user invoked a global } ``` -To determine how your app should handle an incoming command-related interaction, you can look in the `data` object which contains all of the command-specific information (including any options a user selected). Details about the full interactions object your app receives can be found in the [Interactions documentation](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object). +To determine how your app should handle an incoming command-related interaction, you can look in the `data` object which contains all of the command-specific information (including any options a user selected). Details about the full interactions object your app receives can be found in the [Interactions documentation](/developers/docs/interactions/receiving-and-responding#interaction-object). ### After This Section @@ -218,14 +230,15 @@ To determine how your app should handle an incoming command-related interaction, Adding commands to your app can add discoverability and interactivity for users. While porting your app’s features, it’s worth considering how your app will be seen and used inside of the client. This section includes a handful of areas to consider when designing your app’s commands, and what happens after they’re invoked. -> info -> CLIs (Command Line Interfaces) can be a helpful analogy when designing Discord commands, their options and subcommands, and how it all fits together into one experience. A good resource for this can be the open source [Command Interface Guidelines](https://clig.dev/). + +CLIs (Command Line Interfaces) can be a helpful analogy when designing Discord commands, their options and subcommands, and how it all fits together into one experience. A good resource for this can be the open source [Command Interface Guidelines](https://clig.dev/). + ### Choosing a Name When creating a command, keep the name short and relevant. The name of the command should give users an idea of the action they’re invoking. A description can be a bit more verbose, leaving room to be more explicit about the action and its result. -And when your app has several commands (and perhaps [subcommands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/subcommands-and-subcommand-groups)), it’s important to keep the naming scheme consistent. +And when your app has several commands (and perhaps [subcommands](/developers/docs/interactions/application-commands#subcommands-and-subcommand-groups)), it’s important to keep the naming scheme consistent. #### Examples @@ -234,22 +247,23 @@ And when your app has several commands (and perhaps [subcommands](#DOCS_INTERACT ### Collecting User Input -When commands need a bit of input from a user, you can use the `options` field. Command options can be thought of as parameters to your command. They can be one of many [types](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-type), like the standard string or number, or the more Discord-specific role, user, and channel. +When commands need a bit of input from a user, you can use the `options` field. Command options can be thought of as parameters to your command. They can be one of many [types](/developers/docs/interactions/application-commands#application-command-object-application-command-option-type), like the standard string or number, or the more Discord-specific role, user, and channel. -Options are great for short input, but if you need user input that’s longer than a couple of words, like a title or description, you can collect form-like input using [modals](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-modal) as a response to the command invocation. +Options are great for short input, but if you need user input that’s longer than a couple of words, like a title or description, you can collect form-like input using [modals](/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal) as a response to the command invocation. ### Scoping a Command -Commands can optionally be [scoped to specific guilds](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/create-guild-application-command), rather than available everywhere your app is installed. Guild commands can be useful when your app has functionality that may not be relevant to all servers like: +Commands can optionally be [scoped to specific guilds](/developers/docs/interactions/application-commands#create-guild-application-command), rather than available everywhere your app is installed. Guild commands can be useful when your app has functionality that may not be relevant to all servers like: + - When a command is in development - When a specific command is opt-in or opt-out - When specific commands are behind a paywall ### Responding to a Command -Interactions (including commands) can have a hand-picked reply using one of the many available [interaction responses](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/responding-to-an-interaction). +Interactions (including commands) can have a hand-picked reply using one of the many available [interaction responses](/developers/docs/interactions/receiving-and-responding#responding-to-an-interaction). -The specific response type should be picked based on the situation. Some commands may call for sending a message back to the channel where the command was invoked from, or perhaps just to the specific user who ran the command (for this, the [ephemeral message flag](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-messages) can be used). Other commands may necessitate descriptive input from the user, in which case responding with a follow-up modal with text inputs might make the most sense. +The specific response type should be picked based on the situation. Some commands may call for sending a message back to the channel where the command was invoked from, or perhaps just to the specific user who ran the command (for this, the [ephemeral message flag](/developers/docs/interactions/receiving-and-responding#interaction-response-object-messages) can be used). Other commands may necessitate descriptive input from the user, in which case responding with a follow-up modal with text inputs might make the most sense. Regardless of the response, it should be picked based on the specific interaction the user is taking. @@ -259,7 +273,7 @@ For a `/search` command that searches an external service, an app could respond Ephemeral messages can also contain message components, so when relevant, there could be a button to share the information in the ephemeral message to a channel. -![Example of ephemeral message to show search results](examples-ephemeral-message.png) +![Example of ephemeral message to show search results](/images/tutorials/examples-ephemeral-message.png) ## Onboarding Users @@ -273,14 +287,15 @@ As to where to communicate the changes, you can start with any communication cha You can also inform users about changes within the servers your app is installed in as long as it’s done in a non-intrusive way. If your app has a dedicated channel in posts, that would be a good place. -> warn -> Don’t DM all users in a server where your app is installed. It could get your app rate limited, but more importantly, it can be pretty intrusive and might lead to your app being uninstalled. + +Don’t DM all users in a server where your app is installed. It could get your app rate limited, but more importantly, it can be pretty intrusive and might lead to your app being uninstalled. + #### Example The following is an example of an app update that may be sent to communicate the new way to access commands. Depending on the different features your app adopts (like options, subcommands, permissions, etc.), an update message or changelog entry will look very different. -![Example message updating users about a new feature](examples-update-message.png) +![Example message updating users about a new feature](/images/tutorials/examples-update-message.png) ### Making Help Available @@ -292,6 +307,6 @@ This can come in the form of a specific command that shows app usage, a message Hopefully this guide was helpful in considering how to design and implement application commands. Below is a couple of follow-up resources you can use: -- [Application command documentation](#DOCS_INTERACTIONS_APPLICATION_COMMANDS)—I know it's linked a bunch in this guide, but there's a reason! -- Help center article on [message content intent workarounds](https://support.discord.com/hc/en-us/articles/4413259614487-Message-Content-Intent-Alternatives-Workarounds) -- The [DDevs server](https://discord.gg/discord-developers) where you can find API updates, ask questions about developing apps, and connect with other developers \ No newline at end of file +- [Application command documentation](/developers/docs/interactions/application-commands)—I know it's linked a bunch in this guide, but there's a reason! +- Help center article on [message content intent workarounds](https://support-dev.discord.com/hc/en-us/articles/6383579033751-Message-Content-Intent-Alternatives-Workarounds) +- The [DDevs server](https://discord.gg/discord-developers) where you can find API updates, ask questions about developing apps, and connect with other developers diff --git a/discord/docs.json b/discord/docs.json new file mode 100644 index 0000000000..6397fe44ec --- /dev/null +++ b/discord/docs.json @@ -0,0 +1,342 @@ +{ + "$schema": "https://mintlify.com/docs.json", + "theme": "mint", + "name": "Documentation - Discord", + "colors": { + "primary": "#5865F2", + "light": "#5865F2", + "dark": "#5865F2" + }, + "appearance": { + "default": "dark" + }, + "styling": { + "codeblocks": "system", + "eyebrows": "breadcrumbs" + }, + "favicon": "/favicon.png", + "errors": { + "404": { + "redirect": false + } + }, + "interaction": { + "drilldown": false + }, + "contextual": { + "options": [ + "copy", + "view", + "chatgpt", + "claude", + "perplexity" + ] + }, + "navigation": { + "tabs": [ + { + "tab": "Documentation", + "groups": [ + { + "group": "Home", + "pages": [ + "developers/docs/intro", + "developers/docs/reference" + ] + }, + { + "group": "Quick Start", + "pages": [ + "developers/docs/quick-start/overview-of-apps", + "developers/docs/quick-start/getting-started" + ] + }, + { + "group": "Interactions", + "pages": [ + "developers/docs/interactions/overview", + "developers/docs/interactions/receiving-and-responding", + "developers/docs/interactions/application-commands" + ] + }, + { + "group": "Components", + "pages": [ + "developers/docs/components/overview", + "developers/docs/components/using-message-components", + "developers/docs/components/using-modal-components", + "developers/docs/components/reference" + ] + }, + { + "group": "Activities", + "pages": [ + "developers/docs/activities/overview", + "developers/docs/activities/how-activities-work", + "developers/docs/activities/building-an-activity", + { + "group": "Development Guides", + "pages": [ + "developers/docs/activities/development-guides", + "developers/docs/activities/development-guides/local-development", + "developers/docs/activities/development-guides/user-actions", + "developers/docs/activities/development-guides/mobile", + "developers/docs/activities/development-guides/layout", + "developers/docs/activities/development-guides/networking", + "developers/docs/activities/development-guides/multiplayer-experience", + "developers/docs/activities/development-guides/growth-and-referrals", + "developers/docs/activities/development-guides/assets-and-metadata", + "developers/docs/activities/development-guides/production-readiness" + ] + }, + "developers/docs/activities/design-patterns" + ] + }, + { + "group": "Discord Social SDK", + "pages": [ + "developers/docs/discord-social-sdk/overview", + { + "group": "Core Concepts", + "pages": [ + "developers/docs/discord-social-sdk/core-concepts", + "developers/docs/discord-social-sdk/core-concepts/core-features", + "developers/docs/discord-social-sdk/core-concepts/communication-features", + "developers/docs/discord-social-sdk/core-concepts/integration-overview", + "developers/docs/discord-social-sdk/core-concepts/platform-compatibility", + "developers/docs/discord-social-sdk/core-concepts/oauth2-scopes" + ] + }, + { + "group": "Getting Started", + "pages": [ + "developers/docs/discord-social-sdk/getting-started", + "developers/docs/discord-social-sdk/getting-started/using-c++", + "developers/docs/discord-social-sdk/getting-started/using-unity", + "developers/docs/discord-social-sdk/getting-started/using-unreal-engine" + ] + }, + { + "group": "Development Guides", + "pages": [ + "developers/docs/discord-social-sdk/development-guides", + "developers/docs/discord-social-sdk/development-guides/account-linking-with-discord", + "developers/docs/discord-social-sdk/development-guides/account-linking-on-consoles", + "developers/docs/discord-social-sdk/development-guides/using-provisional-accounts", + "developers/docs/discord-social-sdk/development-guides/creating-a-unified-friends-list", + "developers/docs/discord-social-sdk/development-guides/managing-relationships", + "developers/docs/discord-social-sdk/development-guides/setting-rich-presence", + "developers/docs/discord-social-sdk/development-guides/managing-game-invites", + "developers/docs/discord-social-sdk/development-guides/sending-direct-messages", + "developers/docs/discord-social-sdk/development-guides/managing-lobbies", + "developers/docs/discord-social-sdk/development-guides/linked-channels", + "developers/docs/discord-social-sdk/development-guides/managing-voice-chat" + ] + }, + { + "group": "Design Guidelines", + "pages": [ + "developers/docs/discord-social-sdk/design-guidelines", + "developers/docs/discord-social-sdk/design-guidelines/principles", + "developers/docs/discord-social-sdk/design-guidelines/signing-in", + "developers/docs/discord-social-sdk/design-guidelines/connection-points", + "developers/docs/discord-social-sdk/design-guidelines/branding-guidelines", + "developers/docs/discord-social-sdk/design-guidelines/unified-friends-list", + "developers/docs/discord-social-sdk/design-guidelines/direct-messages", + "developers/docs/discord-social-sdk/design-guidelines/chat-history", + "developers/docs/discord-social-sdk/design-guidelines/social-settings", + "developers/docs/discord-social-sdk/design-guidelines/provisional-accounts", + "developers/docs/discord-social-sdk/design-guidelines/status-rich-presence", + "developers/docs/discord-social-sdk/design-guidelines/consoles", + "developers/docs/discord-social-sdk/design-guidelines/game-friends", + "developers/docs/discord-social-sdk/design-guidelines/linked-channels" + ] + }, + { + "group": "How To", + "pages": [ + "developers/docs/discord-social-sdk/how-to", + "developers/docs/discord-social-sdk/how-to/debug-log", + "developers/docs/discord-social-sdk/how-to/use-with-discord-apis", + "developers/docs/discord-social-sdk/how-to/integrate-moderation", + "developers/docs/discord-social-sdk/how-to/market-your-integration", + "developers/docs/discord-social-sdk/how-to/handle-special-characters-display-names" + ] + }, + "developers/docs/discord-social-sdk/social-sdk-reference" + ] + }, + { + "group": "Rich Presence", + "pages": [ + "developers/docs/rich-presence/overview", + "developers/docs/rich-presence/using-with-the-embedded-app-sdk", + "developers/docs/rich-presence/using-with-the-discord-social-sdk", + "developers/docs/rich-presence/using-with-the-game-sdk", + "developers/docs/rich-presence/best-practices" + ] + }, + { + "group": "Monetization", + "pages": [ + "developers/docs/monetization/overview", + "developers/docs/monetization/enabling-monetization", + "developers/docs/monetization/managing-skus", + "developers/docs/monetization/implementing-app-subscriptions", + "developers/docs/monetization/implementing-one-time-purchases", + "developers/docs/monetization/implementing-iap-for-activities" + ] + }, + { + "group": "Discovery", + "pages": [ + "developers/docs/discovery/overview", + "developers/docs/discovery/enabling-discovery", + "developers/docs/discovery/best-practices" + ] + }, + { + "group": "Events", + "pages": [ + "developers/docs/events/overview", + "developers/docs/events/gateway", + "developers/docs/events/gateway-events", + "developers/docs/events/webhook-events" + ] + }, + { + "group": "Developer Tools", + "pages": [ + "developers/docs/developer-tools/embedded-app-sdk", + "developers/docs/developer-tools/community-resources", + "developers/docs/developer-tools/game-sdk" + ] + }, + { + "group": "Resources", + "pages": [ + "developers/docs/resources/application-role-connection-metadata", + "developers/docs/resources/application", + "developers/docs/resources/audit-log", + "developers/docs/resources/auto-moderation", + "developers/docs/resources/channel", + "developers/docs/resources/emoji", + "developers/docs/resources/entitlement", + "developers/docs/resources/guild", + "developers/docs/resources/guild-scheduled-event", + "developers/docs/resources/guild-template", + "developers/docs/resources/invite", + "developers/docs/resources/lobby", + "developers/docs/resources/message", + "developers/docs/resources/poll", + "developers/docs/resources/sku", + "developers/docs/resources/soundboard", + "developers/docs/resources/stage-instance", + "developers/docs/resources/sticker", + "developers/docs/resources/subscription", + "developers/docs/resources/user", + "developers/docs/resources/voice", + "developers/docs/resources/webhook" + ] + }, + { + "group": "Topics", + "pages": [ + "developers/docs/topics/certified-devices", + "developers/docs/topics/oauth2", + "developers/docs/topics/opcodes-and-status-codes", + "developers/docs/topics/permissions", + "developers/docs/topics/rate-limits", + "developers/docs/topics/rpc", + "developers/docs/topics/teams", + "developers/docs/topics/threads", + "developers/docs/topics/voice-connections" + ] + }, + { + "group": "Tutorials", + "pages": [ + "developers/docs/tutorials/configuring-app-metadata-for-linked-roles", + "developers/docs/tutorials/developing-a-user-installable-app", + "developers/docs/tutorials/hosting-on-cloudflare-workers", + "developers/docs/tutorials/upgrading-to-application-commands" + ] + }, + { + "group": "Policies and Agreements", + "pages": [ + "developers/docs/policies/developer-policy", + "developers/docs/policies/developer-terms-of-service" + ] + } + ] + }, + { + "tab": "Change Log", + "pages": [ + "developers/docs/change-log" + ] + } + ] + }, + "redirects": [ + { + "source": "/developers/docs", + "destination": "/developers/docs/intro" + }, + { + "source": "/developers/docs/", + "destination": "/developers/docs/intro" + }, + { + "source": "/developers/docs/index", + "destination": "/developers/docs/intro" + } + ], + "logo": { + "light": "/logo/light.svg", + "dark": "/logo/dark.svg", + "href": "https://discord.com/developers/docs/" + }, + "background": { + "color": { + "light": "#ffffff", + "dark": "#36373e" + } + }, + "api": { + "playground": { + "display": "simple" + } + }, + "navbar": { + "links": [ + { + "label": "Help Center", + "href": "https://support-dev.discord.com/hc/en-us" + } + ], + "primary": { + "type": "button", + "label": "Developer Portal", + "href": "https://discord.com/developers/applications" + } + }, + "footer": { + "socials": { + "github": "https://github.com/discord/discord-api-docs", + "discord": "https://discord.gg/discord-developers", + "youtube": "https://www.youtube.com/@DiscordDevelopers", + "linkedin": "https://www.linkedin.com/showcase/discord-developers" + } + }, + "banner" : { + "content": "New docs? Check out the [change log](/developers/docs/change-log#next-generation-docs-project) to learn more!", + "dismissible": true + }, + "integrations": { + "gtm": { + "tagId": "G-5CWMJQ1S0X" + } + } +} \ No newline at end of file diff --git a/discord/favicon.png b/discord/favicon.png new file mode 100644 index 0000000000..e813e10b9c Binary files /dev/null and b/discord/favicon.png differ diff --git a/discord/images/1.4vs1.5.webp b/discord/images/1.4vs1.5.webp new file mode 100644 index 0000000000..42d18ba6ab Binary files /dev/null and b/discord/images/1.4vs1.5.webp differ diff --git a/discord/images/activities-presence-example.png b/discord/images/activities-presence-example.png new file mode 100644 index 0000000000..a88c5cf2ad Binary files /dev/null and b/discord/images/activities-presence-example.png differ diff --git a/discord/images/activities/activities-hero.png b/discord/images/activities/activities-hero.png new file mode 100644 index 0000000000..56c65a8a0f Binary files /dev/null and b/discord/images/activities/activities-hero.png differ diff --git a/discord/images/activities/activity-instance-validation.jpg b/discord/images/activities/activity-instance-validation.jpg new file mode 100644 index 0000000000..3857dbc42e Binary files /dev/null and b/discord/images/activities/activity-instance-validation.jpg differ diff --git a/discord/images/activities/application-test-mode-prod.gif b/discord/images/activities/application-test-mode-prod.gif new file mode 100644 index 0000000000..ee66fe8963 Binary files /dev/null and b/discord/images/activities/application-test-mode-prod.gif differ diff --git a/discord/images/activities/bobble-bash.png b/discord/images/activities/bobble-bash.png new file mode 100644 index 0000000000..989916990c Binary files /dev/null and b/discord/images/activities/bobble-bash.png differ diff --git a/discord/images/activities/bobble-league.png b/discord/images/activities/bobble-league.png new file mode 100644 index 0000000000..51e1f240df Binary files /dev/null and b/discord/images/activities/bobble-league.png differ diff --git a/discord/images/activities/chess-victory.png b/discord/images/activities/chess-victory.png new file mode 100644 index 0000000000..5fcae0d705 Binary files /dev/null and b/discord/images/activities/chess-victory.png differ diff --git a/discord/images/activities/custom-link-embed.png b/discord/images/activities/custom-link-embed.png new file mode 100644 index 0000000000..ebc0183d6c Binary files /dev/null and b/discord/images/activities/custom-link-embed.png differ diff --git a/discord/images/activities/debug-logs-filtering.gif b/discord/images/activities/debug-logs-filtering.gif new file mode 100644 index 0000000000..ce8d6429f9 Binary files /dev/null and b/discord/images/activities/debug-logs-filtering.gif differ diff --git a/discord/images/activities/default_orientation_lock_state.png b/discord/images/activities/default_orientation_lock_state.png new file mode 100644 index 0000000000..201ae8990a Binary files /dev/null and b/discord/images/activities/default_orientation_lock_state.png differ diff --git a/discord/images/activities/eights.png b/discord/images/activities/eights.png new file mode 100644 index 0000000000..eab453f914 Binary files /dev/null and b/discord/images/activities/eights.png differ diff --git a/discord/images/activities/embedded-app-flow-diagram.svg b/discord/images/activities/embedded-app-flow-diagram.svg new file mode 100644 index 0000000000..90ced7b0b5 --- /dev/null +++ b/discord/images/activities/embedded-app-flow-diagram.svg @@ -0,0 +1 @@ +Application-ServerDiscord APIApplication-IframeDiscord-ClientApplication-ServerDiscord APIApplication-IframeDiscord-Clientalt[SDK setup]alt[Command Example]alt[Event Subscribe Example]Mounts Application iframeInitiate Handshake(No nonce included)Open SocketFetch application infoReturn application infoand set it on the socket objectReady Payload(No nonce included)Request to authorize scopes(This step opens the OAuth modal)Reply with OAuth authorize codeSend OAuth code to application serverUse OAuth code and client secretto fetch access_token from developer portalReply with access_tokenReply with access_tokenAuthenticate with access_tokenValidate authenticationSend Command(includes nonce)Run the commandStrip sensitive informationfrom command responseReturn command response(response verified against nonce)Subscribe to an eventEvent is capturedEvent DISPATCH'd to iframe(no nonce)@discord/embedded-app-sdk: Authentication, Command, and Event flows \ No newline at end of file diff --git a/discord/images/activities/enable-activities.png b/discord/images/activities/enable-activities.png new file mode 100644 index 0000000000..8fde7e3105 Binary files /dev/null and b/discord/images/activities/enable-activities.png differ diff --git a/discord/images/activities/encourage-hardware-acceleration-modal.png b/discord/images/activities/encourage-hardware-acceleration-modal.png new file mode 100644 index 0000000000..85c51fab28 Binary files /dev/null and b/discord/images/activities/encourage-hardware-acceleration-modal.png differ diff --git a/discord/images/activities/external-link-modal.png b/discord/images/activities/external-link-modal.png new file mode 100644 index 0000000000..1465b9d56d Binary files /dev/null and b/discord/images/activities/external-link-modal.png differ diff --git a/discord/images/activities/invite-dialog.png b/discord/images/activities/invite-dialog.png new file mode 100644 index 0000000000..d3bf2abe32 Binary files /dev/null and b/discord/images/activities/invite-dialog.png differ diff --git a/discord/images/activities/join-application.png b/discord/images/activities/join-application.png new file mode 100644 index 0000000000..c336d4cef9 Binary files /dev/null and b/discord/images/activities/join-application.png differ diff --git a/discord/images/activities/oauth-flow-diagram.svg b/discord/images/activities/oauth-flow-diagram.svg new file mode 100644 index 0000000000..6ce500a5a3 --- /dev/null +++ b/discord/images/activities/oauth-flow-diagram.svg @@ -0,0 +1 @@ +Discord APIApplication-ServerApplication-IframeDiscord-ClientDiscord APIApplication-ServerApplication-IframeDiscord-ClientRequest to authorize scopes(This step opens the OAuth modal)Reply with OAuth authorize codeSend OAuth code to application serverUse OAuth code and client secretto fetch access_token from developer portalReply with access_tokenReply with access_tokenApplication Instance Validation \ No newline at end of file diff --git a/discord/images/activities/oauth2-redirect.png b/discord/images/activities/oauth2-redirect.png new file mode 100644 index 0000000000..c095c12023 Binary files /dev/null and b/discord/images/activities/oauth2-redirect.png differ diff --git a/discord/images/activities/share-moment-dialog-example.png b/discord/images/activities/share-moment-dialog-example.png new file mode 100644 index 0000000000..5ba1121471 Binary files /dev/null and b/discord/images/activities/share-moment-dialog-example.png differ diff --git a/discord/images/activities/start-activity.png b/discord/images/activities/start-activity.png new file mode 100644 index 0000000000..2706616a69 Binary files /dev/null and b/discord/images/activities/start-activity.png differ diff --git a/discord/images/activities/supported-platforms.png b/discord/images/activities/supported-platforms.png new file mode 100644 index 0000000000..71f6add725 Binary files /dev/null and b/discord/images/activities/supported-platforms.png differ diff --git a/discord/images/activities/tutorial-auth.png b/discord/images/activities/tutorial-auth.png new file mode 100644 index 0000000000..d70edcae49 Binary files /dev/null and b/discord/images/activities/tutorial-auth.png differ diff --git a/discord/images/activities/tutorial-channel-name.png b/discord/images/activities/tutorial-channel-name.png new file mode 100644 index 0000000000..c2b6a947fe Binary files /dev/null and b/discord/images/activities/tutorial-channel-name.png differ diff --git a/discord/images/activities/tutorial-hero.png b/discord/images/activities/tutorial-hero.png new file mode 100644 index 0000000000..04f72676b2 Binary files /dev/null and b/discord/images/activities/tutorial-hero.png differ diff --git a/discord/images/activities/url-mapping-do.png b/discord/images/activities/url-mapping-do.png new file mode 100644 index 0000000000..e5e0ef26a5 Binary files /dev/null and b/discord/images/activities/url-mapping-do.png differ diff --git a/discord/images/activities/url-mapping-dont.png b/discord/images/activities/url-mapping-dont.png new file mode 100644 index 0000000000..806aefd7f1 Binary files /dev/null and b/discord/images/activities/url-mapping-dont.png differ diff --git a/discord/images/activities/url-mapping-tutorial.png b/discord/images/activities/url-mapping-tutorial.png new file mode 100644 index 0000000000..878a21c9c7 Binary files /dev/null and b/discord/images/activities/url-mapping-tutorial.png differ diff --git a/discord/images/alerts.webp b/discord/images/alerts.webp new file mode 100644 index 0000000000..f9c7affb7f Binary files /dev/null and b/discord/images/alerts.webp differ diff --git a/discord/images/annotated-presence-data-activities.png b/discord/images/annotated-presence-data-activities.png new file mode 100644 index 0000000000..52f9bbb320 Binary files /dev/null and b/discord/images/annotated-presence-data-activities.png differ diff --git a/discord/images/botuser-profile.webp b/discord/images/botuser-profile.webp new file mode 100644 index 0000000000..ccf7a87bac Binary files /dev/null and b/discord/images/botuser-profile.webp differ diff --git a/discord/images/bp-productpage-app-description.webp b/discord/images/bp-productpage-app-description.webp new file mode 100644 index 0000000000..1105a1bb5c Binary files /dev/null and b/discord/images/bp-productpage-app-description.webp differ diff --git a/discord/images/bp-productpage-summary-bad.webp b/discord/images/bp-productpage-summary-bad.webp new file mode 100644 index 0000000000..56018ab762 Binary files /dev/null and b/discord/images/bp-productpage-summary-bad.webp differ diff --git a/discord/images/bp-productpage-summary-good.webp b/discord/images/bp-productpage-summary-good.webp new file mode 100644 index 0000000000..08afa8f7b0 Binary files /dev/null and b/discord/images/bp-productpage-summary-good.webp differ diff --git a/discord/images/bp-productpage-tags.webp b/discord/images/bp-productpage-tags.webp new file mode 100644 index 0000000000..9318f59644 Binary files /dev/null and b/discord/images/bp-productpage-tags.webp differ diff --git a/images/certified-device.png b/discord/images/certified-device.png similarity index 100% rename from images/certified-device.png rename to discord/images/certified-device.png diff --git a/discord/images/certified-device.webp b/discord/images/certified-device.webp new file mode 100644 index 0000000000..1c3c5cb238 Binary files /dev/null and b/discord/images/certified-device.webp differ diff --git a/discord/images/cloudflare-general-overview.webp b/discord/images/cloudflare-general-overview.webp new file mode 100644 index 0000000000..26b35908e6 Binary files /dev/null and b/discord/images/cloudflare-general-overview.webp differ diff --git a/discord/images/cloudflare-interactions-endpoint.webp b/discord/images/cloudflare-interactions-endpoint.webp new file mode 100644 index 0000000000..b147475344 Binary files /dev/null and b/discord/images/cloudflare-interactions-endpoint.webp differ diff --git a/discord/images/cloudflare-ngrok.webp b/discord/images/cloudflare-ngrok.webp new file mode 100644 index 0000000000..2a6e56159a Binary files /dev/null and b/discord/images/cloudflare-ngrok.webp differ diff --git a/discord/images/cloudflare-tutorial-demo.webp b/discord/images/cloudflare-tutorial-demo.webp new file mode 100644 index 0000000000..dbd41fa6d8 Binary files /dev/null and b/discord/images/cloudflare-tutorial-demo.webp differ diff --git a/discord/images/cloudflare-url-generator.webp b/discord/images/cloudflare-url-generator.webp new file mode 100644 index 0000000000..58fa2f64f5 Binary files /dev/null and b/discord/images/cloudflare-url-generator.webp differ diff --git a/discord/images/command-entry-point.png b/discord/images/command-entry-point.png new file mode 100644 index 0000000000..eb359e0560 Binary files /dev/null and b/discord/images/command-entry-point.png differ diff --git a/discord/images/command-entry-point.webp b/discord/images/command-entry-point.webp new file mode 100644 index 0000000000..2fcf0d51d7 Binary files /dev/null and b/discord/images/command-entry-point.webp differ diff --git a/discord/images/command-types.png b/discord/images/command-types.png new file mode 100644 index 0000000000..1497c807f6 Binary files /dev/null and b/discord/images/command-types.png differ diff --git a/discord/images/command-types.webp b/discord/images/command-types.webp new file mode 100644 index 0000000000..bd7c11db03 Binary files /dev/null and b/discord/images/command-types.webp differ diff --git a/images/command-with-groups-subcommands-parameters.png b/discord/images/command-with-groups-subcommands-parameters.png similarity index 100% rename from images/command-with-groups-subcommands-parameters.png rename to discord/images/command-with-groups-subcommands-parameters.png diff --git a/discord/images/command-with-groups-subcommands-parameters.webp b/discord/images/command-with-groups-subcommands-parameters.webp new file mode 100644 index 0000000000..052127ab3f Binary files /dev/null and b/discord/images/command-with-groups-subcommands-parameters.webp differ diff --git a/images/command-with-groups-subcommands.png b/discord/images/command-with-groups-subcommands.png similarity index 100% rename from images/command-with-groups-subcommands.png rename to discord/images/command-with-groups-subcommands.png diff --git a/discord/images/command-with-groups-subcommands.webp b/discord/images/command-with-groups-subcommands.webp new file mode 100644 index 0000000000..33b1b48317 Binary files /dev/null and b/discord/images/command-with-groups-subcommands.webp differ diff --git a/images/command.png b/discord/images/command.png similarity index 100% rename from images/command.png rename to discord/images/command.png diff --git a/discord/images/command.webp b/discord/images/command.webp new file mode 100644 index 0000000000..c190246204 Binary files /dev/null and b/discord/images/command.webp differ diff --git a/discord/images/components/action-row.png b/discord/images/components/action-row.png new file mode 100644 index 0000000000..376bf28294 Binary files /dev/null and b/discord/images/components/action-row.png differ diff --git a/discord/images/components/action-row.webp b/discord/images/components/action-row.webp new file mode 100644 index 0000000000..faca5086d9 Binary files /dev/null and b/discord/images/components/action-row.webp differ diff --git a/discord/images/components/channel-select.png b/discord/images/components/channel-select.png new file mode 100644 index 0000000000..ab89bda496 Binary files /dev/null and b/discord/images/components/channel-select.png differ diff --git a/discord/images/components/channel-select.webp b/discord/images/components/channel-select.webp new file mode 100644 index 0000000000..84021d7fa4 Binary files /dev/null and b/discord/images/components/channel-select.webp differ diff --git a/discord/images/components/container.png b/discord/images/components/container.png new file mode 100644 index 0000000000..68c71c2e58 Binary files /dev/null and b/discord/images/components/container.png differ diff --git a/discord/images/components/container.webp b/discord/images/components/container.webp new file mode 100644 index 0000000000..ad15734e26 Binary files /dev/null and b/discord/images/components/container.webp differ diff --git a/discord/images/components/file-upload-modal-example.webp b/discord/images/components/file-upload-modal-example.webp new file mode 100644 index 0000000000..cf3643d6f2 Binary files /dev/null and b/discord/images/components/file-upload-modal-example.webp differ diff --git a/discord/images/components/file.png b/discord/images/components/file.png new file mode 100644 index 0000000000..28076faa40 Binary files /dev/null and b/discord/images/components/file.png differ diff --git a/discord/images/components/file.webp b/discord/images/components/file.webp new file mode 100644 index 0000000000..117fc726e9 Binary files /dev/null and b/discord/images/components/file.webp differ diff --git a/discord/images/components/hero.png b/discord/images/components/hero.png new file mode 100644 index 0000000000..5245070138 Binary files /dev/null and b/discord/images/components/hero.png differ diff --git a/discord/images/components/hero.webp b/discord/images/components/hero.webp new file mode 100644 index 0000000000..820aef6124 Binary files /dev/null and b/discord/images/components/hero.webp differ diff --git a/discord/images/components/media-gallery.png b/discord/images/components/media-gallery.png new file mode 100644 index 0000000000..6b567864e3 Binary files /dev/null and b/discord/images/components/media-gallery.png differ diff --git a/discord/images/components/media-gallery.webp b/discord/images/components/media-gallery.webp new file mode 100644 index 0000000000..4eca9e5a05 Binary files /dev/null and b/discord/images/components/media-gallery.webp differ diff --git a/discord/images/components/mentionable-select.png b/discord/images/components/mentionable-select.png new file mode 100644 index 0000000000..07c29c7baf Binary files /dev/null and b/discord/images/components/mentionable-select.png differ diff --git a/discord/images/components/mentionable-select.webp b/discord/images/components/mentionable-select.webp new file mode 100644 index 0000000000..c2acb80f17 Binary files /dev/null and b/discord/images/components/mentionable-select.webp differ diff --git a/discord/images/components/modal-channel-select.webp b/discord/images/components/modal-channel-select.webp new file mode 100644 index 0000000000..1aa2c15568 Binary files /dev/null and b/discord/images/components/modal-channel-select.webp differ diff --git a/images/modal-desktop.png b/discord/images/components/modal-desktop.png similarity index 100% rename from images/modal-desktop.png rename to discord/images/components/modal-desktop.png diff --git a/discord/images/components/modal-label.webp b/discord/images/components/modal-label.webp new file mode 100644 index 0000000000..eb3d57256c Binary files /dev/null and b/discord/images/components/modal-label.webp differ diff --git a/discord/images/components/modal-mentionable-select.webp b/discord/images/components/modal-mentionable-select.webp new file mode 100644 index 0000000000..9034e665e4 Binary files /dev/null and b/discord/images/components/modal-mentionable-select.webp differ diff --git a/discord/images/components/modal-role-select.webp b/discord/images/components/modal-role-select.webp new file mode 100644 index 0000000000..c00c8f71ca Binary files /dev/null and b/discord/images/components/modal-role-select.webp differ diff --git a/discord/images/components/modal-string-select.webp b/discord/images/components/modal-string-select.webp new file mode 100644 index 0000000000..17ca8bbb1c Binary files /dev/null and b/discord/images/components/modal-string-select.webp differ diff --git a/discord/images/components/modal-text-display.webp b/discord/images/components/modal-text-display.webp new file mode 100644 index 0000000000..c7d49925a9 Binary files /dev/null and b/discord/images/components/modal-text-display.webp differ diff --git a/discord/images/components/modal-user-select.webp b/discord/images/components/modal-user-select.webp new file mode 100644 index 0000000000..fa2f53d920 Binary files /dev/null and b/discord/images/components/modal-user-select.webp differ diff --git a/discord/images/components/modal.png b/discord/images/components/modal.png new file mode 100644 index 0000000000..765426ee09 Binary files /dev/null and b/discord/images/components/modal.png differ diff --git a/discord/images/components/modal.webp b/discord/images/components/modal.webp new file mode 100644 index 0000000000..63b5ddb3c7 Binary files /dev/null and b/discord/images/components/modal.webp differ diff --git a/discord/images/components/multiple-buttons-example-1.png b/discord/images/components/multiple-buttons-example-1.png new file mode 100644 index 0000000000..fe8acdd25f Binary files /dev/null and b/discord/images/components/multiple-buttons-example-1.png differ diff --git a/discord/images/components/multiple-buttons-example-1.webp b/discord/images/components/multiple-buttons-example-1.webp new file mode 100644 index 0000000000..52c63ecc5b Binary files /dev/null and b/discord/images/components/multiple-buttons-example-1.webp differ diff --git a/discord/images/components/multiple-buttons-example-2.png b/discord/images/components/multiple-buttons-example-2.png new file mode 100644 index 0000000000..f646978dc0 Binary files /dev/null and b/discord/images/components/multiple-buttons-example-2.png differ diff --git a/discord/images/components/multiple-buttons-example-2.webp b/discord/images/components/multiple-buttons-example-2.webp new file mode 100644 index 0000000000..e78887423e Binary files /dev/null and b/discord/images/components/multiple-buttons-example-2.webp differ diff --git a/discord/images/components/premium-button.png b/discord/images/components/premium-button.png new file mode 100644 index 0000000000..8f566c20b2 Binary files /dev/null and b/discord/images/components/premium-button.png differ diff --git a/discord/images/components/premium-button.webp b/discord/images/components/premium-button.webp new file mode 100644 index 0000000000..cf008e864b Binary files /dev/null and b/discord/images/components/premium-button.webp differ diff --git a/discord/images/components/role-select.png b/discord/images/components/role-select.png new file mode 100644 index 0000000000..953881aca1 Binary files /dev/null and b/discord/images/components/role-select.png differ diff --git a/discord/images/components/role-select.webp b/discord/images/components/role-select.webp new file mode 100644 index 0000000000..129e43388d Binary files /dev/null and b/discord/images/components/role-select.webp differ diff --git a/discord/images/components/section.png b/discord/images/components/section.png new file mode 100644 index 0000000000..af9d7dd987 Binary files /dev/null and b/discord/images/components/section.png differ diff --git a/discord/images/components/section.webp b/discord/images/components/section.webp new file mode 100644 index 0000000000..11a007de40 Binary files /dev/null and b/discord/images/components/section.webp differ diff --git a/discord/images/components/separator.png b/discord/images/components/separator.png new file mode 100644 index 0000000000..f7282988a9 Binary files /dev/null and b/discord/images/components/separator.png differ diff --git a/discord/images/components/separator.webp b/discord/images/components/separator.webp new file mode 100644 index 0000000000..3108449c26 Binary files /dev/null and b/discord/images/components/separator.webp differ diff --git a/discord/images/components/string-select.png b/discord/images/components/string-select.png new file mode 100644 index 0000000000..b8a02c2923 Binary files /dev/null and b/discord/images/components/string-select.png differ diff --git a/discord/images/components/string-select.webp b/discord/images/components/string-select.webp new file mode 100644 index 0000000000..5f2a3c50a8 Binary files /dev/null and b/discord/images/components/string-select.webp differ diff --git a/discord/images/components/text-display.png b/discord/images/components/text-display.png new file mode 100644 index 0000000000..f42211b847 Binary files /dev/null and b/discord/images/components/text-display.png differ diff --git a/discord/images/components/text-display.webp b/discord/images/components/text-display.webp new file mode 100644 index 0000000000..2ad6cf14de Binary files /dev/null and b/discord/images/components/text-display.webp differ diff --git a/discord/images/components/user-select.png b/discord/images/components/user-select.png new file mode 100644 index 0000000000..ce64e5c39c Binary files /dev/null and b/discord/images/components/user-select.png differ diff --git a/discord/images/components/user-select.webp b/discord/images/components/user-select.webp new file mode 100644 index 0000000000..681665180b Binary files /dev/null and b/discord/images/components/user-select.webp differ diff --git a/images/cpp-files-sdk.png b/discord/images/cpp-files-sdk.png similarity index 100% rename from images/cpp-files-sdk.png rename to discord/images/cpp-files-sdk.png diff --git a/discord/images/cpp-files-sdk.webp b/discord/images/cpp-files-sdk.webp new file mode 100644 index 0000000000..4228c999be Binary files /dev/null and b/discord/images/cpp-files-sdk.webp differ diff --git a/discord/images/create-team-owned-app.png b/discord/images/create-team-owned-app.png new file mode 100644 index 0000000000..d157c0ceb8 Binary files /dev/null and b/discord/images/create-team-owned-app.png differ diff --git a/discord/images/create-team-owned-app.webp b/discord/images/create-team-owned-app.webp new file mode 100644 index 0000000000..64290be432 Binary files /dev/null and b/discord/images/create-team-owned-app.webp differ diff --git a/discord/images/default-presence-activities.png b/discord/images/default-presence-activities.png new file mode 100644 index 0000000000..1fdbdb07da Binary files /dev/null and b/discord/images/default-presence-activities.png differ diff --git a/discord/images/demo-gif.gif b/discord/images/demo-gif.gif new file mode 100644 index 0000000000..54e123f5f3 Binary files /dev/null and b/discord/images/demo-gif.gif differ diff --git a/discord/images/discovery-app-directory-product-page.webp b/discord/images/discovery-app-directory-product-page.webp new file mode 100644 index 0000000000..2cb9626f0e Binary files /dev/null and b/discord/images/discovery-app-directory-product-page.webp differ diff --git a/discord/images/discovery-app-launcher.webp b/discord/images/discovery-app-launcher.webp new file mode 100644 index 0000000000..35f077d072 Binary files /dev/null and b/discord/images/discovery-app-launcher.webp differ diff --git a/discord/images/discovery-sharing-links.webp b/discord/images/discovery-sharing-links.webp new file mode 100644 index 0000000000..b5ec939341 Binary files /dev/null and b/discord/images/discovery-sharing-links.webp differ diff --git a/discord/images/discovery/app-description.png b/discord/images/discovery/app-description.png new file mode 100644 index 0000000000..c5f4902de1 Binary files /dev/null and b/discord/images/discovery/app-description.png differ diff --git a/discord/images/discovery/app-launcher.png b/discord/images/discovery/app-launcher.png new file mode 100644 index 0000000000..2f5427edfb Binary files /dev/null and b/discord/images/discovery/app-launcher.png differ diff --git a/discord/images/discovery/directory-product-page.png b/discord/images/discovery/directory-product-page.png new file mode 100644 index 0000000000..b780113b8e Binary files /dev/null and b/discord/images/discovery/directory-product-page.png differ diff --git a/discord/images/discovery/sharing-links.png b/discord/images/discovery/sharing-links.png new file mode 100644 index 0000000000..226d8cf100 Binary files /dev/null and b/discord/images/discovery/sharing-links.png differ diff --git a/discord/images/discovery/summary-bad.png b/discord/images/discovery/summary-bad.png new file mode 100644 index 0000000000..fa5615a947 Binary files /dev/null and b/discord/images/discovery/summary-bad.png differ diff --git a/discord/images/discovery/summary-good.png b/discord/images/discovery/summary-good.png new file mode 100644 index 0000000000..ef23e5a8c6 Binary files /dev/null and b/discord/images/discovery/summary-good.png differ diff --git a/discord/images/discovery/tags.png b/discord/images/discovery/tags.png new file mode 100644 index 0000000000..0598e3cffd Binary files /dev/null and b/discord/images/discovery/tags.png differ diff --git a/discord/images/events/gateway-lifecycle.svg b/discord/images/events/gateway-lifecycle.svg new file mode 100644 index 0000000000..34cedcf2b8 --- /dev/null +++ b/discord/images/events/gateway-lifecycle.svg @@ -0,0 +1,463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/discord/images/example-poll.png b/discord/images/example-poll.png new file mode 100644 index 0000000000..2c934fae52 Binary files /dev/null and b/discord/images/example-poll.png differ diff --git a/discord/images/example-poll.webp b/discord/images/example-poll.webp new file mode 100644 index 0000000000..2b752880d5 Binary files /dev/null and b/discord/images/example-poll.webp differ diff --git a/images/examples-ephemeral-message.png b/discord/images/examples-ephemeral-message.png similarity index 100% rename from images/examples-ephemeral-message.png rename to discord/images/examples-ephemeral-message.png diff --git a/images/examples-update-message.png b/discord/images/examples-update-message.png similarity index 100% rename from images/examples-update-message.png rename to discord/images/examples-update-message.png diff --git a/discord/images/frame-wumpus.png b/discord/images/frame-wumpus.png new file mode 100644 index 0000000000..4f736d0d22 Binary files /dev/null and b/discord/images/frame-wumpus.png differ diff --git a/discord/images/game-overlay-sdk-invite.webp b/discord/images/game-overlay-sdk-invite.webp new file mode 100644 index 0000000000..b14f121bc9 Binary files /dev/null and b/discord/images/game-overlay-sdk-invite.webp differ diff --git a/discord/images/game-overlay-sdk-voice-settings.webp b/discord/images/game-overlay-sdk-voice-settings.webp new file mode 100644 index 0000000000..64564af6c0 Binary files /dev/null and b/discord/images/game-overlay-sdk-voice-settings.webp differ diff --git a/discord/images/game-sdk-presence-example.png b/discord/images/game-sdk-presence-example.png new file mode 100644 index 0000000000..8d4c19144f Binary files /dev/null and b/discord/images/game-sdk-presence-example.png differ diff --git a/discord/images/gateway-lifecycle.svg b/discord/images/gateway-lifecycle.svg new file mode 100644 index 0000000000..b9b1bad4e0 --- /dev/null +++ b/discord/images/gateway-lifecycle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/discord/images/getting-started-default-install.png b/discord/images/getting-started-default-install.png new file mode 100644 index 0000000000..de254df6ff Binary files /dev/null and b/discord/images/getting-started-default-install.png differ diff --git a/discord/images/getting-started-default-install.webp b/discord/images/getting-started-default-install.webp new file mode 100644 index 0000000000..6fdd5f1082 Binary files /dev/null and b/discord/images/getting-started-default-install.webp differ diff --git a/images/getting-started-demo.gif b/discord/images/getting-started-demo copy.gif similarity index 100% rename from images/getting-started-demo.gif rename to discord/images/getting-started-demo copy.gif diff --git a/discord/images/getting-started-demo.gif b/discord/images/getting-started-demo.gif new file mode 100644 index 0000000000..54e123f5f3 Binary files /dev/null and b/discord/images/getting-started-demo.gif differ diff --git a/discord/images/getting-started-interactions-endpoint.png b/discord/images/getting-started-interactions-endpoint.png new file mode 100644 index 0000000000..5d79345a9a Binary files /dev/null and b/discord/images/getting-started-interactions-endpoint.png differ diff --git a/discord/images/getting-started-interactions-endpoint.webp b/discord/images/getting-started-interactions-endpoint.webp new file mode 100644 index 0000000000..7fb6f82517 Binary files /dev/null and b/discord/images/getting-started-interactions-endpoint.webp differ diff --git a/discord/images/interactions-url.webp b/discord/images/interactions-url.webp new file mode 100644 index 0000000000..0b23efbbdc Binary files /dev/null and b/discord/images/interactions-url.webp differ diff --git a/discord/images/introduction.svg b/discord/images/introduction.svg new file mode 100644 index 0000000000..788129ff6e --- /dev/null +++ b/discord/images/introduction.svg @@ -0,0 +1,1841 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<657.338Gradient id="linear" x1="0" y1="601.542" x2="748.333" y2="601.542"> + + + + + +<657.516Gradient id="linear" x1="0" y1="587.883" x2="745.548" y2="587.883"> + + + + + +<1587.35Gradient id="linear" x1="0" y1="239.308" x2="1666.71" y2="230.729"> + + + + + + + +<1491.84Gradient id="linear" x1="0" y1="192.359" x2="1576.79" y2="352.727"> + + + + + + +<748.815Gradient id="linear" x1="0" y1="20.0728" x2="748.815" y2="125.178"> + + + + + + + +<955.512Gradient id="linear" x1="0" y1="19.9154" x2="955.512" y2="125.227"> + + + + + + + +<1088.81Gradient id="linear" x1="0" y1="21.0174" x2="1088.81" y2="125.208"> + + + + + + + +<1560.97Gradient id="linear" x1="0" y1="338.518" x2="1544.36" y2="289.719"> + + + + + + + +<1481.77Gradient id="linear" x1="0" y1="315.442" x2="1481.77" y2="274.752"> + + + + + + + +<1577.75Gradient id="linear" x1="0" y1="259.972" x2="1606.63" y2="284.209"> + + + + + + + + +<1536.92Gradient id="linear" x1="0" y1="263.633" x2="1526.25" y2="302.63"> + + + +<1524.87Gradient id="linear" x1="0" y1="259.972" x2="1495.98" y2="284.209"> + + + + + + + + +<1553.4Gradient id="linear" x1="0" y1="338.338" x2="1549.61" y2="349.49"> + + + + + + + +<1573.24Gradient id="linear" x1="0" y1="339.915" x2="1574.45" y2="350.553"> + + + + + + + +<1491.84Gradient id="linear" x1="0" y1="192.359" x2="1576.79" y2="352.727"> + + + + + + + +<895.142Gradient id="linear" x1="0" y1="51.7983" x2="951.261" y2="51.7983"> + + + + + + + +<1471.31Gradient id="linear" x1="0" y1="585.994" x2="1486.53" y2="585.994"> + + + + + + + +<1445.18Gradient id="linear" x1="0" y1="585.994" x2="1460.4" y2="585.994"> + + + + + + + +<1427.81Gradient id="linear" x1="0" y1="466.502" x2="1410.7" y2="419.495"> + + + + + + + + + + +<1516.15Gradient id="linear" x1="0" y1="451.063" x2="1556.9" y2="485.248"> + + + + + + + + +<1450.3Gradient id="linear" x1="0" y1="522.189" x2="1503.5" y2="522.189"> + + + + + + +<871.593Gradient id="linear" x1="0" y1="566.943" x2="871.593" y2="429.236"> + + + + + + + + + + +<1202.55Gradient id="linear" x1="0" y1="375.843" x2="1178.76" y2="348.496"> + + + +<635.916Gradient id="linear" x1="0" y1="55.705" x2="657.791" y2="133.375"> + + + + + + + +<963.837Gradient id="linear" x1="0" y1="337.741" x2="858.368" y2="337.741"> + + + + +<853.221Gradient id="linear" x1="0" y1="254.343" x2="790.42" y2="339.541"> + + + +<823.307Gradient id="linear" x1="0" y1="235.892" x2="801.185" y2="238.608"> + + + + + +<1058.13Gradient id="linear" x1="0" y1="81.5064" x2="1058.13" y2="172.875"> + + + + + + + +<1052.15Gradient id="linear" x1="0" y1="106.472" x2="1114.5" y2="106.472"> + + + + + + + +<843.627Gradient id="linear" x1="0" y1="106.245" x2="843.627" y2="186.356"> + + + + + + + +<678.495Gradient id="linear" x1="0" y1="83.8682" x2="678.495" y2="167.266"> + + + + + + + +<631.567Gradient id="linear" x1="0" y1="56.807" x2="653.609" y2="135.078"> + + + + + + + + + + diff --git a/images/lib-linked-sdk.png b/discord/images/lib-linked-sdk.png similarity index 100% rename from images/lib-linked-sdk.png rename to discord/images/lib-linked-sdk.png diff --git a/discord/images/lib-linked-sdk.webp b/discord/images/lib-linked-sdk.webp new file mode 100644 index 0000000000..413a599ccf Binary files /dev/null and b/discord/images/lib-linked-sdk.webp differ diff --git a/discord/images/linked-roles-connect-account.webp b/discord/images/linked-roles-connect-account.webp new file mode 100644 index 0000000000..39e347b101 Binary files /dev/null and b/discord/images/linked-roles-connect-account.webp differ diff --git a/discord/images/linked-roles-connected.webp b/discord/images/linked-roles-connected.webp new file mode 100644 index 0000000000..b21fe847ee Binary files /dev/null and b/discord/images/linked-roles-connected.webp differ diff --git a/discord/images/linked-roles-consent-dialog.webp b/discord/images/linked-roles-consent-dialog.webp new file mode 100644 index 0000000000..e1d653247d Binary files /dev/null and b/discord/images/linked-roles-consent-dialog.webp differ diff --git a/discord/images/linked-roles-glitch-share-url.webp b/discord/images/linked-roles-glitch-share-url.webp new file mode 100644 index 0000000000..a2f6805dae Binary files /dev/null and b/discord/images/linked-roles-glitch-share-url.webp differ diff --git a/discord/images/linked-roles-glitch.webp b/discord/images/linked-roles-glitch.webp new file mode 100644 index 0000000000..edc0fd2280 Binary files /dev/null and b/discord/images/linked-roles-glitch.webp differ diff --git a/discord/images/linked-roles-oauth-config.webp b/discord/images/linked-roles-oauth-config.webp new file mode 100644 index 0000000000..7adfeea019 Binary files /dev/null and b/discord/images/linked-roles-oauth-config.webp differ diff --git a/discord/images/linked-roles-register.webp b/discord/images/linked-roles-register.webp new file mode 100644 index 0000000000..fc919b6927 Binary files /dev/null and b/discord/images/linked-roles-register.webp differ diff --git a/discord/images/linked-roles-verification-setup.webp b/discord/images/linked-roles-verification-setup.webp new file mode 100644 index 0000000000..fe2506d478 Binary files /dev/null and b/discord/images/linked-roles-verification-setup.webp differ diff --git a/discord/images/linked-roles-verify-endpoint.webp b/discord/images/linked-roles-verify-endpoint.webp new file mode 100644 index 0000000000..d3d978280e Binary files /dev/null and b/discord/images/linked-roles-verify-endpoint.webp differ diff --git a/discord/images/mdx-button.webp b/discord/images/mdx-button.webp new file mode 100644 index 0000000000..a4498d7ecf Binary files /dev/null and b/discord/images/mdx-button.webp differ diff --git a/discord/images/mdx-card.webp b/discord/images/mdx-card.webp new file mode 100644 index 0000000000..d1da118ac2 Binary files /dev/null and b/discord/images/mdx-card.webp differ diff --git a/discord/images/mdx-collapsible.webp b/discord/images/mdx-collapsible.webp new file mode 100644 index 0000000000..e3e342470e Binary files /dev/null and b/discord/images/mdx-collapsible.webp differ diff --git a/images/message-command.png b/discord/images/message-command.png similarity index 100% rename from images/message-command.png rename to discord/images/message-command.png diff --git a/discord/images/message-command.webp b/discord/images/message-command.webp new file mode 100644 index 0000000000..1c7ea90b07 Binary files /dev/null and b/discord/images/message-command.webp differ diff --git a/discord/images/modal-desktop.webp b/discord/images/modal-desktop.webp new file mode 100644 index 0000000000..5efe851773 Binary files /dev/null and b/discord/images/modal-desktop.webp differ diff --git a/discord/images/monetization-overview.webp b/discord/images/monetization-overview.webp new file mode 100644 index 0000000000..17109c0306 Binary files /dev/null and b/discord/images/monetization-overview.webp differ diff --git a/discord/images/monetization/botuser-profile.png b/discord/images/monetization/botuser-profile.png new file mode 100644 index 0000000000..016359e432 Binary files /dev/null and b/discord/images/monetization/botuser-profile.png differ diff --git a/discord/images/monetization/multisub.png b/discord/images/monetization/multisub.png new file mode 100644 index 0000000000..22349c2b4d Binary files /dev/null and b/discord/images/monetization/multisub.png differ diff --git a/discord/images/monetization/overview.png b/discord/images/monetization/overview.png new file mode 100644 index 0000000000..dc450ec99a Binary files /dev/null and b/discord/images/monetization/overview.png differ diff --git a/discord/images/monetization/premium-button.png b/discord/images/monetization/premium-button.png new file mode 100644 index 0000000000..8f566c20b2 Binary files /dev/null and b/discord/images/monetization/premium-button.png differ diff --git a/discord/images/monetization/premium-items.png b/discord/images/monetization/premium-items.png new file mode 100644 index 0000000000..1f813936af Binary files /dev/null and b/discord/images/monetization/premium-items.png differ diff --git a/discord/images/monetization/sku-benefits.png b/discord/images/monetization/sku-benefits.png new file mode 100644 index 0000000000..d3dc2d1af6 Binary files /dev/null and b/discord/images/monetization/sku-benefits.png differ diff --git a/discord/images/monetization/sku_embed.png b/discord/images/monetization/sku_embed.png new file mode 100644 index 0000000000..1e9c198bc0 Binary files /dev/null and b/discord/images/monetization/sku_embed.png differ diff --git a/discord/images/monetization/store_embed.png b/discord/images/monetization/store_embed.png new file mode 100644 index 0000000000..315d14511a Binary files /dev/null and b/discord/images/monetization/store_embed.png differ diff --git a/discord/images/multiple-buttons-example-1.webp b/discord/images/multiple-buttons-example-1.webp new file mode 100644 index 0000000000..3a4165dc10 Binary files /dev/null and b/discord/images/multiple-buttons-example-1.webp differ diff --git a/discord/images/multiple-buttons-example-2.webp b/discord/images/multiple-buttons-example-2.webp new file mode 100644 index 0000000000..c8b49930b8 Binary files /dev/null and b/discord/images/multiple-buttons-example-2.webp differ diff --git a/discord/images/multisub.webp b/discord/images/multisub.webp new file mode 100644 index 0000000000..9f58c2af5c Binary files /dev/null and b/discord/images/multisub.webp differ diff --git a/discord/images/new-permissions-flowchart copy.svg b/discord/images/new-permissions-flowchart copy.svg new file mode 100644 index 0000000000..a07279d3d9 --- /dev/null +++ b/discord/images/new-permissions-flowchart copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/discord/images/new-permissions-flowchart.svg b/discord/images/new-permissions-flowchart.svg new file mode 100644 index 0000000000..d946b97dd3 --- /dev/null +++ b/discord/images/new-permissions-flowchart.svg @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/discord/images/overview-apps.png b/discord/images/overview-apps.png new file mode 100644 index 0000000000..707cfd9f99 Binary files /dev/null and b/discord/images/overview-apps.png differ diff --git a/discord/images/overview-command-desktop.png b/discord/images/overview-command-desktop.png new file mode 100644 index 0000000000..cb3411ad71 Binary files /dev/null and b/discord/images/overview-command-desktop.png differ diff --git a/discord/images/overview-command-desktop.webp b/discord/images/overview-command-desktop.webp new file mode 100644 index 0000000000..062c95adfd Binary files /dev/null and b/discord/images/overview-command-desktop.webp differ diff --git a/discord/images/overview-components.png b/discord/images/overview-components.png new file mode 100644 index 0000000000..19ca8b1a13 Binary files /dev/null and b/discord/images/overview-components.png differ diff --git a/discord/images/overview-components.webp b/discord/images/overview-components.webp new file mode 100644 index 0000000000..00b86df51e Binary files /dev/null and b/discord/images/overview-components.webp differ diff --git a/discord/images/overview-modals.png b/discord/images/overview-modals.png new file mode 100644 index 0000000000..410104e8b4 Binary files /dev/null and b/discord/images/overview-modals.png differ diff --git a/discord/images/overview-modals.webp b/discord/images/overview-modals.webp new file mode 100644 index 0000000000..938f3abd16 Binary files /dev/null and b/discord/images/overview-modals.webp differ diff --git a/discord/images/overview-of-apps-banner.webp b/discord/images/overview-of-apps-banner.webp new file mode 100644 index 0000000000..cd16b13e35 Binary files /dev/null and b/discord/images/overview-of-apps-banner.webp differ diff --git a/discord/images/premium-button.webp b/discord/images/premium-button.webp new file mode 100644 index 0000000000..fb5ff979c2 Binary files /dev/null and b/discord/images/premium-button.webp differ diff --git a/discord/images/premium-items.webp b/discord/images/premium-items.webp new file mode 100644 index 0000000000..74e73820f0 Binary files /dev/null and b/discord/images/premium-items.webp differ diff --git a/discord/images/rich-presence-asset-images.webp b/discord/images/rich-presence-asset-images.webp new file mode 100644 index 0000000000..dd15387708 Binary files /dev/null and b/discord/images/rich-presence-asset-images.webp differ diff --git a/discord/images/rich-presence-examples.png b/discord/images/rich-presence-examples.png new file mode 100644 index 0000000000..d834cb7c87 Binary files /dev/null and b/discord/images/rich-presence-examples.png differ diff --git a/discord/images/rich-presence-invite-image.webp b/discord/images/rich-presence-invite-image.webp new file mode 100644 index 0000000000..3942ffd5be Binary files /dev/null and b/discord/images/rich-presence-invite-image.webp differ diff --git a/discord/images/rich-presence/actionable.png b/discord/images/rich-presence/actionable.png new file mode 100644 index 0000000000..bc19ffdb6b Binary files /dev/null and b/discord/images/rich-presence/actionable.png differ diff --git a/discord/images/rich-presence/activities-example.png b/discord/images/rich-presence/activities-example.png new file mode 100644 index 0000000000..35e013b2f0 Binary files /dev/null and b/discord/images/rich-presence/activities-example.png differ diff --git a/discord/images/rich-presence/all-fields.png b/discord/images/rich-presence/all-fields.png new file mode 100644 index 0000000000..9778ef895a Binary files /dev/null and b/discord/images/rich-presence/all-fields.png differ diff --git a/discord/images/rich-presence/annotated-data-activities.png b/discord/images/rich-presence/annotated-data-activities.png new file mode 100644 index 0000000000..bbe990ac05 Binary files /dev/null and b/discord/images/rich-presence/annotated-data-activities.png differ diff --git a/discord/images/rich-presence/asset-images.png b/discord/images/rich-presence/asset-images.png new file mode 100644 index 0000000000..7ca8681095 Binary files /dev/null and b/discord/images/rich-presence/asset-images.png differ diff --git a/discord/images/rich-presence/default-activities.png b/discord/images/rich-presence/default-activities.png new file mode 100644 index 0000000000..1db6748dcc Binary files /dev/null and b/discord/images/rich-presence/default-activities.png differ diff --git a/discord/images/rich-presence/examples.png b/discord/images/rich-presence/examples.png new file mode 100644 index 0000000000..d834cb7c87 Binary files /dev/null and b/discord/images/rich-presence/examples.png differ diff --git a/discord/images/rich-presence/game-sdk-example.png b/discord/images/rich-presence/game-sdk-example.png new file mode 100644 index 0000000000..4d986a089b Binary files /dev/null and b/discord/images/rich-presence/game-sdk-example.png differ diff --git a/discord/images/rich-presence/good-art.png b/discord/images/rich-presence/good-art.png new file mode 100644 index 0000000000..20b38a03b4 Binary files /dev/null and b/discord/images/rich-presence/good-art.png differ diff --git a/discord/images/rich-presence/invite-image.png b/discord/images/rich-presence/invite-image.png new file mode 100644 index 0000000000..929a4657d8 Binary files /dev/null and b/discord/images/rich-presence/invite-image.png differ diff --git a/discord/images/rich-presence/legend.png b/discord/images/rich-presence/legend.png new file mode 100644 index 0000000000..b0416341f0 Binary files /dev/null and b/discord/images/rich-presence/legend.png differ diff --git a/discord/images/rich-presence/short-strings.png b/discord/images/rich-presence/short-strings.png new file mode 100644 index 0000000000..9ad0faa532 Binary files /dev/null and b/discord/images/rich-presence/short-strings.png differ diff --git a/discord/images/rp-actionable.webp b/discord/images/rp-actionable.webp new file mode 100644 index 0000000000..7bae22d632 Binary files /dev/null and b/discord/images/rp-actionable.webp differ diff --git a/discord/images/rp-all-fields.webp b/discord/images/rp-all-fields.webp new file mode 100644 index 0000000000..68df7468aa Binary files /dev/null and b/discord/images/rp-all-fields.webp differ diff --git a/discord/images/rp-good-art.webp b/discord/images/rp-good-art.webp new file mode 100644 index 0000000000..e6a78d9f73 Binary files /dev/null and b/discord/images/rp-good-art.webp differ diff --git a/discord/images/rp-legend.webp b/discord/images/rp-legend.webp new file mode 100644 index 0000000000..0b9c42d3a9 Binary files /dev/null and b/discord/images/rp-legend.webp differ diff --git a/discord/images/rp-short-strings.webp b/discord/images/rp-short-strings.webp new file mode 100644 index 0000000000..6688ca576e Binary files /dev/null and b/discord/images/rp-short-strings.webp differ diff --git a/discord/images/sku-benefits.webp b/discord/images/sku-benefits.webp new file mode 100644 index 0000000000..311de6ca67 Binary files /dev/null and b/discord/images/sku-benefits.webp differ diff --git a/discord/images/sku-custom.webp b/discord/images/sku-custom.webp new file mode 100644 index 0000000000..ce09b58cea Binary files /dev/null and b/discord/images/sku-custom.webp differ diff --git a/discord/images/sku-unicode.webp b/discord/images/sku-unicode.webp new file mode 100644 index 0000000000..8bfca8815a Binary files /dev/null and b/discord/images/sku-unicode.webp differ diff --git a/discord/images/sku_embed.webp b/discord/images/sku_embed.webp new file mode 100644 index 0000000000..33f4d7ac06 Binary files /dev/null and b/discord/images/sku_embed.webp differ diff --git a/images/slash-command-options.png b/discord/images/slash-command-options.png similarity index 100% rename from images/slash-command-options.png rename to discord/images/slash-command-options.png diff --git a/discord/images/snowflake-to-datetime.svg b/discord/images/snowflake-to-datetime.svg new file mode 100644 index 0000000000..73f3da0a60 --- /dev/null +++ b/discord/images/snowflake-to-datetime.svg @@ -0,0 +1 @@ +1759288472991170634194470579614620151057962016-04-30 11:18:25.796 UTC0000001001110001000001100101101011000001000000100000to binaryto decimalParse unix timestamp (ms)+ 1420070400000Discord Epoch (unix timestamp in ms)Number of milliseconds since the Discord epoch (first seconds of 2015)InternalworkerIDInternalprocessID000000000111Incremented for every generated ID on that process642212017 diff --git a/discord/images/snowflake.png b/discord/images/snowflake.png new file mode 100644 index 0000000000..c8f4fd1485 Binary files /dev/null and b/discord/images/snowflake.png differ diff --git a/discord/images/social-sdk/design-guidelines/Brand-02.png b/discord/images/social-sdk/design-guidelines/Brand-02.png new file mode 100644 index 0000000000..02269ef229 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Brand-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/Brand-03.png b/discord/images/social-sdk/design-guidelines/Brand-03.png new file mode 100644 index 0000000000..42d0962df9 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Brand-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/Brand-04.png b/discord/images/social-sdk/design-guidelines/Brand-04.png new file mode 100644 index 0000000000..2fd7ab4caf Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Brand-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/ChatHistory-02.webp b/discord/images/social-sdk/design-guidelines/ChatHistory-02.webp new file mode 100644 index 0000000000..379512ed45 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/ChatHistory-02.webp differ diff --git a/discord/images/social-sdk/design-guidelines/ChatHistory-03.webp b/discord/images/social-sdk/design-guidelines/ChatHistory-03.webp new file mode 100644 index 0000000000..74f8d90d8e Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/ChatHistory-03.webp differ diff --git a/discord/images/social-sdk/design-guidelines/ConnectionPoints-02.png b/discord/images/social-sdk/design-guidelines/ConnectionPoints-02.png new file mode 100644 index 0000000000..f33ce3fac1 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/ConnectionPoints-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/ConnectionPoints-03.png b/discord/images/social-sdk/design-guidelines/ConnectionPoints-03.png new file mode 100644 index 0000000000..85559c65fa Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/ConnectionPoints-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/ConnectionPoints-04.png b/discord/images/social-sdk/design-guidelines/ConnectionPoints-04.png new file mode 100644 index 0000000000..89ce604340 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/ConnectionPoints-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/ConnectionPoints-05.png b/discord/images/social-sdk/design-guidelines/ConnectionPoints-05.png new file mode 100644 index 0000000000..8c48aea151 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/ConnectionPoints-05.png differ diff --git a/discord/images/social-sdk/design-guidelines/Consoles-01.jpg b/discord/images/social-sdk/design-guidelines/Consoles-01.jpg new file mode 100644 index 0000000000..fdea7b4a67 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Consoles-01.jpg differ diff --git a/discord/images/social-sdk/design-guidelines/Consoles-02.jpg b/discord/images/social-sdk/design-guidelines/Consoles-02.jpg new file mode 100644 index 0000000000..166b861220 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Consoles-02.jpg differ diff --git a/discord/images/social-sdk/design-guidelines/Consoles-03.jpg b/discord/images/social-sdk/design-guidelines/Consoles-03.jpg new file mode 100644 index 0000000000..99ade45742 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Consoles-03.jpg differ diff --git a/discord/images/social-sdk/design-guidelines/Consoles-04.jpg b/discord/images/social-sdk/design-guidelines/Consoles-04.jpg new file mode 100644 index 0000000000..0a4f506d1d Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Consoles-04.jpg differ diff --git a/discord/images/social-sdk/design-guidelines/Consoles-05.jpg b/discord/images/social-sdk/design-guidelines/Consoles-05.jpg new file mode 100644 index 0000000000..c654bd977b Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Consoles-05.jpg differ diff --git a/discord/images/social-sdk/design-guidelines/Consoles-06.jpg b/discord/images/social-sdk/design-guidelines/Consoles-06.jpg new file mode 100644 index 0000000000..c889e587a5 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Consoles-06.jpg differ diff --git a/discord/images/social-sdk/design-guidelines/DMs-02.png b/discord/images/social-sdk/design-guidelines/DMs-02.png new file mode 100644 index 0000000000..1f098307a4 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/DMs-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/DMs-03.png b/discord/images/social-sdk/design-guidelines/DMs-03.png new file mode 100644 index 0000000000..ca7f6cb186 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/DMs-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/DMs-04.png b/discord/images/social-sdk/design-guidelines/DMs-04.png new file mode 100644 index 0000000000..8e68feba8b Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/DMs-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-01.png b/discord/images/social-sdk/design-guidelines/GameFriends-01.png new file mode 100644 index 0000000000..e89a537769 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-01.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-02.png b/discord/images/social-sdk/design-guidelines/GameFriends-02.png new file mode 100644 index 0000000000..d3907aa73d Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-03.png b/discord/images/social-sdk/design-guidelines/GameFriends-03.png new file mode 100644 index 0000000000..af7e74e2e4 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-04.png b/discord/images/social-sdk/design-guidelines/GameFriends-04.png new file mode 100644 index 0000000000..85ab375edf Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-05.png b/discord/images/social-sdk/design-guidelines/GameFriends-05.png new file mode 100644 index 0000000000..e5ee72d173 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-05.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-06.png b/discord/images/social-sdk/design-guidelines/GameFriends-06.png new file mode 100644 index 0000000000..00e4e9440a Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-06.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-07.png b/discord/images/social-sdk/design-guidelines/GameFriends-07.png new file mode 100644 index 0000000000..58451b3238 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-07.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-08.png b/discord/images/social-sdk/design-guidelines/GameFriends-08.png new file mode 100644 index 0000000000..39de2c5e20 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-08.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-09.png b/discord/images/social-sdk/design-guidelines/GameFriends-09.png new file mode 100644 index 0000000000..a940fd7bcd Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-09.png differ diff --git a/discord/images/social-sdk/design-guidelines/GameFriends-10.png b/discord/images/social-sdk/design-guidelines/GameFriends-10.png new file mode 100644 index 0000000000..e60a475212 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/GameFriends-10.png differ diff --git a/discord/images/social-sdk/design-guidelines/LinkedChannels-04.png b/discord/images/social-sdk/design-guidelines/LinkedChannels-04.png new file mode 100644 index 0000000000..d89701b672 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/LinkedChannels-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/LinkedChannels-05.png b/discord/images/social-sdk/design-guidelines/LinkedChannels-05.png new file mode 100644 index 0000000000..d95e9a4e58 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/LinkedChannels-05.png differ diff --git a/discord/images/social-sdk/design-guidelines/LinkedChannels-06.png b/discord/images/social-sdk/design-guidelines/LinkedChannels-06.png new file mode 100644 index 0000000000..0c8286ed5d Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/LinkedChannels-06.png differ diff --git a/discord/images/social-sdk/design-guidelines/LinkedChannels-07.png b/discord/images/social-sdk/design-guidelines/LinkedChannels-07.png new file mode 100644 index 0000000000..8ad42cf506 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/LinkedChannels-07.png differ diff --git a/discord/images/social-sdk/design-guidelines/Principle-02.png b/discord/images/social-sdk/design-guidelines/Principle-02.png new file mode 100644 index 0000000000..71ef86af82 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Principle-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/Principle-03.png b/discord/images/social-sdk/design-guidelines/Principle-03.png new file mode 100644 index 0000000000..97a9a81619 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Principle-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/Principle-04.png b/discord/images/social-sdk/design-guidelines/Principle-04.png new file mode 100644 index 0000000000..8e45d44831 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Principle-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/Principle-05.png b/discord/images/social-sdk/design-guidelines/Principle-05.png new file mode 100644 index 0000000000..006333f543 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Principle-05.png differ diff --git a/discord/images/social-sdk/design-guidelines/Principle-06.png b/discord/images/social-sdk/design-guidelines/Principle-06.png new file mode 100644 index 0000000000..808d10ac0c Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Principle-06.png differ diff --git a/discord/images/social-sdk/design-guidelines/Principle-07.png b/discord/images/social-sdk/design-guidelines/Principle-07.png new file mode 100644 index 0000000000..66c7b30071 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Principle-07.png differ diff --git a/discord/images/social-sdk/design-guidelines/Prov-02.png b/discord/images/social-sdk/design-guidelines/Prov-02.png new file mode 100644 index 0000000000..8ad43b5062 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Prov-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/Prov-03.png b/discord/images/social-sdk/design-guidelines/Prov-03.png new file mode 100644 index 0000000000..7b7a2e3d0b Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Prov-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/Prov-04.png b/discord/images/social-sdk/design-guidelines/Prov-04.png new file mode 100644 index 0000000000..a8b14e2926 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Prov-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/Prov-05.png b/discord/images/social-sdk/design-guidelines/Prov-05.png new file mode 100644 index 0000000000..f3dfc0d39e Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/Prov-05.png differ diff --git a/discord/images/social-sdk/design-guidelines/SigningIn-02.png b/discord/images/social-sdk/design-guidelines/SigningIn-02.png new file mode 100644 index 0000000000..b0881d1805 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SigningIn-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/SigningIn-03.png b/discord/images/social-sdk/design-guidelines/SigningIn-03.png new file mode 100644 index 0000000000..0ca40bc1c1 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SigningIn-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/SigningIn-04.png b/discord/images/social-sdk/design-guidelines/SigningIn-04.png new file mode 100644 index 0000000000..3808f3d78b Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SigningIn-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/SigningIn-05.png b/discord/images/social-sdk/design-guidelines/SigningIn-05.png new file mode 100644 index 0000000000..7f3d91d858 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SigningIn-05.png differ diff --git a/discord/images/social-sdk/design-guidelines/SigningIn-06.png b/discord/images/social-sdk/design-guidelines/SigningIn-06.png new file mode 100644 index 0000000000..3cc5d3a4e6 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SigningIn-06.png differ diff --git a/discord/images/social-sdk/design-guidelines/SigningIn-07.png b/discord/images/social-sdk/design-guidelines/SigningIn-07.png new file mode 100644 index 0000000000..5cb7a1af90 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SigningIn-07.png differ diff --git a/discord/images/social-sdk/design-guidelines/SigningIn-08.png b/discord/images/social-sdk/design-guidelines/SigningIn-08.png new file mode 100644 index 0000000000..10e0d65fd3 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SigningIn-08.png differ diff --git a/discord/images/social-sdk/design-guidelines/SocialSettings-02.webp b/discord/images/social-sdk/design-guidelines/SocialSettings-02.webp new file mode 100644 index 0000000000..7049cf78a9 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SocialSettings-02.webp differ diff --git a/discord/images/social-sdk/design-guidelines/SocialSettings-03.webp b/discord/images/social-sdk/design-guidelines/SocialSettings-03.webp new file mode 100644 index 0000000000..85e03caad7 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SocialSettings-03.webp differ diff --git a/discord/images/social-sdk/design-guidelines/SocialSettings-04.webp b/discord/images/social-sdk/design-guidelines/SocialSettings-04.webp new file mode 100644 index 0000000000..768b583604 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/SocialSettings-04.webp differ diff --git a/discord/images/social-sdk/design-guidelines/StatusPresence-02.png b/discord/images/social-sdk/design-guidelines/StatusPresence-02.png new file mode 100644 index 0000000000..c8be99b2d4 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/StatusPresence-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/StatusPresence-03.png b/discord/images/social-sdk/design-guidelines/StatusPresence-03.png new file mode 100644 index 0000000000..eb3f55239c Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/StatusPresence-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/StatusPresence-04.png b/discord/images/social-sdk/design-guidelines/StatusPresence-04.png new file mode 100644 index 0000000000..6a9933426b Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/StatusPresence-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/StatusPresence-05.png b/discord/images/social-sdk/design-guidelines/StatusPresence-05.png new file mode 100644 index 0000000000..89669c4ffe Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/StatusPresence-05.png differ diff --git a/discord/images/social-sdk/design-guidelines/StatusPresence-06.png b/discord/images/social-sdk/design-guidelines/StatusPresence-06.png new file mode 100644 index 0000000000..dae7578002 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/StatusPresence-06.png differ diff --git a/discord/images/social-sdk/design-guidelines/UFL-02.png b/discord/images/social-sdk/design-guidelines/UFL-02.png new file mode 100644 index 0000000000..62c8ed066c Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/UFL-02.png differ diff --git a/discord/images/social-sdk/design-guidelines/UFL-03.png b/discord/images/social-sdk/design-guidelines/UFL-03.png new file mode 100644 index 0000000000..5adca8c896 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/UFL-03.png differ diff --git a/discord/images/social-sdk/design-guidelines/UFL-04.png b/discord/images/social-sdk/design-guidelines/UFL-04.png new file mode 100644 index 0000000000..7a6234ff15 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/UFL-04.png differ diff --git a/discord/images/social-sdk/design-guidelines/UFL-05.png b/discord/images/social-sdk/design-guidelines/UFL-05.png new file mode 100644 index 0000000000..b617a2e258 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/UFL-05.png differ diff --git a/discord/images/social-sdk/design-guidelines/UFL-06.png b/discord/images/social-sdk/design-guidelines/UFL-06.png new file mode 100644 index 0000000000..6d8d5f1c4f Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/UFL-06.png differ diff --git a/discord/images/social-sdk/design-guidelines/UFL-07.png b/discord/images/social-sdk/design-guidelines/UFL-07.png new file mode 100644 index 0000000000..28916851d6 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/UFL-07.png differ diff --git a/discord/images/social-sdk/design-guidelines/UFL-08.png b/discord/images/social-sdk/design-guidelines/UFL-08.png new file mode 100644 index 0000000000..2712e1d2fe Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/UFL-08.png differ diff --git a/discord/images/social-sdk/design-guidelines/animated/Connecting-via-Browser.gif b/discord/images/social-sdk/design-guidelines/animated/Connecting-via-Browser.gif new file mode 100644 index 0000000000..2fd1fb4c63 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/animated/Connecting-via-Browser.gif differ diff --git a/discord/images/social-sdk/design-guidelines/animated/Connecting-via-Client.gif b/discord/images/social-sdk/design-guidelines/animated/Connecting-via-Client.gif new file mode 100644 index 0000000000..8e23dcccf8 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/animated/Connecting-via-Client.gif differ diff --git a/discord/images/social-sdk/design-guidelines/animated/DMs-02.gif b/discord/images/social-sdk/design-guidelines/animated/DMs-02.gif new file mode 100644 index 0000000000..faf0cf8fdf Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/animated/DMs-02.gif differ diff --git a/discord/images/social-sdk/design-guidelines/animated/LinkedChannel-Setup.gif b/discord/images/social-sdk/design-guidelines/animated/LinkedChannel-Setup.gif new file mode 100644 index 0000000000..331fb9c474 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/animated/LinkedChannel-Setup.gif differ diff --git a/discord/images/social-sdk/design-guidelines/animated/LinkedChannel-Unlink.gif b/discord/images/social-sdk/design-guidelines/animated/LinkedChannel-Unlink.gif new file mode 100644 index 0000000000..09edd3c30f Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/animated/LinkedChannel-Unlink.gif differ diff --git a/discord/images/social-sdk/design-guidelines/animated/LinkedChannels-02.gif b/discord/images/social-sdk/design-guidelines/animated/LinkedChannels-02.gif new file mode 100644 index 0000000000..22a7750c27 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/animated/LinkedChannels-02.gif differ diff --git a/discord/images/social-sdk/design-guidelines/animated/LinkedChannels-08.gif b/discord/images/social-sdk/design-guidelines/animated/LinkedChannels-08.gif new file mode 100644 index 0000000000..8fb8a1875f Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/animated/LinkedChannels-08.gif differ diff --git a/discord/images/social-sdk/design-guidelines/animated/UFL-06.gif b/discord/images/social-sdk/design-guidelines/animated/UFL-06.gif new file mode 100644 index 0000000000..5fbf5c6db1 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/animated/UFL-06.gif differ diff --git a/discord/images/social-sdk/design-guidelines/starter_pack.webp b/discord/images/social-sdk/design-guidelines/starter_pack.webp new file mode 100644 index 0000000000..489fbce901 Binary files /dev/null and b/discord/images/social-sdk/design-guidelines/starter_pack.webp differ diff --git a/discord/images/social-sdk/development-guides/authorize_device.png b/discord/images/social-sdk/development-guides/authorize_device.png new file mode 100644 index 0000000000..7db81bacd9 Binary files /dev/null and b/discord/images/social-sdk/development-guides/authorize_device.png differ diff --git a/discord/images/social-sdk/development-guides/discord-game-dm-settings.png b/discord/images/social-sdk/development-guides/discord-game-dm-settings.png new file mode 100644 index 0000000000..f2b6ff9516 Binary files /dev/null and b/discord/images/social-sdk/development-guides/discord-game-dm-settings.png differ diff --git a/discord/images/social-sdk/development-guides/game-invite.webp b/discord/images/social-sdk/development-guides/game-invite.webp new file mode 100644 index 0000000000..a8b99efe29 Binary files /dev/null and b/discord/images/social-sdk/development-guides/game-invite.webp differ diff --git a/discord/images/social-sdk/getting-started/partials/error.png b/discord/images/social-sdk/getting-started/partials/error.png new file mode 100644 index 0000000000..30a54a671b Binary files /dev/null and b/discord/images/social-sdk/getting-started/partials/error.png differ diff --git a/discord/images/social-sdk/getting-started/partials/open-anyway.png b/discord/images/social-sdk/getting-started/partials/open-anyway.png new file mode 100644 index 0000000000..a76fb99d60 Binary files /dev/null and b/discord/images/social-sdk/getting-started/partials/open-anyway.png differ diff --git a/discord/images/social-sdk/getting-started/partials/security-settings.png b/discord/images/social-sdk/getting-started/partials/security-settings.png new file mode 100644 index 0000000000..b053189f04 Binary files /dev/null and b/discord/images/social-sdk/getting-started/partials/security-settings.png differ diff --git a/discord/images/social-sdk/getting-started/partials/settings-security.webp b/discord/images/social-sdk/getting-started/partials/settings-security.webp new file mode 100644 index 0000000000..9dd9d74f58 Binary files /dev/null and b/discord/images/social-sdk/getting-started/partials/settings-security.webp differ diff --git a/discord/images/social-sdk/getting-started/unity-sample-preview.webp b/discord/images/social-sdk/getting-started/unity-sample-preview.webp new file mode 100644 index 0000000000..d2a2c65b97 Binary files /dev/null and b/discord/images/social-sdk/getting-started/unity-sample-preview.webp differ diff --git a/discord/images/social-sdk/overview/social_sdk_header.png b/discord/images/social-sdk/overview/social_sdk_header.png new file mode 100644 index 0000000000..e163a5e477 Binary files /dev/null and b/discord/images/social-sdk/overview/social_sdk_header.png differ diff --git a/discord/images/store_embed.webp b/discord/images/store_embed.webp new file mode 100644 index 0000000000..5d011c8bd6 Binary files /dev/null and b/discord/images/store_embed.webp differ diff --git a/discord/images/team-page.png b/discord/images/team-page.png new file mode 100644 index 0000000000..461f136b1d Binary files /dev/null and b/discord/images/team-page.png differ diff --git a/discord/images/team-page.webp b/discord/images/team-page.webp new file mode 100644 index 0000000000..b2c1dc2573 Binary files /dev/null and b/discord/images/team-page.webp differ diff --git a/discord/images/transfer-app-to-team.png b/discord/images/transfer-app-to-team.png new file mode 100644 index 0000000000..aee30c80a3 Binary files /dev/null and b/discord/images/transfer-app-to-team.png differ diff --git a/discord/images/transfer-app-to-team.webp b/discord/images/transfer-app-to-team.webp new file mode 100644 index 0000000000..4f59e99ac2 Binary files /dev/null and b/discord/images/transfer-app-to-team.webp differ diff --git a/images/cloudflare-general-overview.png b/discord/images/tutorials/cloudflare-general-overview.png similarity index 100% rename from images/cloudflare-general-overview.png rename to discord/images/tutorials/cloudflare-general-overview.png diff --git a/images/cloudflare-interactions-endpoint.png b/discord/images/tutorials/cloudflare-interactions-endpoint.png similarity index 100% rename from images/cloudflare-interactions-endpoint.png rename to discord/images/tutorials/cloudflare-interactions-endpoint.png diff --git a/images/cloudflare-ngrok.png b/discord/images/tutorials/cloudflare-ngrok.png similarity index 100% rename from images/cloudflare-ngrok.png rename to discord/images/tutorials/cloudflare-ngrok.png diff --git a/images/cloudflare-tutorial-demo.gif b/discord/images/tutorials/cloudflare-tutorial-demo.gif similarity index 100% rename from images/cloudflare-tutorial-demo.gif rename to discord/images/tutorials/cloudflare-tutorial-demo.gif diff --git a/images/cloudflare-url-generator.png b/discord/images/tutorials/cloudflare-url-generator.png similarity index 100% rename from images/cloudflare-url-generator.png rename to discord/images/tutorials/cloudflare-url-generator.png diff --git a/discord/images/tutorials/command-types.png b/discord/images/tutorials/command-types.png new file mode 100644 index 0000000000..aafb9d86f7 Binary files /dev/null and b/discord/images/tutorials/command-types.png differ diff --git a/discord/images/tutorials/examples-ephemeral-message.png b/discord/images/tutorials/examples-ephemeral-message.png new file mode 100644 index 0000000000..bc318a0817 Binary files /dev/null and b/discord/images/tutorials/examples-ephemeral-message.png differ diff --git a/discord/images/tutorials/examples-update-message.png b/discord/images/tutorials/examples-update-message.png new file mode 100644 index 0000000000..9935ecc13e Binary files /dev/null and b/discord/images/tutorials/examples-update-message.png differ diff --git a/discord/images/tutorials/interactions-url.png b/discord/images/tutorials/interactions-url.png new file mode 100644 index 0000000000..f4f9862fb6 Binary files /dev/null and b/discord/images/tutorials/interactions-url.png differ diff --git a/discord/images/tutorials/linked-roles-connect-account.png b/discord/images/tutorials/linked-roles-connect-account.png new file mode 100644 index 0000000000..6f30a20644 Binary files /dev/null and b/discord/images/tutorials/linked-roles-connect-account.png differ diff --git a/discord/images/tutorials/linked-roles-connected.png b/discord/images/tutorials/linked-roles-connected.png new file mode 100644 index 0000000000..e0abee8d48 Binary files /dev/null and b/discord/images/tutorials/linked-roles-connected.png differ diff --git a/discord/images/tutorials/linked-roles-consent-dialog.png b/discord/images/tutorials/linked-roles-consent-dialog.png new file mode 100644 index 0000000000..329163ec3e Binary files /dev/null and b/discord/images/tutorials/linked-roles-consent-dialog.png differ diff --git a/discord/images/tutorials/linked-roles-glitch-shared-url.png b/discord/images/tutorials/linked-roles-glitch-shared-url.png new file mode 100644 index 0000000000..e1ea49ba65 Binary files /dev/null and b/discord/images/tutorials/linked-roles-glitch-shared-url.png differ diff --git a/discord/images/tutorials/linked-roles-glitch.png b/discord/images/tutorials/linked-roles-glitch.png new file mode 100644 index 0000000000..88109b74d9 Binary files /dev/null and b/discord/images/tutorials/linked-roles-glitch.png differ diff --git a/discord/images/tutorials/linked-roles-oauth-config.png b/discord/images/tutorials/linked-roles-oauth-config.png new file mode 100644 index 0000000000..fb66f047f1 Binary files /dev/null and b/discord/images/tutorials/linked-roles-oauth-config.png differ diff --git a/discord/images/tutorials/linked-roles-register.png b/discord/images/tutorials/linked-roles-register.png new file mode 100644 index 0000000000..dfe6035886 Binary files /dev/null and b/discord/images/tutorials/linked-roles-register.png differ diff --git a/discord/images/tutorials/linked-roles-verification-setup.png b/discord/images/tutorials/linked-roles-verification-setup.png new file mode 100644 index 0000000000..b0b29c4e5b Binary files /dev/null and b/discord/images/tutorials/linked-roles-verification-setup.png differ diff --git a/discord/images/tutorials/linked-roles-verify-endpoint.png b/discord/images/tutorials/linked-roles-verify-endpoint.png new file mode 100644 index 0000000000..32f2f1775f Binary files /dev/null and b/discord/images/tutorials/linked-roles-verify-endpoint.png differ diff --git a/discord/images/tutorials/slash-command-options.png b/discord/images/tutorials/slash-command-options.png new file mode 100644 index 0000000000..bf2f7af322 Binary files /dev/null and b/discord/images/tutorials/slash-command-options.png differ diff --git a/discord/images/tutorials/user-install-game-message.png b/discord/images/tutorials/user-install-game-message.png new file mode 100644 index 0000000000..89cbcc8378 Binary files /dev/null and b/discord/images/tutorials/user-install-game-message.png differ diff --git a/discord/images/tutorials/user-install-settings.png b/discord/images/tutorials/user-install-settings.png new file mode 100644 index 0000000000..516901d859 Binary files /dev/null and b/discord/images/tutorials/user-install-settings.png differ diff --git a/discord/images/url-generator.webp b/discord/images/url-generator.webp new file mode 100644 index 0000000000..38087b9430 Binary files /dev/null and b/discord/images/url-generator.webp differ diff --git a/images/user-command.png b/discord/images/user-command.png similarity index 100% rename from images/user-command.png rename to discord/images/user-command.png diff --git a/discord/images/user-command.webp b/discord/images/user-command.webp new file mode 100644 index 0000000000..e71019b693 Binary files /dev/null and b/discord/images/user-command.webp differ diff --git a/discord/images/user-install-game-message.webp b/discord/images/user-install-game-message.webp new file mode 100644 index 0000000000..45c93adf9d Binary files /dev/null and b/discord/images/user-install-game-message.webp differ diff --git a/discord/images/user-install-settings.webp b/discord/images/user-install-settings.webp new file mode 100644 index 0000000000..bcc98af469 Binary files /dev/null and b/discord/images/user-install-settings.webp differ diff --git a/discord/logo/dark.png b/discord/logo/dark.png new file mode 100644 index 0000000000..11c6612c98 Binary files /dev/null and b/discord/logo/dark.png differ diff --git a/discord/logo/dark.svg b/discord/logo/dark.svg new file mode 100644 index 0000000000..d99a9245ff --- /dev/null +++ b/discord/logo/dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/discord/logo/dot_dev.svg b/discord/logo/dot_dev.svg new file mode 100644 index 0000000000..867878bdfe --- /dev/null +++ b/discord/logo/dot_dev.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/discord/logo/light.png b/discord/logo/light.png new file mode 100644 index 0000000000..f88870f616 Binary files /dev/null and b/discord/logo/light.png differ diff --git a/discord/logo/light.svg b/discord/logo/light.svg new file mode 100644 index 0000000000..e21d461422 --- /dev/null +++ b/discord/logo/light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/discord/scripts/ga.js b/discord/scripts/ga.js new file mode 100644 index 0000000000..b607914919 --- /dev/null +++ b/discord/scripts/ga.js @@ -0,0 +1,4 @@ +window.dataLayer = window.dataLayer || []; +function gtag() { dataLayer.push(arguments); } +gtag('js', new Date()); +gtag('config', 'G-5CWMJQ1S0X'); \ No newline at end of file diff --git a/discord/snippets/card.mdx b/discord/snippets/card.mdx new file mode 100644 index 0000000000..5aab8f4649 --- /dev/null +++ b/discord/snippets/card.mdx @@ -0,0 +1,23 @@ +export const IntroCard = ({ img, href, title, description }) => { + return ( + +
+
+ {title} +
+
+

+ {title} +

+

+ {description} +

+
+
+
+ ); +}; \ No newline at end of file diff --git a/discord/snippets/discord-social-sdk/callouts/console-access.mdx b/discord/snippets/discord-social-sdk/callouts/console-access.mdx new file mode 100644 index 0000000000..8f746f80df --- /dev/null +++ b/discord/snippets/discord-social-sdk/callouts/console-access.mdx @@ -0,0 +1,3 @@ + +To use the Discord Social SDK in your console games, you will need to request middleware approval and be an approved developer for the target console. Check out [this article](https://support-dev.discord.com/hc/en-us/articles/30209074764183) to learn more. + diff --git a/discord/snippets/discord-social-sdk/callouts/limited-access.mdx b/discord/snippets/discord-social-sdk/callouts/limited-access.mdx new file mode 100644 index 0000000000..07bb468eae --- /dev/null +++ b/discord/snippets/discord-social-sdk/callouts/limited-access.mdx @@ -0,0 +1,6 @@ + +This feature is currently available with +[limited access](/developers/docs/discord-social-sdk/core-concepts/communication-features#limited-access). To apply for full access +to closed beta features, or to reach out to Discord directly to discuss your game, please fill out +[this form](https://discord.com/developers/social-sdk-closed-beta-access-request-form). + \ No newline at end of file diff --git a/discord/snippets/discord-social-sdk/callouts/oauth-comms-scopes.mdx b/discord/snippets/discord-social-sdk/callouts/oauth-comms-scopes.mdx new file mode 100644 index 0000000000..1e20bb44ca --- /dev/null +++ b/discord/snippets/discord-social-sdk/callouts/oauth-comms-scopes.mdx @@ -0,0 +1,7 @@ + +To utilize this communication feature, you must enable [`Client::GetDefaultCommunicationScopes`] in your OAuth Scope configuration. +See the [OAuth Scopes Core Concepts Guide](/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes) for more details. + + +{/* Autogenerated Reference Links */} +[`Client::GetDefaultCommunicationScopes`]: https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1 \ No newline at end of file diff --git a/discord/snippets/discord-social-sdk/callouts/public-client.mdx b/discord/snippets/discord-social-sdk/callouts/public-client.mdx new file mode 100644 index 0000000000..96fe5d6caf --- /dev/null +++ b/discord/snippets/discord-social-sdk/callouts/public-client.mdx @@ -0,0 +1,3 @@ + +This method requires enabling **Public Client** for your app. Most games will not want to ship with this enabled. [Learn more](/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types) + \ No newline at end of file diff --git a/discord/snippets/discord-social-sdk/callouts/rate-limit.mdx b/discord/snippets/discord-social-sdk/callouts/rate-limit.mdx new file mode 100644 index 0000000000..d62d7bb73a --- /dev/null +++ b/discord/snippets/discord-social-sdk/callouts/rate-limit.mdx @@ -0,0 +1,5 @@ + +This feature is currently available with rate limits. +To remove the rate limits for your game, please follow +[Communication Features: Applying for Rate Limit Removal](/developers/docs/discord-social-sdk/core-concepts/communication-features#applying-for-rate-limit-removal). + \ No newline at end of file diff --git a/discord/snippets/discord-social-sdk/callouts/sony.mdx b/discord/snippets/discord-social-sdk/callouts/sony.mdx new file mode 100644 index 0000000000..7ed72c4255 --- /dev/null +++ b/discord/snippets/discord-social-sdk/callouts/sony.mdx @@ -0,0 +1,8 @@ + +Exception: Prohibited Off-Platform Interactions by Sony
+For integrations experienced on Sony Platforms (e.g. PlayStation 5)… +

1. **Do not allow access to Account Linking** entry points. +
2. **Do not allow access to Linked Channels**. +
3. **Limit messaging and Invites to other players currently playing** the game. +
4. **Do not display off-platform presence and logos** (including Discord). +
\ No newline at end of file diff --git a/discord/snippets/discord-social-sdk/callouts/support.mdx b/discord/snippets/discord-social-sdk/callouts/support.mdx new file mode 100644 index 0000000000..c8f9c6c069 --- /dev/null +++ b/discord/snippets/discord-social-sdk/callouts/support.mdx @@ -0,0 +1,3 @@ +Need help? Join the [Discord Developers Server](https://discord.gg/discord-developers) and share questions in the `#social-sdk-dev-help` channel for support from the community. + +If you encounter a bug while working with the Social SDK, please report it here: https://dis.gd/social-sdk-bug-report diff --git a/discord/snippets/discord-social-sdk/partials/dylib-mac-error.mdx b/discord/snippets/discord-social-sdk/partials/dylib-mac-error.mdx new file mode 100644 index 0000000000..9ae08b4439 --- /dev/null +++ b/discord/snippets/discord-social-sdk/partials/dylib-mac-error.mdx @@ -0,0 +1,14 @@ + +On Mac you may get the error "libdiscord_partner_sdk.dylib" Not Opened because Apple couldn't verify it. If this happens press **Done** on the popup. + +![Error](/images/social-sdk/getting-started/partials/error.png) + +You'll need to open your **System Settings > Privacy & Security** and scroll down to the **Security** section. It will tell you "libdiscord_partner_sdk.dylib" was blocked to protect your Mac. Press **Open Anyway** and try running again. + +![Settings](/images/social-sdk/getting-started/partials/security-settings.png) + +Now when you get the pop up you'll have the option to select **Open Anyway** and it will be able to use it successfully. + +![Open](/images/social-sdk/getting-started/partials/open-anyway.png) + + \ No newline at end of file diff --git a/discord/snippets/discord-social-sdk/partials/getting-started.mdx b/discord/snippets/discord-social-sdk/partials/getting-started.mdx new file mode 100644 index 0000000000..e66c14149b --- /dev/null +++ b/discord/snippets/discord-social-sdk/partials/getting-started.mdx @@ -0,0 +1,35 @@ +## Step 1: Create a Discord Developer Team + +Before you start, you'll need to create a developer team on the Discord Developer Portal. This team will be used to manage your Discord applications and SDK integrations. + +If you already have a team configured, you can skip this step. + +1. Create a developer team on the [Discord Developer Portal](https://discord.com/developers/teams). + +Later, you can invite your team members to your new team to collaborate on your integration. + +--- + +## Step 2: Create a Discord Application + +1. Create a new application on the [Discord Developer Portal](https://discord.com/developers/applications) and assign it to your team. +3. Add a redirect URL in the `OAuth2` tab: + - For desktop applications: `http://127.0.0.1/callback` (this can be changed later). + - See [`discordpp::Client::Authorize`](https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468) for more details on setting up more advanced redirect URIs. +4. Enable the `Public Client` toggle in the `OAuth2` tab. + + +This guide requires enabling **Public Client** to allow you to get started with the SDK quickly. Most games will not want to ship as a public client. This setting should be reviewed by your team prior to releasing your game. [Learn more about public clients](/developers/docs/discord-social-sdk/core-concepts/oauth2-scopes#oauth2-client-types). + + +--- + +## Step 3: Enable Discord Social SDK for Your App + +Once you've created your Discord application, you'll need to enable the Discord Social SDK for it. + +1. From the [Discord Developer Portal](https://discord.com/developers/applications), select your newly created application from Step 2. +2. In the left sidebar for your app, locate and click the `Getting Started` link under `Discord Social SDK`. +3. Fill out the form to share details about your game. +4. Click `Submit` and the Social SDK will be enabled for your application. +5. Once enabled, you'll find binaries for the Social SDK under `Downloads`. \ No newline at end of file diff --git a/discord/snippets/icons/ActivitiesIcon.jsx b/discord/snippets/icons/ActivitiesIcon.jsx new file mode 100644 index 0000000000..efccb2c34e --- /dev/null +++ b/discord/snippets/icons/ActivitiesIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ActivitiesIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/AppsIcon.jsx b/discord/snippets/icons/AppsIcon.jsx new file mode 100644 index 0000000000..cd264fdd6c --- /dev/null +++ b/discord/snippets/icons/AppsIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const AppsIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/BookCheckIcon.jsx b/discord/snippets/icons/BookCheckIcon.jsx new file mode 100644 index 0000000000..7a9776b4ff --- /dev/null +++ b/discord/snippets/icons/BookCheckIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const BookCheckIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/BrowserIcon.jsx b/discord/snippets/icons/BrowserIcon.jsx new file mode 100644 index 0000000000..210d880fc6 --- /dev/null +++ b/discord/snippets/icons/BrowserIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const BrowserIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/BugIcon.jsx b/discord/snippets/icons/BugIcon.jsx new file mode 100644 index 0000000000..c632b969c9 --- /dev/null +++ b/discord/snippets/icons/BugIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const BugIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/ChatDotsIcon.jsx b/discord/snippets/icons/ChatDotsIcon.jsx new file mode 100644 index 0000000000..86b0cdb4ab --- /dev/null +++ b/discord/snippets/icons/ChatDotsIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ChatDotsIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/ChatIcon.jsx b/discord/snippets/icons/ChatIcon.jsx new file mode 100644 index 0000000000..43d28ba8bf --- /dev/null +++ b/discord/snippets/icons/ChatIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ChatIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/CircleQuestionIcon.jsx b/discord/snippets/icons/CircleQuestionIcon.jsx new file mode 100644 index 0000000000..e8cd263aa7 --- /dev/null +++ b/discord/snippets/icons/CircleQuestionIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const CircleQuestionIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/ClydeIcon.jsx b/discord/snippets/icons/ClydeIcon.jsx new file mode 100644 index 0000000000..77c6f8136c --- /dev/null +++ b/discord/snippets/icons/ClydeIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ClydeIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/CompassIcon.jsx b/discord/snippets/icons/CompassIcon.jsx new file mode 100644 index 0000000000..4d565074df --- /dev/null +++ b/discord/snippets/icons/CompassIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const CompassIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/ContactsIcon.jsx b/discord/snippets/icons/ContactsIcon.jsx new file mode 100644 index 0000000000..4139090a7b --- /dev/null +++ b/discord/snippets/icons/ContactsIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ContactsIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/CrownIcon.jsx b/discord/snippets/icons/CrownIcon.jsx new file mode 100644 index 0000000000..75f04522b2 --- /dev/null +++ b/discord/snippets/icons/CrownIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const CrownIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/DoorEnterIcon.jsx b/discord/snippets/icons/DoorEnterIcon.jsx new file mode 100644 index 0000000000..10022c1a4d --- /dev/null +++ b/discord/snippets/icons/DoorEnterIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const DoorEnterIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/DpadIcon.jsx b/discord/snippets/icons/DpadIcon.jsx new file mode 100644 index 0000000000..b84a005a9f --- /dev/null +++ b/discord/snippets/icons/DpadIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const DpadIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/ForumIcon.jsx b/discord/snippets/icons/ForumIcon.jsx new file mode 100644 index 0000000000..06e3fbe0b0 --- /dev/null +++ b/discord/snippets/icons/ForumIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ForumIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/GameControllerIcon.jsx b/discord/snippets/icons/GameControllerIcon.jsx new file mode 100644 index 0000000000..18b454b205 --- /dev/null +++ b/discord/snippets/icons/GameControllerIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const GameControllerIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/GlobeEarthIcon.jsx b/discord/snippets/icons/GlobeEarthIcon.jsx new file mode 100644 index 0000000000..1035ccddc0 --- /dev/null +++ b/discord/snippets/icons/GlobeEarthIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const GlobeEarthIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/GroupIcon.jsx b/discord/snippets/icons/GroupIcon.jsx new file mode 100644 index 0000000000..7d5a4db816 --- /dev/null +++ b/discord/snippets/icons/GroupIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const GroupIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/HammerIcon.jsx b/discord/snippets/icons/HammerIcon.jsx new file mode 100644 index 0000000000..2cd575fe34 --- /dev/null +++ b/discord/snippets/icons/HammerIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const HammerIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/HashmarkIcon.jsx b/discord/snippets/icons/HashmarkIcon.jsx new file mode 100644 index 0000000000..8269515ef0 --- /dev/null +++ b/discord/snippets/icons/HashmarkIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const HashmarkIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/InboxIcon.jsx b/discord/snippets/icons/InboxIcon.jsx new file mode 100644 index 0000000000..634846b0fb --- /dev/null +++ b/discord/snippets/icons/InboxIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const InboxIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/LettersIcon.jsx b/discord/snippets/icons/LettersIcon.jsx new file mode 100644 index 0000000000..746179e063 --- /dev/null +++ b/discord/snippets/icons/LettersIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const LettersIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/LinkIcon.jsx b/discord/snippets/icons/LinkIcon.jsx new file mode 100644 index 0000000000..8554452385 --- /dev/null +++ b/discord/snippets/icons/LinkIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const LinkIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/ListViewIcon.jsx b/discord/snippets/icons/ListViewIcon.jsx new file mode 100644 index 0000000000..0f4500c3f0 --- /dev/null +++ b/discord/snippets/icons/ListViewIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ListViewIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/MagicDoorIcon.jsx b/discord/snippets/icons/MagicDoorIcon.jsx new file mode 100644 index 0000000000..063964f002 --- /dev/null +++ b/discord/snippets/icons/MagicDoorIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const MagicDoorIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/MagicWandIcon.jsx b/discord/snippets/icons/MagicWandIcon.jsx new file mode 100644 index 0000000000..0d3f7a9be5 --- /dev/null +++ b/discord/snippets/icons/MagicWandIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const MagicWandIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/PaintPaletteIcon.jsx b/discord/snippets/icons/PaintPaletteIcon.jsx new file mode 100644 index 0000000000..1be1f298de --- /dev/null +++ b/discord/snippets/icons/PaintPaletteIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const PaintPaletteIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/PhoneCallIcon.jsx b/discord/snippets/icons/PhoneCallIcon.jsx new file mode 100644 index 0000000000..4ac52d4644 --- /dev/null +++ b/discord/snippets/icons/PhoneCallIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const PhoneCallIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/PlayIcon.jsx b/discord/snippets/icons/PlayIcon.jsx new file mode 100644 index 0000000000..c8f123d745 --- /dev/null +++ b/discord/snippets/icons/PlayIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const PlayIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/RobotIcon.jsx b/discord/snippets/icons/RobotIcon.jsx new file mode 100644 index 0000000000..4d6ea650af --- /dev/null +++ b/discord/snippets/icons/RobotIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const RobotIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/ShieldIcon.jsx b/discord/snippets/icons/ShieldIcon.jsx new file mode 100644 index 0000000000..f7bc297815 --- /dev/null +++ b/discord/snippets/icons/ShieldIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ShieldIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/ShopIcon.jsx b/discord/snippets/icons/ShopIcon.jsx new file mode 100644 index 0000000000..ab6459b5cc --- /dev/null +++ b/discord/snippets/icons/ShopIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const ShopIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/SlashBoxIcon.jsx b/discord/snippets/icons/SlashBoxIcon.jsx new file mode 100644 index 0000000000..663f31327f --- /dev/null +++ b/discord/snippets/icons/SlashBoxIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const SlashBoxIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/StarShootingIcon.jsx b/discord/snippets/icons/StarShootingIcon.jsx new file mode 100644 index 0000000000..10474acfdb --- /dev/null +++ b/discord/snippets/icons/StarShootingIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const StarShootingIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/TextControllerIcon.jsx b/discord/snippets/icons/TextControllerIcon.jsx new file mode 100644 index 0000000000..e53b702912 --- /dev/null +++ b/discord/snippets/icons/TextControllerIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const TextControllerIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/TrophyIcon.jsx b/discord/snippets/icons/TrophyIcon.jsx new file mode 100644 index 0000000000..1a1a68744c --- /dev/null +++ b/discord/snippets/icons/TrophyIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const TrophyIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/UserIcon.jsx b/discord/snippets/icons/UserIcon.jsx new file mode 100644 index 0000000000..45fb87df50 --- /dev/null +++ b/discord/snippets/icons/UserIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const UserIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/UserPlatformIcon.jsx b/discord/snippets/icons/UserPlatformIcon.jsx new file mode 100644 index 0000000000..97e42eb19b --- /dev/null +++ b/discord/snippets/icons/UserPlatformIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const UserPlatformIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/UserPlusIcon.jsx b/discord/snippets/icons/UserPlusIcon.jsx new file mode 100644 index 0000000000..b0ebfa0f14 --- /dev/null +++ b/discord/snippets/icons/UserPlusIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const UserPlusIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/UserStatusIcon.jsx b/discord/snippets/icons/UserStatusIcon.jsx new file mode 100644 index 0000000000..070dd6b887 --- /dev/null +++ b/discord/snippets/icons/UserStatusIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const UserStatusIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/VoiceNormalIcon.jsx b/discord/snippets/icons/VoiceNormalIcon.jsx new file mode 100644 index 0000000000..b3e2407762 --- /dev/null +++ b/discord/snippets/icons/VoiceNormalIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const VoiceNormalIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/icons/WrenchIcon.jsx b/discord/snippets/icons/WrenchIcon.jsx new file mode 100644 index 0000000000..1918f37b73 --- /dev/null +++ b/discord/snippets/icons/WrenchIcon.jsx @@ -0,0 +1,3 @@ +// AUTOGENERATED BY tools/icons/generate-icons.js: DO NOT MODIFY DIRECTLY + +export const WrenchIcon = (props) => (); \ No newline at end of file diff --git a/discord/snippets/linkbutton.jsx b/discord/snippets/linkbutton.jsx new file mode 100644 index 0000000000..2157601510 --- /dev/null +++ b/discord/snippets/linkbutton.jsx @@ -0,0 +1,7 @@ +export const LinkButton = ({ children, to, color }) => { + return ( + + {children} + + ); +}; \ No newline at end of file diff --git a/discord/snippets/manualanchor.jsx b/discord/snippets/manualanchor.jsx new file mode 100644 index 0000000000..6bc490e278 --- /dev/null +++ b/discord/snippets/manualanchor.jsx @@ -0,0 +1,5 @@ +export const ManualAnchor = ({ id }) => { + return ( +
+ ); +}; \ No newline at end of file diff --git a/discord/snippets/monospace.jsx b/discord/snippets/monospace.jsx new file mode 100644 index 0000000000..3b1a2bb4a6 --- /dev/null +++ b/discord/snippets/monospace.jsx @@ -0,0 +1,7 @@ +export const Monospace = ({ method, children }) => { + return ( +
+ {children} +
+ ); +}; \ No newline at end of file diff --git a/discord/snippets/route.jsx b/discord/snippets/route.jsx new file mode 100644 index 0000000000..e9540c308e --- /dev/null +++ b/discord/snippets/route.jsx @@ -0,0 +1,8 @@ +export const Route = ({ method, children }) => { + return ( +
+ {method} + {children} +
+ ); +}; \ No newline at end of file diff --git a/discord/snippets/snowflake.jsx b/discord/snippets/snowflake.jsx new file mode 100644 index 0000000000..dcd42b8a62 --- /dev/null +++ b/discord/snippets/snowflake.jsx @@ -0,0 +1,159 @@ +export const Snowflake = () => { + return ( +
+ + + + 175928847299117063 + + + + + 41944705796 + + + + + 1462015105796 + + + + + 2016-04-30 11:18:25.796 UTC + + + + + 000000100111000100000110010110101100000100 + + + + + 00001 + + + + + 00000 + + + + + to binary + + + + + to decimal + + + + + Parse unix timestamp (ms) + + + + + + 1420070400000 + + + Discord Epoch (unix timestamp in ms) + + + + + Number of milliseconds since the Discord epoch{' '} + + + (first seconds of 2015) + + + + + Internal + + + worker + + + ID + + + + + Internal + + + process + + + ID + + + + + 000000000111 + + + + + Incremented{' '} + + + for every{' '} + + + generated ID{' '} + + + on that{' '} + + + process + + + + + 64 + + + + + 22 + + + + + 12 + + + + + 0 + + + + + 17 + + + + + + + +
+ ); +} diff --git a/discord/styles/api.css b/discord/styles/api.css new file mode 100644 index 0000000000..d0a31fd862 --- /dev/null +++ b/discord/styles/api.css @@ -0,0 +1,21 @@ +/* Hide link icon for anchors on hover */ +.param-head .absolute { + display: none; +} + +.param-head .dark\:text-primary-light:is(.dark *) { + color: #929af2; + font-weight: bold; +} + +.api-section .py-6 { + padding: 0.5rem 0.5rem; +} + +.code-group:first-child { + display: none; +} + +#content-side-layout:has(.code-group) { + top: 8.5rem; +} \ No newline at end of file diff --git a/discord/styles/changelog.css b/discord/styles/changelog.css new file mode 100644 index 0000000000..34556fdf6b --- /dev/null +++ b/discord/styles/changelog.css @@ -0,0 +1,6 @@ +#content-area:has(.mdx-content[data-page-href="/src/_props/developers/docs/change-log"]), +#content-area:has(.mdx-content[data-page-href="/developers/docs/change-log"]) { + div#page-context-menu { + display: none; + } +} \ No newline at end of file diff --git a/discord/styles/fonts.css b/discord/styles/fonts.css new file mode 100644 index 0000000000..831660693a --- /dev/null +++ b/discord/styles/fonts.css @@ -0,0 +1,49 @@ +/* +Imports Discord brand font + */ + + @font-face { + font-family: discord; + src: url('https://mintlify-assets.b-cdn.net/fonts/discord/discord.woff2'); + } + @font-face { + font-family: "gg sans"; + font-weight: 200; + src: url('https://mintlify-assets.b-cdn.net/fonts/discord/gg_200.woff2') + } + + @font-face { + font-family: "gg sans"; + font-weight: 400; + src: url('https://mintlify-assets.b-cdn.net/fonts/discord/gg_400.woff2'); + } + + @font-face { + font-family: "gg sans"; + font-weight: 600; + src: url('https://mintlify-assets.b-cdn.net/fonts/discord/gg_600.woff2'); + } + +@font-face { + font-family: "gg sans"; + font-weight: 800; + src: url('https://mintlify-assets.b-cdn.net/fonts/discord/gg_800.woff2') +} + +body { + font-family: "gg sans", var(--font-inter), -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; + font-weight: 400; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "gg sans", var(--font-inter), -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; + font-weight: 600; +} + +.\[\&_\*\:not\(h1\2c h2\2c h3\2c h4\2c h5\2c h6\2c h1_\*\2c h2_\*\2c h3_\*\2c h4_\*\2c h5_\*\2c h6_\*\)\]\:font-bodyWeight :not(h1,h2,h3,h4,h5,h6,h1 *,h2 *,h3 *,h4 *,h5 *,h6 *) { + font-weight: 400; +} + + + + diff --git a/discord/styles/homepage.css b/discord/styles/homepage.css new file mode 100644 index 0000000000..13ff1f9e14 --- /dev/null +++ b/discord/styles/homepage.css @@ -0,0 +1,62 @@ +#homepage-hero { + display: grid; + + margin: -1rem auto 1.5rem auto; + background-image: url(https://mintlify-assets.b-cdn.net/discord-back.png); + background-size: cover; + background-position: center; + grid-area: 1/1; + border-radius: var(--radius-lg); + + #hero-text { + display: flex; + grid-area: 1/1; + align-self: center; + margin: 2rem; + gap: 1rem; + color: white; + } + + #hero-tagline { + font-size: 2rem; + line-height: 2rem; + font-family: 'discord'; + margin: 2rem 0 0.5rem 0; + color: white; + } + + #hero-image { + width: 300px; + height: 300px; + display: none; + } +} + +@media (min-width: 1024px) { + #homepage-hero { + #hero-image { + display: block; + } + #hero-tagline { + font-size: 4rem; + line-height: 4rem; + margin-bottom: 0rem; + } + #hero-text { + margin: 4rem; + } + } +} + +#content-area:has(.mdx-content[data-page-href="/src/_props/developers/docs/intro"]), +#content-area:has(.mdx-content[data-page-href="/developers/docs/intro"]) { + padding-right: 1rem; + + div:has(h1#page-title) { + display: none; + } + + div#page-context-menu { + display: none; + } +} \ No newline at end of file diff --git a/discord/styles/linkbutton.css b/discord/styles/linkbutton.css new file mode 100644 index 0000000000..2f11276640 --- /dev/null +++ b/discord/styles/linkbutton.css @@ -0,0 +1,15 @@ +.MDXLinkButton { + display: block; + color: white !important; + font-size: 15px; + padding: 10px 16px; + border-radius: var(--radius-xs); + width: fit-content; + height: fit-content; + box-sizing: border-box; + line-height: 18px; + + &.brand { + background-color: var(--brand-500); + } +} \ No newline at end of file diff --git a/discord/styles/manualanchor.css b/discord/styles/manualanchor.css new file mode 100644 index 0000000000..7b9bdbb908 --- /dev/null +++ b/discord/styles/manualanchor.css @@ -0,0 +1,10 @@ + +.MDXManualAnchor { + display: inline; + width: 100px; + height: 10px; + background-color: red; + position: relative; + top: -120px; + visibility: hidden; +} \ No newline at end of file diff --git a/discord/styles/monospace.css b/discord/styles/monospace.css new file mode 100644 index 0000000000..63bb56546a --- /dev/null +++ b/discord/styles/monospace.css @@ -0,0 +1,6 @@ +.MDXMonospace { + font-family: var(--font-jetbrains-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-feature-settings: normal; + font-variation-settings: normal; + font-size: 1em; +} \ No newline at end of file diff --git a/discord/styles/route.css b/discord/styles/route.css new file mode 100644 index 0000000000..3406cff0e7 --- /dev/null +++ b/discord/styles/route.css @@ -0,0 +1,47 @@ +.MDXRoute { + display: flex; + align-items: center; + + .verb { + font-size: 14px; + color: var(--white-500); + background-color: var(--primary-600); + border: 1px solid var(--primary-500); + font-family: var(--font-code); + text-transform: uppercase; + margin-right: 10px; + padding: 4px 8px; + padding-bottom: 1.5px; + border-radius: var(--radius-md); + } + + .url { + font-family: var(--font-code); + font-size: 16px; + } + + .delete { + background-color: var(--red-630); + border: 1px solid var(--red-500); + } + + .get { + background-color: var(--brand-700); + border: 1px solid var(--brand-530); + } + + .patch { + background-color: var(--yellow-530); + border: 1px solid var(--yellow-400); + } + + .post { + background-color: var(--green-500); + border: 1px solid var(--green-360); + } + + .put { + background-color: var(--orange-600); + border: 1px solid var(--orange-430); + } +} \ No newline at end of file diff --git a/discord/styles/snowflake.css b/discord/styles/snowflake.css new file mode 100644 index 0000000000..a8ab862c13 --- /dev/null +++ b/discord/styles/snowflake.css @@ -0,0 +1,52 @@ + +/* Light mode styles */ +html[style*="color-scheme: light"] .MDXSnowflake { + text { + fill: black; + } + + .arrow { + fill: oklab(0.251834 0.00131807 -0.008479); + } +} + +html[style*="color-scheme: dark"] .MDXSnowflake { + text { + fill: white; + } +} + +.MDXSnowflake { + display: flex; + justify-content: center; + margin-top: 40px; + margin-bottom: 40px; + + + .arrow { + fill: oklab(0.751834 0.00131807 -0.008479); + } + + .header { + font-weight: bold; + font-size: 16px; + + &.group1 { + fill: oklab(0.747617 0.00671133 -0.126112); + } + + &.group2 { + fill: oklab(0.773758 0.0289854 0.0857202); + } + + &.group3 { + fill: oklab(0.723746 0.150768 0.055049); + } + + &.group4 { + fill: oklab(0.700146 -0.0932133 -0.0629166); + } + } + + +} diff --git a/discord/styles/theme.css b/discord/styles/theme.css new file mode 100644 index 0000000000..6d5162063d --- /dev/null +++ b/discord/styles/theme.css @@ -0,0 +1,535 @@ +/* +Below are tweaks to Mintlify theme CSS. + */ + + /* +Mintlify's way of setting background color can +cause black flashes at bottom of page + */ +body { + background-color: #ffffff; + +} +body:is(.dark *) { + background-color: #36373e; +} + +/* Allow the content to stretch as wide as the page body */ +#content-container { + max-width: unset; +} + +/* Set page width of navigation items */ +.max-w-8xl, +/* Set page width of content */ +.peer.is-not-center~.peer-\[\.is-not-center\]\:max-w-8xl { + max-width: 110rem; +} + +/* +Make HRs thicker +*/ +hr { + border: none; + margin: 48px 0; + height: 1px; + background-color: #e6e6e7; +} + +hr:is(.dark *) { + background-color: #606169; +} +/* +.prose is the container for the main markdown content + */ +.prose,.prose-sm { + font-size: 1rem; + line-height: 1.5rem; + /* Turn all text white */ + p :is(.dark *) { + color: white; + } + + /* + Ensure code tags don't wrap when inline in text + */ + code { + white-space: pre; + overflow-x: auto; + overflow-y: hidden; + line-height: 1.1rem; + } + + code span { + font-size: 0.85rem; + line-height: 1rem; + } + + .code-block { + margin-top: 1.5rem; + margin-bottom: 1.5rem; + } + + /* .accordion { + border-radius: var(--radius-sm); + + summary { + border-radius: calc(var(--radius-sm) -1px) var(--radius-sm) 0 0; + } + } */ + + /* + Ensure lists have reasonable padding + */ + li { + margin: 0px; + } + /* + Make title bold + */ + h1, h2, h3, h4, h5, h6 { + font-weight: bold; + + /* Hide link icon for anchors on hover */ + .absolute { + display: none; + } + } + + /* + Make bold, strong tags bold + */ + b, strong { + font-weight: 800; + } + + [data-component-part=update-label] { + font-size: 1rem; + } + /* + Make changelog tags resemble tags + */ + [data-component-part=update-tag] { + opacity: 0.7; + padding: 0.1rem 0.3rem; + display: inline-block; + font-size: 0.8rem; + border: 1px solid #e6e6e7; + } + [data-component-part=update-tag]:is(.dark *) { + opacity: 0.5; + border-color: white; + } +} + +/* +This pushes all shades of gray to a full white, +which is more similar to legacy docs. + +However it may add some visual clutter. + */ +.text-gray-200:is(.light *), +.text-gray-400:is(.light *), +.text-gray-500:is(.light *), +.text-gray-600:is(.light *), +.text-gray-800:is(.light *) { + color: black; + --tw-text-opacity: 1; +} + +.dark\:text-gray-200:is(.dark *), +.dark\:text-gray-400:is(.dark *), +.dark\:text-gray-500:is(.dark *), +.dark\:text-gray-600:is(.dark *), +.dark\:text-gray-800:is(.dark *) { + color: white; +} + +/* +Remove eyebrow / breadcrumbs + */ +.eyebrow { + display: none; +} + +/* +Style left-hand navigation headers + */ +#sidebar-title { + font-family: "gg sans","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; + text-transform: uppercase; + font-size: 12px; + line-height: 1.3333333333333333; + font-weight: 600; +} + +/* +Indent left-hand navigation areas + */ +#sidebar-group { + margin-left: 1rem; + + li > a, [data-group-tag] > div, [data-group-tag] > button { + padding: 0.1rem; + padding-right: 0.5rem; + border-radius: var(--radius-sm); + } +} + +/* +Reduce spacing between left-hand categories + */ +#navigation-items > div:not(:first-of-type) { + margin-top: 1rem; +} + + +.dark\:text-primary-light:is(.dark *) { + color: rgba(255, 255, 255, 0.8); +} + +/* Inline code blocks */ +:not(pre)>code:is(.dark *) { + background-color: #232428; + color: white; +} + +/* +Darken table of contents (right hand) items + */ +.toc-item a { + opacity: 0.7; + border-width: 0px; +} + +/* +Lighten table of contents selected item + */ +.toc-item a.dark\:text-primary-light:is(.dark *) { + opacity: 1; + color: white; +} + +/* +Hide table of contents items below top depth in tree + */ +.toc-item:not([data-depth="0"]) { + text-decoration: underline; + display: none; +} + +/* +Tighten spacing between table of contents items + */ +.toc-item a { + padding: 0px; +} + +/* +Style anchor tags as blue without underline + */ +.prose a:is(.dark *):not(.card), .prose a:is(.dark *) code, .prose a:is(.dark *) strong{ + color: #7aaef3; + text-decoration: none; + border-bottom: 0px; +} + +/* +Fix code blocks inside links that break page width + */ +.prose a code { + white-space: pre-wrap; + word-break: break-all; + overflow-wrap: break-word; + max-width: 100%; +} + +/* +Fix MDXRoute components breaking page width on mobile + */ +@media (max-width: 768px) { + .MDXRoute { + flex-wrap: wrap; + gap: 0.5rem; + } + + .MDXRoute .url { + word-break: break-all; + overflow-wrap: break-word; + max-width: 100%; + flex: 1; + min-width: 0; + } +} + +/* +Fix inline code blocks breaking page width in prose content + */ +.prose code:not(pre code) { + word-break: break-all; + overflow-wrap: break-word; + white-space: pre-wrap; + max-width: 100%; +} + +.callout { + padding: 0.5rem 1rem; + border-radius: var(--radius-sm); +} + +.callout code { + white-space: pre-wrap; + word-break: break-word; + overflow-wrap: break-word; +} + +/* Expand tables horizontally */ +.prose table { + display: table; + /* Uncomment this and the th,td,tr below to wrap tables */ + table-layout: fixed; +} + +th, td, tr { + word-wrap: break-word; +} + +td { + overflow: hidden; +} + +td code { + white-space: pre-wrap; + word-break: break-word; + overflow-wrap: break-word; +} + +/* +Set table header styles + */ +table > thead > tr > th { + font-family: "gg sans","Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif; + text-transform: uppercase; + font-size: 12px; + line-height: 1.3333333333333333; + font-weight: 800 !important; + text-align: left; +} + +/* +Add padding to table cells + */ +.prose th, .prose td { + padding: 0.5rem; + padding-inline: 0.5rem; +} + +/* +Set default table row color + */ + +tr:is(.dark *) { + background-color: #323338; + border-bottom: 2px solid #161718; +} + +/* +Alternate table row colors with darker color + */ +tr:nth-child(even) { + background-color: #ebedef; +} +tr:nth-child(even):is(.dark *) { + background-color: #232428; +} + +.dark\:bg-zinc-500\/10:is(.dark *) { + background-color: #353c4b; + border-color: #05aafc; +} + +/* +Hide bottom of page pagination + */ +#pagination { + display: none; +} + +/* +On change log only, we remove some HR spacing +*/ +[data-page-title="Change Log"] { + hr { + padding: 0px; + margin: 0px; + } +} + +/* +Shrink and lighten accordian description + */ +[data-component-part=accordion-description] { + font-size: 0.8rem; + opacity: 0.7; +} + + +/* + Move content up to cover eyebrow / breadcrumb space, but only when wide (see media queries below) +*/ +#content-container > div:first-of-type { + padding-top: 8rem; +} + +/* +Very slightly shrink left hand nav +*/ +#sidebar { + width: 17rem; +} + +/* +Hide breadcrumbs when there is only one item +*/ +header > div > .flex-row > div { + display: none; +} + +@media (min-width: 1024px) { + /* Middle breakpoint */ + /* + Show breadcrumbs when the page is wide and there are multiple items + */ + header > div > .flex-row > div:not(:only-child) { + display: flex; + } + + /* Content mostly at top when not mobile */ + #content-container > div:first-of-type { + padding-top: 1.5rem; + } + + #content-container #content-side-layout { + padding-top: 0rem; + } + + /* Widen main content on left and right */ + #content-area:not(:has(div div.not-found-container)) { + margin-left: -5rem; + margin-right: 1rem; + } + /* + Hide TOC when only one category + */ + #sidebar:has(#navigation-items > ul:only-child) { + display: none; + + + #content-container:has([data-page-title="Change Log"]) { + margin-left: -17rem; + } + } +} + +@media (min-width: 1280px) { + /* Wide breakpoint */ + + /* Remove gap between main content and right TOC */ + .gap-12 { + gap: 0rem; + } + + /* + Move TOC up to cover eyebrow / breadcrumb space + */ + #table-of-contents { + padding-top: 1.5rem; + } +} + +/* Fix LEGACY tag in nav */ +.dark .nav-tag-pill { + background-color: black; +} + +.nav-tag-pill { + padding: 0.1rem 0.2rem; + border-radius: var(--radius-sm); +} + +.mdx-content { + margin-top: 1rem; +} + +/* Set sidebar icon color */ +[data-title="Developer Terms of Service"] svg, [data-title="Developer Policy"] svg { + display: none; +} +[data-title] > a > svg:first-child:is(.dark *) { + display: block; + background-color: white; +} + +/* Add some spacing to icons on cards and set a suble background color */ +[data-component-part=card-icon] { + width: 54px; + height: 54px; + padding: 15px; + margin-right: 0.5rem; + background-color: rgba(255, 255, 255, 0.05); + border-radius: var(--radius-sm); + align-self: start; + margin-top: 0.5rem; +} + +[data-component-part=card-icon]:is(.light *) { + background-color: rgba(0, 0, 0, 0.05); +} + +/* Light mode overrides */ +html[style*="color-scheme: light"] { + /* Style inline code blocks with light background */ + .prose code:not(pre code) { + background-color: #f0f1f5; + } + + + .prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)) { + border-bottom: none; + color: rgb(var(--primary)); + } +} + +.chat-assistant-floating-input, #assistant-bar-placeholder { + display: none; +} + +#banner { + a, a:visited { + color: white; + font-weight: bold; + } + + a:hover { + border-bottom: 1px solid white; + } +} + +/* Add top margin when banner is present - mobile only */ +@media (max-width: 1023px) { + body:has(#banner) #content-container { + margin-top: 64px; + } + + /* Override banner styling on mobile */ + #banner { + button { + position: relative; + top: unset; + right: unset; + display: flex; + --tw-translate-y: 0; + padding: 10px; + } + font-size: 0.9rem; + } +} \ No newline at end of file diff --git a/discord/styles/variables.css b/discord/styles/variables.css new file mode 100644 index 0000000000..993a228b4b --- /dev/null +++ b/discord/styles/variables.css @@ -0,0 +1,483 @@ +:root { + /* Border Radius */ + --radius-none: 0px; + --radius-xs: 4px; + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + + /* Colors */ + --saturation-factor: 1; + --red-100: hsl(var(--red-100-hsl) / 1); + --red-100-hsl: 350 calc(var(--saturation-factor, 1) * 75%) 98.431%; + --red-130: hsl(var(--red-130-hsl) / 1); + --red-130-hsl: 0 calc(var(--saturation-factor, 1) * 83.333%) 97.647%; + --red-160: hsl(var(--red-160-hsl) / 1); + --red-160-hsl: 0 calc(var(--saturation-factor, 1) * 90.909%) 95.686%; + --red-200: hsl(var(--red-200-hsl) / 1); + --red-200-hsl: 358.125 calc(var(--saturation-factor, 1) * 88.889%) 92.941%; + --red-230: hsl(var(--red-230-hsl) / 1); + --red-230-hsl: 358.696 calc(var(--saturation-factor, 1) * 85.185%) 89.412%; + --red-260: hsl(var(--red-260-hsl) / 1); + --red-260-hsl: 359.077 calc(var(--saturation-factor, 1) * 89.041%) 85.686%; + --red-300: hsl(var(--red-300-hsl) / 1); + --red-300-hsl: 358.588 calc(var(--saturation-factor, 1) * 91.398%) 81.765%; + --red-330: hsl(var(--red-330-hsl) / 1); + --red-330-hsl: 358.857 calc(var(--saturation-factor, 1) * 91.304%) 77.451%; + --red-345: hsl(var(--red-345-hsl) / 1); + --red-345-hsl: 358.168 calc(var(--saturation-factor, 1) * 92.908%) 72.353%; + --red-360: hsl(var(--red-360-hsl) / 1); + --red-360-hsl: 358.471 calc(var(--saturation-factor, 1) * 91.813%) 66.471%; + --red-400: hsl(var(--red-400-hsl) / 1); + --red-400-hsl: 358.659 calc(var(--saturation-factor, 1) * 87.317%) 59.804%; + --red-430: hsl(var(--red-430-hsl) / 1); + --red-430-hsl: 358.16 calc(var(--saturation-factor, 1) * 68.776%) 53.529%; + --red-460: hsl(var(--red-460-hsl) / 1); + --red-460-hsl: 358.705 calc(var(--saturation-factor, 1) * 59.149%) 46.078%; + --red-500: hsl(var(--red-500-hsl) / 1); + --red-500-hsl: 359.504 calc(var(--saturation-factor, 1) * 60.199%) 39.412%; + --red-530: hsl(var(--red-530-hsl) / 1); + --red-530-hsl: 358.919 calc(var(--saturation-factor, 1) * 63.429%) 34.314%; + --red-560: hsl(var(--red-560-hsl) / 1); + --red-560-hsl: 358.788 calc(var(--saturation-factor, 1) * 63.871%) 30.392%; + --red-600: hsl(var(--red-600-hsl) / 1); + --red-600-hsl: 358.636 calc(var(--saturation-factor, 1) * 64.706%) 26.667%; + --red-630: hsl(var(--red-630-hsl) / 1); + --red-630-hsl: 358.5 calc(var(--saturation-factor, 1) * 65.574%) 23.922%; + --red-660: hsl(var(--red-660-hsl) / 1); + --red-660-hsl: 359.155 calc(var(--saturation-factor, 1) * 65.138%) 21.373%; + --red-700: hsl(var(--red-700-hsl) / 1); + --red-700-hsl: 358.125 calc(var(--saturation-factor, 1) * 66.667%) 18.824%; + --red-730: hsl(var(--red-730-hsl) / 1); + --red-730-hsl: 358.929 calc(var(--saturation-factor, 1) * 66.667%) 16.471%; + --red-760: hsl(var(--red-760-hsl) / 1); + --red-760-hsl: 358.846 calc(var(--saturation-factor, 1) * 70.27%) 14.51%; + --red-800: hsl(var(--red-800-hsl) / 1); + --red-800-hsl: 358.636 calc(var(--saturation-factor, 1) * 68.75%) 12.549%; + --red-830: hsl(var(--red-830-hsl) / 1); + --red-830-hsl: 358.378 calc(var(--saturation-factor, 1) * 67.273%) 10.784%; + --red-860: hsl(var(--red-860-hsl) / 1); + --red-860-hsl: 0 calc(var(--saturation-factor, 1) * 66.667%) 9.412%; + --red-900: hsl(var(--red-900-hsl) / 1); + --red-900-hsl: 0 calc(var(--saturation-factor, 1) * 70%) 7.843%; + --orange-100: hsl(var(--orange-100-hsl) / 1); + --orange-100-hsl: 18 calc(var(--saturation-factor, 1) * 100%) 98.039%; + --orange-130: hsl(var(--orange-130-hsl) / 1); + --orange-130-hsl: 23.333 calc(var(--saturation-factor, 1) * 100%) 96.471%; + --orange-160: hsl(var(--orange-160-hsl) / 1); + --orange-160-hsl: 21.29 calc(var(--saturation-factor, 1) * 93.939%) 93.529%; + --orange-200: hsl(var(--orange-200-hsl) / 1); + --orange-200-hsl: 21.25 calc(var(--saturation-factor, 1) * 92.308%) 89.804%; + --orange-230: hsl(var(--orange-230-hsl) / 1); + --orange-230-hsl: 23.514 calc(var(--saturation-factor, 1) * 92.5%) 84.314%; + --orange-260: hsl(var(--orange-260-hsl) / 1); + --orange-260-hsl: 23.301 calc(var(--saturation-factor, 1) * 94.495%) 78.627%; + --orange-300: hsl(var(--orange-300-hsl) / 1); + --orange-300-hsl: 25.468 calc(var(--saturation-factor, 1) * 94.558%) 71.176%; + --orange-330: hsl(var(--orange-330-hsl) / 1); + --orange-330-hsl: 27 calc(var(--saturation-factor, 1) * 94.737%) 62.745%; + --orange-345: hsl(var(--orange-345-hsl) / 1); + --orange-345-hsl: 28.342 calc(var(--saturation-factor, 1) * 87.665%) 55.49%; + --orange-360: hsl(var(--orange-360-hsl) / 1); + --orange-360-hsl: 26.145 calc(var(--saturation-factor, 1) * 71.315%) 50.784%; + --orange-400: hsl(var(--orange-400-hsl) / 1); + --orange-400-hsl: 25.443 calc(var(--saturation-factor, 1) * 67.521%) 45.882%; + --orange-430: hsl(var(--orange-430-hsl) / 1); + --orange-430-hsl: 24.681 calc(var(--saturation-factor, 1) * 69.458%) 39.804%; + --orange-460: hsl(var(--orange-460-hsl) / 1); + --orange-460-hsl: 23.538 calc(var(--saturation-factor, 1) * 72.222%) 35.294%; + --orange-500: hsl(var(--orange-500-hsl) / 1); + --orange-500-hsl: 22.314 calc(var(--saturation-factor, 1) * 76.101%) 31.176%; + --orange-530: hsl(var(--orange-530-hsl) / 1); + --orange-530-hsl: 21.869 calc(var(--saturation-factor, 1) * 76.978%) 27.255%; + --orange-560: hsl(var(--orange-560-hsl) / 1); + --orange-560-hsl: 21.474 calc(var(--saturation-factor, 1) * 77.236%) 24.118%; + --orange-600: hsl(var(--orange-600-hsl) / 1); + --orange-600-hsl: 22.857 calc(var(--saturation-factor, 1) * 79.245%) 20.784%; + --orange-630: hsl(var(--orange-630-hsl) / 1); + --orange-630-hsl: 22.105 calc(var(--saturation-factor, 1) * 79.167%) 18.824%; + --orange-660: hsl(var(--orange-660-hsl) / 1); + --orange-660-hsl: 22.388 calc(var(--saturation-factor, 1) * 78.824%) 16.667%; + --orange-700: hsl(var(--orange-700-hsl) / 1); + --orange-700-hsl: 21.724 calc(var(--saturation-factor, 1) * 76.316%) 14.902%; + --orange-730: hsl(var(--orange-730-hsl) / 1); + --orange-730-hsl: 21.923 calc(var(--saturation-factor, 1) * 78.788%) 12.941%; + --orange-760: hsl(var(--orange-760-hsl) / 1); + --orange-760-hsl: 21.333 calc(var(--saturation-factor, 1) * 78.947%) 11.176%; + --orange-800: hsl(var(--orange-800-hsl) / 1); + --orange-800-hsl: 21 calc(var(--saturation-factor, 1) * 80%) 9.804%; + --orange-830: hsl(var(--orange-830-hsl) / 1); + --orange-830-hsl: 20 calc(var(--saturation-factor, 1) * 76.744%) 8.431%; + --orange-860: hsl(var(--orange-860-hsl) / 1); + --orange-860-hsl: 20 calc(var(--saturation-factor, 1) * 72.973%) 7.255%; + --orange-900: hsl(var(--orange-900-hsl) / 1); + --orange-900-hsl: 25.714 calc(var(--saturation-factor, 1) * 72.414%) 5.686%; + --yellow-100: hsl(var(--yellow-100-hsl) / 1); + --yellow-100-hsl: 33.75 calc(var(--saturation-factor, 1) * 100%) 96.863%; + --yellow-130: hsl(var(--yellow-130-hsl) / 1); + --yellow-130-hsl: 31.304 calc(var(--saturation-factor, 1) * 100%) 95.49%; + --yellow-160: hsl(var(--yellow-160-hsl) / 1); + --yellow-160-hsl: 32.727 calc(var(--saturation-factor, 1) * 100%) 91.373%; + --yellow-200: hsl(var(--yellow-200-hsl) / 1); + --yellow-200-hsl: 35 calc(var(--saturation-factor, 1) * 97.297%) 85.49%; + --yellow-230: hsl(var(--yellow-230-hsl) / 1); + --yellow-230-hsl: 36.637 calc(var(--saturation-factor, 1) * 96.581%) 77.059%; + --yellow-260: hsl(var(--yellow-260-hsl) / 1); + --yellow-260-hsl: 40.656 calc(var(--saturation-factor, 1) * 96.825%) 62.941%; + --yellow-300: hsl(var(--yellow-300-hsl) / 1); + --yellow-300-hsl: 40.421 calc(var(--saturation-factor, 1) * 86.364%) 56.863%; + --yellow-330: hsl(var(--yellow-330-hsl) / 1); + --yellow-330-hsl: 40 calc(var(--saturation-factor, 1) * 75.309%) 52.353%; + --yellow-345: hsl(var(--yellow-345-hsl) / 1); + --yellow-345-hsl: 39.545 calc(var(--saturation-factor, 1) * 70.968%) 48.627%; + --yellow-360: hsl(var(--yellow-360-hsl) / 1); + --yellow-360-hsl: 39.018 calc(var(--saturation-factor, 1) * 74.429%) 42.941%; + --yellow-400: hsl(var(--yellow-400-hsl) / 1); + --yellow-400-hsl: 37.792 calc(var(--saturation-factor, 1) * 78.571%) 38.431%; + --yellow-430: hsl(var(--yellow-430-hsl) / 1); + --yellow-430-hsl: 37.447 calc(var(--saturation-factor, 1) * 84.431%) 32.745%; + --yellow-460: hsl(var(--yellow-460-hsl) / 1); + --yellow-460-hsl: 36.279 calc(var(--saturation-factor, 1) * 87.755%) 28.824%; + --yellow-500: hsl(var(--yellow-500-hsl) / 1); + --yellow-500-hsl: 35.5 calc(var(--saturation-factor, 1) * 93.75%) 25.098%; + --yellow-530: hsl(var(--yellow-530-hsl) / 1); + --yellow-530-hsl: 34.857 calc(var(--saturation-factor, 1) * 92.92%) 22.157%; + --yellow-560: hsl(var(--yellow-560-hsl) / 1); + --yellow-560-hsl: 33.83 calc(var(--saturation-factor, 1) * 94%) 19.608%; + --yellow-600: hsl(var(--yellow-600-hsl) / 1); + --yellow-600-hsl: 34.815 calc(var(--saturation-factor, 1) * 93.103%) 17.059%; + --yellow-630: hsl(var(--yellow-630-hsl) / 1); + --yellow-630-hsl: 34.521 calc(var(--saturation-factor, 1) * 92.405%) 15.49%; + --yellow-660: hsl(var(--yellow-660-hsl) / 1); + --yellow-660-hsl: 34.688 calc(var(--saturation-factor, 1) * 91.429%) 13.725%; + --yellow-700: hsl(var(--yellow-700-hsl) / 1); + --yellow-700-hsl: 34.286 calc(var(--saturation-factor, 1) * 90.323%) 12.157%; + --yellow-730: hsl(var(--yellow-730-hsl) / 1); + --yellow-730-hsl: 32.941 calc(var(--saturation-factor, 1) * 92.727%) 10.784%; + --yellow-760: hsl(var(--yellow-760-hsl) / 1); + --yellow-760-hsl: 32.727 calc(var(--saturation-factor, 1) * 91.667%) 9.412%; + --yellow-800: hsl(var(--yellow-800-hsl) / 1); + --yellow-800-hsl: 32.432 calc(var(--saturation-factor, 1) * 90.244%) 8.039%; + --yellow-830: hsl(var(--yellow-830-hsl) / 1); + --yellow-830-hsl: 31.875 calc(var(--saturation-factor, 1) * 88.889%) 7.059%; + --yellow-860: hsl(var(--yellow-860-hsl) / 1); + --yellow-860-hsl: 32.308 calc(var(--saturation-factor, 1) * 86.667%) 5.882%; + --yellow-900: hsl(var(--yellow-900-hsl) / 1); + --yellow-900-hsl: 36 calc(var(--saturation-factor, 1) * 83.333%) 4.706%; + --green-100: hsl(var(--green-100-hsl) / 1); + --green-100-hsl: 136.667 calc(var(--saturation-factor, 1) * 90%) 96.078%; + --green-130: hsl(var(--green-130-hsl) / 1); + --green-130-hsl: 136.875 calc(var(--saturation-factor, 1) * 94.118%) 93.333%; + --green-160: hsl(var(--green-160-hsl) / 1); + --green-160-hsl: 140.952 calc(var(--saturation-factor, 1) * 91.304%) 86.471%; + --green-200: hsl(var(--green-200-hsl) / 1); + --green-200-hsl: 143.478 calc(var(--saturation-factor, 1) * 93.496%) 75.882%; + --green-230: hsl(var(--green-230-hsl) / 1); + --green-230-hsl: 146.323 calc(var(--saturation-factor, 1) * 86.592%) 64.902%; + --green-260: hsl(var(--green-260-hsl) / 1); + --green-260-hsl: 145.605 calc(var(--saturation-factor, 1) * 75.12%) 59.02%; + --green-300: hsl(var(--green-300-hsl) / 1); + --green-300-hsl: 146.323 calc(var(--saturation-factor, 1) * 65.401%) 53.529%; + --green-330: hsl(var(--green-330-hsl) / 1); + --green-330-hsl: 146.494 calc(var(--saturation-factor, 1) * 63.115%) 47.843%; + --green-345: hsl(var(--green-345-hsl) / 1); + --green-345-hsl: 146.939 calc(var(--saturation-factor, 1) * 65.919%) 43.725%; + --green-360: hsl(var(--green-360-hsl) / 1); + --green-360-hsl: 145.385 calc(var(--saturation-factor, 1) * 65%) 39.216%; + --green-400: hsl(var(--green-400-hsl) / 1); + --green-400-hsl: 142.703 calc(var(--saturation-factor, 1) * 60.656%) 35.882%; + --green-430: hsl(var(--green-430-hsl) / 1); + --green-430-hsl: 141.522 calc(var(--saturation-factor, 1) * 56.098%) 32.157%; + --green-460: hsl(var(--green-460-hsl) / 1); + --green-460-hsl: 141.481 calc(var(--saturation-factor, 1) * 56.643%) 28.039%; + --green-500: hsl(var(--green-500-hsl) / 1); + --green-500-hsl: 141.37 calc(var(--saturation-factor, 1) * 58.4%) 24.51%; + --green-530: hsl(var(--green-530-hsl) / 1); + --green-530-hsl: 140.308 calc(var(--saturation-factor, 1) * 60.748%) 20.98%; + --green-560: hsl(var(--green-560-hsl) / 1); + --green-560-hsl: 138.621 calc(var(--saturation-factor, 1) * 61.702%) 18.431%; + --green-600: hsl(var(--green-600-hsl) / 1); + --green-600-hsl: 139.245 calc(var(--saturation-factor, 1) * 65.432%) 15.882%; + --green-630: hsl(var(--green-630-hsl) / 1); + --green-630-hsl: 140 calc(var(--saturation-factor, 1) * 66.667%) 14.118%; + --green-660: hsl(var(--green-660-hsl) / 1); + --green-660-hsl: 139.091 calc(var(--saturation-factor, 1) * 68.75%) 12.549%; + --green-700: hsl(var(--green-700-hsl) / 1); + --green-700-hsl: 141 calc(var(--saturation-factor, 1) * 74.074%) 10.588%; + --green-730: hsl(var(--green-730-hsl) / 1); + --green-730-hsl: 138.333 calc(var(--saturation-factor, 1) * 78.261%) 9.02%; + --green-760: hsl(var(--green-760-hsl) / 1); + --green-760-hsl: 140 calc(var(--saturation-factor, 1) * 84.615%) 7.647%; + --green-800: hsl(var(--green-800-hsl) / 1); + --green-800-hsl: 139.286 calc(var(--saturation-factor, 1) * 82.353%) 6.667%; + --green-830: hsl(var(--green-830-hsl) / 1); + --green-830-hsl: 137.5 calc(var(--saturation-factor, 1) * 80%) 5.882%; + --green-860: hsl(var(--green-860-hsl) / 1); + --green-860-hsl: 132.632 calc(var(--saturation-factor, 1) * 70.37%) 5.294%; + --green-900: hsl(var(--green-900-hsl) / 1); + --green-900-hsl: 128.571 calc(var(--saturation-factor, 1) * 58.333%) 4.706%; + --blue-100: hsl(var(--blue-100-hsl) / 1); + --blue-100-hsl: 210 calc(var(--saturation-factor, 1) * 80%) 98.039%; + --blue-130: hsl(var(--blue-130-hsl) / 1); + --blue-130-hsl: 210 calc(var(--saturation-factor, 1) * 87.5%) 96.863%; + --blue-160: hsl(var(--blue-160-hsl) / 1); + --blue-160-hsl: 208.889 calc(var(--saturation-factor, 1) * 87.097%) 93.922%; + --blue-200: hsl(var(--blue-200-hsl) / 1); + --blue-200-hsl: 206.25 calc(var(--saturation-factor, 1) * 92.308%) 89.804%; + --blue-230: hsl(var(--blue-230-hsl) / 1); + --blue-230-hsl: 205.135 calc(var(--saturation-factor, 1) * 92.5%) 84.314%; + --blue-260: hsl(var(--blue-260-hsl) / 1); + --blue-260-hsl: 204.231 calc(var(--saturation-factor, 1) * 94.545%) 78.431%; + --blue-300: hsl(var(--blue-300-hsl) / 1); + --blue-300-hsl: 202.649 calc(var(--saturation-factor, 1) * 97.419%) 69.608%; + --blue-330: hsl(var(--blue-330-hsl) / 1); + --blue-330-hsl: 200.957 calc(var(--saturation-factor, 1) * 100%) 59.02%; + --blue-345: hsl(var(--blue-345-hsl) / 1); + --blue-345-hsl: 199.524 calc(var(--saturation-factor, 1) * 100%) 49.412%; + --blue-360: hsl(var(--blue-360-hsl) / 1); + --blue-360-hsl: 202.562 calc(var(--saturation-factor, 1) * 100%) 47.451%; + --blue-400: hsl(var(--blue-400-hsl) / 1); + --blue-400-hsl: 206.809 calc(var(--saturation-factor, 1) * 100%) 46.078%; + --blue-430: hsl(var(--blue-430-hsl) / 1); + --blue-430-hsl: 212.208 calc(var(--saturation-factor, 1) * 100%) 45.294%; + --blue-460: hsl(var(--blue-460-hsl) / 1); + --blue-460-hsl: 213.589 calc(var(--saturation-factor, 1) * 100%) 40.98%; + --blue-500: hsl(var(--blue-500-hsl) / 1); + --blue-500-hsl: 213.297 calc(var(--saturation-factor, 1) * 100%) 35.686%; + --blue-530: hsl(var(--blue-530-hsl) / 1); + --blue-530-hsl: 212.129 calc(var(--saturation-factor, 1) * 100%) 30.392%; + --blue-560: hsl(var(--blue-560-hsl) / 1); + --blue-560-hsl: 211.765 calc(var(--saturation-factor, 1) * 100%) 26.667%; + --blue-600: hsl(var(--blue-600-hsl) / 1); + --blue-600-hsl: 211.017 calc(var(--saturation-factor, 1) * 100%) 23.137%; + --blue-630: hsl(var(--blue-630-hsl) / 1); + --blue-630-hsl: 211.132 calc(var(--saturation-factor, 1) * 100%) 20.784%; + --blue-660: hsl(var(--blue-660-hsl) / 1); + --blue-660-hsl: 211.579 calc(var(--saturation-factor, 1) * 100%) 18.627%; + --blue-700: hsl(var(--blue-700-hsl) / 1); + --blue-700-hsl: 211.765 calc(var(--saturation-factor, 1) * 100%) 16.667%; + --blue-730: hsl(var(--blue-730-hsl) / 1); + --blue-730-hsl: 210.833 calc(var(--saturation-factor, 1) * 100%) 14.118%; + --blue-760: hsl(var(--blue-760-hsl) / 1); + --blue-760-hsl: 211.429 calc(var(--saturation-factor, 1) * 100%) 12.353%; + --blue-800: hsl(var(--blue-800-hsl) / 1); + --blue-800-hsl: 211.111 calc(var(--saturation-factor, 1) * 100%) 10.588%; + --blue-830: hsl(var(--blue-830-hsl) / 1); + --blue-830-hsl: 212.5 calc(var(--saturation-factor, 1) * 100%) 9.412%; + --blue-860: hsl(var(--blue-860-hsl) / 1); + --blue-860-hsl: 213.488 calc(var(--saturation-factor, 1) * 100%) 8.431%; + --blue-900: hsl(var(--blue-900-hsl) / 1); + --blue-900-hsl: 213.333 calc(var(--saturation-factor, 1) * 100%) 7.059%; + --teal-100: hsl(var(--teal-100-hsl) / 1); + --teal-100-hsl: 193.333 calc(var(--saturation-factor, 1) * 69.231%) 97.451%; + --teal-130: hsl(var(--teal-130-hsl) / 1); + --teal-130-hsl: 192 calc(var(--saturation-factor, 1) * 83.333%) 95.294%; + --teal-160: hsl(var(--teal-160-hsl) / 1); + --teal-160-hsl: 190.5 calc(var(--saturation-factor, 1) * 83.333%) 90.588%; + --teal-200: hsl(var(--teal-200-hsl) / 1); + --teal-200-hsl: 188.333 calc(var(--saturation-factor, 1) * 85.714%) 83.529%; + --teal-230: hsl(var(--teal-230-hsl) / 1); + --teal-230-hsl: 187.934 calc(var(--saturation-factor, 1) * 88.321%) 73.137%; + --teal-260: hsl(var(--teal-260-hsl) / 1); + --teal-260-hsl: 188.276 calc(var(--saturation-factor, 1) * 81.921%) 65.294%; + --teal-300: hsl(var(--teal-300-hsl) / 1); + --teal-300-hsl: 188.903 calc(var(--saturation-factor, 1) * 72.77%) 58.235%; + --teal-330: hsl(var(--teal-330-hsl) / 1); + --teal-330-hsl: 189.375 calc(var(--saturation-factor, 1) * 65.574%) 52.157%; + --teal-345: hsl(var(--teal-345-hsl) / 1); + --teal-345-hsl: 189.677 calc(var(--saturation-factor, 1) * 62.753%) 48.431%; + --teal-360: hsl(var(--teal-360-hsl) / 1); + --teal-360-hsl: 189.718 calc(var(--saturation-factor, 1) * 63.964%) 43.529%; + --teal-400: hsl(var(--teal-400-hsl) / 1); + --teal-400-hsl: 189.6 calc(var(--saturation-factor, 1) * 63.452%) 38.627%; + --teal-430: hsl(var(--teal-430-hsl) / 1); + --teal-430-hsl: 190.459 calc(var(--saturation-factor, 1) * 63.006%) 33.922%; + --teal-460: hsl(var(--teal-460-hsl) / 1); + --teal-460-hsl: 190.515 calc(var(--saturation-factor, 1) * 64.238%) 29.608%; + --teal-500: hsl(var(--teal-500-hsl) / 1); + --teal-500-hsl: 190.465 calc(var(--saturation-factor, 1) * 65.152%) 25.882%; + --teal-530: hsl(var(--teal-530-hsl) / 1); + --teal-530-hsl: 190.263 calc(var(--saturation-factor, 1) * 66.667%) 22.353%; + --teal-560: hsl(var(--teal-560-hsl) / 1); + --teal-560-hsl: 189.565 calc(var(--saturation-factor, 1) * 69.697%) 19.412%; + --teal-600: hsl(var(--teal-600-hsl) / 1); + --teal-600-hsl: 188.852 calc(var(--saturation-factor, 1) * 71.765%) 16.667%; + --teal-630: hsl(var(--teal-630-hsl) / 1); + --teal-630-hsl: 189.643 calc(var(--saturation-factor, 1) * 73.684%) 14.902%; + --teal-660: hsl(var(--teal-660-hsl) / 1); + --teal-660-hsl: 188.4 calc(var(--saturation-factor, 1) * 75.758%) 12.941%; + --teal-700: hsl(var(--teal-700-hsl) / 1); + --teal-700-hsl: 189.13 calc(var(--saturation-factor, 1) * 79.31%) 11.373%; + --teal-730: hsl(var(--teal-730-hsl) / 1); + --teal-730-hsl: 189 calc(var(--saturation-factor, 1) * 80%) 9.804%; + --teal-760: hsl(var(--teal-760-hsl) / 1); + --teal-760-hsl: 188.571 calc(var(--saturation-factor, 1) * 81.395%) 8.431%; + --teal-800: hsl(var(--teal-800-hsl) / 1); + --teal-800-hsl: 188 calc(var(--saturation-factor, 1) * 83.333%) 7.059%; + --teal-830: hsl(var(--teal-830-hsl) / 1); + --teal-830-hsl: 188.889 calc(var(--saturation-factor, 1) * 87.097%) 6.078%; + --teal-860: hsl(var(--teal-860-hsl) / 1); + --teal-860-hsl: 187.826 calc(var(--saturation-factor, 1) * 85.185%) 5.294%; + --teal-900: hsl(var(--teal-900-hsl) / 1); + --teal-900-hsl: 189 calc(var(--saturation-factor, 1) * 90.909%) 4.314%; + --white: hsl(var(--white-hsl) / 1); + --white-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-100: hsl(var(--white-100-hsl) / 1); + --white-100-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-130: hsl(var(--white-130-hsl) / 1); + --white-130-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-160: hsl(var(--white-160-hsl) / 1); + --white-160-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-200: hsl(var(--white-200-hsl) / 1); + --white-200-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-230: hsl(var(--white-230-hsl) / 1); + --white-230-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-260: hsl(var(--white-260-hsl) / 1); + --white-260-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-300: hsl(var(--white-300-hsl) / 1); + --white-300-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-330: hsl(var(--white-330-hsl) / 1); + --white-330-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-345: hsl(var(--white-345-hsl) / 1); + --white-345-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-360: hsl(var(--white-360-hsl) / 1); + --white-360-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-400: hsl(var(--white-400-hsl) / 1); + --white-400-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-430: hsl(var(--white-430-hsl) / 1); + --white-430-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-460: hsl(var(--white-460-hsl) / 1); + --white-460-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-500: hsl(var(--white-500-hsl) / 1); + --white-500-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 100%; + --white-530: hsl(var(--white-530-hsl) / 1); + --white-530-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 90.98%; + --white-560: hsl(var(--white-560-hsl) / 1); + --white-560-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 81.176%; + --white-600: hsl(var(--white-600-hsl) / 1); + --white-600-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 67.843%; + --white-630: hsl(var(--white-630-hsl) / 1); + --white-630-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 58.824%; + --white-660: hsl(var(--white-660-hsl) / 1); + --white-660-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 51.373%; + --white-700: hsl(var(--white-700-hsl) / 1); + --white-700-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 40%; + --white-730: hsl(var(--white-730-hsl) / 1); + --white-730-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 37.255%; + --white-760: hsl(var(--white-760-hsl) / 1); + --white-760-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 34.51%; + --white-800: hsl(var(--white-800-hsl) / 1); + --white-800-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 30.196%; + --white-830: hsl(var(--white-830-hsl) / 1); + --white-830-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 23.137%; + --white-860: hsl(var(--white-860-hsl) / 1); + --white-860-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 14.902%; + --white-900: hsl(var(--white-900-hsl) / 1); + --white-900-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 5.098%; + --black: hsl(var(--black-hsl) / 1); + --black-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-100: hsl(var(--black-100-hsl) / 1); + --black-100-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 94.902%; + --black-130: hsl(var(--black-130-hsl) / 1); + --black-130-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 90.98%; + --black-160: hsl(var(--black-160-hsl) / 1); + --black-160-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 85.49%; + --black-200: hsl(var(--black-200-hsl) / 1); + --black-200-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 80%; + --black-230: hsl(var(--black-230-hsl) / 1); + --black-230-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 74.118%; + --black-260: hsl(var(--black-260-hsl) / 1); + --black-260-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 67.451%; + --black-300: hsl(var(--black-300-hsl) / 1); + --black-300-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 60%; + --black-330: hsl(var(--black-330-hsl) / 1); + --black-330-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 47.843%; + --black-345: hsl(var(--black-345-hsl) / 1); + --black-345-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 40%; + --black-360: hsl(var(--black-360-hsl) / 1); + --black-360-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 36.078%; + --black-400: hsl(var(--black-400-hsl) / 1); + --black-400-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 20%; + --black-430: hsl(var(--black-430-hsl) / 1); + --black-430-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 14.51%; + --black-460: hsl(var(--black-460-hsl) / 1); + --black-460-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 7.843%; + --black-500: hsl(var(--black-500-hsl) / 1); + --black-500-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-530: hsl(var(--black-530-hsl) / 1); + --black-530-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-560: hsl(var(--black-560-hsl) / 1); + --black-560-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-600: hsl(var(--black-600-hsl) / 1); + --black-600-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-630: hsl(var(--black-630-hsl) / 1); + --black-630-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-660: hsl(var(--black-660-hsl) / 1); + --black-660-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-700: hsl(var(--black-700-hsl) / 1); + --black-700-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-730: hsl(var(--black-730-hsl) / 1); + --black-730-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-760: hsl(var(--black-760-hsl) / 1); + --black-760-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-800: hsl(var(--black-800-hsl) / 1); + --black-800-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-830: hsl(var(--black-830-hsl) / 1); + --black-830-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-860: hsl(var(--black-860-hsl) / 1); + --black-860-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --black-900: hsl(var(--black-900-hsl) / 1); + --black-900-hsl: 0 calc(var(--saturation-factor, 1) * 0%) 0%; + --brand-100: hsl(var(--brand-100-hsl) / 1); + --brand-100-hsl: 240 calc(var(--saturation-factor, 1) * 77.778%) 98.235%; + --brand-130: hsl(var(--brand-130-hsl) / 1); + --brand-130-hsl: 235.714 calc(var(--saturation-factor, 1) * 87.5%) 96.863%; + --brand-160: hsl(var(--brand-160-hsl) / 1); + --brand-160-hsl: 234.545 calc(var(--saturation-factor, 1) * 84.615%) 94.902%; + --brand-200: hsl(var(--brand-200-hsl) / 1); + --brand-200-hsl: 236 calc(var(--saturation-factor, 1) * 83.333%) 92.941%; + --brand-230: hsl(var(--brand-230-hsl) / 1); + --brand-230-hsl: 235.5 calc(var(--saturation-factor, 1) * 86.957%) 90.98%; + --brand-260: hsl(var(--brand-260-hsl) / 1); + --brand-260-hsl: 235.2 calc(var(--saturation-factor, 1) * 86.207%) 88.627%; + --brand-300: hsl(var(--brand-300-hsl) / 1); + --brand-300-hsl: 235.161 calc(var(--saturation-factor, 1) * 86.111%) 85.882%; + --brand-330: hsl(var(--brand-330-hsl) / 1); + --brand-330-hsl: 234.75 calc(var(--saturation-factor, 1) * 85.106%) 81.569%; + --brand-345: hsl(var(--brand-345-hsl) / 1); + --brand-345-hsl: 234.783 calc(var(--saturation-factor, 1) * 85.185%) 78.824%; + --brand-360: hsl(var(--brand-360-hsl) / 1); + --brand-360-hsl: 235.152 calc(var(--saturation-factor, 1) * 86.087%) 77.451%; + --brand-400: hsl(var(--brand-400-hsl) / 1); + --brand-400-hsl: 234.677 calc(var(--saturation-factor, 1) * 86.111%) 71.765%; + --brand-430: hsl(var(--brand-430-hsl) / 1); + --brand-430-hsl: 235 calc(var(--saturation-factor, 1) * 85.714%) 69.804%; + --brand-460: hsl(var(--brand-460-hsl) / 1); + --brand-460-hsl: 234.93 calc(var(--saturation-factor, 1) * 85.542%) 67.451%; + --brand-500: hsl(var(--brand-500-hsl) / 1); + --brand-500-hsl: 234.935 calc(var(--saturation-factor, 1) * 85.556%) 64.706%; + --brand-530: hsl(var(--brand-530-hsl) / 1); + --brand-530-hsl: 234.857 calc(var(--saturation-factor, 1) * 66.667%) 58.824%; + --brand-560: hsl(var(--brand-560-hsl) / 1); + --brand-560-hsl: 234.72 calc(var(--saturation-factor, 1) * 51.44%) 52.353%; + --brand-600: hsl(var(--brand-600-hsl) / 1); + --brand-600-hsl: 234.857 calc(var(--saturation-factor, 1) * 46.667%) 44.118%; + --brand-630: hsl(var(--brand-630-hsl) / 1); + --brand-630-hsl: 235.385 calc(var(--saturation-factor, 1) * 46.667%) 38.235%; + --brand-660: hsl(var(--brand-660-hsl) / 1); + --brand-660-hsl: 234.75 calc(var(--saturation-factor, 1) * 47.059%) 33.333%; + --brand-700: hsl(var(--brand-700-hsl) / 1); + --brand-700-hsl: 235.161 calc(var(--saturation-factor, 1) * 46.97%) 25.882%; + --brand-730: hsl(var(--brand-730-hsl) / 1); + --brand-730-hsl: 234.828 calc(var(--saturation-factor, 1) * 46.774%) 24.314%; + --brand-760: hsl(var(--brand-760-hsl) / 1); + --brand-760-hsl: 234.34 calc(var(--saturation-factor, 1) * 46.903%) 22.157%; + --brand-800: hsl(var(--brand-800-hsl) / 1); + --brand-800-hsl: 234.894 calc(var(--saturation-factor, 1) * 47.475%) 19.412%; + --brand-830: hsl(var(--brand-830-hsl) / 1); + --brand-830-hsl: 235 calc(var(--saturation-factor, 1) * 47.368%) 14.902%; + --brand-860: hsl(var(--brand-860-hsl) / 1); + --brand-860-hsl: 234.783 calc(var(--saturation-factor, 1) * 46.939%) 9.608%; + --brand-900: hsl(var(--brand-900-hsl) / 1); + --brand-900-hsl: 232.5 calc(var(--saturation-factor, 1) * 50%) 3.137%; +} \ No newline at end of file diff --git a/discord/vale/config/vocabularies/Discord/accept.txt b/discord/vale/config/vocabularies/Discord/accept.txt new file mode 100644 index 0000000000..62fee0dd27 --- /dev/null +++ b/discord/vale/config/vocabularies/Discord/accept.txt @@ -0,0 +1,288 @@ +_asset +_banner +_event +_id +ACKs +afk +anonymized +API's +api +applicationId +application_id +app_id +Arctis +Astley +audioinput +authed +Autoincrementing +authentication +backoff +badging +bigint +bitfield +bitfields +bitmask +bitrate +Bitrate +bitwise +Bitwise +Blurple +boolean +booleans +bot's +breakpoint +bytearray +bynweekday +byweekday +clientId +cloudflared +cmd +comms +composable +cooldown +Ciphertext +ciphertext +crosspost +Crosspost +crossposted +cron +crypto +CSRCs +CTAs +Currated +currentSize +custom_id +cya +datapoint +datapoints +deadbeef +deallocated +Deeplink +deduplicated +Deletable +deserialize +devs +Devs +dialogs +deauthorized +deduped +discordSdk +discrim +DLLs +DMs +dms +DMing +dnd +downsample +dropoff +endian +Enums +enums +entity_type +embed's +evt +expirations +extensibly +falsy +Figma +Fitbit +Fraggers +framerate +FString +gameplay +gank +GDMs +gif +GIFs +gifs +gifv +globbing +guild_id +hardcode +Heartbeating +heya +heyo +hoges +hostname +Hypesquad +ie +image_id +incentivized +Incentivized +Inclusivity +inexhaustive +influencer +influencers +instanceId +interaction_token +invitable +inviter +ip +joinSecret +keypair +largeImage +largeText +laterz +launchable +leaderboard +leaderboards +Lotties +Lua +lurkable +maxSize +mediaUrl +mentionables +message_id +modmins +ngrok +ngrok's +nsfw +nullable +Nullable +num_shards +Ogg +ogg +onwards +param +passthrough +pdb +performant +permissioning +pid +pingable +Playstation +prepending +primary_color +proxied +pseudocode +qos +querystring +rasterized +realtime +reapprove +redistributable +redstone +referrer_id +renderable +repurpose +requester's +requestor +Sandscape +sandboxed +scrollback +SDKs +shaders +shard_id +Sharding +sharding +shareLink +signum +signalHandler +Sku +sku +sku_ids +skus +SKUs +slowmode +smallImage +smallText +social_layer +Soundshare +spectateSecret +Spotify +Stahp +steamId +Steamworks +Struture +Subcommand +subcommand +Subcommands +subcommands +submenu +Substring +substrings +Supa +templated +threadsafe +toolbelt +toolkits +tooltips +touchpoints +tts +twitchusername +uhm +ulong +unarchive +Unarchiving +unarchiving +unarchived +Unencrypted +unencrypted +unicode +Unlinking +unlinks +Unmerging +unmerge +unmerging +Unpublish +unpublish +Unpublishing +unpublishing +unprocessable +unranked +Unrenderable +Unsyncing +unsynced +untappable +upsell +upsells +upserts +URIs +urls +user_id +userId +UUIDs +UIs +version_number +videoinput +VMs +Vulkan +walkthrough +Walkthrough +webserver +websockets +Woah +Wordmark +wordmark +wss +Wumpus +wumpus +Xbox +youtube +zlib +zombied +zstd +command_id +channel_id +prepended +glibc +timeframe +rollout +whitepaper +Whitepaper +deeplinking +deauthorization +distros +starter_pack +guildId +unmerges +unmerged +application_asset_id +dateutil +setActivity +ratelimit +audiooutput +startTimestamp +partySize +partyMax +Wump \ No newline at end of file diff --git a/discord/vale/config/vocabularies/Mintlify/accept.txt b/discord/vale/config/vocabularies/Mintlify/accept.txt new file mode 100644 index 0000000000..8799898d5a --- /dev/null +++ b/discord/vale/config/vocabularies/Mintlify/accept.txt @@ -0,0 +1,267 @@ +Mintlify +mintlify +VSCode +openapi +OpenAPI +Github +APIs + +repo +npm +dev + +Lorem +ipsum +impsum +amet + +const +myName +myObject +bearerAuth +favicon +topbar +url +borderRadius +args +modeToggle +ModeToggle +isHidden +autoplay + +_italic_ +Strikethrough +Blockquotes +Blockquote +Singleline +Multiline + +onboarding + +async +await +boolean +enum +func +impl +init +instanceof +typeof +params +stdin +stdout +stderr +stdout +stdin +var +const +let +null +undefined +struct +bool + +cors +csrf +env +xhr +xhr2 +jwt +oauth +websocket +localhost +middleware +runtime +webhook +stdin +stdout + +json +yaml +yml +md +txt +tsx +jsx +css +scss +html +png +jpg +svg + +cdn +cli +css +dom +dto +env +git +gui +http +https +ide +jvm +mvc +orm +rpc +sdk +sql +ssh +ssl +tcp +tls +uri +url +ux +ui + +nodejs +npm +yarn +pnpm +eslint +pytest +golang +rustc +kubectl +mongo +postgres +redis + +JavaScript +TypeScript +Python +Ruby +Rust +Go +Golang +Java +Kotlin +Swift +Node.js +NodeJS +Deno + +React +Vue +Angular +Next.js +Nuxt +Express +Django +Flask +Spring +Laravel +Redux +Vuex +TensorFlow +PostgreSQL +MongoDB +Redis +PNPM + +Docker +Kubernetes +AWS +Azure +GCP +Terraform +Jenkins +CircleCI +GitLab +Heroku + +Git +git +GitHub +GitLab +Bitbucket +VSCode +Visual Studio Code +IntelliJ +WebStorm +ESLint +eslint +Prettier +prettier +Webpack +webpack +Vite +vite +Babel +babel +Jest +jest +Mocha +Cypress +Postman + +HTTP +HTTPS +OAuth +JWT +GraphQL +REST +WebSocket +TCP/IP + +NPM +Yarn +PNPM +Pip +PIP +Cargo +RubyGems + +Swagger +OpenAPI +Markdown +MDX +Storybook +TypeDoc +JSDoc + +MySQL +PostgreSQL +MongoDB +Redis +Elasticsearch +DynamoDB + +Linux +Unix +macOS +iOS + +Firefox +Chromium +WebKit + +config +ctx +desc +dir +elem +err +len +msg +num +obj +prev +proc +ptr +req +res +str +tmp +val +vars + +todo +href +lang +nav +prev +next +toc diff --git a/docs/Change_Log.md b/docs/Change_Log.md deleted file mode 100644 index 5e56f8edfe..0000000000 --- a/docs/Change_Log.md +++ /dev/null @@ -1,706 +0,0 @@ -# Change Log - -## Default Sort Order for Forum Channels - -#### Sep 22, 2022 - -`default_sort_order` is an optional field in the [channel object](#DOCS_RESOURCES_CHANNEL) that indicates how the threads in a [forum channel](#DOCS_TOPICS_THREADS/forums) will be sorted for users by default. Setting `default_sort_order` requires the `MANAGE_CHANNELS` permission. - -If `default_sort_order` hasn't been set, its value will be `null`. - -## AutoMod Spam and Mention Spam Trigger Types - -#### Sep 21, 2022 - -Two new [trigger types](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-types) were added to AutoMod: - -- `MENTION_SPAM` blocks messages that mention more than a set number of unique server members or roles. Apps can define the number (up to 50) using the `mention_total_limit` field in the [trigger metadata object](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-metadata) when creating or updating an Auto Moderation rule. -- `SPAM` blocks links and messages that are identified as spam. - -More information can be found in the [AutoMod documentation](#DOCS_RESOURCES_AUTO_MODERATION). - -## Forum Channels Release - -#### Sep 14, 2022 - -Forum channels ([`GUILD_FORUM` or `15`](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types)) have been released to all community servers. `GUILD_FORUM` channels are a new channel type that only supports threads, which display differently than in text (`GUILD_TEXT`) channels. - -Check out the [forums topic](#DOCS_TOPICS_THREADS/forums) for more information on the relevant APIs and technical details, and the [Forums FAQ](https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ#h_01G69FJQWTWN88HFEHK7Z6X79N) for more about the feature. - -## Message Content is a Privileged Intent - -#### Sep 1, 2022 - -> danger -> This entry includes breaking changes - -As of today, [message content](#DOCS_TOPICS_GATEWAY/message-content-intent) is a privileged intent for all verified apps *and* apps eligible for verification. More details about why it's becoming a privileged intent and how to apply for it is in the [Help Center FAQ](https://support-dev.discord.com/hc/articles/4404772028055-Message-Content-Privileged-Intent-FAQ). - -Any app that does not have the message content intent configured in its app's settings within the Developer Portal wiIl receive empty values in fields that expose message content across Discord's APIs (including the `content`, `embeds`, `attachments`, and `components` fields). These restrictions do not apply for messages that a bot or app sends, in DMs that it receives, or in messages in which it is mentioned. - -#### If your app is verified - -Verified apps and verification-eligible apps must be approved for the message content intent to receive message content. If your verified app isn’t approved, or doesn’t account for the new message content restrictions, it will break for users. - -##### Temporary Message Content Intent - -Verified apps or apps that have submitted for verification can temporarily opt-in to a grace period which will allow your app to continue receiving message content until October 1. However, if you opt-in to the grace period, your app will be prevented from joining any additional servers until you opt-out. More details are in the [Help Center article](https://support-dev.discord.com/hc/en-us/articles/8561391080471). - -#### If your app is unverified -Unverified apps must still must enable the intent in your app’s settings within the Developer Portal. - -Existing unverified apps will automatically have the message content intent toggled on in their settings. New unverified apps will have to manually toggle the intent in the Developer Portal. - -## Slash Command Mentions - -#### Aug 22, 2022 - -This week, [Slash Command mentions](#DOCS_REFERENCE/message-formatting) are rolling out across all Discord clients (for Android, mentions are limited to the [React Native client](https://discord.com/blog/android-react-native-framework-update)). Clicking a Slash Command mention will auto-populate the command in the user's message input. - -Slash Command mentions use the following format: ``. You can also use `` and `` for subcommands and subcommand groups. - -## Session-specific Gateway Resume URLs - -#### Aug 9, 2022 - -> warn -> Starting on **September 12, 2022**, apps that aren’t using the new `resume_gateway_url` field to resume gateway sessions will be disconnected significantly faster than normal. - -A new `resume_gateway_url` field has been added to the [Ready](#DOCS_TOPICS_GATEWAY_EVENTS/ready) gateway event to support session-specific gateway connections. The value of `resume_gateway_url` is a session-specific URL that should be used when resuming the gateway session after a disconnect. Previously, `wss://gateway.discord.gg` was used to connect *and* resume sessions, but should now only be used during the connection. - -At the moment, the value of `resume_gateway_url` will always be `wss://gateway.discord.gg` to give developers more time to adopt the new field. In the near future, the value will change to the session-specific URLs. - -## Upcoming Permissions Change to Webhook Routes - -#### July 13, 2022 - -On August 8th, 2022 we will begin requiring the `VIEW_CHANNEL (1 << 10)` permission for webhook routes which require `MANAGE_WEBHOOKS (1 << 29)`, to align with our documented behavior. We don't expect that many applications will be affected by this, but in case you are, please ensure you have updated permissions needed for accessing the following routes: - -- [`GET /webhooks/{webhook.id}`](#DOCS_RESOURCES_WEBHOOK/get-webhook) -- [`DELETE /webhooks/{webhook.id}`](#DOCS_RESOURCES_WEBHOOK/delete-webhook) -- [`PATCH /webhooks/{webhook.id}`](#DOCS_RESOURCES_WEBHOOK/modify-webhook) -- [`GET /channels/{channel.id}/webhooks`](#DOCS_RESOURCES_WEBHOOK/get-channel-webhooks) -- [`POST /channels/{channel.id}/webhooks`](#DOCS_RESOURCES_WEBHOOK/create-webhook) - -## Min and Max Length for Command Options - -#### July 1, 2022 - -Application [command options](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) of type `STRING` now includes optional `min_length` and `max_length` fields to control the length of text a user can input. - -The value of `min_length` must be greater or equal to `0`, and the value of `max_length` must be greater or equal to `1`. - -## Add Subcommand Groups and Subcommands to Message Interaction Objects - -> danger -> This entry includes breaking changes - -#### July 1, 2022 - -While this is a breaking change, most apps only rely on interaction responses (`INTERACTION_CREATE`), *not* message interaction objects (`MESSAGE_CREATE`). [Interaction responses](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/message-interaction-object/interaction-object-interaction-data) are unaffected by this change. - -#### Upcoming Changes - -Starting **July 18, 2022**, the `name` field for [message interaction objects](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/message-interaction-object) will now include subcommands and subcommand groups in the value (along with the existing top-level command). In the future, we recommend not relying on this message interaction field. - -The format of the value will be the different command levels (if they exist), separated by spaces: -` ` - -The `name` field is only seen on messages that are a response to an interaction without an existing message, so interaction objects for message components don’t include this field. - -#### Updating your app - -Most apps only rely on interaction responses, not message interaction objects. - -We don't recommend that your app relies on the `name` field for message interactions objects, but if it does you should update your app to handle subcommands and subcommand groups that your app may encounter. - -As an example of the change, pretend your app had a command `/role` with subcommands `add` and `remove`. Currently, the `name` field in the original interaction payload would contain `role`. If you responded to that interaction with a message then fetched its contents, the `name` field for that message interaction object would contain `role` as well. - -After this change, the `name` field for the original interaction payload will still contain `role`. However, now if you responded to that interaction with a message then fetched its contents, the `name` field for that message interaction object would contain `role add` or `role remove`. - -## Changes to Bot Permissions for Interactions and Webhooks - -> danger -> This entry includes breaking changes - -#### Jun 29, 2022 - -#### Upcoming Changes - -> warn -> `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` are the only permissions that will be affected by this change. In a previous version of this changelog, it was indicated that `ATTACH_FILES` and `EMBED_LINKS` would be affected but this is no longer the case. - -Starting **August 3, 2022**, the way some of a bot's `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` [permissions](#DOCS_TOPICS_PERMISSIONS/permissions) are calculated is changing in two cases: -- When **responding to an [interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING)** (like application commands or message components) -- When **executing a [webhook](#DOCS_RESOURCES_WEBHOOK) that the bot created** - -Going forward, in the above cases, a bot’s `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` and `USE_EXTERNAL_EMOJIS` permissions will be calculated based on the permissions its granted, *including* any [overwrites](#DOCS_TOPICS_PERMISSIONS/permission-overwrites). Previously, a bot’s permissions in these cases relied only on those granted to `@everyone`. - -This change *only* applies to bots. The permissions for an app without a bot user (or without the `bot` scope) will still depend on `@everyone`. - -#### Updating Your App - -If your bot wants to use the `MENTION_EVERYONE`, `SEND_TTS_MESSAGES` or `USE_EXTERNAL_EMOJIS` permissions when responding to interactions or executing a webhook, **ensure that the bot was installed (or explicitly granted) with them**. - -Note that even if your bot is installed with certain permissions, they can be changed using overwrites. For interactions, you can use the [`app_permissions` field](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-structure) to determine your app or bot's contextual permissions before replying. - -## Calculated Permissions in Interaction Payloads - -#### Jun 29, 2022 - -Interaction payloads now contain an `app_permissions` field whose value is the computed [permissions](#DOCS_TOPICS_PERMISSIONS/permissions-bitwise-permission-flags) for a bot or app in the context of a specific interaction (including any channel overwrites). Similar to other permission fields, the value of `app_permissions` is a bitwise OR-ed set of permissions expressed as a string. Read details in the [interactions documentation](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object). - -For apps without a bot user (or without the `bot` scope), the value of `app_permissions` will be the same as the permissions for `@everyone`, but limited to the permissions that can be used in interaction responses (currently `ATTACH_FILES`, `EMBED_LINKS`, `MENTION_EVERYONE`, and `USE_EXTERNAL_EMOJIS`). - - -## Message Content in AutoMod events - -#### Jun 21, 2022 - -#### Breaking Changes - -In API v10, the `MESSAGE_CONTENT` (`1 << 15`) intent is now required to receive non-empty values for the `content` and `matched_content` fields in [`AUTO_MODERATION_ACTION_EXECUTION`](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-action-execution) gateway events. This matches the intended behavior for message content across the API. - -## Updated Connection Property Field Names - -#### Jun 17, 2022 - -The `$` prefix in [identify connection properties](#DOCS_TOPICS_GATEWAY_EVENTS/identify-identify-connection-properties) are deprecated. The new field names are `os`, `browser`, and `device`. When passed, the `$`-prefixed names will resolve to the new ones. - -In API v11, support for the previous field names (`$os`, `$browser`, and `$device`) will be removed. - -## Auto Moderation - -#### Jun 16, 2022 - -Add new [Auto Moderation feature](#DOCS_RESOURCES_AUTO_MODERATION) which enables guilds to moderate message content based on keywords, harmful links, and unwanted spam. This change includes: -- New endpoints for [creating](#DOCS_RESOURCES_AUTO_MODERATION/create-auto-moderation-rule), [updating](#DOCS_RESOURCES_AUTO_MODERATION/modify-auto-moderation-rule), and [deleting](#DOCS_RESOURCES_AUTO_MODERATION/delete-auto-moderation-rule) Auto Moderation rules -- New gateway events emitted when Auto Moderation rules are [created](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-create) (`AUTO_MODERATION_RULE_CREATE`), [updated](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-update) (`AUTO_MODERATION_RULE_UPDATE `), and [deleted](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-delete) (`AUTO_MODERATION_RULE_DELETE `). Requires the `AUTO_MODERATION_CONFIGURATION` (`1 << 20`) intent -- New gateway event emitted when an [action is executed](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-action-execution) (`AUTO_MODERATION_ACTION_EXECUTION`). Requires the `AUTO_MODERATION_EXECUTION` (`1 << 21`) intent -- New [audit log entries](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object-audit-log-events) when rules are created (`AUTO_MODERATION_RULE_CREATE`), updated (`AUTO_MODERATION_RULE_UPDATE`), or deleted (`AUTO_MODERATION_RULE_DELETE`), or when AutoMod performs an action (`AUTO_MODERATION_BLOCK_MESSAGE`) - -## Updated Command Permissions - -#### Apr 27, 2022 - -Application command permissions have been updated to add more granular control and access to commands in Discord. You can read the major changes below, and [the updated documentation](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/permissions) for details. - -#### Breaking changes - -- Bearer tokens are now required to edit command permissions. Bearer tokens are tokens tied to an authenticating user's permissions, and can be [retrieved using OAuth](#DOCS_TOPICS_OAUTH2). The user must have permission to manage the guild and roles. -- [`applications.commands.permissions.update`](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes) scope was added as a requirement to edit command permissions. -- Disabled the batch editing endpoint ([`PUT /applications/{application.id}/guilds/{guild.id}/commands/permissions`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/batch-edit-application-command-permissions)). - -#### Other changes - -- Created a [`CHANNEL` command permission type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permission-type) -- Increase permission limit from `10` to `100` -- [constant (`guild_id - 1`)](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permissions-constants) to represent all channels in command permissions -- Added `default_member_permissions` field, which is a bitwise OR-ed set of [permissions](#DOCS_TOPICS_PERMISSIONS/permissions-bitwise-permission-flags), expressed as a string. This replaces the `default_permission` field, which will soon be deprecated. -- Added `dm_permission`, which is a boolean flag used to indicate whether a command is available in DMs (only for global application commands). If no value is passed, the global command will be visible in DMs. -- Added `APPLICATION_COMMAND_PERMISSIONS_UPDATE` [gateway](#DOCS_TOPICS_GATEWAY_EVENTS/application-command-permissions-update) event and `APPLICATION_COMMAND_PERMISSION_UPDATE` [audit log](#DOCS_RESOURCES_AUDIT_LOG) event. - -## Forum Channels - -#### Apr 06, 2022 - -Added new channel type, `GUILD_FORUM` (15). A `GUILD_FORUM` channel is an unreleased feature that is very similar (from an API perspective) to a `GUILD_TEXT` channel, except only threads can be created in that channel; messages cannot be sent directly in that channel. Check out the [forums topic](#DOCS_TOPICS_THREADS/forums) for more information. - -## Guild Bans Pagination - -#### Mar 31, 2022 - -The `GET /guilds/{guild.id}/bans` endpoint has been migrated to require pagination to improve reliability and stability. Check out the [endpoint docs](#DOCS_RESOURCES_GUILD/get-guild-bans) for more information. - -## API v10 - -#### Feb 14, 2022 - -- API v8 is now deprecated. -- `GET /channels/{channel.id}/threads/active` is decommissioned in favor of [`GET /guilds/{guild.id}/threads/active`](#DOCS_RESOURCES_GUILD/list-active-guild-threads). -- Starting in v10, you must specify the message content intent (`1 << 15`) to receive content-related fields in message dispatches. Read more in the [Gateway Intents documentation](#DOCS_TOPICS_GATEWAY/gateway-intents). -- To specify a reason for an administrative action in audit logs, apps must now pass the `X-Audit-Log-Reason` header rather than the `reason` parameter for all endpoints. Read more in the [Audit Logs documentation](#DOCS_RESOURCES_AUDIT_LOG). -- Message routes (like [`POST /channels/{channel.id}/messages`](#DOCS_RESOURCES_CHANNEL/create-message)) now use the `embeds` field (an array of embed objects) instead of `embed`. -- The `summary` field for [applications](#DOCS_RESOURCES_APPLICATION) now returns an empty string for all API versions. -- The `name` and `description` fields for [Achievements](#DOCS_GAME_SDK_ACHIEVEMENTS/data-models-achievement-struct) are now strings, and localization info is now passed in new `name_localizations` and `description_localizations` dictionaries. This change standardizes localization to match [Application Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/localization). Read details in the [Achievements documentation](#DOCS_GAME_SDK_ACHIEVEMENTS/data-models-achievement-struct). -- Existing attachments must be specified when [`PATCH`ing messages with new attachments](#DOCS_REFERENCE/editing-message-attachments). Any attachments not specified will be removed and replaced with the specified list -- Requests to v10 and higher will no longer be supported on `discordapp.com` (this does **not** affect `cdn.discordapp.com`) - -#### Upcoming changes - -- API v6 and v7 will be decommissioned **in early 2023** -- `MESSAGE_CONTENT` is becoming a privileged intent for verified bots in 75+ servers **on August 31, 2022**. Read details in [the FAQ](https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Privileged-Intent-FAQ) or follow [the guide](#DOCS_TUTORIALS_UPGRADING_TO_APPLICATION_COMMANDS) on updating your app. -- The `summary` field for applications will be removed in the next API version (v11) - -## Interaction Modals and Application Command Attachment Option Type - -#### Feb 8, 2022 - -Interaction modals are now available, allowing applications to prompt users for further detailed input. Check out [the modal docs](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-modal) for more information. - -Application Commands can now add an attachment option type. See [the option type table](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-type) for more information. - -## Guild Member Timeouts - -#### Dec 20, 2021 - -Add new documentation for the recently released guild member timeout feature. - -## Guild Scheduled Events - -#### Nov 23, 2021 - -- Add official support for `guild_scheduled_events` field on `Guild` resource sent with `GUILD_CREATE` event - -#### Nov 18, 2021 - -- Breaking change for return type for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}/users` -- Add `with_user_count` query param for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}` -- Return additional `creator` field by default in response for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}` -- More details and clarification for the guild scheduled events feature. -- Document support for `before` and `after` query params for `GET /guilds/{guild.id}/scheduled-events/{guild_scheduled_event.id}/users` - -#### Nov 15, 2021 - -Add new documentation for the recently released Guild Scheduled Events feature. - -## Application Command Autocomplete Interactions - -#### October 27, 2021 - -Autocomplete interactions are now available, allowing application commands to provide server completed options. Check out [the autocomplete interaction docs](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/autocomplete) for more information. - -## Updated Thread Permissions - -#### September 16, 2021 - -Thread permissions have been updated and simplified: - -- "Use Public Threads" is now "Create Public Threads", which allows users to create public threads and announcement threads in a channel, even if they cannot send messages in that channel. -- "Use Private Threads" is now "Create Private Threads", which allows users to create private threads in a channel, even if they cannot send messages in that channel. - -A new permission has also been added: - -- "Send Messages in Threads", which allows users to send a message in a thread. The "Send Messages" permission has no effect in threads: users **must** have "Send Messages in Threads" to send a message in a thread. This allows for setups where a user can participate in a thread but cannot send a message in the parent channel (like a thread on an announcement post). - -## User and Message Commands - -#### August 10, 2021 - -[User commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/user-commands) and [message commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/message-commands) are now live! These commands appear on context menus for users and messages, with more to come in the future. - -Context menu commands are a type of application command. The "Slash Commands" documentation page has been renamed to "Application Commands" and split out by type to show this. - -## Select Menu Components - -#### June 30, 2021 - -Select Menus are now part of the components API! They're the greatest thing since the invention of buttons yesterday. Select menus allow you to offer users a choice of one or many options in a friendly UI-based way. - -Select menus can be used like other [message components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/). Learn all the specifics in the [documentation](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/select-menus). - -## Support for Multiple Embeds in Message Routes - -#### June 10, 2021 - -Message routes now accept an embeds array in addition to the existing embed field. Bots can now send up to 10 embeds per message, to be consistent with webhook behavior. The existing embed field is considered deprecated and will be removed in the next API version. - -## Buttons and Message Components - -#### May 26, 2021 - -Message components are now available with our first two components: a layout-based `ActionRow` and...buttons! - -You can now include buttons on messages sent by your app, whether they're bot messages or responses to interactions. [Learn more about message components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/). - -The addition of message components means new fields and response types: - -- An optional `components` field has been added to the [message object](#DOCS_RESOURCES_CHANNEL/message-object) -- New response types `6` and `7` have been added for [interaction responses](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-interaction-callback-type), valid only for component-based interactions - -## API v9 - -#### April 28, 2021 - -API v9 is now available. - -API v9 includes support for [threads](#DOCS_TOPICS_THREADS), an upcoming feature. Older API versions will not receive any Gateway Events for threads, so it is important to update soon! We've prepared a [migration guide](#DOCS_TOPICS_THREADS) to help make the upgrade process very straightforward. - -This documentation is being published early so bots can have at least two months to upgrade before threads launch. - -Additionally, API v9 also removes the `/channels/:id/messages/:id/suppress-embeds` route. - - -## Application Command Permissions - -#### April 5, 2021 - -Need to keep some of your commands safe from prying eyes, or only available to the right people? Commands now support [command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/permissions)! - -You can enable or disable a command (guild or global) for a specific user or role in a guild. For now, users will still be able to see the commands, but won't be able to use them. - -New routes have been added to support this functionality: - -- [`GET Guild Application Command Permissions`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/get-guild-application-command-permissions) -- [`GET Application Command Permissions`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/get-application-command-permissions) -- [`PUT Application Command Permissions`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/batch-edit-application-command-permissions) - -A `default_permission` field has also been added to the [ApplicationCommand](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-structure) model. This field allows you to disable commands for everyone in a guild by default, if you prefer to make some of your commands an opt-in experience. - -## Large Bot Sharding Lowered to 150,000 Guilds - -#### March 15, 2021 - -There have been reports that sessions have higher frequency of errors when starting if a bot has joined too many guilds (the gateway connection times out). To account for this we have lowered the requirement for large bot sharding down to 150,000 guilds in order to improve reliability. - -## Changes to Slash Command Response Types and Flags - -#### March 5, 2021 - -Changes to interaction response types have been made to support better designs for application commands: - -- Type `2` `Acknowledge` has been deprecated -- Type `3` `ChannelMessage` has been deprecated -- Type `5` `AcknowledgeWithSource` has been renamed to `DeferredChannelMessageWithSource` - -These deprecated types will be removed and break on **April 9, 2021**. - -Additionally, `flags` has been documented on [InteractionApplicationCommandCallbackData](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-interaction-callback-data-structure). Setting `flags` to `64` will make the interaction response ephemeral. - -## Slash Commands in DMs - -#### February 9, 2021 - -Slash Commands are now supported in DMs with bots. Due to this change, some of the fields on the [Interaction object](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-structure) have been made optional. Newly optional fields don't reflect any behavior changes in Slash Commands within guilds; they are to support commands in the context of a DM only. - -## Change to Permission Checking when Creating Channels - -#### January 22, 2021 - -Permission overwrites in the guild channel creation endpoint are now validated against the permissions your bot has in the guild. Permission overwrites specified in the request body when creating guild channels will now require your bot to also have the permissions being applied. Setting `MANAGE_ROLES` permission in channel overwrites is only possible for guild administrators or users with `MANAGE_ROLES` as a permission overwrite in the channel. - -## Slash Commands and Interactions - -#### December 15, 2020 - -Slash Commands are here! There's a _lot_ to cover, so go check out specific documentation under [Slash Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/). - -Slash Commands include some new features for webhooks as well: - -- Webhooks can now update previously-sent messages from the same webhook using [Edit Webhook Message](#DOCS_RESOURCES_WEBHOOK/edit-webhook-message) and [Delete Webhook Message](#DOCS_RESOURCES_WEBHOOK/delete-webhook-message) - -This PR also documents the `application` field on the `READY` gateway event, which is a partial [application object](#DOCS_RESOURCES_APPLICATION/application-object) containing `id` and `flags`. - -## Inline Replies - -#### November 16, 2020 - -Inline Replies have been added to our documentation. They behave differently in v6 and v8, so be cautious in your implementation: - -- Inline replies are type `19` in v8, but remain type `0` in v6 -- You can now add a `message_reference` on message create to create a reply -- A new field `referenced_message` has been added to the [Message Object](#DOCS_RESOURCES_CHANNEL/message-object) -- A new field `replied_user` has been added to the [Allowed Mentions Object](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) -- [Message Create](#DOCS_TOPICS_GATEWAY_EVENTS/message-create) gateway event is guaranteed to have a `referenced_message` if the message created is a reply. Otherwise, that field is not guaranteed. - -## Stickers - -#### November 13, 2020 - -Stickers are now documented as part of the [message](#DOCS_RESOURCES_CHANNEL/message-object) object. - -## Gateway v6 Intent Restrictions - -#### October 27, 2020 - -The v6 gateway now applies the restrictions for gateway intents. This means the new chunking limitations are now in effect, regardless of intents being used. See [Request Guild Members](#DOCS_TOPICS_GATEWAY_EVENTS/request-guild-members) for further details. -Additionally, if privileged intents are not enabled in the application dashboard the bot will not receive the events for those intents. - -All other intents are always enabled by default unless specified otherwise by the identify payload. We have made a support article to explain some of the changes and resulting issues with more details: [Gateway Update FAQ](https://dis.gd/gwupdate) - -## API and Gateway V8 - -#### September 24, 2020 - -We've introduced API and Gateway v8! Changes are noted throughout the documentation, and you can also read [this commit in our docs repo](https://github.com/discord/discord-api-docs/commit/545ff4a7883e5eee7ee91d19a5e5d760a0730033) for a full diff. - -The changes are: - -- API and Gateway v8 are now available. v6 is still the default for the time being. -- [Gateway Intents](#DOCS_TOPICS_GATEWAY/gateway-intents) are now required -- Removed `guild_subscriptions` in identify in favor of [Gateway Intents](#DOCS_TOPICS_GATEWAY/gateway-intents). -- All permissions have been converted to strings-serialized numbers. As such, `permissions_new`, `allow_new`, and `deny_new` have been removed -- The `game` field has been removed. If you need a direct replacement, you can instead reference the first element of `activities` -- Channel Permission Overwrite `type`s are now numbers (0 and 1) instead of strings ("role" and "member"). However due to a current technical constraint, they are string-serialized numbers in audit log `options`. -- `embed_enabled` and `embed_channel_id` have been removed. Use `widget_enabled` and `widget_channel_id` instead. -- Form body errors have been improved to include more helpful messaging on validation. [See more here](#DOCS_REFERENCE/error-messages) -- The `Retry-After` header value and `retry_after` body value is now based in seconds instead of milliseconds (e.g. `123` means 123 seconds) -- The `X-RateLimit-Precision` header is no longer respected. `X-RateLimit-Reset` and `X-RateLimit-Reset-After` are always returned at millisecond precision (e.g. `123.456` instead of `124`) -- Bots no longer receive [Channel Create Gateway Event](#DOCS_TOPICS_GATEWAY_EVENTS/channel-create) for DMs -- `delete-message-days` is no longer available. Use `delete_message_days`. -- Removed `roles`, `premium_since`, and `nick` from [Presence Update Gateway Event](#DOCS_TOPICS_GATEWAY_EVENTS/presence-update) -- Removed some [integration object](#DOCS_RESOURCES_GUILD/integration-object) fields for Discord application integrations -- Removed `include_applications` from [Get Guild Integrations](#DOCS_RESOURCES_GUILD/get-guild-integrations). Application integrations are always included. -- The following deprecated routes have been removed for better naming conventions: - -Removed in favor of `/guilds//widget`: - -- `/guilds//embed` - -Removed in favor of `/guilds//widget.json`: - -- `/servers//embed.json` -- `/servers//widget.json` -- `/guilds//embed.json` - -Removed in favor of `/guilds//widget.png`: - -- `/guilds//embed.png` - -Removed in favor of `/channels//messages/bulk-delete`: - -- `/channels//messages/bulk_delete/` - -Removed in favor of `/invites//`: - -- `/invite//` - -## New Permission Fields - -#### July 28, 2020 - -Documented `permissions_new`, `allow_new`, and `deny_new` as string-serialized permission bitfields. - -## Legacy Mention Behavior Deprecation - -#### May 11, 2020 - -The legacy mention behavior for bots is now removed, and granular control of mentions should use the [Allowed Mentions](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) API moving forwards. - -## New Properties on Guild Members Chunk Event - -#### April 24, 2020 - -The [Guild Members Chunk](#DOCS_TOPICS_GATEWAY_EVENTS/guild-members-chunk) gateway event now contains two properties: `chunk_index` and `chunk_count`. These values can be used to keep track of how many events you have left to receive in response to a [Request Guild Members](#DOCS_TOPICS_GATEWAY_EVENTS/request-guild-members) command. - -## New Allowed Mentions Object - -#### March 3, 2020 - -We've added a way to specify mentions in a more granular form. This change also begins the start of a 60 day deprecation cycle on legacy mention behavior. Read more: - -- [Allowed mentions object](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) - -## New Invite Events and Reactions Endpoint - -We've added a new endpoint for deleting all reactions of a specific emoji from a message, as well as some new invite and reaction gateway events. Read more: - -- [Delete All Reactions for Emoji](#DOCS_RESOURCES_CHANNEL/delete-all-reactions-for-emoji) -- [Invite Create](#DOCS_TOPICS_GATEWAY_EVENTS/invite-create) -- [Invite Delete](#DOCS_TOPICS_GATEWAY_EVENTS/invite-delete) -- [Message Reaction Remove Emoji](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-remove-emoji) - -## Rich Presence Spectate Approval - -#### February 26, 2020 - -The [Spectate](#DOCS_GAME_SDK_ACTIVITIES/onactivityspectate) functionality of Rich Presence no longer requires whitelisting or approval. - -## Gateway Intents - -#### February 14, 2020 - -We've added documentation around a brand new feature: [Gateway Intents!](#DOCS_TOPICS_GATEWAY/gateway-intents) Gateway Intents are a great way to specify which events you want to receive from our gateway. Go on, save yourself some bandwidth and CPU usage. - -Using Intents will change the behavior of some existing events and commands, so please refer to: - -- [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) -- [Request Guild Members](#DOCS_TOPICS_GATEWAY_EVENTS/request-guild-members) -- [Guild Member Add](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-add) -- [Guild Member Remove](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-remove) -- [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) -- [Presence Update](#DOCS_TOPICS_GATEWAY_EVENTS/presence-update) -- [List Guild Members](#DOCS_RESOURCES_GUILD/list-guild-members) - -## IP Discovery Updates - -#### December 6, 2019 - -Updated our [IP discovery message](#DOCS_TOPICS_VOICE_CONNECTIONS/ip-discovery). The old message is deprecated and will be removed in the future. - -## GameSDK Version 2.5.6 - -#### November 27, 2019 - -Fixed a bug from the 2.5.5 release that caused network handshakes to fail, resulting in no networking data being sent. The networking manager and integrated lobby networking should be full operational again after updating. - -## GameSDK Version 2.5.5 - -#### November 14, 2019 - -We've shipped some updates to the GameSDK, including support for Linux as well as the IL2CPP backend system for Unity. These changes also fixed a bug in the [`SetUserAchievement()`](#DOCS_GAME_SDK_ACHIEVEMENTS/setuserachievement) method. - -Get the latest at the top of the [Getting Started](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/step-1-get-the-thing) documentation. If you're looking for help interacting with the GameSDK or want to report a bug, join us on the [official Discord](https://discord.gg/discord-developers). - -## Changes to Special Channels - -#### August 22, 2019 - -News Channels are now changed to [Announcement Channels](#DOCS_GAME_AND_SERVER_MANAGEMENT_SPECIAL_CHANNELS/announcement-channels). Developer License owners will continue to get access to them (both existing and new). Underlying channel type (GUILD_NEWS = 5) remains the same. - -## More Precise Rate Limits - -#### August 12, 2019 - -You can now get more precise rate limit reset times, via a new request header. Check out the [rate limits](#DOCS_TOPICS_RATE_LIMITS/) documentation for more information. - -## Bot Tokens for Achievements - -#### July 18, 2019 - -You can now use Bot tokens for authorization headers against the HTTP API for [Achievements](#DOCS_GAME_SDK_ACHIEVEMENTS/the-api-way). - -## Additional Team Information - -#### June 19, 2019 - -Additional information around Teams has been added to both the API and the documentation. The [Teams](#DOCS_TOPICS_TEAMS/teams) page now includes information about the team and team member objects. Additionally, the [Get Current Application Information](#DOCS_TOPICS_OAUTH2/get-current-bot-application-information) endpoint now returns a `team` object if that application belongs to a team. That documentation has also been updated to includes fields that were missing for applications that are games sold on Discord. - -## Added Info Around Nitro Boosting Experiment - -#### May 29, 2019 - -Additional information has been documented to support [Server Nitro Boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting). This includes the addition of a few [message types](#DOCS_RESOURCES_CHANNEL/message-object-message-types), as well as some [new fields on guilds](#DOCS_RESOURCES_GUILD/guild-object-premium-tier). Please note that this feature is currently under experimentation, and these fields may be subject to change. - -## Deprecation of Discord-RPC Rich Presence SDK - -#### April 29, 2019 - -The [Discord-RPC](https://github.com/discord/discord-rpc) implementation of Rich Presence has been deprecated in favor of Discord's new GameSDK. If you're interested in using Rich Presence, please read our [SDK Starter Guide](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/) and check out the relevant functions in the [Activity Manager](#DOCS_GAME_SDK_ACTIVITIES/). - -## New Invite Object Fields - -#### April 18, 2019 - -The [Invite Object](#DOCS_RESOURCES_INVITE/invite-object) now includes two additional fields, `target_user` and `target_user_type`. - -## Ask to Join & Rich Presence SDK - -#### January 14, 2019 - -Ask to Join no longer requires approval or whitelisting to use. You are welcome to create in-game UI, but all Ask to Join requests are also now handled by the Discord overlay. - -There have also been some small additions to the Rich Presence SDK. The previously undocumented `UpdateHandlers()` function is now documented. - -## Documentation: Dispatch Store Listings - -#### December 11, 2018 - -Dispatch documentation around store listings has been removed. Store pages for the Discord Store are now managed entirely within the [Developer Portal](https://discord.com/developers). - -## Enhancement: User Object - -#### November 30, 2018 - -The [User object](#DOCS_RESOURCES_USER/user-object) now includes two new additional fields, `premium_type` and `flags`. These can be used to know the Nitro status of a user, or determine which HypeSquad house a user is in. - -## Documentation Fix: List of Open DMS in Certain Payloads - -#### June 19, 2018 - -The documentation has been updated to correctly note that the `private_channels` field in the [Ready](#DOCS_TOPICS_GATEWAY_EVENTS/ready) should be an empty array, as well as the response from `/users/@me/channels` for a bot user. This change has been in effect for a long time, but the documentation was not updated. - -## Deprecation: RPC online member count and members list - -#### June 11, 2018 - -We released server changes that allow guilds to represent an incomplete state of the member list in our clients, which results in inaccurate member lists and online counts over RPC. These fields are now deprecated and will now return an empty members array and an online count of 0 moving forward. - -## Enhancement: New Message Properties - -#### February 5, 2018 - -Additional `activity` and `application` fields—as well as corresponding object documentation—have been added to the [Message](#DOCS_RESOURCES_CHANNEL/message-object) object in support of our newly-released [Spotify integration](https://support.discord.com/hc/en-us/articles/360000167212-Discord-Spotify-Connection) and previous Rich Presence enhancements. - -## Enhancement: Get Guild Emoji Endpoint - -#### January 30, 2018 - -The [Get Guild Emoji](#DOCS_RESOURCES_EMOJI/get-guild-emoji) response now also includes a user object if the emoji was added by a user. - -## Deprecation: Accept Invite Endpoint - -#### January 23, 2018 - -The [Accept Invite](#DOCS_RESOURCES_INVITE/) endpoint is deprecated starting today, and will be discontinued on March 23, 2018. The [Add Guild Member](#DOCS_RESOURCES_GUILD/add-guild-member) endpoint should be used in its place. - -## Semi-Breaking Change: Very Large Bot Sharding - -#### January 3, 2018 - -Additional sharding requirements and information for bots in over 100,000 guilds has been added. This requires a small change in numbers of shards for affected bots. See the [documentation](#DOCS_TOPICS_GATEWAY/sharding-for-large-bots) for more information. - -## New Feature: Rich Presence - -#### November 9, 2017 - -Rich Presence is now live and available for all developers! Rich Presence allows developers to closely integrate with Discord in a number of new, cool ways like: - -- Showing more information about a user's current game in their profile -- Allowing users to post invitations to join their party or spectate their game in chat -- Displaying "Spectate" and "Ask to Join" buttons on users' profiles - -For more information, check out our [Rich Presence site](https://discord.com/rich-presence). To get started on development, [read the docs](#DOCS_RICH_PRESENCE_HOW_TO/)! - -## Breaking Change: API & Gateway Below v6 Discontinued - -#### October 16, 2017 - -[API](#DOCS_REFERENCE/api-versioning) and Gateway versions below v6 are now discontinued after being previously deprecated. Version 6 is now the default API and Gateway version. Attempting to use a version below 6 will result in an error. - -## New Feature: Channel Categories - -#### September 20, 2017 - -Changes have been made throughout the documentation to reflect the addition of channel categories to Discord. These includes an additional field—`parent_id`—to the base [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object and a new [channel category example](#DOCS_RESOURCES_CHANNEL/channel-object-example-channel-category). - -## New Feature: Emoji Endpoints - -#### September 10, 2017 - -[Emoji endpoints](#DOCS_RESOURCES_EMOJI/emoji-resource) have been added to the API. Bots can now manage guild emojis to their robo-hearts' content! - -## Breaking Change: Presence Activity Objects - -#### August 16, 2017 - -The `type` field in the [activity object](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object) for [Gateway Status Update](#DOCS_TOPICS_GATEWAY_EVENTS/update-presence) and [Presence Update](#DOCS_TOPICS_GATEWAY_EVENTS/presence-update) payloads is no longer optional when the activity object is not null. - -## Breaking Change: Default Channels - -#### August 3, 2017 - -After today, we are changing how default channels function. The "default" channel for a given user is now the channel with the highest position that their permissions allow them to see. New guilds will no longer have a default channel with the same id as the guild. Existing guilds will not have their #general channel id changed. It is possible, if permissions are set in such a way, that a user will not have a default channel in a guild. - -We saw a use case in many servers where the previously-default #general channel was being repurposed as an announcement-only, non-writable channel for new members by using bots to clear the entire message history. Now, that channel can simply be deleted and re-created with the desired permissions. This change also allows dynamic default channels for users based on permissions. - -We are also rolling out a change in conjunction that will allow Discord to remember your last-visited channel in a guild across sessions. Newly-joined users will be directed to the guild's default channel on first join; existing members will return to whichever channel they last visited. - -## New Feature: Audit Logs - -#### July 24, 2017 - -Audit logs are here! Well, they've been here all along, but now we've got [documentation](#DOCS_RESOURCES_AUDIT_LOG/) about them. Check it out, but remember: with great power comes great responsibility. - -## Breaking Change: Version 6 - -#### July 19, 2017 - -- [Channel](#DOCS_RESOURCES_CHANNEL/channel-object) Object - - `is_private` removed - - [`type`](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) is now an integer - - `recipient` is now `recipients`, an array of [user](#DOCS_RESOURCES_USER/user-object) objects -- [Message](#DOCS_RESOURCES_CHANNEL/message-object) Object - - [`type`](#DOCS_RESOURCES_CHANNEL/message-object-message-types) added to support system messages -- [Status Update](#DOCS_TOPICS_GATEWAY_EVENTS/update-presence-gateway-presence-update-structure) Object - - `idle_since` renamed to `since` diff --git a/docs/Getting_Started.md b/docs/Getting_Started.md deleted file mode 100644 index 2fc0f87670..0000000000 --- a/docs/Getting_Started.md +++ /dev/null @@ -1,435 +0,0 @@ -# Getting started with Discord app development - -Discord apps let you customize your servers with interactions and automation. This guide is meant to walk through building and running your first Discord app using JavaScript. At the end of this guide, you’ll have an app with a bot user that uses slash commands, sends messages, and interacts with message components. - -While this guide is beginner-focused, it assumes a basic understanding of [JavaScript](https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics). - -> info -> When developing apps, you should build and test in a server that isn’t actively used by others. If you don’t have your own server already, you can [create one for free](https://support.discord.com/hc/en-us/articles/204849977-How-do-I-create-a-server-). - ---- - -## Overview - -In this guide, we’ll be building a Discord app that lets server members play a *slightly*-enhanced version of rock paper scissors (with 7 choices instead of the usual 3). - -### Resources used in this guide -- **[Github repository](https://github.com/discord/discord-example-app)** where the code from this guide lives along with additional feature-specific examples. -- **[discord-interactions](https://github.com/discord/discord-interactions-js)**, an npm package which provides types and helper functions for Discord apps. -- **[Glitch](https://glitch.com/)**, an online environment that simplifies building and hosting apps during early prototyping and development. You can also develop locally with a tool like **[ngrok](https://ngrok.com/)**. - -And here's what the finished app will look like: - -![Demo of example app](getting-started-demo.gif) - -To make the user flow a bit more explicit: -1. User 1 initiates a new game and picks their object using the app’s `/challenge` slash command -2. A message is sent to channel with a button inviting others to accept the challenge -3. User 2 presses the **Accept** button -4. User 2 is sent an ephemeral message where they select their object of choice -5. The result of the game is posted back into the original channel for all to see - -## Creating an app - -The first thing we’ll need to do is create an app. Navigate to [the developer dashboard](https://discord.com/developers/applications), then click **New Application** in the upper right corner. - -![App creation modal](app-create-modal.png) - -Enter a name for your app, then click **Create**. - -Once you create an app, you'll land on the **General Overview** page of the app's settings. Here you can view and configure basic information about the app, like its description and icon. You’ll also see an **Application ID** and **Interactions Endpoint URL**, which we’ll use a bit later in the guide. - -### Configuring a bot - -Next we'll add a bot user to your app, which allows it to appear in Discord similar to other members. On the left hand sidebar click **Bot**, then the **Add Bot** button. - -Once you create a bot, you’ll have an option to update its icon and username. Under that, there’s a **Token** section with a **Reset Token** button. - -![Bot tab in app settings](app-add-bot.png) - -Bot tokens are used to authorize API requests and carry all of your bot user’s permissions, making them *highly sensitive*. Make sure to *never* share your token or check it into any kind of version control. - -Go ahead and click **Reset Token**, and store the token somewhere safe (like in a password manager). - -> warn -> You won’t be able to view your token again unless you regenerate it, so make sure to keep it somewhere safe. - -### Adding scopes and permissions - -Apps need approval from installing users to perform actions inside of Discord (like creating a slash command or adding emojis). So before installing your app, let's add some scopes and permissions to request during installation. Click on **OAuth2** in the left sidebar, then **URL generator**. - -> info -> The URL generator helps create an installation link with the scopes and permissions your app needs to function. You can use the link to install the app onto your server, or share it with others so they can install it on their own. - -For now, we’ll just add two scopes: -- `applications.commands` lets your app create commands in guilds its installed -- `bot` is to enable your bot user. After you click bot, you can also add different user permissions to the bot. For now, just check **Send Messages**. - -See a list of all [OAuth2 scopes](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes), or read more on [user permissions](#DOCS_TOPICS_PERMISSIONS) in the documentation. - -![URL generator screenshot](url-generator.png) - -Once you add scopes, you should see a URL that you can copy to install your app. - -### Installing your app - -Copy the URL from above, and paste it into your browser. You’ll be guided through the installation flow, where you should make sure you’re installing the app on a server where you can develop and test. - -After installing your app, you can head over to your server and see that it has joined ✨ - -With your app configured and installed, let’s start developing it. - -## Running your app - -All of the code used in the example app can be found in [the Github repository](https://github.com/discord/discord-example-app). - -To make development a bit simpler, the app uses [discord-interactions](https://github.com/discord/discord-interactions-js), which provides types and helper functions. If you prefer to use other languages or libraries, there’s [a page with community-built resources](#DOCS_TOPICS_COMMUNITY_RESOURCES) which you can browse through. - -### Remix the project - -This guide uses Glitch, which allows you to quickly clone and develop an app from within your browser. There are also instructions on developing locally using ngrok [in the README](https://github.com/discord/discord-example-app#running-app-locally) if you'd prefer. - -> info -> While Glitch is great for development and testing, [it has technical limitations](https://help.glitch.com/kb/article/17-technical-restrictions/) so other hosting providers should be considered for production apps. - -To start, **[remix (or clone) the Glitch project 🎏](https://glitch.com/edit/#!/remix/getting-started-discord)** - -When you remix the project, you'll see a new Glitch project with a unique name similar to this: - -![Glitch project overview](glitch-project.png) - -#### Project structure - -All of the files for the project are on the left-hand side. Here's a quick glimpse at the structure: - -``` -├── examples -> short, feature-specific sample apps -│ ├── app.js -> completed app.js code -│ ├── button.js -│ ├── command.js -│ ├── modal.js -│ ├── selectMenu.js -├── .env -> your credentials and IDs -├── app.js -> main entrypoint for app -├── commands.js -> slash command payloads + helpers -├── game.js -> logic specific to RPS -├── utils.js -> utility functions and enums -├── package.json -├── README.md -└── .gitignore -``` - -### Adding credentials - -There's already some code in your `app.js` file, but you’ll need your app’s token and ID to make requests. All of your credentials can be stored directly in the `.env` file. - -> warn -> It bears repeating that you should *never* check any credentials or secrets into source control. The getting started project's `.gitignore` comes pre-loaded with `.env` to prevent it. - -First, copy your bot user’s token from earlier and paste it in the **`DISCORD_TOKEN`** variable in your `.env` file. - -Next, navigate to your app settings in the developer portal and copy the **App ID** and **Public Key** from the **General Overview** page. Paste the values in your `.env` file as **`APP_ID`** and **`PUBLIC_KEY`**. - -Finally, fetch your guild ID by navigating to the server where you installed your app. Copy the first number in the URL after `channels/` (for example, in the URL `https://discord.com/channels/12345/678910`, the guild ID would be `12345`). Save this value as **`GUILD_ID`** in your `.env` file. - -With your credentials configured, let's install and handle slash commands. - -### Installing slash commands - -> info -> To install slash commands, the app is using [`node-fetch`](https://github.com/node-fetch/node-fetch). You can see the implementation for the installation in `utils.js` within the `DiscordRequest()` function. More information about Discord's REST API can be found in [the API reference](#DOCS_REFERENCE). - -If you look in the `listen` callback at the bottom of `app.js`, you’ll see that `HasGuildCommands()` is called. `HasGuildCommands()` is a utility function that checks whether specific slash commands are installed—and if they aren't, installs them. The code for `HasGuildCommands()` is inside of the top-level `commands.js` file. - -To install guild-scoped slash commands, apps can call the [`​​/applications//guilds//commands`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/create-guild-application-command) endpoint (which is what `HasGuildCommands()` does). An example focused on installing and handling slash commands can be found within the `examples/` folder, in `examples/command.js`. - -> info -> Commands can either be installed to a specific guild, or installed globally, though guild commands are recommended for development since they update faster. Read more about the differences [in the documentation](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/registering-a-command). - -If you go back to your guild and refresh it, you should see the slash command appear. But if you try to run it, nothing will happen because the request coming from Discord isn't being handled. - -## Handling interactivity - -To enable your app to receive slash command requests (and other interactions), Discord needs a public URL to send them. This URL can be configured in your app settings as **Interaction Endpoint URL**. - -### Adding interaction endpoint URL -Glitch projects have a public URL exposed by default. Copy your project's URL by clicking **Share** in the top right, then copying the live project link at the bottom of the modal. - -In the following example, the link would be `https://vast-thorn-plant.glitch.me`: - -![Glitch share modal](glitch-project-share.png) - -> info -> If you're developing locally, there are instructions for tunneling requests to your local environment [on the Github README](https://github.com/discord/discord-example-app#running-app-locally). - -With that link copied, go to your app settings from [the developer portal](https://discord.com/developers/applications). - -On your app’s **General Information** page, there’s an **Interactive Endpoint URL** option, where you can paste your app’s URL and append `/interactions` to it, which is where the Express app is configured to listen for requests. - -![Interactions endpoint URL in app settings](interactions-url.png) - -Click **Save Changes** and ensure your endpoint is successfully verified. - -> info -> Verification requires your app to verify signature headers and respond to `PING` events. You can read more about preparing to receive interactions in [the interactions documentation](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/receiving-an-interaction). - -The sample app handles verification in two ways: -- It uses the `PUBLIC_KEY` and [discord-interactions package](https://github.com/discord/discord-interactions-js#usage) with a wrapper function (imported from `utils.js`) that makes it conform to [Express’s `verify` interface](http://expressjs.com/en/5x/api.html#express.json). This is run on every incoming request to your app. -- It responds to incoming `PING` requests. - -### Handling slash command requests - -With the endpoint verified, go back to your code (in `app.js`) and look for the code block that handles the `/test` command: - -```javascript -// "test" guild command -if (name === 'test') { - // Send a message into the channel where command was triggered from - return res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - // Fetches a random emoji to send from a helper function - content: 'hello world ' + getRandomEmoji(), - }, - }); -} -``` - -The above code is responding to the interaction with a message in the channel it originated from. You can see all of the different possible response types, like responding with a modal, [in the documentation](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-interaction-callback-type). - -> info -> `InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE` is a constant [exported from `discord-interactions`](https://github.com/discord/discord-interactions-js/blob/main/src/index.ts#L33) - -Go to the server that matches your configured `GUILD_ID` and make sure your app’s `/test` slash command works. When you trigger it, your app should send “hello world” with a random emoji appended. - -If you don’t want to add any additional interactivity, you could skip to [next steps](#DOCS_GETTING_STARTED/next-steps). But in the following section, we’ll add an additional command that uses slash command options, buttons, and select menus to build the rock-paper-scissors game. - -## Adding message components - -The `/challenge` command will be how our rock-paper-scissors-style game is initiated. When the command is triggered, the app will send message components to the channel, which will guide the users to complete the game. - -### Adding a command with options - -The `/challenge` command, exported as `CHALLENGE_COMMAND` in `commands.js`, has an array called `options`. For this app, the options are the different objects a user can select for our game, generated using keys of `RPSChoices` in `game.js`. - -You can read more about command options and their structure [in the documentation](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure). - -> info -> While this guide won't touch much on the `game.js` file, feel free to poke around and change commands or the options in the commands. - -To handle the `/challenge` command, add the following code after the `if name === “test”` if block: - -```javascript -// "challenge" guild command -if (name === 'challenge' && id) { - const userId = req.body.member.user.id; - // User's object choice - const objectName = req.body.data.options[0].value; - - // Create active game using message ID as the game ID - activeGames[id] = { - id: userId, - objectName, - }; - - return res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - // Fetches a random emoji to send from a helper function - content: `Rock papers scissors challenge from <@${userId}>`, - components: [ - { - type: MessageComponentTypes.ACTION_ROW, - components: [ - { - type: MessageComponentTypes.BUTTON, - // Append the game ID to use later on - custom_id: `accept_button_${req.body.id}`, - label: 'Accept', - style: ButtonStyleTypes.PRIMARY, - }, - ], - }, - ], - }, - }); -} -``` - -> info -> If you aren’t sure where to paste the code, you can see the full code in `examples/app.js` in the Glitch project or the root `app.js` [on Github](https://github.com/discord/discord-example-app/blob/main/app.js). - -The above code is doing a few things: -1. Parses the request body to get the ID of the user who triggered the slash command (`userId`), and the option (object choice) they selected (`objectName`). -2. Adds a new game to the `activeGames` object using the interaction ID. The active game records the `userId` and `objectName`. -3. Sends a message back to the channel with a button with a `custom_id` of `accept_button_`. - -> warn -> The sample code uses an object as in-memory storage, but for production apps you should use a database. - -When sending a message with [message components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/what-is-a-component), the individual payloads are appended to a `components` array. Actionable components (like buttons) need to be inside of an [action row](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/action-rows), which you can see in the code sample. - -Note the unique `custom_id` sent with message components, in this case `accept_button_` with the active game's ID appended to it. A `custom_id` can be used to handle requests that Discord sends you when someone interacts with the component, which you'll see in a moment. - -Now when you run the `/challenge` command and pick an option, your app will send a message with an **Accept** button. Let's add code to handle the button press. - -### Handling button interactions - -When users interact with a message component, Discord will send a request with an [interaction type](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-type) of `3` (or the `MESSAGE_COMPONENT` value when using `discord-interactions`). - -To set up a handler for the button, we’ll check the `type` of interaction, followed by matching the `custom_id`. Paste the following code under the type handler for `APPLICATION_COMMAND`s: - -```javascript -if (type === InteractionType.MESSAGE_COMPONENT) { -// custom_id set in payload when sending message component -const componentId = data.custom_id; - - if (componentId.startsWith('accept_button_')) { - // get the associated game ID - const gameId = componentId.replace('accept_button_', ''); - // Delete message with token in request body - const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`; - try { - await res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - // Fetches a random emoji to send from a helper function - content: 'What is your object of choice?', - // Indicates it'll be an ephemeral message - flags: InteractionResponseFlags.EPHEMERAL, - components: [ - { - type: MessageComponentTypes.ACTION_ROW, - components: [ - { - type: MessageComponentTypes.STRING_SELECT, - // Append game ID - custom_id: `select_choice_${gameId}`, - options: getShuffledOptions(), - }, - ], - }, - ], - }, - }); - // Delete previous message - await DiscordRequest(endpoint, { method: 'DELETE' }); - } catch (err) { - console.error('Error sending message:', err); - } - } -} -``` - -To briefly go over what the above code is doing: -1. Checks for a `custom_id` that matches what we originally sent (in this case, it starts with `accept_button_`). The custom ID also has the active game ID appended, so we store that in `gameID`. -2. [Deletes the original message](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/delete-original-interaction-response) calling a webhook using `node-fetch` and passing the unique interaction `token` in the request body. This is done to clean up the channel, and so other users can’t click the button. -3. Responds to the request by sending a message that contains a select menu with the object choices for the game. The payload should look fairly similar to the previous one, with the exception of the `options` array and `flags: 64`, [which indicates that the message is ephemeral](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/create-followup-message). - -The `options` array is populated using the `getShuffledOptions()` method in `game.js`, which manipulates the `RPSChoices` values to conform to the shape of [message component options](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/select-menu-object-select-option-structure). - -### Handling select menu interactions - -The last thing to add is code to handle select menu interactions and send the result of the game to channel. - -Since select menus are just another message component, the code to handle interactions with them will be similar to buttons. Modify the code above to handle the select menu: - -```javascript -if (type === InteractionType.MESSAGE_COMPONENT) { -// custom_id set in payload when sending message component -const componentId = data.custom_id; - - if (componentId.startsWith('accept_button_')) { - // get the associated game ID - const gameId = componentId.replace('accept_button_', ''); - // Delete message with token in request body - const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`; - try { - await res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { - // Fetches a random emoji to send from a helper function - content: 'What is your object of choice?', - // Indicates it'll be an ephemeral message - flags: InteractionResponseFlags.EPHEMERAL, - components: [ - { - type: MessageComponentTypes.ACTION_ROW, - components: [ - { - type: MessageComponentTypes.STRING_SELECT, - // Append game ID - custom_id: `select_choice_${gameId}`, - options: getShuffledOptions(), - }, - ], - }, - ], - }, - }); - // Delete previous message - await DiscordRequest(endpoint, { method: 'DELETE' }); - } catch (err) { - console.error('Error sending message:', err); - } - } else if (componentId.startsWith('select_choice_')) { - // get the associated game ID - const gameId = componentId.replace('select_choice_', ''); - - if (activeGames[gameId]) { - // Get user ID and object choice for responding user - const userId = req.body.member.user.id; - const objectName = data.values[0]; - // Calculate result from helper function - const resultStr = getResult(activeGames[gameId], { - id: userId, - objectName, - }); - - // Remove game from storage - delete activeGames[gameId]; - // Update message with token in request body - const endpoint = `webhooks/${process.env.APP_ID}/${req.body.token}/messages/${req.body.message.id}`; - - try { - // Send results - await res.send({ - type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - data: { content: resultStr }, - }); - // Update ephemeral message - await DiscordRequest(endpoint, { - method: 'PATCH', - body: { - content: 'Nice choice ' + getRandomEmoji(), - components: [] - } - }); - } catch (err) { - console.error('Error sending message:', err); - } - } - } -} -``` - -Similar to earlier code, the code above is getting the user ID and their object selection from the interaction request. - -That information, along with the original user's ID and selection from the `activeGames` object, are passed to the `getResult()` function. `getResult()` determines the winner, then builds a readable string to send back to channel. - -We’re also calling another webhook, this time to [update the follow-up ephemeral message](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/edit-followup-message) since it can't be deleted. - -Finally, the results are sent in channel using the `CHANNEL_MESSAGE_WITH_SOURCE` interaction response type. - -....and that's it 🎊 Go ahead and test your app and make sure everything works. - -## Next steps - -Congrats on building your first Discord app! 🤖 - -Hopefully you learned a bit about Discord apps, how to configure them, and how to make them interactive. From here, you can continue building out your app or explore what else is possible: -- Read **[the documentation](#DOCS_INTRO)** for in-depth information about API features -- Browse the `examples/` folder in this project for smaller, feature-specific code examples -- Check out **[community resources](#DOCS_TOPICS_COMMUNITY_RESOURCES)** for language-specific tools maintained by community members -- Read our tutorial on [hosting Discord apps on Cloudflare Workers](#DOCS_TUTORIALS_HOSTING_ON_CLOUDFLARE_WORKERS) -- Join the **[Discord Developers server](https://discord.gg/discord-developers)** to ask questions about the API, attend events hosted by the Discord API team, and interact with other devs diff --git a/docs/Intro.md b/docs/Intro.md deleted file mode 100644 index e6ead64d6d..0000000000 --- a/docs/Intro.md +++ /dev/null @@ -1,48 +0,0 @@ -# Introduction - -You’ve found the Discord Developer Documentation! These pages are dedicated to showing you all the ways that you can use Discord to make cool stuff. Whether you’re looking to create awesome bots for your community, empower your applications with our API, or hook us right into your game with Rich Presence or the GameSDK, Discord has something for you. - -All of our [documentation is on GitHub](https://github.com/discord/discord-api-docs) and we <3 corrections and improvements! - -## Bugs - -If you believe you're experiencing a bug with our API or want to report incorrect documentation, open an issue on our [issue tracker](https://github.com/discord/discord-api-docs/issues). - -## Bots and Apps - -Bots and apps are the lifeblood of the Discord development community. They come in all shapes and sizes, from small hobby projects for your server with friends, to huge projects that live in hundreds of thousands of servers. We love seeing the unique, fun, and sometimes downright strange (in a good way) creations that come from our community. - -Discord offers an open API to serve requests for both bots and OAuth2 integrations. So whether you’re making your own [`/wumpus` commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/) or looking to [`Log In With Discord`](#DOCS_TOPICS_OAUTH2/), we’ve got you covered. - -So go do it! Go! Go [make an app](https://discord.com/developers/applications) and create something awesome. - -## Games - -We love games, and we love helping game developers. If YOU have a game that you want to supercharge with Discord, then we have just the thing for you. - -#### Your Game and Discord - -We’ve got a whole suite of tools to help bring your game to life with Discord. Need networking? Have [networking](#DOCS_GAME_SDK_NETWORKING/)! Want a friends list? Take [relationships](#DOCS_GAME_SDK_RELATIONSHIPS/) too! - -#### Rich Presence - -No matter where your game lives, it should have a first-class experience in Discord. [Rich Presence](https://discord.com/rich-presence) is the way to make that a reality! We got tired of exchanging usernames, friend codes, and lobby passwords, so we created Rich Presence, an easy-to-use, easy-to-integrate way to get people playing games together that lets you: - -- Display rich game data on your players’ profiles -- Empower them to send game invites to each other -- Ask to Join and Spectate their friends' games -- Spend more time playing together and less time setting up - -It’s free, easy, and self-serve, so check out [the GameSDK Activity Manager](#DOCS_GAME_SDK_ACTIVITIES/) and get started! - -## Still need some help? - -Join the [Official Discord Developers server](https://discord.gg/discord-developers) for support and discussion regarding Discord's APIs. - -## Go Make Cool Stuff! - -We love our developers, and we plan to keep making practical tools so that YOU can keep making cool stuff. Build a bot, integrate our account system, or put us right in your game; whatever you do, do it with Discord. - -We can’t wait to see what you make. - --- Discord diff --git a/docs/Reference.md b/docs/Reference.md deleted file mode 100644 index ce65884668..0000000000 --- a/docs/Reference.md +++ /dev/null @@ -1,485 +0,0 @@ -# API Reference - -Discord's API is based around two core layers, a HTTPS/REST API for general operations, and persistent secure WebSocket based connection for sending and subscribing to real-time events. The most common use case of the Discord API will be providing a service, or access to a platform through the [OAuth2](https://oauth.net/2/) API. - -###### Base URL - -``` -https://discord.com/api -``` - -## API Versioning - -> danger -> Some API and Gateway versions are now non-functioning, and are labeled as discontinued in the table below for posterity. Trying to use these versions will fail and return 400 Bad Request. - -Discord exposes different versions of our API[.](https://c.tenor.com/BuZl66EegkgAAAAC/westworld-dolores.gif) You should specify which version to use by including it in the request path like `https://discord.com/api/v{version_number}`. Omitting the version number from the route will route requests to the current default version (marked below). You can find the change log for the newest API version [here](https://discord.com/developers/docs/change-log). - -###### API Versions - -| Version | Status | Default | -| ------- | -------------------------------- | ------- | -| 10 | Available | | -| 9 | Available | | -| 8 | Deprecated | | -| 7 | Deprecated | | -| 6 | Deprecated | ✓ | -| 5 | Discontinued | | -| 4 | Discontinued | | -| 3 | Discontinued | | - -## Error Messages - -Starting in API v8, we've improved error formatting in form error responses. The response will tell you which JSON key contains the error, the error code, and a human readable error message. We will be frequently adding new error messages, so a complete list of errors is not feasible and would be almost instantly out of date. Here are some examples instead: - -###### Array Error - -```json -{ - "code": 50035, - "errors": { - "activities": { - "0": { - "platform": { - "_errors": [ - { - "code": "BASE_TYPE_CHOICES", - "message": "Value must be one of ('desktop', 'android', 'ios')." - } - ] - }, - "type": { - "_errors": [ - { - "code": "BASE_TYPE_CHOICES", - "message": "Value must be one of (0, 1, 2, 3, 4, 5)." - } - ] - } - } - } - }, - "message": "Invalid Form Body" -} -``` - -###### Object Error - -```json -{ - "code": 50035, - "errors": { - "access_token": { - "_errors": [ - { - "code": "BASE_TYPE_REQUIRED", - "message": "This field is required" - } - ] - } - }, - "message": "Invalid Form Body" -} -``` - -###### Request Error - -```json -{ - "code": 50035, - "message": "Invalid Form Body", - "errors": { - "_errors": [ - { - "code": "APPLICATION_COMMAND_TOO_LARGE", - "message": "Command exceeds maximum size (4000)" - } - ] - } -} -``` - -## Authentication - -Authenticating with the Discord API can be done in one of two ways: - -1. Using a bot token gained by [registering a bot](#APPLICATIONS), for more information on bots see [bots vs user accounts](#DOCS_TOPICS_OAUTH2/bot-vs-user-accounts). -2. Using an OAuth2 bearer token gained through the [OAuth2 API](#DOCS_TOPICS_OAUTH2/oauth2). - -For all authentication types, authentication is performed with the `Authorization` HTTP header in the format `Authorization: TOKEN_TYPE TOKEN`. - -###### Example Bot Token Authorization Header - -``` -Authorization: Bot MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs -``` - -###### Example Bearer Token Authorization Header - -``` -Authorization: Bearer CZhtkLDpNYXgPH9Ml6shqh2OwykChw -``` - -## Encryption - -All HTTP-layer services and protocols (e.g. HTTP, WebSocket) within the Discord API are using TLS 1.2. - -## Snowflakes - -Discord utilizes Twitter's [snowflake](https://github.com/twitter/snowflake/tree/snowflake-2010) format for uniquely identifiable descriptors (IDs). These IDs are guaranteed to be unique across all of Discord, except in some unique scenarios in which child objects share their parent's ID. Because Snowflake IDs are up to 64 bits in size (e.g. a uint64), they are always returned as strings in the HTTP API to prevent integer overflows in some languages. See [Gateway ETF/JSON](#DOCS_TOPICS_GATEWAY/encoding-and-compression) for more information regarding Gateway encoding. - -###### Snowflake ID Broken Down in Binary - -``` -111111111111111111111111111111111111111111 11111 11111 111111111111 -64 22 17 12 0 -``` - -###### Snowflake ID Format Structure (Left to Right) - -| Field | Bits | Number of bits | Description | Retrieval | -| ------------------- | -------- | -------------- | ---------------------------------------------------------------------------- | ----------------------------------- | -| Timestamp | 63 to 22 | 42 bits | Milliseconds since Discord Epoch, the first second of 2015 or 1420070400000. | `(snowflake >> 22) + 1420070400000` | -| Internal worker ID | 21 to 17 | 5 bits | | `(snowflake & 0x3E0000) >> 17` | -| Internal process ID | 16 to 12 | 5 bits | | `(snowflake & 0x1F000) >> 12` | -| Increment | 11 to 0 | 12 bits | For every ID that is generated on that process, this number is incremented | `snowflake & 0xFFF` | - -### Convert Snowflake to DateTime - -![Graphical representation of how a Snowflake is constructed](snowflake.png) - -### Snowflake IDs in Pagination - -We typically use snowflake IDs in many of our API routes for pagination. The standardized pagination paradigm we utilize is one in which you can specify IDs `before` and `after` in combination with `limit` to retrieve a desired page of results. You will want to refer to the specific endpoint documentation for details. - -It is useful to note that snowflake IDs are just numbers with a timestamp, so when dealing with pagination where you want results from the beginning of time (in Discord Epoch, but `0` works here too) or before/after a specific time you can generate a snowflake ID for that time. - -###### Generating a snowflake ID from a Timestamp Example - -``` -(timestamp_ms - DISCORD_EPOCH) << 22 -``` - -## ID Serialization - -There are some cases in which our API and Gateway may return IDs in an unexpected format. Internally, Discord stores IDs as integer snowflakes. When we serialize IDs to JSON, we transform `bigints` into strings. Given that all Discord IDs are snowflakes, you should always expect a string. - -However, there are cases in which passing something to our API will instead return IDs serialized as an integer; this is the case when you send our API or Gateway a value in an `id` field that is not `bigint` size. For example, when requesting `GUILD_MEMBERS_CHUNK` from our gateway: - -``` -// Send -{ - op: 8, - d: { - guild_id: '308994132968210433', - user_ids: [ '123123' ] - } -} - -// Receive -{ - t: 'GUILD_MEMBERS_CHUNK', - s: 3, - op: 0, - d: { - not_found: [ 123123 ], - members: [], - guild_id: '308994132968210433' - } -} -``` - -You can see in this case that the sent `user_id` is not a `bigint`; therefore, when it is serialized back to JSON by Discord, it is not transformed into a string. **This will never happen with IDs that come from Discord.** But, this can happen if you send malformed data in your requests. - -## ISO8601 Date/Time - -Discord utilizes the [ISO8601 format](https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf) for most Date/Times returned in our models. This format is referred to as type `ISO8601` within tables in this documentation. - -## Nullable and Optional Resource Fields - -Resource fields that may contain a `null` value have types that are prefixed with a question mark. -Resource fields that are optional have names that are suffixed with a question mark. - -###### Example Nullable and Optional Fields - -| Field | Type | -| ---------------------------- | ------- | -| optional_field? | string | -| nullable_field | ?string | -| optional_and_nullable_field? | ?string | - -## Consistency - -Discord operates at a scale where true consistency is impossible. Because of this, lots of operations in our API and in-between our services are [eventually consistent](https://en.wikipedia.org/wiki/Eventual_consistency). Due to this, client actions can never be serialized and may be executed in _any_ order (if executed at all). Along with these constraints, events in Discord may: - -- Never be sent to a client -- Be sent _exactly_ one time to the client -- Be sent up to N times per client - -Clients should operate on events and results from the API in as much of an idempotent behavior as possible. - -## HTTP API - -### User Agent - -Clients using the HTTP API must provide a valid [User Agent](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43) which specifies information about the client library and version in the following format: - -###### User Agent Example - -``` -User-Agent: DiscordBot ($url, $versionNumber) -``` - -Clients may append more information and metadata to the _end_ of this string as they wish. - -> warn -> Client requests that do not have a valid User Agent specified may be blocked and return a [Cloudflare error](https://support.cloudflare.com/hc/en-us/articles/360029779472-Troubleshooting-Cloudflare-1XXX-errors). - -### Rate Limiting - -The HTTP API implements a process for limiting and preventing excessive requests in accordance with [RFC 6585](https://tools.ietf.org/html/rfc6585#section-4). API users that regularly hit and ignore rate limits will have their API keys revoked, and be blocked from the platform. For more information on rate limiting of requests, please see the [Rate Limits](#DOCS_TOPICS_RATE_LIMITS/rate-limits) section. - -### Boolean Query Strings - -Certain endpoints in the API are documented to accept booleans for their query string parameters. While there is no standard system for boolean representation in query string parameters, Discord represents such cases using `True`, `true`, or `1` for true and `False`, `false` or `0` for false. - -## Gateway (WebSocket) API - -Discord's Gateway API is used for maintaining persistent, stateful websocket connections between your client and our servers. These connections are used for sending and receiving real-time events your client can use to track and update local state. The Gateway API uses secure websocket connections as specified in [RFC 6455](https://tools.ietf.org/html/rfc6455). For information on opening Gateway connections, please see the [Gateway API](#DOCS_TOPICS_GATEWAY/connections) section. - -## Message Formatting - -Discord utilizes a subset of markdown for rendering message content on its clients, while also adding some custom functionality to enable things like mentioning users and channels. This functionality uses the following formats: - -###### Formats - -| Type | Structure | Example | -| ------------------------ | ------------------- | ----------------------------- | -| User | <@USER_ID> | <@80351110224678912> | -| User \* | <@!USER_ID> | <@!80351110224678912> | -| Channel | <#CHANNEL_ID> | <#103735883630395392> | -| Role | <@&ROLE_ID> | <@&165511591545143296> | -| Slash Command \*\* | | | -| Standard Emoji | Unicode Characters | 💯 | -| Custom Emoji | <:NAME:ID> | <:mmLol:216154654256398347> | -| Custom Emoji (Animated) | | | -| Unix Timestamp | | | -| Unix Timestamp (Styled) | | | - -Using the markdown for either users, roles, or channels will usually mention the target(s) accordingly, but this can be suppressed using the `allowed_mentions` parameter when creating a message. Standard emoji are currently rendered using [Twemoji](https://twemoji.twitter.com/) for Desktop/Android and Apple's native emoji on iOS. - -Timestamps are expressed in seconds and display the given timestamp in the user's timezone and locale. - -\* User mentions with an exclamation mark are deprecated and should be handled like any other user mention. - -\*\* Subcommands and subcommand groups can also be mentioned by using respectively `` and ``. - -###### Timestamp Styles - -| Style | Example Output | Description | -| ----- | ---------------------------- | --------------- | -| t | 16:20 | Short Time | -| T | 16:20:30 | Long Time | -| d | 20/04/2021 | Short Date | -| D | 20 April 2021 | Long Date | -| f \* | 20 April 2021 16:20 | Short Date/Time | -| F | Tuesday, 20 April 2021 16:20 | Long Date/Time | -| R | 2 months ago | Relative Time | - -\*default - -## Image Formatting - -###### Image Base Url - -``` -https://cdn.discordapp.com/ -``` - -Discord uses ids and hashes to render images in the client. These hashes can be retrieved through various API requests, like [Get User](#DOCS_RESOURCES_USER/get-user). Below are the formats, size limitations, and CDN endpoints for images in Discord. The returned format can be changed by changing the [extension name](#DOCS_REFERENCE/image-formatting-image-formats) at the end of the URL. The returned size can be changed by appending a querystring of `?size=desired_size` to the URL. Image size can be any power of two between 16 and 4096. - -###### Image Formats - -| Name | Extension | -| ------ | ----------- | -| JPEG | .jpg, .jpeg | -| PNG | .png | -| WebP | .webp | -| GIF | .gif | -| Lottie | .json | - -###### CDN Endpoints - -| Type | Path | Supports | -| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | -| Custom Emoji | emojis/[emoji_id](#DOCS_RESOURCES_EMOJI/emoji-object).png | PNG, JPEG, WebP, GIF | -| Guild Icon | icons/[guild_id](#DOCS_RESOURCES_GUILD/guild-object)/[guild_icon](#DOCS_RESOURCES_GUILD/guild-object).png \* | PNG, JPEG, WebP, GIF | -| Guild Splash | splashes/[guild_id](#DOCS_RESOURCES_GUILD/guild-object)/[guild_splash](#DOCS_RESOURCES_GUILD/guild-object).png | PNG, JPEG, WebP | -| Guild Discovery Splash | discovery-splashes/[guild_id](#DOCS_RESOURCES_GUILD/guild-object)/[guild_discovery_splash](#DOCS_RESOURCES_GUILD/guild-object).png | PNG, JPEG, WebP | -| Guild Banner | banners/[guild_id](#DOCS_RESOURCES_GUILD/guild-object)/[guild_banner](#DOCS_RESOURCES_GUILD/guild-object).png \* | PNG, JPEG, WebP, GIF | -| User Banner | banners/[user_id](#DOCS_RESOURCES_USER/user-object)/[user_banner](#DOCS_RESOURCES_USER/user-object).png \* | PNG, JPEG, WebP, GIF | -| Default User Avatar | embed/avatars/[user_discriminator](#DOCS_RESOURCES_USER/user-object).png \*\* \*\*\* | PNG | -| User Avatar | avatars/[user_id](#DOCS_RESOURCES_USER/user-object)/[user_avatar](#DOCS_RESOURCES_USER/user-object).png \* | PNG, JPEG, WebP, GIF | -| Guild Member Avatar | guilds/[guild_id](#DOCS_RESOURCES_GUILD/guild-object)/users/[user_id](#DOCS_RESOURCES_USER/user-object)/avatars/[member_avatar](#DOCS_RESOURCES_GUILD/guild-member-object).png \* | PNG, JPEG, WebP, GIF | -| Application Icon | app-icons/[application_id](#DOCS_RESOURCES_APPLICATION/application-object)/[icon](#DOCS_RESOURCES_APPLICATION/application-object).png | PNG, JPEG, WebP | -| Application Cover | app-icons/[application_id](#DOCS_RESOURCES_APPLICATION/application-object)/[cover_image](#DOCS_RESOURCES_APPLICATION/application-object).png | PNG, JPEG, WebP | -| Application Asset | app-assets/[application_id](#DOCS_RESOURCES_APPLICATION/application-object)/[asset_id](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-assets).png | PNG, JPEG, WebP | -| Achievement Icon | app-assets/[application_id](#DOCS_RESOURCES_APPLICATION/application-object)/achievements/[achievement_id](#DOCS_GAME_SDK_ACHIEVEMENTS/data-models-user-achievement-struct)/icons/[icon_hash](#DOCS_GAME_SDK_ACHIEVEMENTS/data-models-user-achievement-struct).png | PNG, JPEG, WebP | -| Sticker Pack Banner | app-assets/710982414301790216/store/[sticker_pack_banner_asset_id](#DOCS_RESOURCES_STICKER/sticker-pack-object).png | PNG, JPEG, WebP | -| Team Icon | team-icons/[team_id](#DOCS_TOPICS_TEAMS/data-models-team-object)/[team_icon](#DOCS_TOPICS_TEAMS/data-models-team-object).png | PNG, JPEG, WebP | -| Sticker | stickers/[sticker_id](#DOCS_RESOURCES_STICKER/sticker-object).png \*\*\* \*\*\*\* | PNG, Lottie | -| Role Icon | role-icons/[role_id](#DOCS_TOPICS_PERMISSIONS/role-object)/[role_icon](#DOCS_TOPICS_PERMISSIONS/role-object).png | PNG, JPEG, WebP | -| Guild Scheduled Event Cover | guild-events/[scheduled_event_id](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object)/[scheduled_event_cover_image](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object).png | PNG, JPEG, WebP | -| Guild Member Banner | guilds/[guild_id](#DOCS_RESOURCES_GUILD/guild-object)/users/[user_id](#DOCS_RESOURCES_USER/user-object)/banners/[member_banner](#DOCS_RESOURCES_GUILD/guild-member-object).png \* | PNG, JPEG, WebP, GIF | - -\* In the case of endpoints that support GIFs, the hash will begin with `a_` if it is available in GIF format. (example: `a_1269e74af4df7417b13759eae50c83dc`) - -\*\* In the case of the Default User Avatar endpoint, the value for `user_discriminator` in the path should be the user's discriminator modulo 5—Test#1337 would be `1337 % 5`, which evaluates to 2. - -\*\*\* In the case of the Default User Avatar and Sticker endpoints, the size of images returned is constant with the "size" querystring parameter being ignored. - -\*\*\*\* In the case of the Sticker endpoint, the sticker will be available as PNG if its [`format_type`](#DOCS_RESOURCES_STICKER/sticker-object) is `PNG` or `APNG`, and as [Lottie](https://airbnb.io/lottie/#/) if its `format_type` is `LOTTIE`. - -## Image Data - -Image data is a [Data URI scheme](https://en.wikipedia.org/wiki/Data_URI_scheme) that supports JPG, GIF, and PNG formats. An example Data URI format is: - -``` -data:image/jpeg;base64,BASE64_ENCODED_JPEG_IMAGE_DATA -``` - -Ensure you use the proper content type (`image/jpeg`, `image/png`, `image/gif`) that matches the image data being provided. - -## Uploading Files - -> info -> A file upload size limit applies to *all* files in a request (rather than each individual file). While the limit depends on the [**Boost Tier**](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-FAQ-#h_419c3bd5-addd-4989-b7cf-c7957ef92583) of a guild, it is `8 MiB` by default. - -Some endpoints support file attachments, indicated by the `files[n]` parameter. To add file(s), the standard `application/json` body must be replaced by a `multipart/form-data` body. The JSON message body can optionally be provided using the `payload_json` parameter. - -All `files[n]` parameters must include a valid `Content-Disposition` subpart header with a `filename` and unique `name` parameter. Each file parameter must be uniquely named in the format `files[n]` such as `files[0]`, `files[1]`, or `files[42]`. The suffixed index `n` is the *snowflake placeholder* that can be used in the `attachments` field, which can be passed to the `payload_json` parameter (or [Callback Data Payloads](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-interaction-callback-data-structure)). - -Images can also be referenced in embeds using the `attachment://filename` URL. The `filename` for these URLs must be ASCII alphanumeric with underscores, dashes, or dots. An example payload is provided below. - -### Editing Message Attachments - -The `attachments` JSON parameter includes all files that will be appended to the message, including new files and their respective snowflake placeholders (referenced above). When making a `PATCH` request, only files listed in the `attachments` parameter will be appended to the message. Any previously-added files that aren't included will be removed. - -###### Example Request Bodies (multipart/form-data) - -Note that these examples are small sections of an HTTP request to demonstrate behaviour of this endpoint - client libraries will set their own form boundaries (`boundary` is just an example). For more information, refer to the [multipart/form-data spec](https://tools.ietf.org/html/rfc7578#section-4). - -This example demonstrates usage of the endpoint *without* `payload_json`. - -``` ---boundary -Content-Disposition: form-data; name="content" - -Hello, World! ---boundary -Content-Disposition: form-data; name="tts" - -true ---boundary-- -``` - -This example demonstrates usage of the endpoint *with* `payload_json` and all content fields (`content`, `embeds`, `files[n]`) set. - -``` ---boundary -Content-Disposition: form-data; name="payload_json" -Content-Type: application/json - -{ - "content": "Hello, World!", - "embeds": [{ - "title": "Hello, Embed!", - "description": "This is an embedded message.", - "thumbnail": { - "url": "attachment://myfilename.png" - }, - "image": { - "url": "attachment://mygif.gif" - } - }], - "message_reference": { - "message_id": "233648473390448641" - }, - "attachments": [{ - "id": 0, - "description": "Image of a cute little cat", - "filename": "myfilename.png" - }, { - "id": 1, - "description": "Rickroll gif", - "filename": "mygif.gif" - }] -} ---boundary -Content-Disposition: form-data; name="files[0]"; filename="myfilename.png" -Content-Type: image/png - -[image bytes] ---boundary -Content-Disposition: form-data; name="files[1]"; filename="mygif.gif" -Content-Type: image/gif - -[image bytes] ---boundary-- -``` - -###### Using Attachments within Embeds - -You can upload attachments when creating a message and use those attachments within your embed. To do this, you will want to upload files as part of your `multipart/form-data` body. Make sure that you're uploading files which contain a filename, as you will need to reference it in your payload. - -> warn -> Only filenames with [supported image extensions](#DOCS_REFERENCE/image-formatting-image-formats) may be used at this time. - -Within an embed object, you can set an image to use an attachment as its URL with the attachment scheme syntax: `attachment://filename.png` - -For example: - -```json -{ - "embeds": [{ - "image": { - "url": "attachment://screenshot.png" - } - }] -} -``` - -## Locales - -| Locale | Language Name | Native Name | -| ------ | --------------------- | ------------------- | -| da | Danish | Dansk | -| de | German | Deutsch | -| en-GB | English, UK | English, UK | -| en-US | English, US | English, US | -| es-ES | Spanish | Español | -| fr | French | Français | -| hr | Croatian | Hrvatski | -| it | Italian | Italiano | -| lt | Lithuanian | Lietuviškai | -| hu | Hungarian | Magyar | -| nl | Dutch | Nederlands | -| no | Norwegian | Norsk | -| pl | Polish | Polski | -| pt-BR | Portuguese, Brazilian | Português do Brasil | -| ro | Romanian, Romania | Română | -| fi | Finnish | Suomi | -| sv-SE | Swedish | Svenska | -| vi | Vietnamese | Tiếng Việt | -| tr | Turkish | Türkçe | -| cs | Czech | Čeština | -| el | Greek | Ελληνικά | -| bg | Bulgarian | български | -| ru | Russian | Pусский | -| uk | Ukrainian | Українська | -| hi | Hindi | हिन्दी | -| th | Thai | ไทย | -| zh-CN | Chinese, China | 中文 | -| ja | Japanese | 日本語 | -| zh-TW | Chinese, Taiwan | 繁體中文 | -| ko | Korean | 한국어 | diff --git a/docs/dispatch/Branches_and_Builds.md b/docs/dispatch/Branches_and_Builds.md deleted file mode 100644 index e0a602357f..0000000000 --- a/docs/dispatch/Branches_and_Builds.md +++ /dev/null @@ -1,654 +0,0 @@ -# Branches and Builds - -> info -> Need help with Dispatch? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -In order for other people to download your game from Discord's servers, you need to _upload_ your game to Discord's servers. Let's learn how to do that! - -## Getting Set Up - -First, get Dispatch for your operating system. - -- [Windows 64](https://dl-dispatch.discordapp.net/download/win64) -- [Windows 32](https://dl-dispatch.discordapp.net/download/win32) -- [Mac](https://dl-dispatch.discordapp.net/download/macos) -- [Linux](https://dl-dispatch.discordapp.net/download/linux) - -You'll want to be able to use Dispatch across your projects, so let's handle that now by adding it to our PATH. - -**MacOS/Linux:** - -Bust out those \*nix skills. - -- Open your choice of terminal -- Stick dispatch in a folder that will be added to your PATH. If you don't have a good spot for this stuff already, you can `mkdir ~/bin/` and call that Dispatch's new home (as well as any other executables you might want to access in the future) -- In that folder, run `chmod u+x dispatch-macos` so that Dispatch can be used as an executable - - Feel free to also rename `dispatch-macos` to just `dispatch` if you don't feel like typing so much -- Add that ~/bin directory to your path in a permanent way. I have `export PATH=$PATH:~/bin` at the bottom of my `~/.zshrc`, or `~/.bashrc` -- Restart your terminal -- `echo $PATH` and make sure you see the directory in the output -- Run `dispatch --help` to make sure it works - -You're good! You can now call the dispatch command from anywhere! - -**Windows** - -Not as 1337 as some scripting skillz, but the Windows GUI will do just fine - -- Stick `dispatch-x64` or `dispatch-x86`, depending on your OS, in a folder that will be added to your PATH - - Feel free to rename it to just `dispatch` if you don't feel like typing so much -- Hit that Windows key and start typing "Environment Variables" -- Select "Edit the system environment variables" -- Hit the "Environment Variables" button in the bottom right -- Edit the PATH entry for either the system or the current user, adding dispatch's directory -- Restart your command line -- Run `dispatch --help` to make sure it works - -You're good! You can now call the dispatch command from anywhere! - -> info -> Want some really fancy skills? You can set up autocompletions for dispatch in whatever shell you work. -> Run `dispatch completions -—help` to see instructions for a variety of popular shells. - -## Authorizing Yourself to Use It - -Run `dispatch login`, which will open a web browser and prompt you to authorize your Discord account with Dispatch. - -Yup, that's it. - -Small thing to note - the default `login` method works via an OAuth2 bearer token with special scopes. That means that if you run `dispatch login` on another machine—like a CI setup—it will invalidate your other tokens. If you want to set up build machines for your game, you'll want to use an alternate method of authorization. - -First, find the `credentials.json` file at: - -- Windows: `C:\Users\\.dispatch\credentials.json` -- macOS: `~/.dispatch/credentials.json` - -Inside that, we can use our Bot token for our application that will _not_ be invalidated across different machines. - -> info -> Note that this token is only good for its owning application, so if you want to make one build machine deploy multiple applications, you'll need to edit this file per game. - -You can get your bot token by going to your app in the Dev Portal --> `Bot` --> `Add Bot` --> copy the token. In our credentials file, replace the JSON with: - -```json -{ - "BotCredentials": { - "application_id": "my_application_id", - "token": "my_token" - } -} -``` - -Voila! You can now use dispatch for that application with this token. - -ONWARDS! - -## Creating Branches - -> warn -> If you have not yet set up a Team and an application, please follow the steps in [Get Set Up](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up) - -Now that we're set up to use the tool, let's make some branches! Branches, builds, and everything in between in Discord are tied to an application. To get one for your game, ask a Discord team member to hook you up! - -Then, head over to our [developer portal](https://discord.com/developers/), click into that new application, and copy the `Client Id` at the top of the page, keeping it handy throughout this process. Client ID and "application id" are interchangeable phrases. For the rest of this documentation, we'll refer to it as application id. - -Back to your terminal, type: - -``` -dispatch branch create -``` - -This will create a named branch under that application id that can be pushed to. You can see your branches with: - -``` -dispatch branch list -``` - -## Setting Up Our First Build - -In order for Discord to understand what you're sending, you need to set up a config file for your game. This config file tells Discord which files to bundle together, how to run them, and other metadata to include. You should keep this file safe in your version control system of choice; that way, Discord is always up to date with whatever you've got locally. You'll reference these manifests later when building your store pages, so that Discord knows what to download to someone's computer when they hit that Big Buy Button. - -Let's break an example `config.json` file down into pieces, and then put it together at the end. - -## Basic Information - -```js -{ - "application": { - "id": 467102538279999224, - "manifests": [] - } -} -``` - -This is the top level of the config file. It has an `application` object at the top. `id` is your application id. `manifests` are the heart of this file, and offer a lot of customization for tagging and uploading data for the build. Let's jump into that now. - -## Labels, Platforms, and Local Roots - -```js -{ - "label": "my-awesome-game/windows", - "platforms": ["win32", "win64"], - "locales": [], - "local_root": "./game-files/windows", - "redistributables": [ - "directx_june_2010" - ] -} -``` - -We're in the heart of a manifest now! `label` is the name you want to give an individual manifest/bundle of files. Make these easily identifiable so you can reference them later in your store page creation. - -`platforms` are the platforms for which the manifest is valid. Most simple configs, like our example, will use the same manifest for `win32` and `win64`, and then make other manifests for `macos` and `linux`. However, some older games may need specific configurations for 32bit and 64bit systems, and therefore need separate manifests. - -`locales` is an array of locales for which the manifest is valid; leaving it empty denotes it's valid for all locales. For example, your base game files will probably have an empty `locales` array, but you may other manifests for things like language packs defined for `es-ES` or `fr` or other locales. - -`local_root` is the relative path to the directory that contains the raw game files to upload for this manifest. This may be particularly useful if you have multiple manifests with different relative root directories, like: - -```json -// Imaginary directory structure: -// C:\game\binary -// C:\game\langs\en-US -// C:\game\langs\fr - -{ - "manifests": [ - { - "label": "game-files", - "local_root": "binary" - }, - { - "label": "english-language-pack", - "local_root": "en-US" - }, - { - "label": "french-language-pack", - "local_root": "fr" - } - ] -} -``` - -That way, you can `dispatch build push` from your actual root directory, but dispatch will be smart enough to separate the files properly. - -`redistributables` is an array of any redistributable packages your game may need to function, like a certain install of DirectX, or a Microsoft C++ redistributable. A list of valid values can be found in [Field Values](#DOCS_DISPATCH_FIELD_VALUES/). - -## File Rules - -```js -{ - "file_rules": { - "mappings": [ - { - "local_path": ".", - "install_path": "." - }, - { - "local_path": "./languages/en-US/no-but-the-data-is-really-in-here/", - "install_path": "./english" - } - ], - "properties": [ - { - "install_path": "save/*", - "attributes": ["user_data"] - } - ], - "exclusions": [ - { - "local_path": "**/*.pdb" - }, - { - "local_path": "**/*.verycoolfile" - } - ] - } -} -``` - -File rules is a special, and somewhat confusing, part of the manifest, but we'll get through it together! The `file_rules` object lets you: - -1. Specify the way in which files get installed on a user's computer -2. Mark files as protected, so they don't get overwritten -3. Exclude certain files from being uploaded - -`mappings` lets you tell Dispatch to download files to a certain place in the install directory on a user's machine, letting you create the folder structure you need. Your game build files may be tucked deep in a subdirectory on your machine, because who ever cleans up folder structures, but you can make sure it looks nice and clean for your players. - -`properties` allows you to mark properties on globs of files. In this case, marking a glob of files as `user_data` tells Dispatch not to touch these files in any way if it sees them; don't want that save data overwritten! - -`exclusions` also allow you to mark off globs of files. File globs here will not be uploaded by Dispatch on a build push. In the above example, debug files that match the `*.pdb` or `*.verycoolfile` patterns in any directory will be ignored. - -> warn -> Dispatch supports [Rust globbing patterns](https://docs.rs/glob/0.2.11/glob/struct.Pattern.html). - -## Cloud Storage - -```js -{ - "storage": { - "sync": true, - "roots": [ - { - "id": "my-save-files", - "paths": [ - { - "platform": "windows", - "path": "${DOCUMENTS}/My Games/My Awesome Game/Saves" - }, - { - "platform": "macos", - "path": "${DOCUMENTS}/Games/My Awesome Game/Saves" - } - ], - "patterns": ["**/*"] - } - ] - } -} -``` - -Discord supports cloud saves! Let's learn how to use it! This piece of the manifest, `storage`, helps Discord support cloud saves for your game. When `sync` is set to `true`, Discord will look in the `paths` provided here for any files that match one of the `patterns`. If it finds any, it will sync them to the cloud, so your user will have access to them across machines. - -If you are **not** using our [Storage Manager](#DOCS_GAME_SDK_STORAGE/) to manage your game's save files, make sure to outline your save paths and file glob patterns here. - -If you **are** using the Storage Manager in the GameSDK, just set `sync` to `true` and omit the `roots` key. - -> danger -> `id` must be a constant, immutable value once set. You can pick whatever you'd like when first set, but ensure it does not change afterwards. Otherwise, Discord may incorrectly overwrite and/or delete users' save data. - -We support a number of filepath replacements/shorteners like `{$DOCUMENTS}`, so you can have something like `${SAVEDGAMES}/My Awesome Game/${USERID}` and create user-specific save files. No longer will you need to worry about your little brother overwriting your save file! For the full list of path replacements, see [Cloud Save Path Replacements](#DOCS_DISPATCH_FIELD_VALUES/manifests-cloud-save-path-replacements). - -As a side note, there may be a case where you might have multiple manifests that each have storage information defined. In the case that the two manifests define the same storage path but have _conflicting_ data, the source of truth will be the manifest that appears **later** in the array in the config file. So, if you have: - -```js -{ - "manifests": [ - { - "label": "one", - "storage": { - "sync": true, - "roots": [ - { - "id": "one", - "paths": [ - { - "platform": "windows", - "path": "${HOME}" - } - ], - "patterns": ["**/*"] - } - ] - } - }, - { - "label": "two", - "storage": { - "sync": true, - "roots": [ - { - "id": "two", - "paths": [ - { - "platform": "windows", - "path": "${HOME}" - } - ], - "patterns": ["**/*"] - } - ] - } - } - ] -} -``` - -Then, manifest `two` would be the source of truth in a data conflict. Wew, ok, good work. On to the next part! - -## Registry Keys and Install Scripts - -```js -{ - "install_scripts": [ - { - "name": "SDB Compatibility", - "executable": "Install.bat", - "arguments": ["/silent"], - "requires_admin": true, - "platforms": ["win32", "win64"], - "completion_registry_key": { - "key": "Software\\My Game Company\\InstallScripts\\SDB-win32", - "value": 1 - } - } - ], - "registry_keys": [ - { - "key": "Software\\My Game Company\\My Awesome Game\\FixAspctRatio", - "value": "1" - } - ] -} -``` - -Some games may need specific registry keys set after installation, or might have some installation scripts that need to be run. If so, those can be set here! - -For installation scripts, `name` is a user friendly name that Discord will surface to users when explaining what's happening during the installation process. `executable` is the name of the script that needs to be run. `arguments` is an array that takes any arguments that may need to be passed to the script. `requires_admin`, when marked `true`, will run the install scripts with admin privileges; for some Windows users, this may force a User Access Control security popup. You must also tell Discord the `completion_registry_key` of the scripts, so that we know not to try and run the script again if the given registry key is found on the user's machine. - -`registry_keys` is a simple array of key/value pairs that will be written to the user's computer's registry. By default, Discord will create these keys in `HKEY_CURRENT_USER`. If your game requires registry keys in `HKEY_LOCAL_MACHINE`, they can be specified like: - -```js -{ - "key": "HKEY_LOCAL_MACHINE\\SOFTWARE\\My Game Company\\My Awesome Game\\MagicFix", - "value": "1" -} -``` - -Don't forget to the notice the double backward slashes in the path name! - -## Launch Options - -```js -{ - "launch_options": [ - { - "name": "My Awesome Game", - "executable": "my-awesome-game.exe", - "arguments": [], - "platforms": ["win32", "win64"], - "working_dir": "important-files-here/" - }, - { - "name": "My Awesome Map Editor", - "executable": "my-awesome-map-editor.exe", - "arguments": [], - "platforms": ["win32", "win64"], - "working_dir": "important-files-here/" - } - ] -} -``` - -The last bit of the config file is the launch options for your game. This is where you should tell Discord which executables your game can launch. In most cases, you'll just have one object, which is the main executable for your game. However, in the case that your game may have multiple executables that users can launch, you can specify all of them here. - -> warn -> The `name` field _must_ be unique for each launch option. - -When launching the game from their Game Library, players will be able to choose which executable is being launched, with the first option in the list as the default. So, for example, if your game comes with the game and a map editor, they'll have access to both without needing multiple entries in their library. Discord will smartly remember their choice for the future, but they'll always have the option to swap to a different one if they want. - -You can also specify any arguments that need to be passed to your game on launch, like `--fullscreen` or `--console` or `--360-no-scope`. - -If your game needs to have a specific working directly, you can also specify that here. Otherwise we'll default to the `content/` folder in the install directory. - -> danger -> Currently, `executable` and `working_dir` are relative to the _install path_ on a user's machine, not your local root. This path is `${INSTALLDIR}/content/` unless otherwise specified in [File Rules](#DOCS_DISPATCH_BRANCHES_AND_BUILDS/file-rules). - -## All Together Now - -Let's see what one looks like all together! - -```js -{ - "application": { - "id": 467102538279999224, - "manifests": [ - { - "label": "my-awesome-game/windows", - "platforms": ["win32", "win64"], - "locales": [], - "local_root": "./", - "file_rules": { - "mappings": [ - { - "local_path": ".", - "install_path": "." - } - ], - "properties": [ - { - "install_path": "save/*", - "attributes": ["user_data"] - } - ], - "exclusions": [ - { - "local_path": "**/*.pdb" - }, - { - "local_path": "**/*.verycoolfile" - } - ] - }, - "storage": { - "sync": true, - "roots": [ - { - "id": "my-save-files", - "paths": [ - { - "platform": "windows", - "path": "${DOCUMENTS}/My Games/My Awesome Game/Saves" - }, - { - "platform": "macos", - "path": "${DOCUMENTS}/Games/My Awesome Game/Saves" - } - ], - "patterns": ["**/*"] - } - ] - }, - "install_scripts": [ - { - "name": "SDB Compatibility", - "executable": "Install.bat", - "arguments": ["/silent"], - "requires_admin": true, - "platforms": ["win32", "win64"], - "completion_registry_key": { - "key": "Software\\My Game Company\\InstallScripts\\SDB-win32", - "value": 1 - } - } - ], - "registry_keys": [ - { - "key": "Software\\My Game Company\\My Awesome Game\\FixAspctRatio", - "value": "1" - } - ], - "launch_options": [ - { - "name": "My Awesome Game", - "executable": "my-awesome-game.exe", - "arguments": [], - "platforms": ["win32", "win64"], - "working_dir": "important-files-in-here/" - }, - { - "name": "My Awesome Map Editor", - "executable": "my-awesome-map-editor.exe", - "arguments": [], - "platforms": ["win32", "win64"], - "working_dir": "important-files-in-here/" - } - ] - } - ] - } -} -``` - -It seems like a lot of lines to parse, but now you know what they all mean! - -## Multiple Manifests and DLC Content - -If you're publishing a game with additional DLC content, this section is for you! Oftentimes in newer games, a user purchasing DLC content does not necessarily mean them downloading additional files to their computer, like expansion packs of ye olden days. A game will see that a user is entitled to a new thing, some flag in the code will flip, and presto! They can now explore the new area. - -However, some games do rely on downloading additional files for DLC content. If that is the case with your game, let's see how Dispatch can help. What's gonna help here is making use of multiple manifests. When you create a `config.json` file to upload your game, you've got something that looks sort of like this: - -```json -// A much smaller config example than the behemoth just above -{ - "application": { - "id": 1234567890, - "manifests": [ - { - // a bunch of stuff - } - ] - } -} -``` - -"manifests" is an array, which means it can contain multiple items. What you'll want to do is create two manifests: one for your base game, and one for your DLC. Depending on how your build folder is set up, you can exclude the DLC files from being uploaded when you upload the base game. Let's pretend your build folder—the one on your local computer that you're uploading from—looks like this: - -``` -game/ -|_ config.json -|_ build/ - |_ game_data - |_ Assets/ - |_ AssetBundles/ - |_ Base/ - |_ DLC/ -``` - -Your manifest would look something like this: - -``` -{ - "application": { - "id": your_app_id, - "manifests": [ - { - "label": "base-game", - "local_root": "build", - "file_rules": { - "mappings": [ - { - "local_path": ".", // This makes the files appear in the base content/ directory, trust me :D - "install_path": "." - } - ], - "exclusions": [ - { - "local_path": "./game_data/Assets/AssetBundles/DLC" // This manifest will NOT include the DLC - } - ] - }, - // The rest of the config with launch options, etc. - }, - { - "label": "dlc", // Now we have a second manifest for the DLC files - "local_root": "build/game_data/Assets/AssetBundles/DLC", // Uploading files from the DLC folder - "file_rules": { - "mappings": [ - { - "local_path": ".", - "install_path": "./game_data/Assets/AssetBundles/DLC" // Puts the DLC in the proper folder structure - } - ] - } - } - ] - } -} -``` - -So, what we've done is defined two manifests—or bundles of files—in one config. Now, how do we make 'em work? - -When you create SKUs in the dev portal, you assign manifests to SKUs. You'll want to assign `base-game`​ to your base game SKU, and `dlc` ​to your DLC. Now, when players buy your base game, they'll get entitlement to the `base-game` manifest, and Discord will only download that one. Once they purchase `dlc`, they'll become entitled to that manifest, and Discord will patch the game with the new content they received. - -## DRM - -You can choose to add DRM to your game. Dispatch will wrap your executables and prevent a user from launching the game if they are not logged into Discord. - -> danger -> This is a destructive operation. It will wrap your executable in place. It will not back up your executable. Make sure you have a backup somewhere else. - -If you understand and agree to the above, run the following command on each of the executables you want to wrap. - -``` -dispatch build drm-wrap -``` - -This function will only work with Windows executables. If you want to wrap a unix executable, you'll need to instead use [ValidateOrExit](#DOCS_GAME_SDK_APPLICATIONS/validateorexit). - -## Pushing Our First Build - -We've got all our files ready for processing; let's ship this baby! - -``` -dispatch build push -``` - -You'll see a bunch of request logging as it gets sent up to Discord—when it's done, you're greeted with a friendly completion message: - -``` -[2018-06-14][11:38:51][INFO] Pushed build. build_id=456889899375656960 -``` - -To double check that it's all ready for publishing, run: - -``` -dispatch build list -``` - -You'll see a table with the current status of your builds. Once the one you uploaded is marked as READY, you're good to publish it to the masses! - -## Publishing Our First Build - -Once you're ready to make a build live, run: - -``` -dispatch build publish -``` - -To make sure it worked, you can again run: - -``` -dispatch branch list -``` - -And look for a `LIVE_BUILD_ID` under your application! - -## Downloading a Build for Testing - -Now that you've got a build pushed and published, we should make sure that what you've sent out into the world works as intended. You can download your published build on a given branch to your local machine with the command: - -``` -dispatch build update --platform --locale -``` - -`install_path` is any filepath on your local machine; this is where the files will be downloaded to. `--platform` and `--locale` are whatever values you set as valid platforms and locales in your config file for a given build. The default probably looks something like `win64` and `en-US`. - -Here's an example of the command with our info: - -``` -dispatch build update 467102538279999224 456889899375659999 C:\my-game --platform win64 --locale en-US -``` - -Now you can run the executable for your game in that directory and make sure it works! - -## Patching - -If you need to push out a patch or a new build, no problem! Just repeat the process: - -- Update your game files locally to whatever they need to be -- Push your build again -- Publish the build again -- List your branches to make sure the new build is live - -Discord will do some magic in the background to diff your files, ensuring that your players only have to download the changes they need and letting them do it quickly so they can get back in the game. Or, really, without them even needing to know there _was_ a patch! Your players will automatically download your latest and greatest stuff, and quickly! - -If you have your own patcher and do not want Discord to handle patching, set `"should_patch": false` in the application configuration. If `should_patch` is false, Discord will install the game and never patch, even if you update your game. On uninstall, Discord will delete the entire directory, including saves and user data. - -```js -{ - "application": { - "id": 467102538279999224, - "should_patch": false, - } -} -``` diff --git a/docs/dispatch/Dispatch_and_You.md b/docs/dispatch/Dispatch_and_You.md deleted file mode 100644 index 4ecbdc1a5a..0000000000 --- a/docs/dispatch/Dispatch_and_You.md +++ /dev/null @@ -1,17 +0,0 @@ -# Dispatch and You - -> info -> Need help with Dispatch? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -If you're distributing a game on Discord, you'll need to send us the files! We quickly learned that email has a file size limit, and we ran out of carrier pigeons. Don't ask. Very touchy subject. - -In light of those learnings, we built Dispatch! Great name, isn't it? _Dis_-patch? Anyway, Dispatch is your all-in-one uploader tool to get your builds, artwork, patches, DLC, and anything else onto Discord's servers. It's fast, easy to use, and I've been assured by our engineers that it's made of 100% farm-raised, cage-free, grass-fed code. - -If you are looking to update your store page, head on over to our handy dandy [Developer Portal](https://discord.com/developers). It has all the tools you need to make changes to your store page for your game. To view your store page either make a store channel in the server you selected for your application and choose the SKU that you made the store page for or you can just hit the view store page button in the discord developer portal. - -Let's get you pointed in the right direction; follow this little Table of Contents to start, and you'll be a pro with this tool faster than you can say "But wait I don't actually know what a terminal is." - -1. [Creating Branches and Upload Builds](#DOCS_DISPATCH_BRANCHES_AND_BUILDS/) -2. [Pre-defined Field Values](#DOCS_DISPATCH_FIELD_VALUES/) - -Hey, some cool community folks have made unofficial tools around dispatch, check them out [here](#DOCS_TOPICS_COMMUNITY_RESOURCES). diff --git a/docs/dispatch/Error_Codes.md b/docs/dispatch/Error_Codes.md deleted file mode 100644 index a0aa444df7..0000000000 --- a/docs/dispatch/Error_Codes.md +++ /dev/null @@ -1,26 +0,0 @@ -# Error Codes - -> info -> Need help with Dispatch? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -This page outlines some of the common errors codes that may be encountered when using Dispatch. - -| Code | Name | Possible Solution | -| ---- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| 2020 | Request Signing Failed | Check user entitlement | -| 2022 | Disk Space Low | Free up disk space | -| 2023 | Disk Permission Denied | Choose a new location, or change write permissions on desired location | -| 2024 | Uninstall Failed | Attempt to manually remove game files from disk | -| 2025 | Install Script Failed | Restart Discord, attempt to uninstall/reinstall the game, ensure script is correct | -| 2029 | Build Not Found | Completely close and re-open Discord | -| 2051 | Panic! | Escalate in the #dispatch channel of the [Discord Developers Server](https://discord.gg/discord-developers) | -| 2058 | Too Many API Retries | Escalate in the #dispatch channel of the [Discord Developers Server](https://discord.gg/discord-developers) | -| 2059 | Failed to set Registry Key | User most likely denied Windows administrator permissions prompt. Try again, and accept the prompt | -| 2064 | Failed to Patch File | Attempted to patch the game while running: ensure the game process is entirely ended, try restarting Discord, try disabling antivirus | -| 2065 | No Manifests | Ensure that your manifests are properly selected in the Developer Portal for your SKU | -| 2069 | API Error | Intermittent API issues. Wait, escalate in the #dispatch channel of the [Discord Developers Server](https://discord.gg/discord-developers) | -| 2070 | Bad Response | Intermittent API issues. Wait, escalate in the #dispatch channel of the [Discord Developers Server](https://discord.gg/discord-developers) | -| 2073 | Not Entitled | Check that your manifests are properly configured in the Developer Portal. Have the user install the game from the Library, not the store page | -| 2076 | Two Clients Patching | User has multiple Discords open trying to patch the same game; only use one | -| 9001 | Unknown | Catch-all error code. Escalate in the #dispatch channel of the [Discord Developers Server](https://discord.gg/discord-developers) with repro steps/as much info as possible | - diff --git a/docs/dispatch/Field_Values.md b/docs/dispatch/Field_Values.md deleted file mode 100644 index 7594488aaa..0000000000 --- a/docs/dispatch/Field_Values.md +++ /dev/null @@ -1,48 +0,0 @@ -# Predefined Field Values - -> info -> Need help with Dispatch? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -## Manifests - -###### Platform Values - -| Platform | -| -------- | -| macos | -| win32 | -| win64 | -| linux | - -###### Redistributable values - -| Redistributable | -| -------------------------- | -| directx_june_2010 | -| vcredist_2005_x86 | -| vcredist_2008_sp1_x86 | -| vcredist_2010_x64 | -| vcredist_2010_x86 | -| vcredist_2012_update_4_x64 | -| vcredist_2012_update_4_x86 | -| vcredist_2013_x64 | -| vcredist_2013_x86 | -| vcredist_2015_x64 | -| vcredist_2015_x86 | -| vcredist_2017_x64 | -| vcredist_2017_x86 | -| xnafx_40 | - -###### Cloud Save Path Replacements - -| value | Windows path | macOS path | linux path | -| ---------------- | ----------------------------------------------------------------------------------- | ----------------------------- | ------------------- | -| \${HOME} | %USERPROFILE% | ~/ | ~/ | -| \${DOCUMENTS} | %USERPROFILE%\Documents | ~/Documents | \$XDG_DOCUMENTS_DIR | -| \${DATA} | %USERPROFILE%\AppData\Roaming | ~/Library/Application Support | \$XDG_DATA_HOME | -| \${DATALOCAL} | %USERPROFILE%\AppData\Local | ~/Library/Application Support | \$XDG_DATA_HOME | -| \${DATALOCALLOW} | %USERPROFILE%\AppData\LocalLow | ~/Library/Application Support | \$XDG_DATA_HOME | -| \${SAVEDGAMES} | %USERPROFILE%\Saved Games | (not supported) | (not supported) | -| \${INSTALLDIR} | the game's install directory | (same) | (same) | -| \${USERID} | the user's id - use within a path to define saves for multiple users | (same) | (same) | -| \${BRANCHID} | the id of the game branch - use within a path to define saves for multiple branches | (same) | (same) | diff --git a/docs/dispatch/List_of_Commands.md b/docs/dispatch/List_of_Commands.md deleted file mode 100644 index 4856f351ce..0000000000 --- a/docs/dispatch/List_of_Commands.md +++ /dev/null @@ -1,315 +0,0 @@ -# List of Commands - -> info -> Need help with Dispatch? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -Some people don't like to read full pages of documentation. Personally, I think those people are missing out. But we want to make sure that we cater to everyone, so here's a list of every Dispatch command and what it does. No frills, no jokes. Okay, maybe some jokes. - -## login - -Authorizes you to do these things! - -###### Arguments - -None - -###### Example - -``` --> dispatch login -[2018-09-17][15:26:15][INFO] Already logged in -``` - -## branch create - -Creates a new branch. If you have not yet made a master branch, this command will also automatically create a master branch. - -###### Arguments - -| name | values | description | -| -------------- | ------ | ----------------------------- | -| application_id | int | your application ID/client ID | -| branch_name | string | the name for your new branch | - -###### Example - -``` --> dispatch branch create 290926444748734465 "test" -Branch created with id 491362538965958686 -``` - -## branch delete - -Deletes a branch. - -###### Arguments - -| name | values | description | -| -------------- | ------ | ------------------------------ | -| application_id | int | your application ID/client ID | -| branch_id | int | the id of the branch to delete | - -###### Example - -``` --> dispatch branch delete 290926444748734465 491362538965958686 -``` - -## branch list - -Lists all branches for an application. - -###### Arguments - -| name | values | description | -| -------------- | ------ | ----------------------------- | -| application_id | int | your application ID/client ID | - -###### Example - -``` --> dispatch branch list 290926444748734465 -| APPLICATION ID | BRANCH ID | NAME | LIVE_BUILD_ID | CREATED AT | -| -------------------- | -------------------- | -------------------- | -------------------- | ------------------------------ | -| 290926444748734465 | 471164707759996234 | master | | 2018-07-24 04:00:20.146588Z | -``` - -## branch promote - -Promotes the live build of one branch to another. - -###### Arguments - -| name | values | description | -| ---------------- | ------ | --------------------------------- | -| application_id | int | your application ID/client ID | -| branch_id | int | the id of the branch to promote | -| target_branch_id | int | the id of the branch to overwrite | - -###### Example - -``` --> dispatch branch promote 290926444748734465 471164707759996234 491362538965958686 -``` - -## build delete - -Deletes a build from a branch. - -###### Arguments - -| name | values | description | -| -------------- | ------ | ----------------------------- | -| application_id | int | your application ID/client ID | -| build_id | int | the id of the build to delete | - -###### Example - -``` --> dispatch build delete 290926444748734465 491362538965958686 -``` - -## build drm-wrap - -Wraps your executable in Discord's DRM. This only works for Windows executables. If you want to DRM wrap a unix executable, you'll need to instead use [ValidateOrExit](#DOCS_GAME_SDK_APPLICATIONS/validateorexit). - -> danger -> This action is destructive and overwrites the executable. Make sure you've got a backup handy if needed! - -###### Arguments - -| name | values | description | -| ------------------ | --------- | ---------------------------------------------------------------------------------- | -| application_id | int | your application ID/client ID | -| path_to_executable | file path | the path to the executable, either explicit or relative to the dispatch executable | - -###### Example - -``` --> dispatch build drm-wrap 290926444748734465 C:\my-game\my-game.exe -``` - -## build list - -Lists the builds available on the given branch. - -###### Arguments - -| name | values | description | -| -------------- | ------ | ----------------------------- | -| application_id | int | your application ID/client ID | -| branch_id | int | the id of the branch to check | - -###### Example - -``` --> dispatch build list 290926444748734465 491362538965958686 -| APPLICATION ID | BUILD ID | STATUS | CREATION BRANCH | CREATED AT | -| -------------------- | -------------------- | -------------------- | -------------------- | ------------------------------ | -| 290926444748734465 | 489230031839821824 | READY | master | 2018-09-12 00:25:29.045554Z | -| 290926444748734465 | 479469321974841354 | READY | master | 2018-08-16 01:59:54.481336Z | -``` - -## build publish - -Marks a given build as the live build for a given branch. - -###### Arguments - -| name | values | description | -| -------------- | ------ | ------------------------------ | -| application_id | int | your application ID/client ID | -| branch_id | int | the id of the branch to check | -| build_id | int | the id of the build to publish | - -###### Example - -``` --> dispatch build publish 290926444748734465 491362538965958686 489230031839821824 -``` - -## build push - -Pushes a new build to the given branch. The JSON config file tells Dispatch how to organize the files and which to push relative to the `application_root`. - -###### Arguments - -| name | values | description | -| ---------------- | --------- | ----------------------------------------------------------------------------------------------------- | -| branch_id | int | the id of the branch to check | -| config_file | filename | the [JSON config file](#DOCS_DISPATCH_BRANCHES_AND_BUILDS/setting-up-our-first-build) for the build | -| application_root | file path | the directory that dispatch will treat as the local root for operations—`.` for the current directory | -| -c | flag | forces a re-chunk of files even if the edited file timestamp hasn't changed | -| -p | flag | automatically publishes the build if push is successful | - -###### Example - -``` --> dispatch build push 491362538965958686 config.json . -c -p -``` - -## build update - -Downloads the build for the given application id and branch id to the given install path, for the given platform and locale. - -`install_path` can be any file path on your machine to download the build to. - -###### Arguments - -| name | values | description | -| -------------- | ----------------------------------------------------------------- | ------------------------------ | -| application_id | int | your application ID/client ID | -| branch_id | int | the id of the branch to check | -| install_path | file path | the path to install to | -| --platform | [platform](#DOCS_DISPATCH_FIELD_VALUES/manifests-platform-values) | the build platform to download | -| --locale | [locale](#DOCS_REFERENCE/locales) | the build locale to download | - -###### Example - -``` --> dispatch build update 290926444748734465 491362538965958686 C:\my-game --platform win64 --locale en-US -``` - -## build corrupt - -Mark a build as corrupted. - -###### Arguments - -| name | values | description | -| -------------- | ------ | ------------------------------ | -| application_id | int | your application ID/client ID | -| build_id | int | the id of the build to corrupt | - -###### Example - -``` --> dispatch build corrupt 290926444748734465 491362538965958686 -Corrupted build "491362538965958686" -``` - -## build preview-files - -Displays a preview of the install paths that a build will put files in, for a given platform/locale. Additionally, will show which files are considered user data. - -###### Arguments - -| name | values | description | -| ---------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| config_file | filename | the [JSON config file](#DOCS_DISPATCH_BRANCHES_AND_BUILDS/setting-up-our-first-build) for the build | -| application_root | file path | the directory that dispatch will treat as the local root for operations—`.` for the current directory | -| --locale | [locale](#DOCS_REFERENCE/locales) | the build locale to preview | -| --platform | [platform](#DOCS_DISPATCH_FIELD_VALUES/manifests-platform-values) | the build platform to preview | - -###### Example - -``` --> build preview-files config.json . --locale en-US --platform win64 -``` - -## build repair - -Repairs an application build. - -###### Arguments - -| name | values | description | -| ---------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| application_id | int | your application ID/client ID | -| branch_id | int | the id of the branch to check | -| build_id | int | the id of the build to repair | -| application_root | file path | the directory that dispatch will treat as the local root for operations—`.` for the current directory | -| --platform | [platform](#DOCS_DISPATCH_FIELD_VALUES/manifests-platform-values) | the build platform to repair | - -###### Example - -``` --> build repair 290926444748734465 491362538965958686 489230031839821824 . --platform win64 -``` - -## build run-launch-setup - -Runs the launch setup for an application. - -###### Arguments - -| name | values | description | -| ---------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| application_root | file path | the directory that dispatch will treat as the local root for operations—`.` for the current directory | -| --platform | [platform](#DOCS_DISPATCH_FIELD_VALUES/manifests-platform-values) | the build platform to do the launch setup | - -###### Example - -``` --> build run-launch-setup . --platform win64 -``` - -## completions generate - -Generations shell command completions; run `dispatch completions --help` for more info, as it varies by shell - -###### Example - -``` --> dispatch completions generate --help -``` - -## manifest-label list - -Lists created manifest labels. These labels are created from the JSON config file when pushing builds. - -###### Arguments - -| name | values | description | -| -------------- | ------ | ----------------------------- | -| application_id | int | your application ID/client ID | - -###### Example - -``` --> dispatch manifest-label list 290926444748734465 -| APPLICATION ID | ID | NAME | -| -------------------- | -------------------- | -------------------- | -| 290926444748734465 | 471165178650999608 | my-game | -| 290926444748734465 | 471169990397324288 | my-game-dlc | -``` diff --git a/docs/game_and_server_management/Alpha_and_Beta_Testing.md b/docs/game_and_server_management/Alpha_and_Beta_Testing.md deleted file mode 100644 index ef18f08679..0000000000 --- a/docs/game_and_server_management/Alpha_and_Beta_Testing.md +++ /dev/null @@ -1,52 +0,0 @@ -# Alpha and Beta Testing - -Alphas and betas are a critical part of a game's development lifecycle. We understand the value of getting feedback early and often and involving your community early on. That's why we've built some awesome alpha and beta testing functionality for you! - -## Beta Entitlements - -As you develop your game, you'll constantly be making improvements. You want real-time feedback from players, and there's no better place for that than Discord. In the past, players would need to dig through menus to find out how to unlock test branches, as well as remembering secret passwords _and_ typing them in correctly. Was that an `I` or an `l`? `O` or `0`? - -One of the cool features of [Store Channels](#DOCS_GAME_AND_SERVER_MANAGEMENT_SPECIAL_CHANNELS/) is their ability to grant a Beta Entitlement for your game. That means that while you keep your master branch safe and sound, users can test on `beta-branch-please-ignore-bugs` in an isolated test environment. - -> info -> To learn more about creating branches, start with [Dispatch and You](#DOCS_DISPATCH_DISPATCH_AND_YOU/). - -When you create a store channel in your server, you'll be prompted with a choice to make it a Beta channel. That will allow you to grant users entitlement to the non-master branch of your game when visiting the store page. When your beta period is over and your game is ready for release and purchase, you can brick/delete these branches if you choose, and prompt users to get the full game by putting up a new store channel that's not Beta. - -> warn -> Beta branches cannot be sold. If a user has access to a store channel for a beta entitlement, they will be able to claim it for free. - -## Role-Based Entitlement - -So, you've started getting a bunch of fans into your server. Hundreds, thousands, millions! Uh, if you hit millions, let us know. Within your hordes of adoring fans lies your Trusted Testing Group (tm), the best of the best when it comes to feedback. Maybe you want to give them access to a special `insider-only-private-beta` branch of your game. You can, with role-based entitlement! - -Store channels in Discord are just like any other channel in that they can have a set of permissions applied to them. What is special about them is that if they are set as a Beta branch, entitlement to that branch is tied to having permissions to view the channel. So, all your testers with a `Beta Tester` role, for example, will be able to get entitlement to the `insider-only-private-beta` branch. And, if they are later removed from that role, entitlement from that branch will be removed. - -A scenario might look like this: you create a Beta store channel. Only users with role `Awesome Tester` can view it. Player A is an `Awesome Tester`. They can view the channel and get entitlement to the beta branch. Player A becomes inactive; you remove them from the `Awesome Tester` role. Player A loses entitlement to `insider-only-private-beta` - -> danger -> Role-based entitlement will only work if you use Discord's DRM, which is a simple entitlement check. You can do that with the Dispatch command [`dispatch build drm-wrap`](#DOCS_DISPATCH_LIST_OF_COMMANDS/build-drmwrap) or by calling [`ApplicationManager.ValidateOrExit()`](#DOCS_GAME_SDK_APPLICATIONS/validateorexit). Otherwise, Discord will not check the user's entitlement when the game starts. - -Role-based entitlements help keep your beta community active and interactive, while also providing a way to reward your biggest fans with secret access to special things. - -## Gift Codes - -> warn -> Note that only Approved Games can generate gift codes. - -If you want to push out access to your game at scale, rather than inviting people into your server and giving them a role, you can create gift codes in the Developer Portal! Gift codes are like game keys on other platforms that you might be familiar with, with an added bonus: the ability to specify the entitlement time window. That means that you can create codes for your game to give to everyone, but they won't be able to play until the `VALID FROM` date. Similarly, you can set a `VALID UNTIL` date on all your gift codes if you want the entitlements to expire after a certain time, removing access from players. - -Note that the `VALID UNTIL` date will not remove the game from the user's library, but they will no longer be able to launch it. - -Up to 25,000 gift codes can be created at a time. When you go to create gift codes, you'll notice there are a few fields for you to fill out. Here's what they mean: - -![Screenshot of the modal to create gift codes with on the developer dashboard](gift-code-creation.png) - -- SKU: the SKU that you want the user to get -- Branch: the branch of your game you want the user to get -- Valid From: the start date at which the entitlement is valid -- Valid Until: the date at which the entitlement is no longer valid -- Amount: the number of codes you want to create (up to 25,000 per batch) -- Description: a short description of why you made the codes, so you remember! - -Once you've made your codes, you can download the `.csv` that has them all in it, and distribute them however you'd like! diff --git a/docs/game_and_server_management/How_to_Get_Your_Game_on_Discord.md b/docs/game_and_server_management/How_to_Get_Your_Game_on_Discord.md deleted file mode 100644 index 0e709c0a80..0000000000 --- a/docs/game_and_server_management/How_to_Get_Your_Game_on_Discord.md +++ /dev/null @@ -1,110 +0,0 @@ -# How to Get Your Game on Discord - -> danger -> Selling SKUs on Discord has been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -Welcome, adventurer! If you're here, you want to find out how to get your game up and running on Discord and learn about our awesome Server Commerce features. You've come to the right place, so let's get started. - -## Get the Band Back Together - -First things first: it's time to make a Team. Your dream team of artists, writers, engineers, designers, all working on the beautiful creation of Your Game. Build your dream team with these steps: - -1. Create a new Team at [https://discord.com/developers/teams](https://discord.com/developers/teams) -2. Invite people! - -You've now passed the first test with flying colors! - -## Apps and Games - -Throughout this process, we'll reference your "app". An app, or application, is an entity on Discord that represents something you've built: a bot, an OAuth2 app, or in this case your game. We need to make an app for your game: - -1. Create a new app at [https://discord.com/developers/applications](https://discord.com/developers/applications) -2. When doing so, make sure you select your team from the `Team` dropdown - -> info -> All apps on Discord for games must be owned by a team. If you accidentally made a personal app, you can transfer it to a team at any time by hitting the `Transfer App to Team` button within the app. - -You're now looking at a blank slate of creativity. Give your game a pretty icon and exciting description here. Next, click on `Server Commerce Checklist` in the sidebar, and we'll get to the good stuff. - -## Join the Club - -From here on forward, we'll be managing settings that tie directly to your game and its presence on Discord. We want to make sure that we're laser-focused on making the best experience possible for game developers, so we ask that you pay our Application License Fee in order to get access to our store tools, which let you: - -- make store pages for your game directly in your server -- run alpha and beta tests for your game, using the magic of channel permissions to create role-based page access -- apply for approval, letting you start making money (if you want to) - -Now that we've weeded out the trolls, let's get your server up and running with some fancy new features! - -## Your Server - Your Kingdom - -You're now looking at a checklist with a bunch of items and a prompt to select a server. Your Application License Fee entitles you to the following features in your server: - -- store channel creation -- alpha and beta testing - -Select the server that you wish to grant these features, confirm to be double-extra-super-sure, and poof! You can now create channels for both news and store pages in your server. To learn more about store page channels, read [Store Channels](#DOCS_GAME_AND_SERVER_MANAGEMENT_SPECIAL_CHANNELS/store-channels). To learn more about Announcement Channels, read [Announcement Channels](#DOCS_GAME_AND_SERVER_MANAGEMENT_SPECIAL_CHANNELS/announcement-channels). To learn about alpha and beta testing, read [Alpha and Beta Testing](#DOCS_GAME_AND_SERVER_MANAGEMENT_ALPHA_AND_BETA_TESTING/) -(you get the gist). - -Now, what do you do next? You start building! Build your community, make your game, invite testers into your server to get hype! Now is a great time to get familiar with all the tools at your disposal like: - -- [Discord's Developer Portal](https://discord.com/developers/applications), your one-stop-game-management-shop -- [The Discord Game SDK](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/), a spellbook full of useful game dev incantations including Discord's crystal-clear voice chat -- [Dispatch](#DOCS_DISPATCH_DISPATCH_AND_YOU/), Discord's game patcher and downloader that's so fast, you'll swear it's magic - -To guide you through this process to success, you can follow that handy checklist. It'll tell you the steps you need to complete, show you where and how to do them, and give you a blissful sense of satisfaction as the green check marks pile up. - -The next and final step in your on-boarding journey is getting approved. Once approved, you'll be able to start making money (or karma), and you'll unlock a ton of cool server customization features to make your kingdom cozy for your players. - -## Testing Your Game - -As you go through the process of building your game and getting it ready for release on Discord, you'll want to be able to test it! One way to do so is to utilize Beta Store channels, which you can learn more about here: [Alpha and Beta Testing](#DOCS_GAME_AND_SERVER_MANAGEMENT_ALPHA_AND_BETA_TESTING/). - -If you are on the Team that owns the game, you can also use another special system for testing that we call Application Test Mode. Test Mode allows you to purchase unavailable, unlisted SKUs for free, making it helpful for testing IAP flows. It also automatically makes every branch of your game show up in your Library when Test Mode is turned on. You can read more here: [Application Test Mode](#DOCS_GAME_SDK_STORE/application-test-mode). - -## Getting Approved - -Your game looks wonderful. You've got a beautiful store page, and awesome game, and an eager community. It's time to get approved and plant your flag. Note that if your game is in an alpha or beta state, or you're interesting in Early Access, you are welcome to apply for approval whenever you are ready. Your game doesn't need to be the finished 1.0 version; give us something you'd feel comfortable giving to your players. - -Once you've completed all the items on your checklist, the `Get Approved` button will unlock. Once you submit for approval, Discord will take a look at the following things: - -- your game, to ensure that it runs properly and follows our [Game Submission Guidelines](https://support-dev.discord.com/hc/en-us/articles/360025028592-Game-Submission-Guidelines) -- your store pages, to provide helpful guidance in putting your best foot forward -- your server, to make sure you're abiding by our [Community Guidelines](https://discord.com/guidelines) - -If we need to see some changes, you'll receive an email directing you on your steps to success. Otherwise, you'll get **APPROVED!!!!!!!!!!** What's that mean? It means you can start charging for your game and making money! Don't spend it all in one place. Or do. Treat yourself. Or maybe you want your game to be **free!** Live your best life. We support you! - -## Available vs Store Page Published - -Now that you've been approved, there's a couple new toggles to flip in the Developer Portal: `Available`, and `Store Page Published`. - -![Screenshot of the SKU information page with the "available" and "store page published" switches highlighted](available-published.png) - -`Available` means that your game is now available for normal purchase/distribution; your non-beta store channel will no longer say `Coming soon!` when this is toggled, and users will be able to get your game. - -> danger -> Toggling your SKU as `Available` will make it available for purchase; make sure your price is set properly! - -If you no longer wish to make your SKU available for sale, you can again mark it unavailable. This will not remove it from users' libraries; it just stops new users from purchasing it. - -`Store Page Published` will do...well, what it says! Though your store page is always visible from within the channel in your server, there are other things that require you to explicitly mark your page viewable from outside that context, like gift code embeds for your game, or from in-game store fronts! - -TL;DR - When you're ready to distribute your game after getting approved, turn those two toggles on. - -## Getting Verified and Discovered - -Apart from being approved, you can also get your server Verified. Getting verified means your server will get that oh-so-exclusive checkmark, to let people know you're the legit home for your game. - -To get verified, head on over to our [Verification page](https://discord.com/verification) and fill out the form at the bottom. - -## Make Good Decisions - -Your game is off and running, and we couldn't be more proud. As it flourishes and your playerbase grows, keep an eye on your analytics, which you can find within your app. They'll give you important insight like revenue data, acquisitions funnels, and player retention to help you make informed decision about your game's lifecycle. Check out our [Analytics Help Article](https://support-dev.discord.com/hc/en-us/articles/360024852152) to learn more about how to read all those charts, and how to do things like [Conversion Tracking with UTM Links](https://support-dev.discord.com/hc/en-us/articles/360025153051-How-to-track-conversions-with-UTM-links). - -## What Comes Next - -Your game's success is in your own hands now. Continue to build your community and keep them informed with updates. Consider integrating more deeply with Discord to deliver cool features to your players; we recommend [Rich Presence Game Invites](#DOCS_GAME_SDK_ACTIVITIES/). They feel like magic. Drop new content by creating DLC and IAP for your game, run fun tournaments based on [HypeSquad Membership](#DOCS_GAME_SDK_USERS/currentuserhasflag), give something cool to [Discord Nitro Subscribers](#DOCS_GAME_SDK_USERS/getcurrentuserpremiumtype) because they're awesome. Or maybe, just maybe, think about your next upcoming game? - -We'd love to help you with that one, too. - --- Discord <3 diff --git a/docs/game_and_server_management/Special_Channels.md b/docs/game_and_server_management/Special_Channels.md deleted file mode 100644 index b61a2ca5cf..0000000000 --- a/docs/game_and_server_management/Special_Channels.md +++ /dev/null @@ -1,38 +0,0 @@ -# Store Channels - -> danger -> Selling SKUs on Discord has been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - - -One of the new flagship features of Verified Servers and Game Servers are the ability to host your store pages directly in your server. We know that community is everything for games, and that your server is already the place where your community lives. Rather than the friction and impersonality of traditional storefronts, you can let your fans find your game in the cozy comfort of their home (your server!). - -In order to create a store channel for your game, you'll need to make sure you've followed [the Walkthrough](#DOCS_GAME_AND_SERVER_MANAGEMENT_HOW_TO_GET_YOUR_GAME_ON_DISCORD/) up to `Getting Approved`. If you've done that, you'll see in your server that you can create a new channel type: `Store`. - -![Screenshot of the modal to create a new Store Channel](create-store-channel.png) - -Along with entering a name for your new channel, you'll be prompted to select an application. This should be the app you created when you started; it's the one linked to your server. After selecting your app, you'll be prompted to select a SKU. What's a SKU? A SKU is simply a thing that a user can buy. This is most likely your game (a SKU of type `Game`), but could also be `IAP`, `DLC`, or a `Bundle`. - -You can create SKUs to put up for sale by going to your app and selecting `SKU Management`. - -![Screenshot of the SKU Management dashboard](sku-management.png) - -Once you select your SKU and create your channel...it's there! You now have a store channel in your server! If you want, you can set permissions on this channel like any channel in a server to restrict who can see it. Learn more about that by reading [Alpha and Beta Testing](#DOCS_GAME_AND_SERVER_MANAGEMENT_ALPHA_AND_BETA_TESTING/). - -## Lurker Mode - -Store channels also have a special property that we call `lurkability`. "Lurker Mode" is a feature that we've been experimenting with throughout Discord; it allows users to read public channels in a server without _actually_ joining the server. It also allows users to look at these channels without being logged in to Discord. - -> info -> Users viewing channels in Lurker Mode will not be able to send messages in those channels. They'll be prompted to log in and join the server if they want to chat. - -Lurker Mode is automatically enabled for store channels in your server. When someone accepts a server invite that links to a store channel, they'll be viewing in "Lurker Mode". - -We know that your server and community are at the heart of your game's success on Discord. Having Discord users see your store pages in the context of your server will help drive more members to your community and help you grow that awesome fanbase for your game. - -# Announcement Channels - -As part of our ongoing effort to help you build your game community, all [Community servers](https://dis.gd/communityservers) have the ability to create Announcement Channels! - -Unlike a regular text channel, Announcement Channels comes with a “Follow” button that allows your superfans to hook and connect your channel into their own personal servers. Now, select messages in your Announcement Channels can be "published" in your players' friend servers as regular messages, allowing them to get the latest updates of their favorite game in the places they hang out most. Because these posts appear and function as messages, it means that everyone in your superfan’s server can find them, discuss them live, and receive them as mobile notifications, if they have this setting enabled. - -[Learn more here](https://support.discord.com/hc/en-us/articles/360032008192). diff --git a/docs/game_sdk/Achievements.md b/docs/game_sdk/Achievements.md deleted file mode 100644 index 3253cb64c9..0000000000 --- a/docs/game_sdk/Achievements.md +++ /dev/null @@ -1,386 +0,0 @@ -# Achievements - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -There's no feeling quite like accomplishing a goal that you've set out to achieve. Is killing 1000 zombies in a game as great an achievement as climbing Mt. Everest? Of course it is, and I didn't even have to leave my house. So get off my back, society. - -Anyway—Discord has achievements! Show your players just how successful they are. - -Achievements are managed in the [Developer Portal](https://discord.com/developers/applications). Head over to your application --> `Achievements` to create and manage achievements for your game. You'll give them an icon, a name, and a description; then they'll be assigned an id. - -You can also mark achievements as `secret` and `secure`. "Secret" achievements will _not_ be shown to the user until they've unlocked them. "Secure" achievements can only be set via HTTP calls from your server, _not_ by a game client using the SDK. No cheaters here! - -## Data Models - -###### Achievement Struct - -| name | type | description | -| ------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------- | -| application_id | Int64 | Unique ID of the application | -| name | string | Name of the achievement | -| name_localizations | ?dictionary with keys as [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `name` field | -| description | string | Description of the achievement | -| description_localizations | ?dictionary with keys as [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `description` field | -| secret | boolean | If the achievement is secret | -| secure | boolean | If the achievement is secure | -| id | Int64 | Unique ID of the achievement | -| icon_hash | string | [Hash of the icon](#DOCS_REFERENCE/image-formatting) | - -###### User Achievement Struct - -| name | type | description | -| --------------- | ------ | ------------------------------------------------------------------------------------------ | -| UserId | Int64 | the unique ID of the user working on the achievement | -| AchievementId | Int64 | the unique ID of the achievement | -| PercentComplete | UInt8 | how far along the user is to completing the achievement (0-100) | -| UnlockedAt | string | the timestamp at which the user completed the achievement (PercentComplete was set to 100) | - -## SetUserAchievement - -Updates the current user's status for a given achievement. If `percentComplete` is set to `100`, the `UnlockedAt` field will be automatically updated with the current timestamp. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| --------------- | ----- | -------------------------------------- | -| achievementId | Int64 | the ID of the achievement to update | -| percentComplete | UInt8 | the user's updated percentage progress | - -###### Example - -```cs -achievementManager.SetUserAchievement(580159119969878046, 25, (res) => -{ - if (res == Discord.Result.Ok) - { - Console.WriteLine("Achievement updated for user"); - } -}); -``` - -## FetchUserAchievements - -Loads a stable list of the current user's achievements to iterate over. If the user has any achievements, do your iteration within the callback of this function. - -Returns `Discord.Result` via callback. - -> info -> Remember to only iterate when there are results! - -###### Parameters - -None. - -###### Example - -```cs -achievementManager.FetchUserAchievements((res) => -{ - if (res == Discord.Result.Ok) - { - // Count() - // for() loop - } -}); -``` - -## CountUserAchievements - -Counts the list of a user's achievements for iteration. - -Returns `Int32`. - -###### Parameters - -None - -###### Example - -```cs -achievementManager.FetchUserAchievements((res) => -{ - if (res == Discord.Result.Ok) - { - Console.WriteLine("User has {0} achievements for this game", achievementManager.CountUserAchievements()); - } -}); -``` - -## GetUserAchievementAt - -Gets the user's achievement at a given index of their list of achievements. - -Returns `Discord.UserAchievement` - -###### Parameters - -| name | type | description | -| ----- | ----- | ----------------------------------------- | -| index | Int32 | the index at which to get the achievement | - -###### Example - -```cs -achievementManager.FetchUserAchievements((res) => -{ - if (res == Discord.Result.Ok) - { - for (int i = 0; i < achievementManager.CountUserAchievements(); i++) - { - var achievement = achievementManager.GetUserAchievementAt(i); - Console.WriteLine("Achievement progress for {0} for user {1}: {2}", - achievement.AchievementId, - achievement.UserId, - achievement.PercentComplete); - } - } -}); -``` - -## GetUserAchievement - -Gets the user achievement for the given achievement id. If you keep a hardcoded mapping of `achievement <--> ID` in your codebase, this will be better than iterating over each achievement. Make sure to call `FetchUserAchievements()` first still! - -###### Parameters - -| name | type | description | -| ------------- | ----- | -------------------------------- | -| achievementId | Int64 | the ID of the achievement to get | - -###### Example - -```cs -achievementManager.FetchUserAchievements((res) => -{ - if (res == Discord.Result.Ok) - { - var achievement = achievementManager.GetUserAchievement(580159119969878046); - Console.WriteLine("Achievement progress for {0} for user {1}: {2}", - achievement.AchievementId, - achievement.UserId, - achievement.PercentComplete); - } -}); -``` - -## OnUserAchievementUpdate - -Fires when an achievement is updated for the currently connected user - -###### Parameters - -| name | type | description | -| ----------- | ------------------- | -------------------------------- | -| achievement | ref UserAchievement | the achievement that was updated | - -## The API Way - -Below are the API endpoints and the parameters they accept. If you choose to interface directly with the Discord API, you will need a "Bot token". This is a special authorization token with which your application can access Discord's HTTP API. Head on over to [your app's dashboard](https://discord.com/developers/), and hit the big "Add a Bot User" button. From there, mutter _abra kadabra_ and reveal the token. This token is used as an authorization header against our API like so: - -``` -curl -x POST -h "Authorization: Bot " https://discord.com/api/some-route/that-does-a-thing -``` - -> info -> Make sure to prepend your token with "Bot"! - -## Get Achievements % GET /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/achievements - -Returns all achievements for the given application. This endpoint has a rate limit of 5 requests per 5 seconds per application. - -###### Return Object - -```json -[ - { - "application_id": "461618159171141643", - "name": { - "default": "Win the Game" - }, - "description": { - "default": "You won!" - }, - "secret": false, - "icon_hash": "52c1636444f64ad7cb5368b158847def", - "id": "580159119969878046", - "secure": false - } -] -``` - -## Get Achievement % GET /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/achievements/{achievement.id#DOCS_GAME_SDK_ACHIEVEMENTS/data-models-achievement-struct} - -Returns the given achievement for the given application. This endpoint has a rate limit of 5 requests per 5 seconds per application. - -###### Return Object - -```json -{ - "application_id": "461618159171141643", - "name": { - "default": "Win the Game" - }, - "description": { - "default": "You won!" - }, - "secret": false, - "icon_hash": "52c1636444f64ad7cb5368b158847def", - "id": "580159119969878046", - "secure": false -} -``` - -## Create Achievement % POST /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/achievements - -Creates a new achievement for your application. Applications can have a maximum of 1000 achievements. This endpoint has a rate limit of 5 requests per 5 seconds per application. - -###### Parameters - -| name | type | description | -| ----------- | --------- | --------------------------------------- | -| name | string | the name of the achievement | -| description | string | the user-facing achievement description | -| secret | bool | if the achievement is secret | -| secure | bool | if the achievement is secure | -| icon | ImageType | the icon for the achievement | - -###### Example: Creating an Achievement - -```json -{ - "name": { - "default": "Find the Secret" - }, - "description": { - "default": "You found it!" - }, - "secret": true, - "secure": false, - "icon": "data:image/png;base64,base64_data_here" -} -``` - -###### Return Object - -```json -{ - "application_id": "461618159171141643", - "name": { - "default": "Find the Secret" - }, - "description": { - "default": "You found it!" - }, - "secret": true, - "icon_hash": "52c1636444f64ad7cb5368b158847def", - "id": "597763781871861018", - "secure": false -} -``` - -## Update Achievement % PATCH /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/achievements/{achievement.id#DOCS_GAME_SDK_ACHIEVEMENTS/data-models-achievement-struct} - -Updates the achievement for **\_\_ALL USERS\_\_**. This is **NOT** to update a single user's achievement progress; this is to edit the UserAchievement itself. This endpoint has a rate limit of 5 requests per 5 seconds per application. - -###### Parameters - -| name | type | description | -| ----------- | --------- | --------------------------------------- | -| name | string | the name of the achievement | -| description | string | the user-facing achievement description | -| secret | bool | if the achievement is secret | -| secure | bool | if the achievement is secure | -| icon | ImageType | the icon for the achievement | - -###### Example: Updating an Achievement - -```json -{ - "name": { - "default": "How do methods break up?" - }, - "description": { - "default": "They stop calling each other!" - }, - "secret": false, - "secure": false, - "icon": "data:image/png;base64,base64_data_here" -} -``` - -###### Return Object - -```json -{ - "application_id": "461618159171141643", - "name": { - "default": "How do methods break up?" - }, - "description": { - "default": "They stop calling each other!" - }, - "secret": false, - "icon_hash": "7d698b594c691e3d28c92e226b28293c", - "id": "597638720379682816", - "secure": false -} -``` - -## Delete Achievement % DELETE /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/achievements/{achievement.id#DOCS_GAME_SDK_ACHIEVEMENTS/data-models-achievement-struct} - -Deletes the given achievement from your application. This endpoint has a rate limit of 5 requests per 5 seconds per application. - -###### Return Object - -```json -// 204 No Content -``` - -## Update User Achievement % PUT /users/{user.id#DOCS_RESOURCES_USER/user-object}/applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/achievements/{achievement.id#DOCS_GAME_SDK_ACHIEVEMENTS/data-models-achievement-struct} - -Updates the UserAchievement record for a given user. Use this endpoint to update `secure` achievement progress for users. This endpoint has a rate limit of 5 requests per 5 seconds per application. - -###### Parameters - -| name | type | description | -| ---------------- | ---- | ------------------------------------------------------ | -| percent_complete | int | the user's progress towards completing the achievement | - -###### Return Object - -```json -{} -``` - -## Get User Achievements % GET /users/@me/applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/achievements - -Returns a list of achievements for the user whose token you're making the request with. This endpoint will **NOT** accept the Bearer token for your application generated via the [Client Credentials Grant](#DOCS_TOPICS_OAUTH2/client-credentials-grant). You will need the _user's_ bearer token, gotten via either the [Authorization Code OAuth2 Grant](#DOCS_TOPICS_OAUTH2/authorization-code-grant) or via the SDK with [GetOAuth2Token](#DOCS_GAME_SDK_APPLICATIONS/getoauth2token). This endpoint has a rate limit of 2 requests per 5 seconds per application per user. - -> info -> This endpoint will _not_ return any achievements marked as `secret` that the user has not yet completed. - -###### Return Object - -```json -[ - { - "application_id": "461618159171141643", - "name": { - "default": "Win the Game" - }, - "description": { - "default": "You won!" - }, - "secret": false, - "icon_hash": "52c1636444f64ad7cb5368b158847def", - "id": "580159119969878046", - "secure": false - } -] -``` diff --git a/docs/game_sdk/Activities.md b/docs/game_sdk/Activities.md deleted file mode 100644 index b62bd7088f..0000000000 --- a/docs/game_sdk/Activities.md +++ /dev/null @@ -1,561 +0,0 @@ -# Activities - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -Looking to integrate Rich Presence into your game? No need to manage multiple SDKs—this one does all that awesome stuff, too!. Delight your players with the ability to post game invites in chat and party up directly from Discord. Surface interesting live game data in their profile and on the Games Tab for their friends, encouraging them to group up and play together. - -For more detailed information and documentation around the Rich Presence feature set and integration tips, check out our [Rich Presence Documentation](https://discord.com/developers/docs/rich-presence/how-to). - -## Data Models - -###### User Struct - -| name | type | description | -| ------------- | ------ | ----------------------------- | -| Id | Int64 | the user's id | -| Username | string | their name | -| Discriminator | string | the user's unique discrim | -| Avatar | string | the hash of the user's avatar | -| Bot | bool | if the user is a bot user | - -###### Activity Struct - -| name | type | description | -| ------------- | ------------------ | --------------------------------------------------------------- | -| ApplicationId | Int64 | your application id - this is a read-only field | -| Name | string | name of the application - this is a read-only field | -| State | string | the player's current party status | -| Details | string | what the player is currently doing | -| Timestamps | ActivityTimestamps | helps create elapsed/remaining timestamps on a player's profile | -| Assets | ActivityAssets | assets to display on the player's profile | -| Party | ActivityParty | information about the player's party | -| Secrets | ActivitySecrets | secret passwords for joining and spectating the player's game | -| Instance | bool | whether this activity is an instanced context, like a match | - -###### ActivityTimestamps Struct - -| name | type | description | -| ----- | ----- | ------------------------------------------------------ | -| Start | Int64 | unix timestamp - send this to have an "elapsed" timer | -| End | Int64 | unix timestamp - send this to have a "remaining" timer | - -###### ActivityAssets Struct - -| name | type | description | -| ---------- | ------ | ------------------------------------------------------------------------------------- | -| LargeImage | string | see [Activity Asset Image](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-asset-image) | -| LargeText | string | hover text for the large image | -| SmallImage | string | see [Activity Asset Image](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-asset-image) | -| SmallText | string | hover text for the small image | - -###### ActivityParty Struct - -| name | type | description | -| ---- | --------- | ---------------------------------- | -| Id | string | a unique identifier for this party | -| Size | PartySize | info about the size of the party | - -###### PartySize Struct - -| name | type | description | -| ----------- | ----- | ---------------------------------- | -| CurrentSize | Int32 | the current size of the party | -| MaxSize | Int32 | the max possible size of the party | - -###### ActivitySecrets Struct - -| name | type | description | -| -------- | ------ | -------------------------------------------- | -| Match | string | unique hash for the given match context | -| Join | string | unique hash for chat invites and Ask to Join | -| Spectate | string | unique hash for Spectate button | - -###### ActivityType Enum - -| name | Value | -| --------- | ----- | -| Playing | 0 | -| Streaming | 1 | -| Listening | 2 | -| Watching | 3 | -| Custom | 4 | -| Competing | 5 | - -For more details about the activity types, [see Gateway documentation](#DOCS_TOPICS_GATEWAY_EVENTS//activity-object-activity-types). - -`ActivityType` is strictly for the purpose of handling events that you receive from Discord; though the SDK/our API will not reject a payload with an `ActivityType` sent, it will be discarded and will not change anything in the client. - -###### ActivityJoinRequestReply Enum - -| name | value | -| ------ | ----- | -| No | 0 | -| Yes | 1 | -| Ignore | 2 | - -###### ActivityActionType Enum - -| name | value | -| -------- | ----- | -| Join | 1 | -| Spectate | 2 | - -## Activity Action Field Requirements - -If you want to hook up joining and spectating for your games, there are certain fields in the activity payload that need to be sent. Refer to the following handy table for what needs to be set for certain actions. - -###### Requirements - -| Field | Custom Artwork | Spectate | Join | Ask to Join | -| ------------------------------ | :------------: | :------: | :--: | :---------: | -| State | | | | | -| Details | | | | | -| ActivityTimestamps.Start | | | | | -| ActivityTimestamps.End | | | | | -| ActivityAssets.LargeImage | x | | | | -| ActivityAssets.SmallImage | x | | | | -| ActivityAssets.LargeText | x | | | | -| ActivityAssets.SmallText | x | | | | -| ActivityParty.Id | | | x | x | -| ActivityParty.Size.CurrentSize | | | x | x | -| ActivityParty.Size.MaxSize | | | x | x | -| ActivitySecrets.Join | | | x | x | -| ActivitySecrets.Spectate | | x | | | - -## RegisterCommand - -Registers a command by which Discord can launch your game. This might be a custom protocol, like `my-awesome-game://`, or a path to an executable. It also supports any launch parameters that may be needed, like `game.exe --full-screen --no-hax`. - -On macOS, due to the way Discord registers executables, your game needs to be bundled for this command to work. That means it should be a `.app`. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------- | ------ | ----------------------- | -| command | string | the command to register | - -###### Example - -```cs -activityManager.RegisterCommand("my-awesome-game://run --full-screen"); -``` - -## RegisterSteam - -Used if you are distributing this SDK on Steam. Registers your game's Steam app id for the protocol `steam://run-game-id/`. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------- | ------ | ------------------------ | -| steamId | UInt32 | your game's Steam app id | - -###### Example - -```cs -activityManager.RegisterSteam(1938123); -``` - -## UpdateActivity - -Sets a user's presence in Discord to a new activity. This has a rate limit of 5 updates per 20 seconds. - -> info -> It is possible for users to hide their presence on Discord (User Settings -> Game Activity). Presence set through this SDK may not be visible when this setting is toggled off. - -Returns a `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| -------- | -------- | ----------------------------- | -| activity | Activity | the new activity for the user | - -###### Example - -```cs -var activity = new Discord.Activity -{ - State = "In Play Mode", - Details = "Playing the Trumpet!", - Timestamps = - { - Start = 5, - }, - Assets = - { - LargeImage = "foo largeImageKey", // Larger Image Asset Value - LargeText = "foo largeImageText", // Large Image Tooltip - SmallImage = "foo smallImageKey", // Small Image Asset Value - SmallText = "foo smallImageText", // Small Image Tooltip - }, - Party = - { - Id = "foo partyID", - Size = { - CurrentSize = 1, - MaxSize = 4, - }, - }, - Secrets = - { - Match = "foo matchSecret", - Join = "foo joinSecret", - Spectate = "foo spectateSecret", - }, - Instance = true, -}; - -activityManager.UpdateActivity(activity, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Success!"); - } - else - { - Console.WriteLine("Failed"); - } -}); -``` - -## ClearActivity - -Clear's a user's presence in Discord to make it show nothing. - -Results a `Discord.Result` via callback. - -###### Parameters - -None - -###### Example - -```cs -activityManager.ClearActivity((result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Success!"); - } - else - { - Console.WriteLine("Failed"); - } -}); -``` - -## SendRequestReply - -Sends a reply to an Ask to Join request. - -Returns a `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------ | ------------------------ | ------------------------------------------- | -| userId | Int64 | the user id of the person who asked to join | -| reply | ActivityJoinRequestReply | No, Yes, or Ignore | - -###### Example - -```cs -activityManager.OnActivityJoinRequest += user => -{ - Console.WriteLine("Join request from: {0}", user.Id); - activityManager.SendRequestReply(user.Id, Discord.ActivityJoinRequestReply.Yes, (res) => - { - if (res == Discord.Result.Ok) - { - Console.WriteLine("Responded successfully"); - } - }); -} -``` - -## SendInvite - -Sends a game invite to a given user. If you do not have a valid activity with all the required fields, this call will error. See [Activity Action Field Requirements](#DOCS_GAME_SDK_ACTIVITIES/activity-action-field-requirements) for the fields required to have join and spectate invites function properly. - -Returns a `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------- | ------------------ | ----------------------------------------------------- | -| userId | Int64 | the id of the user to invite | -| type | ActivityActionType | marks the invite as an invitation to join or spectate | -| content | string | a message to send along with the invite | - -###### Example - -```cs -var userId = 53908232506183680; -activityManager.SendInvite(userId, Discord.ActivityActionType.Join, "Come play!", (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Success!"); - } - else - { - Console.WriteLine("Failed"); - } -}); -``` - -## AcceptInvite - -Accepts a game invitation from a given userId. - -Returns a `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------ | ----- | ---------------------------------- | -| userId | Int64 | the id of the user who invited you | - -###### Example - -```cs -activityManager.AcceptInvite(290926444748734466, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Success!"); - } - else - { - Console.WriteLine("Failed"); - } -}); -``` - -## OnActivityJoin - -Fires when a user accepts a game chat invite or receives confirmation from Asking to Join. - -###### Parameters - -| name | type | description | -| ---------- | ------ | ---------------------------------- | -| joinSecret | string | the secret to join the user's game | - -###### Example - -```cs -// Received when someone accepts a request to join or invite. -// Use secrets to receive back the information needed to add the user to the group/party/match -activityManager.OnActivityJoin += secret => { - Console.WriteLine("OnJoin {0}", secret); - lobbyManager.ConnectLobbyWithActivitySecret(secret, (Discord.Result result, ref Discord.Lobby lobby) => - { - Console.WriteLine("Connected to lobby: {0}", lobby.Id); - // Connect to voice chat, used in this case to actually know in overlay if your successful in connecting. - lobbyManager.ConnectVoice(lobby.Id, (Discord.Result voiceResult) => { - - if (voiceResult == Discord.Result.Ok) - { - Console.WriteLine("New User Connected to Voice! Say Hello! Result: {0}", voiceResult); - } - else - { - Console.WriteLine("Failed with Result: {0}", voiceResult); - }; - }); - //Connect to given lobby with lobby Id - lobbyManager.ConnectNetwork(lobby.Id); - lobbyManager.OpenNetworkChannel(lobby.Id, 0, true); - foreach (var user in lobbyManager.GetMemberUsers(lobby.Id)) - { - //Send a hello message to everyone in the lobby - lobbyManager.SendNetworkMessage(lobby.Id, user.Id, 0, - Encoding.UTF8.GetBytes(String.Format("Hello, {0}!", user.Username))); - } - //Sends this off to a Activity callback named here as 'UpdateActivity' passing in the discord instance details and lobby details - UpdateActivity(discord, lobby); - }); -}; - -void UpdateActivity(Discord.Discord discord, Discord.Lobby lobby) - { - //Creates a Static String for Spectate Secret. - string discordSpectateSecret = "wdn3kvj320r8vk3"; - spectateActivitySecret = discordSpectateSecret; - var activity = new Discord.Activity - { - State = "Playing Co-Op", - Details = "In a Multiplayer Match!", - Timestamps = - { - Start = startTimeStamp, - }, - Assets = - { - LargeImage = "matchimage1", - LargeText = "Inside the Arena!", - }, - Party = { - Id = lobby.Id.ToString(), - Size = { - CurrentSize = lobbyManager.MemberCount(lobby.Id), - MaxSize = (int)lobby.Capacity, - }, - }, - Secrets = { - Spectate = spectateActivitySecret, - Join = joinActivitySecret, - }, - Instance = true, - }; - - activityManager.UpdateActivity(activity, result => - { - Debug.LogFormat("Updated to Multiplayer Activity: {0}", result); - - // Send an invite to another user for this activity. - // Receiver should see an invite in their DM. - // Use a relationship user's ID for this. - // activityManager - // .SendInvite( - // 364843917537050624, - // Discord.ActivityActionType.Join, - // "", - // inviteResult => - // { - // Console.WriteLine("Invite {0}", inviteResult); - // } - // ); - }); - } -``` - -## OnActivitySpectate - -Fires when a user accepts a spectate chat invite or clicks the Spectate button on a user's profile. - -###### Parameters - -| name | type | description | -| -------------- | ------ | ------------------------------------------------- | -| spectateSecret | string | the secret to join the user's game as a spectator | - -###### Example - -```cs -// Received when someone accepts a request to spectate -activityManager.OnActivitySpectate += secret => -{ - Console.WriteLine("OnSpectate {0}", secret); -}; -``` - -## OnActivityJoinRequest - -Fires when a user asks to join the current user's game. - -###### Parameters - -| name | type | description | -| ---- | ---- | ----------------------- | -| user | User | the user asking to join | - -###### Example - -```cs -// A join request has been received. Render the request on the UI. -activityManager.OnActivityJoinRequest += (ref Discord.User user) => -{ - Console.WriteLine("OnJoinRequest {0} {1}", user.Username, user.Id); -}; -``` - -## OnActivityInvite - -Fires when the user receives a join or spectate invite. - -###### Parameters - -| name | type | description | -| -------- | ------------------ | ------------------------------------------ | -| type | ActivityActiontype | whether this invite is to join or spectate | -| user | User | the user sending the invite | -| activity | Activity | the inviting user's current activity | - -###### Example - -```cs -// An invite has been received. Consider rendering the user / activity on the UI. -activityManager.OnActivityInvite += (Discord.ActivityActionType Type, ref Discord.User user, ref Discord.Activity activity2) => -{ - Console.WriteLine("Received Invite Type: {0}, from User: {1}, with Activity Name: {2}", Type, user.Username, activity2.Name); - // activityManager.AcceptInvite(user.Id, result => - // { - // Console.WriteLine("AcceptInvite {0}", result); - // }); -}; -``` - -## Example: Inviting a User to a Game - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); - -// Update user's activity for your game. -// Party and secrets are vital. -var activity = new Discord.Activity -{ - State = "olleh", - Details = "foo details", - Timestamps = - { - Start = 5, - End = 6, - }, - Assets = - { - LargeImage = "foo largeImageKey", - LargeText = "foo largeImageText", - SmallImage = "foo smallImageKey", - SmallText = "foo smallImageText", - }, - Party = - { - Id = "foo partyID", - Size = { - CurrentSize = 1, - MaxSize = 4, - }, - }, - Secrets = - { - Match = "foo matchSecret", - Join = "foo joinSecret", - Spectate = "foo spectateSecret", - }, - Instance = true, -}; - -activityManager.UpdateActivity(activity, (result) => -{ - Console.WriteLine("Update Activity {0}", result); - - // Send an invite to another user for this activity. - // Receiver should see an invite in their DM. - // Use a relationship user's ID for this. - activityManager.SendInvite(364843917537050999, Discord.ActivityActionType.Join, "", (inviteUserResult) => - { - Console.WriteLine("Invite User {0}", inviteUserResult); - }); -}); -``` diff --git a/docs/game_sdk/Applications.md b/docs/game_sdk/Applications.md deleted file mode 100644 index 67f7ab3b38..0000000000 --- a/docs/game_sdk/Applications.md +++ /dev/null @@ -1,237 +0,0 @@ -# Applications - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -Many games run their own backend servers for things like user authentication. If one of those many games is yours, then we've got something for you! This manager gives you access to a bearer token for the currently connected Discord user, which you can send off to your server to do user authentication. - -This token is also useful for retrieving information about the connected user's account. Check out our [OAuth2 documentation](https://discord.com/developers/docs/topics/oauth2) for more information. - -These bearer tokens are good for seven days, after which they will expire. When a user reconnects to your game, and is online and connected to the internet, they'll receive a new token that you can grab. - -This manager also includes a couple useful helper functions, like getting the locale in which the user has chosen to use their Discord client, and knowing which game branch the game is running on. More about branches in the Dispatch CLI tool section of the documentation. - -## Data Models - -###### OAuth2Token Struct - -| name | type | description | -| ----------- | ------ | ----------------------------------------------------------------------------------------------- | -| AccessToken | string | a bearer token for the current user | -| Scopes | string | a list of oauth2 scopes as a single string, delineated by spaces like `"identify rpc gdm.join"` | -| Expires | Int64 | the timestamp at which the token expires | - -###### SignedAppTicket Struct - -| name | type | description | -| -------------- | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- | -| application_id | Int64 | the application id for the ticket | -| user | [User](#DOCS_GAME_SDK_USERS/data-models-user-struct) | the user for the ticket | -| entitlements | list of partial [Entitlements](#DOCS_GAME_SDK_STORE/data-models-entitlement-struct) structs that contain just the SKU id | the list of the user's entitlements for this application | -| timestamp | string | the ISO 8601 timestamp for the ticket | - -## GetCurrentLocale - -> info -> Value from environment variable `DISCORD_CURRENT_LOCALE` - -Gets the locale the current user has Discord set to. - -Returns a `string`. - -###### Parameters - -None - -###### Example - -```cs -var locale = applicationManager.GetCurrentLocale(); -Console.WriteLine("This user's language is {0}", locale); -``` - -## GetCurrentBranch - -> info -> Value from environment variable `DISCORD_CURRENT_BRANCH` - -Get the name of pushed branch on which the game is running. These are branches that you created and pushed using Dispatch. - -###### Parameters - -None - -###### Example - -```cs -var branch = applicationManager.GetCurrentBranch(); -if (branch != MyBranches.Stable) -{ - Console.WriteLine("You are on a beta branch; expect bugs!"); -} -``` - -## GetOAuth2Token - -> info -> value from environment variable `DISCORD_ACCESS_TOKEN` - -> warn -> Ensure that you have `http://127.0.0.1` set as a valid redirect URI for your application in the Developer Portal, or this method will always return an error. - -Retrieve an oauth2 bearer token for the current user. If your game was launched from Discord and you call this function, you will automatically receive the token. If the game was _not_ launched from Discord and this method is called, Discord will focus itself and prompt the user for authorization. - -Returns a `Discord.Result` and a `ref Discord.OAuth2Token` via callback. - -###### Parameters - -None - -###### Example - -```cs -applicationManager.GetOAuth2Token((Discord.Result result, ref Discord.OAuth2Token token) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Token for the user: {0}. Expires in {1}", token.AccessToken, token.Expires); - // You may now use this token against Discord's HTTP API - } -}); -``` - -## ValidateOrExit - -Checks if the current user has the entitlement to run this game. - -Returns a `Discord.Result` via callback. - -###### Parameters - -None - -###### Example - -```cs -applicationManager.ValidateOrExit((result) => -{ - if (result == Discord.Result.Ok) - { - // Game keeps running - } - else - { - Console.WriteLine("Oops! Something went wrong, closing game..."); - } -}); -``` - -## GetTicket - -###### Current Version - -| version | status | -| ------- | ------- | -| 2 | current | - -Get the signed app ticket for the current user. The structure of the ticket is: `version.signature.base64encodedjson`, so you should split the string by the `.` character. Ensure that the `version` matches the current version. The `signature` is used to verify the ticket using the libsodium library of your choice, and the `base64encodedjson` is what you can transform after verification. It contains: - -- the application id tied to the ticket -- the user's user id -- a timestamp for the ticket -- the list of the user's [entitlements](#DOCS_GAME_SDK_STORE/data-models-entitlement-struct) for the application id - -These values can be accessed by transforming the string into a [SignedAppTicket](#DOCS_GAME_SDK_APPLICATIONS/data-models-signedappticket-struct) with your application's private key. The ticket is signed using [libsodium](https://github.com/jedisct1/libsodium) which should be available for any programming language. Here's a [list of available libraries](https://download.libsodium.org/doc/bindings_for_other_languages). - -Note that both the public key you receive from Discord and the signature within the app ticket from the SDK are both in hex, and will need to be converted to `byte[]` before use with libsodium. - -Returns a `Discord.Result` and `ref string` via callback. - -###### Parameters - -None - -###### Example - -```cs -// Handle serialization however works best for you -// This is just an easy example -[Serializable] -public class SignedAppTicket -{ - public long application_id; - public Discord.User user; - public List entitlements; - public string timestamp; -} - -public void DoTheThing() -{ - // This example is using the libsodium-net library - // https://github.com/adamcaudill/libsodium-net - var appManager = discord.GetApplicationManager(); - var MY_PUBLIC_KEY = "460cab5f2237b71e3c2c06bzze217f4f68d55db16dae672bdfb6618235589999"; - var MY_SKU_ID = "492432195219099999"; - - // Get the ticket - appManager.GetTicket((Discord.Result res, ref string ticket) => - { - // Split the ticket into its parts - var parts = ticket.Split('.'); - - // Ensure the version matches - if(parts[0] == "2") - { - // Verify the signature - // Your public key will be given to you by Discord - if (Sodium.PublicKeyAuth.VerifyDetached(HexToByte(parts[1]), System.Text.Encoding.UTF8.GetBytes(parts[2]), HexToByte(MY_PUBLIC_KEY))) - { - // If valid, decode the string - var byteData = Convert.FromBase64String(parts[2]); - var json = System.Text.Encoding.UTF8.GetString(byteData); - - // Deserialize it into the ticket object - var myTicket = Newtonsoft.Json.JsonConvert.DeserializeObject(json); - - // Check for entitlement to the SKU! - if (myTicket.entitlements.Any(x => x.SkuId == MY_SKU_ID)) - { - Console.WriteLine("User has entitlement to your game"); - } - else - { - Console.WriteLine("Not entitled"); - } - } - } - }); -} - -public byte[] HexToByte(string hex) -{ - byte[] data = new byte[hex.Length / 2]; - for (int i = 0; i < hex.Length; i +=2) - { - data[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); - } - return data; -} -``` - -## Example: Get OAuth2 Token - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); -var appManager = discord.GetApplicationManager(); - -// Retrieve the token -appManager.GetOAuth2Token((Discord.Result result, ref Discord.OAuth2Token token) => -{ - Console.WriteLine(token.AccessToken); - - // Now send that token off to your server to do your own validation! -}) -``` diff --git a/docs/game_sdk/Discord.md b/docs/game_sdk/Discord.md deleted file mode 100644 index c0067ace21..0000000000 --- a/docs/game_sdk/Discord.md +++ /dev/null @@ -1,442 +0,0 @@ -# Discord - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -Making a game? Need a whole bunch of fancy APIs to help make it great and your players' lives a breeze? Look no further! The Discord GameSDK is an easy drop-in SDK to help you manage all the hard things that come with making a game. Well, all the hards things about coding it at least. Interpersonal communication skills are on you (have you heard of this cool chat app called Discord?). - -OK, cool, so how's it work? - -## General Structure - -At a high level, the Discord GameSDK has a class, `Discord`. This class is in charge of the creation of a few "manager" sub-classes. They are: - -- `AchievementManager` - for achievements! -- `ActivityManager` - for Rich Presence and game invites -- `ApplicationManager` - for retrieving a user's OAuth2 bearer token, locale, and current game branch -- `ImageManager` - for getting data about images on Discord, like user avatars or chat images -- `LobbyManager` - for multiplayer lobbies -- `NetworkManager` - for all your networking layer needs -- `OverlayManager` - for interacting with Discord's built-in overlay -- `RelationshipManager` - for users' social relationships across Discord, including friends list -- `StorageManager` - for saving game data to the disk and the cloud -- `StoreManager` - for all things entitlements and SKUs, including IAP functionality -- `UserManager` - for fetching user data for a given id and the current user -- `VoiceManager` - to make use of Discord's awesome voice chat - -Each one of these managers contain a number of methods and events used to interact with Discord in the context of the manager. For example, `RelationshipManager` has a function called `Filter()`, which lets you pare down a user's inter-Discord relationships based on a boolean condition, like being friends! - -## Functions in the SDK - -Most functions in the Discord GameSDK, uh, _function_ in a similar way. They take whatever parameters are required for the function to do its job—a user id, the requested size for an image, etc.—and a callback by means of a function pointer. That callback is fired when the function completes its work, letting you handle events without worrying about piping asynchronously-returned data to the right context. - -Some functions behave with a normal return behavior; e.g. `RelationshipManager.Count()` just returns the number directly. Don't worry, it's outlined in the docs. - -A quick example with our C# binding: - -```c# -var userManager = discord.GetUserManager(); - -// Return via callback -userManager.GetUser(290926444748734465, (Discord.Result result, ref Discord.User otherUser) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine(otherUser.Username); - Console.WriteLine(otherUser.Id); - } -}); - - -// Return normally -userManager.OnCurrentUserUpdate += () => -{ - var currentUser = userManager.GetCurrentUser(); - Console.WriteLine(currentUser.Username); - Console.WriteLine(currentUser.Discriminator); - Console.WriteLine(currentUser.Id); -}; -``` - -## Environment Variables - -Discord passes a number of environment variables down to the SDK. These are accessed by various methods in the SDK and can be changed for local testing by changing the value in your local environment. - -###### SDK Environment Variables - -| name | method | description | -|------------------------|---------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| -| DISCORD_INSTANCE_ID | [Local Testing](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/testing-locally-with-two-clients) | the locally running instance of Discord to connect to; allows you to choose between multiple running clients | -| DISCORD_ACCESS_TOKEN | [ApplicationManager.GetOAuth2Token()](#DOCS_GAME_SDK_APPLICATIONS/getoauth2token) | the connected user's bearer token | -| DISCORD_CURRENT_LOCALE | [ApplicationManager.GetCurrentLocale()](#DOCS_GAME_SDK_APPLICATIONS/getcurrentlocale) | the language that Discord is in for the connected user | -| DISCORD_CURRENT_BRANCH | [ApplicationManager.GetCurrentBranch()](#DOCS_GAME_SDK_APPLICATIONS/getcurrentbranch) | the branch of the running application that the user has launched | -| DISCORD_STORAGE_PATH | [StorageManager.GetPath()](#DOCS_GAME_SDK_STORAGE/getpath) | the path to which Discord will save files if you're using the StorageManager | - -## Error Handling - -Debugging is a pain, so before we get into the meat of the SDK, we want to make sure you're prepared for when things go awry. Within the Discord core is a function called `SetLogHook()`. It takes a `level`, which is minimum level of log message you want to listen to, and a callback function: - -```cs -public void LogProblemsFunction(Discord.LogLevel level, string message) -{ - Console.WriteLine("Discord:{0} - {1}", level, message); -} - -discord.SetLogHook(Discord.LogLevel.Debug, LogProblemsFunction); -``` - -You should begin your integration by setting up this callback to help you debug. Helpfully, if you put a breakpoint inside the callback function you register here, you'll be able to see the stack trace for errors you run into (as long as they fail synchronously). Take the guess work out of debugging, or hey, ignore any and all logging by setting a callback that does nothing. We're not here to judge. - -## Data Models - -###### Result Enum - -| Code | value | description | -|------|---------------------------------|-------------------------------------------------------------------------------------------------| -| 0 | Ok | everything is good | -| 1 | ServiceUnavailable | Discord isn't working | -| 2 | InvalidVersion | the SDK version may be outdated | -| 3 | LockFailed | an internal error on transactional operations | -| 4 | InternalError | something on our side went wrong | -| 5 | InvalidPayload | the data you sent didn't match what we expect | -| 6 | InvalidCommand | that's not a thing you can do | -| 7 | InvalidPermissions | you aren't authorized to do that | -| 8 | NotFetched | couldn't fetch what you wanted | -| 9 | NotFound | what you're looking for doesn't exist | -| 10 | Conflict | user already has a network connection open on that channel | -| 11 | InvalidSecret | activity secrets must be unique and not match party id | -| 12 | InvalidJoinSecret | join request for that user does not exist | -| 13 | NoEligibleActivity | you accidentally set an `ApplicationId` in your `UpdateActivity()` payload | -| 14 | InvalidInvite | your game invite is no longer valid | -| 15 | NotAuthenticated | the internal auth call failed for the user, and you can't do this | -| 16 | InvalidAccessToken | the user's bearer token is invalid | -| 17 | ApplicationMismatch | access token belongs to another application | -| 18 | InvalidDataUrl | something internally went wrong fetching image data | -| 19 | InvalidBase64 | not valid Base64 data | -| 20 | NotFiltered | you're trying to access the list before creating a stable list with `Filter()` | -| 21 | LobbyFull | the lobby is full | -| 22 | InvalidLobbySecret | the secret you're using to connect is wrong | -| 23 | InvalidFilename | file name is too long | -| 24 | InvalidFileSize | file is too large | -| 25 | InvalidEntitlement | the user does not have the right entitlement for this game | -| 26 | NotInstalled | Discord is not installed | -| 27 | NotRunning | Discord is not running | -| 28 | InsufficientBuffer | insufficient buffer space when trying to write | -| 29 | PurchaseCancelled | user cancelled the purchase flow | -| 30 | InvalidGuild | Discord guild does not exist | -| 31 | InvalidEvent | the event you're trying to subscribe to does not exist | -| 32 | InvalidChannel | Discord channel does not exist | -| 33 | InvalidOrigin | the origin header on the socket does not match what you've registered (you should not see this) | -| 34 | RateLimited | you are calling that method too quickly | -| 35 | OAuth2Error | the OAuth2 process failed at some point | -| 36 | SelectChannelTimeout | the user took too long selecting a channel for an invite | -| 37 | GetGuildTimeout | took too long trying to fetch the guild | -| 38 | SelectVoiceForceRequired | push to talk is required for this channel | -| 39 | CaptureShortcutAlreadyListening | that push to talk shortcut is already registered | -| 40 | UnauthorizedForAchievement | your application cannot update this achievement | -| 41 | InvalidGiftCode | the gift code is not valid | -| 42 | PurchaseError | something went wrong during the purchase flow | -| 43 | TransactionAborted | purchase flow aborted because the SDK is being torn down | - -###### LogLevel Enum - -| value | description | -|---------|--------------------------------| -| Error | Log only errors | -| Warning | Log warnings and errors | -| Info | Log info, warnings, and errors | -| Debug | Log _all_ the things! | - -###### CreateFlags Enum - -| value | description | -|------------------|---------------------------------------------------------------------| -| Default | Requires Discord to be running to play the game | -| NoRequireDiscord | Does not require Discord to be running, use this on other platforms | - -## Create - -Creates an instance of Discord to initialize the SDK. This is the overlord of all things Discord. We like to call her Nelly. - -Returns a new `Discord`. - -###### Parameters - -| name | type | description | -|----------|-------------|-----------------------------------------------------| -| clientId | Int64 | your application's client id | -| flags | CreateFlags | the creation parameters for the SDK, outlined above | - -###### Example - -``` -// c++ land -discord::Core* core{}; -discord::Core::Create(53908232506183680, DiscordCreateFlags_Default, &core); - -// c# land -var discord = new Discord(53908232506183680, (UInt64)Discord.CreateFlags.Default); -``` - -## Destroy - -Destroys the instance. Wave goodbye, Nelly! You monster. In C# land, this is `Dispose()`. - -> info -> The C++ binding does not include a `destroy()` method, as the destructor for the Core does the work for you. - -Returns `void`. - -###### Parameters - -None - -###### Example - -```cs -discord.Dispose(); -``` - -## SetLogHook - -Registers a logging callback function with the minimum level of message to receive. The callback function should have a signature of: - -``` -MyCallbackFunction(LogLevel level, string message); -``` - -Returns `void`. - -###### Parameters - -| name | type | description | -|----------|----------|---------------------------------------------| -| level | LogLevel | the minimum level of event to log | -| callback | function | the callback function to catch the messages | - -###### Example - -```cs -public void LogProblemsFunction(Discord.LogLevel level, string message) -{ - Console.WriteLine("Discord:{0} - {1}", level, message); -} - -discord.SetLogHook(Discord.LogLevel.Debug, LogProblemFunctions); -``` - -## RunCallbacks - -Runs all pending SDK callbacks. Put this in your game's main event loop, like `Update()` in Unity. That way, the first thing your game does is check for any new info from Discord. - -This function also serves as a way to know that the local Discord client is still connected. If the user closes Discord while playing your game, `RunCallbacks()` will return/throw `Discord.Result.NotRunning`. - -In C and C++, this function returns `Discord.Result`. In C#, it returns `void` and will throw `Discord.Result` error if something went wrong. - -###### Parameters - -None - -###### Example - -```cs -void Update() -{ - discord.RunCallbacks(); -} -``` - -## GetActivityManager - -Fetches an instance of the manager for interfacing with activities in the SDK. - -Returns an `ActivityManager`. - -###### Parameters - -None - -###### Example - -```cs -var activityManager = discord.GetActivityManager(); -``` - -## GetRelationshipManager - -Fetches an instance of the manager for interfacing with relationships in the SDK. - -Returns a `RelationshipManager`. - -###### Parameters - -None - -###### Example - -```cs -var relationshipManager = discord.GetRelationshipManager(); -``` - -## GetImageManager - -Fetches an instance of the manager for interfacing with images in the SDK. - -Returns an `ImageManager`. - -###### Parameters - -None - -###### Example - -```cs -var imageManager = discord.GetImageManager(); -``` - -## GetUserManager - -Fetches an instance of the manager for interfacing with users in the SDK. - -Returns an `UserManager`. - -###### Parameters - -None - -###### Example - -```cs -var userManager = discord.GetUserManager(); -``` - -## GetLobbyManager - -Fetches an instance of the manager for interfacing with lobbies in the SDK. - -Returns a `LobbyManager`. - -###### Parameters - -None - -###### Example - -```cs -var lobbyManager = discord.GetLobbyManager(); -``` - -## GetNetworkManager - -Fetches an instance of the manager for interfacing with networking in the SDK. - -Returns an `NetworkManager`. - -###### Parameters - -None - -###### Example - -```cs -var networkManager = discord.GetNetworkManager(); -``` - -## GetOverlayManager - -Fetches an instance of the manager for interfacing with the overlay in the SDK. - -Returns an `OverlayManager`. - -###### Parameters - -None - -###### Example - -```cs -var overlayManager = discord.GetOverlayManager(); -``` - -## GetApplicationManager - -Fetches an instance of the manager for interfacing with applications in the SDK. - -Returns an `ApplicationManager`. - -###### Parameters - -None - -###### Example - -```cs -var applicationManager = discord.GetApplicationManager(); -``` - -## GetStorageManager - -Fetches an instance of the manager for interfacing with storage in the SDK. - -Returns a `StorageManager`. - -###### Parameters - -None - -###### Example - -```cs -var storageManager = discord.GetStorageManager(); -``` - -## GetStoreManager - -Fetches an instance of the manager for interfacing with SKUs and Entitlements in the SDK. - -Returns a `StoreManager`. - -###### Parameters - -None - -###### Example - -```cs -var storeManager = discord.GetStoreManager(); -``` - -## GetVoiceManager - -Fetches an instance of the manager for interfacing with voice chat in the SDK. - -Returns a `VoiceManager`. - -###### Parameters - -None - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -``` - -## GetAchievementManager - -Fetches an instance of the manager for interfacing with achievements in the SDK. - -Returns an `AchievementManager`. - -###### Parameters - -None - -###### Example - -```cs -var achievementManager = discord.GetAchievementManager(); -``` diff --git a/docs/game_sdk/Discord_Voice.md b/docs/game_sdk/Discord_Voice.md deleted file mode 100644 index 1874c1cecc..0000000000 --- a/docs/game_sdk/Discord_Voice.md +++ /dev/null @@ -1,289 +0,0 @@ -# Voice - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -Discord's pride and joy is its voice chat. Well, ok, also its memes, but mostly the voice chat. Text and video chat are pretty great, too. And have you seen that store? Anyway. - -If you want people playing your game to be able to talk with each other, this Voice manager can help you out! Note that the main functionality for voice in this SDK is not _only_ in this manager. Connecting players to a voice chat happens with [ConnectVoice](#DOCS_GAME_SDK_LOBBIES/connectvoice) in the Lobby manager, and robust voice settings work through [OpenVoiceSettings](#DOCS_GAME_SDK_OVERLAY/openvoicesettings) in the Overlay manager. The Voice manager handles a few fine-grain details like self muting/deafening, swapping between VAD/PTT voice modes, and setting a PTT key. It's a subset of the robust settings from the overlay call for those of you that prefer to build UI and control things from your own game. - -## Data Models - -###### InputModeType Enum - -| name | value | -| ------------- | ----- | -| VoiceActivity | 0 | -| PushToTalk | 1 | - -###### InputMode Struct - -| name | type | description | -| -------- | ------------- | --------------------------------------------- | -| Type | InputModeType | set either VAD or PTT as the voice input mode | -| Shortcut | string | the PTT hotkey for the user | - -###### Shortcut Keys - -Keys can be mapped as a combination by adding a "+" between values, such as `"shift + 4"` or `"ctrl + v"`. - -| key type | value | -| ------------- | --------------------------------------------------------- | -| Alphabetical | "a", "b", "c", etc. | -| Numerical | "1", "2", "3", etc. | -| Symbols | "-", "+", ".", "/", etc. | -| Function Keys | "f1", "f2", "f3", etc. | -| Gamepads | standard XInput api values - "GAMEPAD0", "GAMEPAD1", etc. | -| Enter | "enter" | -| Tab | "tab" | -| Spacebar | "space" | -| Backspace | "backspace" | -| Escape | "esc" | -| Meta | "meta" | -| Shift | "shift" | -| Caps Lock | "caps lock" | -| Alt | "alt" | -| Control | "ctrl" | -| Right Shift | "right shift" | -| Right Alt | "right alt" | -| Right Control | "right ctrl" | -| Right Meta | "right meta" | -| Page Up | "page up" | -| Page Down | "page down" | -| Scroll Lock | "scroll lock" | -| Print Screen | "print screen" | -| Rewind | "rewind" | -| Play | "play" | -| Fast Forward | "fast forward" | -| Delete | "del" | -| End | "end" | -| Insert | "insert" | -| Break | "break" | -| Home | "home" | -| Up Arrow | "up" | -| Down Arrow | "down" | -| Left Arrow | "left" | -| Right Arrow | "right" | - -## GetInputMode - -Get the current voice input mode for the user. - -Returns a `Discord.InputMode`. - -###### Parameters - -None - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -var inputMode = voiceManager.GetInputMode(); -Console.WriteLine("The current input mode is {0}. The current PTT hotkey is set to {1}", inputMode.Type, inputMode.Shortcut); -``` - -## SetInputMode - -Sets a new voice input mode for the user. Refer to [Shortcut Keys](#DOCS_GAME_SDK_DISCORD_VOICE/data-models-shortcut-keys) for a table of valid values for shortcuts. - -Returns a `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| --------- | --------- | ------------------------------- | -| inputMode | InputMode | the new input mode for the user | - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -var newMode = new Discord.InputMode() -{ - Type = Discord.InputModeType.PushToTalk, - Shortcut = "ctrl" -}; -voiceManager.SetInputMode(newMode, (res) => -{ - if (res == Discord.Result.Ok) - { - Console.WriteLine("New input mode set"); - } -}); -``` - -## IsSelfMute - -Whether the connected user is currently muted. - -Returns `bool`. - -###### Parameters - -None - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -if (voiceManager.IsSelfMute()) -{ - Console.WriteLine("You are muted"); -} -``` - -## SetSelfMute - -Mutes or unmutes the currently connected user. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ---- | ---- | ------------------------------- | -| mute | bool | true for mute, false for unmute | - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -if (voiceManager.IsSelfMute()) -{ - voiceManager.SetSelfMute(false); - Console.WriteLine("We automatically unmuted you"); -} -``` - -## IsSelfDeaf - -Whether the connected user is currently deafened. - -Returns `bool`. - -###### Parameters - -None - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -if (voiceManager.IsSelfDeaf()) -{ - Console.WriteLine("You are deafened. You can't hear anyone!"); -} -``` - -## SetSelfDeaf - -Deafens or undeafens the currently connected user. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ---- | ---- | ------------------------------- | -| deaf | bool | true for mute, false for unmute | - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -if (voiceManager.IsSelfDeaf()) -{ - voiceManager.SetSelfDeaf(false); - Console.WriteLine("We automatically undeafened you. You can hear now!"); -} -``` - -## IsLocalMute - -Whether the given user is currently muted by the connected user. - -Returns `bool`. - -###### Parameters - -| name | type | description | -| ------ | ----- | --------------------------- | -| userId | Int64 | the id of the user to check | - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -if (voiceManager.IsLocalMute(53908232506183680)) -{ - Console.WriteLine("User is locally muted"); -} -``` - -## SetLocalMute - -Mutes or unmutes the given user for the currently connected user. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------ | ----- | ------------------------------- | -| userId | Int64 | the id of the user to mute | -| mute | bool | true for mute, false for unmute | - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -if (!voiceManager.IsLocalMute(53908232506183680)) -{ - voiceManager.SetLocalMute(53908232506183680, true); - Console.WriteLine("Muted that user for you"); -} -``` - -## GetLocalVolume - -Gets the local volume for a given user. This is the volume level at which the currently connected users hears the given user speak. - -Returns `byte`. - -###### Parameters - -| name | type | description | -| ------ | ----- | --------------------------- | -| userId | Int64 | the id of the user to check | - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -var volume = voiceManager.GetLocalVolume(53908232506183680); -Console.WriteLine("User is at volume level {0}", volume); -``` - -## SetLocalVolume - -Sets the local volume for a given user. This is the volume level at which the currently connected users hears the given user speak. Valid volume values are from `0` to `200`, with `100` being the default. Lower than `100` will be a reduced volume level from default, whereas over `100` will be a boosted volume level from default. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------ | ----- | ------------------------------------------------- | -| userId | Int64 | the id of the user to change | -| volume | byte | the volume at which to set the user, `0` to `200` | - -###### Example - -```cs -var voiceManager = discord.GetVoiceManager(); -voiceManager.SetLocalVolume(53908232506183680, 70); -``` diff --git a/docs/game_sdk/Images.md b/docs/game_sdk/Images.md deleted file mode 100644 index 5bffa7af64..0000000000 --- a/docs/game_sdk/Images.md +++ /dev/null @@ -1,196 +0,0 @@ -# Images - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -Discord is like a book; it's better with pictures. The image manager helps you fetch image data for images in Discord, including user's avatars. They worked hard to pick out those photos and gifs. Show them you care, too. - -## Data Models - -###### ImageDimensions Struct - -| name | type | description | -| ------ | ------ | ----------------------- | -| Width | UInt32 | the width of the image | -| Height | UInt32 | the height of the image | - -###### ImageType Enum - -| value | description | -| ----- | ------------------------ | -| User | image is a user's avatar | - -###### ImageHandle Struct - -| name | type | description | -| ---- | --------- | ----------------------------------------------- | -| Type | ImageType | the source of the image | -| Id | Int64 | the id of the user whose avatar you want to get | -| Size | UInt32 | the resolution at which you want the image | - -## Fetch - -Prepares an image to later retrieve data about it. - -Returns a `Discord.Result` and `Discord.ImageHandle` via callback. - -###### Parameters - -| name | type | description | -| ------- | ----------- | ----------------------------------------------------------- | -| handle | ImageHandle | contains the desired userId and size for the returned image | -| refresh | bool | whether to use cached data for fetch anew | - -###### Example - -```cs -var handle = new Discord.ImageHandle() -{ - Id = 53908232506183680, - Size = 1024 -}; - -imageManager.Fetch(handle, false, (result, returnedHandle) => -{ - if (result == Discord.Result.Ok) - { - var data = imageManager.GetData(returnedHandle); - // Do stuff with the byte[] data - } -}); -``` - -## GetDimensions - -Gets the dimensions for the given user's avatar's source image. - -Returns `Discord.ImageDimensions`. - -###### Parameters - -| name | type | description | -| ------ | ----------- | ----------------------------------------------------------- | -| handle | ImageHandle | contains the desired userId and size for the returned image | - -###### Example - -```cs -var handle = new Discord.ImageHandle() -{ - Id = 53908232506183680, - Size = 1024 -}; -var dimensions = imageManager.GetDimensions(handle); -``` - -## GetData - -Gets the image data for a given user's avatar. In C#, this is overloaded by a helper function that will directly return a `byte[]` with the image data in it. In C++/C, this function reads image data into a passed pointer of defined size. - -###### Parameters - -| name | type | description | -| ------ | ----------- | --------------------------------------------- | -| handle | ImageHandle | the image handle from the `Fetch()` callback | -| data | uint8_t\* | a buffer to read image data into (C++/C only) | -| size | uint | the size of the buffer (C++/C only) | - -###### Example - -```cs -var handle = new Discord.ImageHandle() -{ - Id = 53908232506183680, - Size = 1024 -}; - -imageManager.Fetch(handle, false, (result, handle) => -{ - if (result == Discord.Result.Ok) - { - var data = imageManager.GetData(handle); - // Do stuff with data now - } -}); -``` - -###### Example Cpp - -```cpp -core->ImageManager().Fetch( - handle, true, [&state](discord::Result res, discord::ImageHandle handle) { - if (res == discord::Result::Ok) { - discord::ImageDimensions dims{}; - state.core->ImageManager().GetDimensions(handle, &dims); - std::cout << "Fetched " << dims.GetWidth() << "x" << dims.GetHeight() - << " avatar!\n"; - - std::vector data; - data.reserve(dims.GetWidth() * dims.GetHeight() * 4); - uint8_t* d = data.data(); - state.core->ImageManager().GetData(handle, d, data.size()); - } - } -); -``` - -## GetTexture - -> warn -> This is only exposed in Unity - -Gets the `Texture2D` for a given user's avatar for use within a Unity environment. - -Returns a `Texture2D`. - -###### Parameters - -| name | type | description | -| ------ | ----------- | -------------------------------------------- | -| handle | ImageHandle | the image handle from the `Fetch()` callback | - -###### Example - -```cs -var handle = new Discord.ImageHandle() -{ - Id = 53908232506183680, - Size = 1024 -}; - -imageManager.Fetch(handle, false, (result, handle) => -{ - if (result == Discord.Result.Ok) - { - var texture = imageManager.GetTexture(handle); - // Do stuff with texture now - } -}); -``` - -## Example: User's Avatar Data - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); - -// Request user's avatar data. Sizes can be powers of 2 between 16 and 2048 -imageManager.Fetch(Discord.ImageHandle.User(53908232506183680, 128), (result, handle) => -{ - { - if (result == Discord.Result.Ok) - { - // If you are working in Unity, you can also use GetTexture() - // Which is only exposed for Unity builds - // These return raw RGBA. - var data = imageManager.GetData(handle); - } - else - { - Console.WriteLine("image error {0}", handle.Id); - } - } -}; -``` diff --git a/docs/game_sdk/Lobbies.md b/docs/game_sdk/Lobbies.md deleted file mode 100644 index 7c74efaabe..0000000000 --- a/docs/game_sdk/Lobbies.md +++ /dev/null @@ -1,1627 +0,0 @@ -# Lobbies - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -Looking to integrate multiplayer into your game? Lobbies are a great way to organize players into contexts to play together. This manager works hand in hand with the networking layer of our SDK to make multiplayer integrations a breeze by: - -- Creating, managing, and joining lobbies -- Matchmaking users based on lobby metadata, like ELO -- Getting and setting arbitrary metadata on lobbies and lobby members - -Lobbies in Discord work in one of two ways. By using calls from the SDK, lobbies are effectively "owned" by the user who's client creates the lobby. Someone boots up the game, hits your "Create Lobby" button, and their game client calls `LobbyManager.CreateLobby()` from the Discord SDK. - -There is also another way to create and "own" lobbies with which the source of truth can be your own server. These SDK functions calls map to API endpoints that are exposed in Discord's HTTP API. In lieu of creating and managing lobbies in the SDK, you can call those endpoints directly with a token for your application and take care of it all on some far-away, totally secure server. Let's go over the SDK first. - -## The SDK Way - -In order to ensure that Discord lobbies are consistent for all players, this manager works with "transactions". A `LobbyTransaction` is needed to set lobby properties, like capacity. A `MemberTransaction` is needed to set lobby member properties, like metadata. - -To update a user or a lobby, create or get a transaction for that resource, call the needed methods on it, and then pass it to the `Create()` or `Update()` methods. When passed to a `Create()` or `Update()` method, the transaction objected is consumed. So, if you want to do another `Create()` or `Update()`, you need to get a new transaction. - -###### Example: Creating a Lobby - -```cs -var lobbyManager = discord.GetLobbyManager(); - -// Create the transaction -var txn = lobbyManager.GetLobbyCreateTransaction(); - -// Set lobby information -txn.SetCapacity(6); -txn.SetType(Discord.LobbyType.Public); -txn.SetMetadata("a", "123"); - -// Create it! -lobbyManager.CreateLobby(txn, (Discord.Result result, ref Discord.Lobby lobby) => -{ - Console.WriteLine("lobby {0} created with secret {1}", lobby.Id, lobby.Secret); - - // We want to update the capacity of the lobby - // So we get a new transaction for the lobby - var newTxn = lobbyManager.GetLobbyUpdateTransaction(lobby.id); - newTxn.SetCapacity(5); - - lobbyManager.UpdateLobby(lobby.id, newTxn, (result) => - { - Console.WriteLine("lobby {0} updated", lobby.Id); - }); -}); -``` - -## Data Models - -###### LobbyType Enum - -| name | value | -| ------- | ----- | -| Private | 1 | -| Public | 2 | - -###### Lobby Struct - -| name | type | description | -| -------- | --------- | -------------------------------------- | -| Id | Int64 | the unique id of the lobby | -| Type | LobbyType | if the lobby is public or private | -| OwnerId | Int64 | the userId of the lobby owner | -| Secret | string | the password to the lobby | -| Capacity | UInt32 | the max capacity of the lobby | -| Locked | bool | whether or not the lobby can be joined | - -###### LobbySearchComparison Enum - -| name | value | -| ------------------ | ----- | -| LessThanOrEqual | -2 | -| LessThan | -1 | -| Equal | 0 | -| GreaterThan | 1 | -| GreaterThanOrEqual | 2 | -| NotEqual | 3 | - -###### LobbySearchCast Enum - -| name | value | -| ------ | ----- | -| String | 1 | -| Number | 2 | - -###### LobbySearchDistance Enum - -| name | value | description | -| -------- | ----- | ------------------------------------ | -| Local | 0 | within the same region | -| Default | 1 | within the same and adjacent regions | -| Extended | 2 | far distances, like US to EU | -| Global | 3 | all regions | - -###### LobbyTransaction Struct - -Has no values, but has member functions, outlined later. - -###### LobbyMemberTransaction Struct - -Has no values, but has member functions, outlined later. - -###### LobbySearchQuery Struct - -Has no values, but has member functions, outlined later. - -## LobbyTransaction.SetType - -Marks a lobby as private or public. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ---- | --------- | ----------------- | -| type | LobbyType | private or public | - -###### Example - -```cs -var txn = lobbyManager.GetLobbyUpdateTransaction(); -txn.SetType(Discord.LobbyType.Public); -lobbyManager.UpdateLobby(lobbyId, txn, (Discord.Result result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby type updated!"); - } -}); -``` - -## LobbyTransaction.SetOwner - -> warn -> This method is only valid for `LobbyUpdateTransactions` and may cause issues if you set it on a `LobbyCreateTransaction`. - -Sets a new owner for the lobby. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------ | ----- | ----------------------- | -| userId | Int64 | the new owner's user id | - -###### Example - -```cs -var txn = lobbyManager.GetLobbyUpdateTransaction(); -txn.SetOwner(53908232506183680); -lobbyManager.UpdateLobby(lobbyId, txn, (Discord.Result result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby owner updated!"); - } -}); -``` - -## LobbyTransaction.SetCapacity - -Sets a new capacity for the lobby. - -Returns `void`. - -###### Parameters - -| name | type | description | -| -------- | ------ | ---------------------- | -| capacity | UInt32 | the new max lobby size | - -###### Example - -```cs -var txn = lobbyManager.GetLobbyUpdateTransaction(); -txn.SetCapacity(10); -lobbyManager.UpdateLobby(lobbyId, txn, (Discord.Result result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby capacity updated!"); - } -}); -``` - -## LobbyTransaction.SetMetadata - -Sets metadata value under a given key name for the lobby. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ----- | ------ | ---------------- | -| key | string | key for the data | -| value | string | data value | - -###### Example - -```cs -var txn = lobbyManager.GetLobbyUpdateTransaction(); -txn.SetMetadata("average_mmr", "4500"); -lobbyManager.UpdateLobby(lobbyId, txn, (Discord.Result result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby metadata updated!"); - } -}); -``` - -## LobbyTransaction.DeleteMetadata - -Deletes the lobby metadata for a key. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ---- | ------ | ---------------- | -| key | string | key for the data | - -###### Example - -```cs -var txn = lobbyManager.GetLobbyUpdateTransaction(); -txn.DeleteMetadata("average_mmr"); -lobbyManager.UpdateLobby(lobbyId, txn, (Discord.Result result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby metadata updated!"); - } -}); -``` - -## LobbyTransaction.SetLocked - -Sets the lobby to locked or unlocked. When locked, new users cannot join the lobby. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------ | ---- | ----------------------------------- | -| locked | bool | whether to lock or unlock the lobby | - -###### Example - -```cs -var txn = lobbyManager.GetLobbyUpdateTransaction(); -txn.SetLocked(true); -lobbyManager.UpdateLobby(lobbyId, txn, (Discord.Result result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby type updated!"); - } -}); -``` - -## LobbyMemberTransaction.SetMetadata - -Sets metadata value under a given key name for the current user. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ----- | ------ | ---------------- | -| key | string | key for the data | -| value | string | data value | - -###### Example - -```cs -var txn = lobbyManager.GetMemberUpdateTransaction(); -txn.SetMetadata("current_mmr", "4267"); -lobbyManager.UpdateMember(lobbyId, memberId, txn, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Member metadata updated!"); - } -}); -``` - -## LobbyMemberTransaction.DeleteMetadata - -Sets metadata value under a given key name for the current user. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ---- | ------ | ---------------- | -| key | string | key for the data | - -###### Example - -```cs -var txn = lobbyManager.GetMemberUpdateTransaction(); -txn.DeleteMetadata("current_mmr"); -lobbyManager.UpdateMember(lobbyId, memberId, txn, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Member metadata updated!"); - } -}); -``` - -## LobbySearchQuery.Filter - -Filters lobbies based on metadata comparison. Available filter values are `owner_id`, `capacity`, `slots`, and `metadata`. If you are filtering based on metadata, make sure you prepend your key with `"metadata."` For example, filtering on matchmaking rating would be `"metadata.matchmaking_rating"`. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ----- | --------------------- | -------------------------------------------------------------------------- | -| key | string | key to search for filter data | -| comp | LobbySearchComparison | how the value on the lobby metadata should be compared to the search value | -| cast | LobbySearchCast | should the search value be cast as a string or a number | -| value | string | the value to filter against | - -###### Example - -```cs -var query = lobbyManager.GetSearchQuery(); -query.Filter("metadata.matchmaking_rating", LobbySearchComparison.GreaterThan, LobbySearchCast.Number, "455"); -``` - -## LobbySearchQuery.Sort - -Sorts the filtered lobbies based on "near-ness" to a given value. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ----- | --------------- | ------------------------------------------------------- | -| key | string | key for the data | -| cast | LobbySearchCast | should the search value be cast as a string or a number | -| value | string | the value to sort by | - -###### Example - -```cs -var query = lobbyManager.GetSearchQuery(); -query.Sort("metadata.ELO", LobbySearchCast.Number, "1337"); -``` - -## LobbySearchQuery.Limit - -Limits the number of lobbies returned in a search. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ----- | ------ | -------------------------------------- | -| limit | UInt32 | the number of lobbies to return at max | - -###### Example - -```cs -var query = lobbyManager.GetSearchQuery(); -query.Limit(10); -``` - -## LobbySearchQuery.Distance - -Filters lobby results to within certain regions relative to the user's location. - -Returns `void`. - -###### Parameters - -| name | type | description | -| -------- | --------------------------- | ----------------------------------------------- | -| distance | Discord.LobbySearchDistance | the distance within which to search for lobbies | - -###### Example - -```cs -var query = lobbyManager.GetSearchQuery(); -query.Distance(LobbySearchDistance.Local); -``` - -## GetLobbyCreateTransaction - -Gets a Lobby transaction used for creating a new lobby - -Returns a `Discord.LobbyTransaction`. - -###### Parameters - -None - -###### Example - -```cs -var txn = lobbyManager.GetLobbyCreateTransaction(); -``` - -## GetLobbyUpdateTransaction - -Gets a lobby transaction used for updating an existing lobby. - -Returns a `Discord.LobbyTransaction`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ---------------------------- | -| lobbyId | Int64 | the lobby you want to change | - -###### Example - -```cs -var txn = lobbyManager.GetLobbyUpdateTransaction(290926798626357250); -``` - -## GetMemberUpdateTransaction - -Gets a new member transaction for a lobby member in a given lobby. - -Returns a `Discord.LobbyMemberTransaction`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ---------------------------- | -| lobbyId | Int64 | the lobby you want to change | -| userId | Int64 | the user you wish to change | - -###### Example - -```cs -var txn = lobbyManager.GetMemberUpdateTransaction(290926798626357250, 53908232506183680); -``` - -## CreateLobby - -Creates a lobby. Creating a lobby auto-joins the connected user to it. **Do not call `SetOwner()` in the transaction for this method.** - -Returns `Discord.Result` and `ref Discord.Lobby` via callback. - -###### Parameters - -| name | type | description | -| ----------- | ---------------- | ----------------------------------------------------- | -| transaction | LobbyTransaction | a lobby transaction with set properties like capacity | - -###### Example - -```cs -lobbyManager.CreateLobby(txn, (Discord.Result result, ref Discord.Lobby lobby) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Created lobby {0}", lobby.Id); - } -}); -``` - -## UpdateLobby - -Updates a lobby with data from the given transaction. You _can_ call `SetOwner()` in this transaction. - -Returns `Discord.Result` via callback. - -> warn -> This call has a rate limit of 10 updates per 5 seconds. If you fear you might hit that, it may be a good idea to batch your lobby updates into transactions. - -###### Parameters - -| name | type | description | -| ----------- | ---------------- | ----------------------------------- | -| lobbyId | Int64 | the lobby you want to change | -| transaction | LobbyTransaction | the transaction with wanted changes | - -###### Example - -```cs -lobbymanager.UpdateLobby(290926798626357250, transaction, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby updated!"); - } -}); -``` - -## DeleteLobby - -Deletes a given lobby. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------- | ----- | ---------------------------- | -| lobbyId | Int64 | the lobby you want to delete | - -###### Example - -```cs -lobbyManager.DeleteLobby(290926798626357250, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby deleted!"); - } -}); -``` - -## ConnectLobby - -Connects the current user to a given lobby. You can be connected to up to five lobbies at a time. - -Returns `Discord.Result` and `ref Discord.Lobby` via callback. - -###### Parameters - -| name | type | description | -| ----------- | ------ | -------------------------------- | -| lobbyId | Int64 | the lobby you want to connect to | -| lobbySecret | string | the password for the lobby | - -###### Example - -```cs -lobbyManager.ConnectLobby(290926798626357250, "363446008341987328:123123", (Discord.Result result, ref Discord.Lobby lobby) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Connected to lobby {0}!", lobby.Id); - } -}); -``` - -## ConnectLobbyWithActivitySecret - -Connects the current user to a lobby; requires the special activity secret from the lobby which is a concatenated lobbyId and secret. - -Returns `Discord.Result` and `ref Discord.Lobby` via callback. - -###### Parameters - -| name | type | description | -| -------------- | ------ | ----------------------------------------- | -| activitySecret | string | the special activity secret for the lobby | - -###### Example - -```cs -lobbyManager.ConnectLobbyWithActivitySecret("363446008341987328:123123", (Discord.Result result, ref Discord.Lobby lobby) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Connected to lobby {0}!", lobby.Id); - } -}); -``` - -## GetLobbyActivitySecret - -Gets the special activity secret for a given lobby. If you are creating lobbies from game clients, use this to easily interact with Rich Presence invites. Set the returned secret to your activity's `JoinSecret`. - -Returns `string`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ---------------------------------------- | -| lobbyId | Int64 | the lobby you want to get the secret for | - -###### Example - -```cs -var activitySecret = lobbyManager.GetLobbyActivitySecret(290926798626357250); -var activity = new Discord.Activity -{ - State = "olleh", - Details = "foo details", - Party = { - Id = "foo partyID", - Size = { - CurrentSize = 1, - MaxSize = 4, - }, - }, - Secrets = { - Join = activitySecret, - }, - Instance = true, -}; - -ActivityManager.UpdateActivity(activity, (result) => -{ - Console.WriteLine("Update Activity {0}", result); -}); -``` - -## DisconnectLobby - -Disconnects the current user from a lobby. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------- | ----- | --------------------------- | -| lobbyId | Int64 | the lobby you want to leave | - -###### Example - -```cs -lobbyManager.DisconnectLobby(290926798626357250, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Left lobby!"); - } -}); -``` - -## GetLobby - -Gets the lobby object for a given lobby id. Because of the way that the SDK is architected, you must first call [`Search()`](#DOCS_GAME_SDK_LOBBIES/search) to build a stable list of lobbies. This function will then query those lobbies for ones with a matching id. - -Returns a `Discord.Lobby`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------- | -| lobbyId | Int64 | the lobby you want to get | - -###### Example - -```cs -var lobbyId = 12345; - -var query = lobbyManager.GetSearchQuery(); -lobbyManager.Search(query, (res) => -{ - if (res == Discord.Result.Ok) - { - // This will return you a lobby - var lobby = lobbyManager.GetLobby(lobbyId); - } -}); - -// This will return NotFound -var lobby_2 = lobbyManager.GetLobby(lobbyId); -``` - -## LobbyMetadataCount - -Returns the number of metadata key/value pairs on a given lobby. Used for accessing metadata by iterating over the list. - -Returns `Int32`. - -###### Parameters - -| name | type | description | -| ------- | ----- | -------------------------------------- | -| lobbyId | Int64 | the lobby you want to get metadata for | - -###### Example - -```cs -var count = lobbyManager.LobbyMetadataCount(290926798626357250); -for (int i = 0; i < count; i++) -{ - var value = lobbyManager.GetLobbyMetadataKey(290926798626357250, i); -} -``` - -## GetLobbyMetadataKey - -Returns the key for the lobby metadata at the given index. - -Returns `string`. - -###### Parameters - -| name | type | description | -| ------- | ----- | -------------------------------------- | -| lobbyId | Int64 | the lobby you want to get metadata for | -| index | Int32 | the index of lobby metadata to access | - -###### Example - -```cs -var count = lobbyManager.GetLobbyMetadataCount(290926798626357250); -for (int i = 0; i < count; i++) -{ - var value = lobbyManager.GetLobbyMetadataKey(290926798626357250, i); -} -``` - -## GetLobbyMetadataValue - -Returns lobby metadata value for a given key and id. Can be used with iteration, or direct access by keyname. - -###### Parameters - -| name | type | description | -| ------- | ------ | -------------------------------------- | -| lobbyId | Int64 | the lobby you want to get metadata for | -| key | string | the key name to access | - -###### Example - -```cs -var averageMmr = lobbyManager.GetLobbyMetadataValue(290926798626357250, "metadata.average_mmr"); -``` - -## MemberCount - -Get the number of members in a lobby. - -Returns `Int32`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------------------- | -| lobbyId | Int64 | the lobby you want to get members for | - -###### Example - -```cs -var count = lobbyManager.MemberCount(290926798626357250); -for (int i = 0; i < count; i++) -{ - var id = lobbyManager.GetMemberUserId(290926798626357250, i); -} -``` - -## GetMemberUserId - -Gets the user id of the lobby member at the given index. - -Returns `Int64`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------------------- | -| lobbyId | Int64 | the lobby you want to get members for | -| index | Int32 | the index of lobby member to access | - -###### Example - -```cs -var count = lobbyManager.MemberCount(290926798626357250); -for (int i = 0; i < count; i++) -{ - var id = lobbyManager.GetMemberUserId(290926798626357250, i); -} -``` - -## GetMemberUser - -Gets the user object for a given user id. - -Returns `Discord.User`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------------------- | -| lobbyId | Int64 | the lobby you want to get members for | -| userId | Int64 | the user's userId | - -###### Example - -```cs -var count = lobbyManager.MemberCount(290926798626357250); -for (int i = 0; i < count; i++) -{ - var id = lobbyManager.GetMemberUserId(290926798626357250, i); - var user = lobbyManager.GetMemberUser(290926798626357250, id); - Console.WriteLine("Got user {0}", user.Id); -} -``` - -## MemberMetadataCount - -Gets the number of metadata key/value pairs for the given lobby member. Used for accessing metadata by iterating over a list. - -Returns `Int32`. - -###### Parameters - -| name | type | description | -| ------- | ----- | -------------------------------------- | -| lobbyId | Int64 | the lobby the member belongs to | -| userId | Int64 | the id of the user to get metadata for | - -###### Example - -```cs -var count = lobbyManager.MemberMetadataCount(290926798626357250, 53908232506183680); -for (int i = 0; i < count; i++) -{ - var key = lobbyManager.GetMemberMetadataKey(290926798626357250, 53908232506183680, i); -} -``` - -## GetMemberMetadataKey - -Gets the key for the lobby metadata at the given index on a lobby member. - -Returns `string`. - -###### Parameters - -| name | type | description | -| ------- | ----- | -------------------------------------- | -| lobbyId | Int64 | the lobby the member belongs to | -| userId | Int64 | the id of the user to get metadata for | -| index | Int32 | the index of metadata to access | - -###### Example - -```cs -var count = lobbyManager.MemberMetadataCount(290926798626357250, 53908232506183680); -for (int i = 0; i < count; i++) -{ - var key = lobbyManager.GetMemberMetadataKey(290926798626357250, 53908232506183680, i); -} -``` - -## GetMemberMetadataValue - -Returns user metadata for a given key. Can be used in conjunction with the count and get key functions if you're iterating over metadata. Or you can access the metadata directly by keyname - -Returns `string`. - -###### Parameters - -| name | type | description | -| ------- | ------ | -------------------------------------- | -| lobbyId | Int64 | the lobby the member belongs to | -| userId | Int64 | the id of the user to get metadata for | -| key | string | the metadata key to access | - -###### Example - -```cs -var count = lobbyManager.MemberMetadataCount(290926798626357250, 53908232506183680); -for (int i = 0; i < count; i++) -{ - var key = lobbyManager.GetMemberMetadataKey(290926798626357250, 53908232506183680, i); - var value = lobbyManager.GetMemberMetadataValue(290926798626357250, 53908232506183680, key); - Console.WriteLine("Value: {0}", value); -} -``` - -## UpdateMember - -Updates lobby member info for a given member of the lobby. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ----------- | ---------------------- | --------------------------------- | -| lobbyId | Int64 | lobby the member belongs to | -| userId | Int64 | id of the user | -| transaction | LobbyMemberTransaction | transaction with the changed data | - -###### Example - -```cs -var txn = lobbyManager.GetMemberUpdateTransaction(); -txn.SetMetadata("my_mmr", "9999"); -lobbyManager.UpdateMember(290926798626357250, 53908232506183680, txn, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Lobby member updated!"); - } -}); -``` - -## SendLobbyMessage - -Sends a message to the lobby on behalf of the current user. You must be connected to the lobby you are messaging. You should use this function for message sending if you are _not_ using the built in networking layer for the lobby. If you are, you should use [SendNetworkMessage](#DOCS_GAME_SDK_LOBBIES/sendnetworkmessage) instead. - -This method has a rate limit of 10 messages per 5 seconds. - -Returns a `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------- | ------ | --------------------------- | -| lobbyId | Int64 | lobby the member belongs to | -| data | byte[] | the data to send | - -###### Example - -```cs -lobbyManager.SendLobbyMessage(290926798626357250, Encoding.UTF8.GetBytes("hey."), (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Message sent successfully"); - } -}); -``` - -## GetSearchQuery - -Creates a search object to search available lobbies. - -Returns `Discord.LobbySearchQuery`. - -###### Parameters - -None - -###### Example - -```cs -var search = lobbyManager.GetSearchQuery(); -``` - -## Search - -Searches available lobbies based on the search criteria chosen in the `Discord.LobbySearch` member functions. Lobbies that meet the criteria are then globally filtered, and can be accessed via iteration with `LobbyCount()` and `GetLobbyId()`. The callback fires when the list of lobbies is stable and ready for iteration. - -You do not necessarily need to access the filtered lobbies within the context of the result callback, but it may make it easier for the sake of asynchronous timing. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------ | ----------- | ------------------- | -| search | LobbySearch | the search criteria | - -###### Example - -```cs -var search = lobbyManger.GetSearchQuery(); -search.Filter("metadata.matchmaking_rating", LobbySearchComparison.GreaterThan, LobbySearchCast.Number, "455"); -search.Sort("metadata.matchmaking_rating", LobbySearchCast.Number, "456"); -search.Limit(10); -lobbyManager.Search(search, (result) => -{ - if (result == Discord.Result.Ok) - { - var count = lobbyManager.LobbyCount(); - Console.WriteLine("There are {0} lobbies that match your search criteria", count); - } -}); -``` - -## LobbyCount - -Get the number of lobbies that match the search. - -Returns `Int32`. - -###### Parameters - -None - -###### Example - -```cs -lobbyManager.Search(search, () => -{ - var count = lobbyManager.LobbyCount(); - Console.WriteLine("There are {0} lobbies that match your search criteria", count); -}); -``` - -## GetLobbyId - -Returns the id for the lobby at the given index. - -Returns `Int64`. - -###### Parameters - -| name | type | description | -| ----- | ----- | ------------------------------------------------ | -| index | Int32 | the index at which to access the list of lobbies | - -###### Example - -```cs -lobbyManager.Search(search, () => -{ - var count = lobbyManager.LobbyCount(); - for (int i = 0; i < count; i++) - { - var id = lobbyManager.GetLobbyId(i); - Console.WriteLine("Found lobby {0}", id); - } -}); -``` - -## ConnectVoice - -Connects to the voice channel of the current lobby. When connected to voice, the user can open their Discord overlay to see a list of other users with whom they are in voice, allowing them to mute/deafen themselves as well as mute/adjust the volume of other lobby members. - -You can also allow users to adjust voice settings for your game with [Overlay OpenVoiceSettings](#DOCS_GAME_SDK_OVERLAY/openvoicesettings). - -When integrating lobby voice into your game, be thoughtful about the user's experience. Auto-joining to voice can be jarring for users who may not be expecting it. We recommend voice always being opt-in, or at least that you provide an option for a player to choose whether or not to auto-join the voice channel of lobbies they join. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------- | -| lobbyId | Int64 | lobby to voice connect to | - -###### Example - -```cs -lobbyManager.ConnectVoice(290926798626357250, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Voice connected!"); - } -}); -``` - -## DisconnectVoice - -Disconnects from the voice channel of a given lobby. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------------ | -| lobbyId | Int64 | lobby to voice disconnect from | - -###### Example - -```cs -lobbyManager.DisconnectVoice(290926798626357250, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Voice disconnected!"); - } -}); -``` - -## OnLobbyUpdate - -Fires when a lobby is updated. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------ | -| lobbyId | Int64 | lobby that updated | - -###### Example - -```cs -lobbyManager.OnLobbyUpdate += (lobbyID) => -{ - Console.WriteLine("lobby successfully updated: {0}", lobbyID); -}; -``` - -## OnLobbyDelete - -Fired when a lobby is deleted. - -###### Parameters - -| name | type | description | -| ------- | ------ | ---------------------------------------------- | -| lobbyId | Int64 | lobby that was deleted | -| reason | string | reason for deletion - this is a system message | - -###### Example - -```cs -lobbyManager.OnLobbyDelete += (lobbyID, reason) => -{ - Console.WriteLine("lobby successfully deleted: {0} with reason: {1}", lobbyID, reason); -}; -``` - -## OnMemberConnect - -Fires when a new member joins the lobby. - -###### Parameters - -| name | type | description | -| ------- | ----- | --------------------- | -| lobbyId | Int64 | lobby the user joined | -| userId | Int64 | user that joined | - -###### Example - -```cs -lobbyManager.OnMemberConnect += (lobbyID, userID) => -{ - Console.WriteLine("user {0} connected to lobby: {1}", userID, lobbyID); -}; -``` - -## OnMemberUpdate - -Fires when data for a lobby member is updated. - -###### Parameters - -| name | type | description | -| ------- | ----- | ----------------------------- | -| lobbyId | Int64 | lobby the user is a member of | -| userId | Int64 | user that was updated | - -###### Example - -```cs -lobbyManager.OnMemberUpdate += (lobbyID, userID) => -{ - Console.WriteLine("user {0} got updated in lobby: {1}", userID, lobbyID); -}; -``` - -## OnMemberDisconnect - -Fires when a member leaves the lobby. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------------ | -| lobbyId | Int64 | lobby the user was a member of | -| userId | Int64 | user that left | - -###### Example - -```cs -lobbyManager.OnMemberDisconnect += (lobbyID, userID) => -{ - Console.WriteLine("user {0} disconnected from lobby: {1}", userID, lobbyID); -}; -``` - -## OnLobbyMessage - -Fires when a message is sent to the lobby. - -###### Parameters - -| name | type | description | -| ------- | ------ | ---------------------------- | -| lobbyId | Int64 | lobby the message is sent to | -| userId | Int64 | user that sent the message | -| data | byte[] | the message contents | - -###### Example - -```cs -lobbyManager.OnLobbyMessage += (lobbyID, userID, data) => -{ - Console.WriteLine("lobby message: {0}, user who sent the message: {1}, Containing: {2}", lobbyID, userID, Encoding.UTF8.GetString(data)); -}; -``` - -## OnSpeaking - -Fires when a user connected to voice starts or stops speaking. - -###### Parameters - -| name | type | description | -| -------- | ----- | ------------------------------------------------------- | -| lobbyId | Int64 | lobby the user is connected to | -| userId | Int64 | user in voice | -| speaking | bool | `true` == started speaking, `false` == stopped speaking | - -###### Example - -```cs -lobbyManager.OnSpeaking += (lobbyID, userID, speaking) => -{ - Console.WriteLine("lobby speaking: {0} {1} {2}", lobbyID, userID, speaking); -}; -``` - -## Connecting to Lobbies - -In the preceding section, you probably noticed there are a couple different methods for connecting to a lobby: `Connect()` and `ConnectWithActivitySecret()`. Lobbies in Discord are even more useful when hooked together with Activities/Rich Presence functionality; they give you everything you need to create an awesome game invite system. - -If you are creating lobbies for users in the game client, and not on a backend server, consider using `GetLobbyActivitySecret` and `ConnectWithActivitySecret()`. `GetLobbyActivitySecret()` will return you a unique secret for the lobby concatenated with the lobby's id. You can pipe this value directly to the `Secrets.Join` field of the `Activity` payload. Then, when a user receives the secret, their client can call `ConnectWithActivitySecret()` with just the secret; the lobby id is parsed out automatically. This saves you the effort of concatenating the secret + id together yourself and then parsing them out again. As a code example: - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); -var lobbyManager = discord.GetLobbyManager(); -var activityManager = discord.GetActivityManager(); - -// Create a lobby -var txn = lobbyManager.GetLobbyCreateTransaction(); -txn.SetCapacity(5); -txn.SetType(Discord.LobbyType.Private); - -lobbyManager.CreateLobby(txn, (Discord.Result result, ref Discord.Lobby lobby) => -{ - // Get the special activity secret - var secret = lobbyManager.GetLobbyActivitySecret(lobby.id); - - // Create a new activity - // Set the party id to the lobby id, so everyone in the lobby has the same value - // Set the join secret to the special activity secret - var activity = new Discord.Activity - { - Party = - { - Id = lobby.id, - Size = { - CurrentSize = 1, - MaxSize = 5 - } - }, - Secrets = - { - Join = secret - } - }; - - activityManager.UpdateActivity(activity, (result) => - { - // Now, you can send chat invites, or others can ask to join - // When other clients receive the OnActivityJoin() event, they'll receive the special activity secret - // They can then directly call lobbyManager.ConnectLobbyWithActivitySecret() and be put into the lobby together - }) -}); -``` - -If you are creating lobbies with your own backend system (see the section below), this method may not be useful for you. In that case, you can use `Connect()` and pass the lobby id and secret as you normally would. If you're hooking up to Activities, just make sure you send both the lobby secret and the lobby id in the `Secrets.Join` field, so anyone who tries to join has the right data. - -### Example: Search for a Lobby, Connect, and Join Voice - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); - -// Search lobbies. -var query = lobbyManager.GetSearchQuery(); - -// Filter by a metadata value. -query.Filter("metadata.ELO", Discord.LobbySearchComparison.EqualTo, Discord.LobbySearchCast.Number, "1337"); - -// Only return 1 result max. -query.Limit(1); - -lobbyManager.Search(query, () => -{ - Console.WriteLine("search returned {0} lobbies", lobbyManager.LobbyCount()); - - if (lobbyManager.LobbyCount() == 1) - { - Console.WriteLine("first lobby: {0}", lobbyManager.GetLobbyId(0)); - } - - // Get the id of the lobby, and connect to voice - var id = lobbyManager.GetLobbyId(0); - lobbyManager.ConnectVoice(id, (result) => - { - Console.WriteLine("Connected to voice!"); - }); -}); -``` - -## Example: Crossplayish - -So, an explanation. Because the DLL that you ship with your game is a stub that calls out to the local Discord client for actual operations, the SDK does not necessarily care if the game was launched from Discord. As long as the player launching the game: - -1. Has Discord installed -2. Has a Discord account -3. Has logged into Discord on their machine (whether or not Discord is open) - -The SDK will function as if the game were launched from Discord and everything will work; if Discord is not currently launched, the SDK will launch it. - -That means that if Player A is launching Your Amazing Game from Discord, and Player B is launching it from Other Cool But Not As Cool As Discord Game Store, as long as Player B meets the above criteria, both players can play with each other using Discord's lobbies + networking functions. If the SDK is not able to launch Discord for Player B—maybe they've never installed/used Discord before!—you'll get an error saying as much. We're not saying what you _should_ do, but hey, wouldn't this make a really neat in-game touchpoint for your players to join their friends on Discord, and maybe even join your game's [verified server](https://discord.com/verification)? - -OK so this wasn't really a code example, but I think you get how this works. - -## The API Way - -Below are the API endpoints and the parameters they accept. If you choose to interface directly with the Discord API, you will need a "Bot token". This is a special authorization token with which your application can access Discord's HTTP API. Head on over to [your app's dashboard](https://discord.com/developers/), and hit the big "Add a Bot User" button. From there, mutter _abra kadabra_ and reveal the token. This token is used as an authorization header against our API like so: - -``` -curl -x POST -h "Authorization: Bot " https://discord.com/api/some-route/that-does-a-thing -``` - -> info -> Make sure to prepend your token with "Bot"! - -Here are the routes; they all expect JSON bodies. Also, hey, while you're here. You've got a bot token. You're looking at our API. You should check out all the other [awesome stuff](https://discord.com/developers/docs/intro) you can do with it! - -## Create Lobby % POST /lobbies - -Creates a new lobby. Returns an object similar to the SDK `Lobby` struct, documented below. - -To get a list of valid regions, call the [List Voice Regions](https://discord.com/developers/docs/resources/voice#list-voice-regions) endpoint. - -###### Parameters - -| name | type | description | -| -------------- | --------- | ---------------------------------------------------------------------------------------------------- | -| application_id | string | your application id | -| type | LobbyType | the type of lobby | -| metadata | dict | metadata for the lobby - key/value pairs with types `string` | -| capacity | int | max lobby capacity with a default of 16 | -| region | string | the region in which to make the lobby - defaults to the region of the requesting server's IP address | - -###### Return Object - -```json -{ - "capacity": 10, - "region": "us-west", - "secret": "400316b221351324", - "application_id": "299996444748734465", - "metadata": { - "a": "wow", - "b": "some test metadata" - }, - "type": 1, - "id": "469317204969993265", - "owner_id": "53908232599983680" -} -``` - -## Update Lobby % PATCH /lobbies/{lobby.id#DOCS_LOBBIES/data-models-lobby-struct} - -Updates a lobby. - -###### Parameters - -| name | type | description | -| -------- | --------- | ------------------------------------------------------------ | -| type | LobbyType | the type of lobby | -| metadata | dict | metadata for the lobby - key/value pairs with types `string` | -| capacity | int | max lobby capacity with a default of 16 | - -## Delete Lobby % DELETE /lobbies/{lobby.id#DOCS_GAME_SDK_LOBBIES/data-models-lobby-struct} - -Deletes a lobby. - -## Update Lobby Member % PATCH /lobbies/{lobby.id#DOCS_GAME_SDK_LOBBIES/data-models-lobby-struct}/members/{user.id#DOCS_RESOURCES_USER/user-object} - -Updates the metadata for a lobby member. - -###### Parameters - -| name | type | description | -| -------- | ---- | ------------------------------------------------------------------- | -| metadata | dict | metadata for the lobby member - key/value pairs with types `string` | - -## Create Lobby Search % POST /lobbies/search - -Creates a lobby search for matchmaking around given criteria. - -###### Parameters - -| name | type | description | -| -------------- | ----------------------------- | ---------------------------------------- | -| application_id | string | your application id | -| filter | array of SearchFilter objects | the filter to check against | -| sort | array of SearchSort objects | how to sort the results | -| limit | int | limit of lobbies returned, default of 25 | - -###### SearchFilter Object - -| name | type | description | -| ---------- | ---------------- | ------------------------------------------------- | -| key | string | the metadata key to search | -| value | string | the value of the metadata key to validate against | -| cast | SearchCast | the type to cast `value` as | -| comparison | SearchComparison | how to compare the metadata values | - -###### SearchComparison Types - -| name | value | -| ------------------------ | ----- | -| EQUAL_TO_OR_LESS_THAN | -2 | -| LESS_THAN | -1 | -| EQUAL | 0 | -| EQUAL_TO_OR_GREATER_THAN | 1 | -| GREATER_THAN | 2 | -| NOT_EQUAL | 3 | - -###### SearchSort Object - -| name | type | description | -| ---------- | ---------- | ----------------------------------------------------------------------- | -| key | string | the metadata key on which to sort lobbies that meet the search criteria | -| cast | SearchCast | the type to cast `value` as | -| near_value | string | the value around which to sort the key | - -###### SearchCast Types - -| name | value | -| ------ | ----- | -| STRING | 1 | -| NUMBER | 2 | - -## Send Lobby Data % POST /lobbies/{lobby.id#DOCS_GAME_SDK_LOBBIES/data-models-lobby-struct}/send - -Sends a message to the lobby, fanning it out to other lobby members. - -This endpoints accepts a UTF8 string. If your message is already a string, you're good to go! If you want to send binary, you can send it to this endpoint as a base64 encoded data uri. - -###### Parameters - -| name | type | description | -| ---- | ------ | ------------------------------------------- | -| data | string | a message to be sent to other lobby members | - -## Integrated Networking - -Discord lobbies have the option of being used with a wrapped networking layer, allowing you to start sending packets quickly and easily without needing to do state management around routes, peer IDs, member metadata, and the like. - -This layer allows you to easily connect to the network and open channels to all lobby members with one function call. You can then send network messages to users by their user ID, easily retrieved via lobby methods. - -We take care of all the route updating for you, so you can get up and running quickly and easily. If you'd like to see how the lower level networking functionality works, or want to try it yourself so you can tweak it to your liking, check out [Networking](#DOCS_GAME_SDK_NETWORKING/); - -## ConnectNetwork - -Connects to the networking layer for the given lobby ID. Call this when connecting to the lobby. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------------ | -| lobbyId | Int64 | the ID of the lobby you are in | - -###### Example - -```cs -lobbyManager.ConnectLobby(lobbyId, (result, lobby) => -{ - lobbyManager.ConnectNetwork(lobby.Id); -}); -``` - -## DisconnectNetwork - -Disconnects from the networking layer for the given lobby ID. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------- | ----- | ------------------------------ | -| lobbyId | Int64 | the ID of the lobby you are in | - -###### Example - -```cs -lobbyManager.DisconnectNetwork(lobby.Id); -``` - -## FlushNetwork - -Flushes the network. Call this when you're done sending messages. In Unity, this should be in `LateUpdate()`. - -Returns `void`. - -###### Parameters - -None - -###### Example - -```cs -void LateUpdate() -{ - var lobbyManager = discord.GetLobbyManager(); - lobbyManager.FlushNetwork(); -} -``` - -## OpenNetworkChannel - -Opens a network channel to all users in a lobby on the given channel number. No need to iterate over everyone! - -Returns `void`. - -###### Parameters - -| name | type | description | -| --------- | ----- | ---------------------------------------------------- | -| lobbyId | Int64 | the ID of the lobby you are in | -| channelId | byte | the channel on which to connect | -| reliable | bool | whether the channel should be unreliable or reliable | - -###### Example - -```cs -var lobbyManager = discord.GetLobbyManager(); -lobbyManager.ConnectLobby(lobbyId, (result, lobby) => -{ - lobbyManager.ConnectNetwork(lobby.Id); - lobbyManager.OpenNetworkChannel(lobby.Id, 0, true); -}); -``` - -## SendNetworkMessage - -Sends a network message to the given user ID that is a member of the given lobby ID over the given channel ID. - -Returns `void`. - -###### Parameters - -| name | type | description | -| --------- | ------ | --------------------------------------- | -| lobbyId | Int64 | the ID of the lobby you are in | -| userId | Int64 | the ID of the user to send a message to | -| channelId | byte | the channel on which to connect | -| data | byte[] | the message to send | - -###### Example - -```cs -var lobbyManager = discord.GetLobbyManager(); -lobbyManager.ConnectLobby(lobbyId, (result, lobby) => -{ - lobbyManager.ConnectNetwork(lobby.Id); - lobbyManager.OpenNetworkChannel(lobby.Id, 0, true); - for (int i = 0; i < lobbyManager.MemberCount(); i++) - { - var userId = lobbyManager.GetMemberUserId(i); - lobbyManager.SendNetworkMessage(lobby.Id, userId, 0, System.Text.Encoding.UTF8.GetBytes("Hello!")); - } -}); -``` - -## OnNetworkMessage - -Fires when the user receives a message from the lobby's networking layer. - -###### Parameters - -| name | type | description | -| --------- | ------ | --------------------------------------- | -| lobbyId | Int64 | the ID of the lobby you are in | -| userId | Int64 | the ID of the user who sent the message | -| channelId | byte | the channel the message was sent on | -| data | byte[] | the message | - -## Example: Networking the Easy Way - -```cs -// We can create a helper method to easily connect to the networking layer of the lobby -public void InitNetworking(Int64 lobbyId) -{ - // First, connect to the lobby network layer - var lobbyManager = discord.GetLobbyManager(); - lobbyManager.ConnectNetwork(lobbyId); - - // Next, deterministically open our channels - // Reliable on 0, unreliable on 1 - lobbyManager.OpenNetworkChannel(lobbyId, 0, true); - lobbyManager.OpenNetworkChannel(lobbyId, 1, false); - - // We're ready to go! -} - -// Let's say we got a game invite from Rich Presence -activityManager.OnActivityJoin += secret => -{ - var lobbyManager = discord.GetLobbyManager(); - lobbyManager.ConnectLobbyWithActivitySecret(secret, (Discord.Result result, ref Discord.Lobby lobby) => - { - // Connect to networking - InitNetworking(lobby.Id); - - // Say hello! - for (int i = 0; i < lobbyManager.MemberCount(); i++) - { - var userId = lobbyManager.GetMemberUserId(i); - lobbyManager.SendNetworkMessage(lobby.Id, userId, 0, System.Text.Encoding.UTF8.GetBytes("Hello!")); - } - }); -} -``` diff --git a/docs/game_sdk/Networking.md b/docs/game_sdk/Networking.md deleted file mode 100644 index a619adeb64..0000000000 --- a/docs/game_sdk/Networking.md +++ /dev/null @@ -1,377 +0,0 @@ -# Networking - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -A note before starting: this documentation covers the "low layer" networking level of the Discord GameSDK. What that means is that using the network manager directly affords you the flexibility to update routes, open channels, and handle events directly emitted by the SDK. If you're looking for something a bit easier and faster to integrate, we recommend that you check out the networking wrapper around our lobby documentation: [Integrated Networking](#DOCS_GAME_SDK_LOBBIES/integrated-networking) - -Need a networking layer? Have a networking layer! This manager handles all things packets so you can get data from player to player and make your multiplayer...work. It: - -- Functions as a connection-oriented, TCP-like API, but over UDP! -- Supports "reliable" and "unreliable" connections - - Packets with loot in them always get there, but player positioning can be eventually consistent -- Features P2P-like connections, but routed through Discord's high-end server infrastructure - - All the benefits of direct connections, without the IP leaks! -- Is encrypted! - -An important note to make here is that our networking layer **is not peer-to-peer**. Discord has always promised that we will not leak your IP, and we promise to keep it that way. Though it seems like you are connected directly to another user, it routes through Discord's servers in the middle, ensuring both security and robust networking thanks to our servers. - -## GetPeerId - -Get the networking peer ID for the current user, allowing other users to send packets to them. - -Returns a `UInt64`. - -###### Parameters - -None - -###### Example - -```cs -var myPeerId = networkManager.GetPeerId(); -``` - -## Flush - -Flushes the network. Run this at the end of your game's loop, once you've finished sending all you need to send. In Unity, for example, stick this in `LateUpdate()`. - -Returns `void`. - -###### Parameters - -None - -###### Example - -```cs -void LateUpdate() -{ - networkManager.Flush(); -} -``` - -## OpenChannel - -Opens a channel to a user with their given peer ID on the given channel number. - -Unreliable channels—`reliable = false`—should be used for loss-tolerant data, like player positioning in the world. Reliable channels—`reliable = true`—should be used for data that _must_ get to the user, like loot drops! - -Returns `void`. - -###### Parameters - -| name | type | description | -| --------- | ------ | ---------------------------------------------------- | -| peerId | UInt64 | the peerId of the user to connect to | -| channelId | byte | the channel on which to connect | -| reliable | bool | whether the channel should be unreliable or reliable | - -###### Example - -```cs -var userId = 53908232506183680; -var lobbyId = 290926798626357250; - -var rawPeerId = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "metadata.peer_id"); -// Metadata is stored as a string, so we need to make it an integer for OpenChannel -var peerId = System.Convert.ToUInt64(rawPeerId); -networkManager.OpenChannel(peerId, 0, false); -``` - -## OpenPeer - -Opens a network connection to another Discord user. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------ | ------ | -------------------------------------------- | -| peerId | UInt64 | the peerId of the user to connect to | -| route | string | the route the user is currently broadcasting | - -###### Example - -```cs -var userId = 53908232506183680; -var lobbyId = 290926798626357250; - -var rawPeerId = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "metadata.peer_id"); -// Metadata is stored as a string, so we need to make it an integer for OpenChannel -var peerId = System.Convert.ToUInt64(rawPeerId); -var route = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "metadata.route"); -networkManager.OpenPeer(peerId, route); -``` - -## UpdatePeer - -Updates the network connection to another Discord user. You'll want to call this when notified that the route for a user to which you are connected has changed, most likely from a lobby member update event. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------ | ------ | -------------------------- | -| peerId | UInt64 | the user's peerId | -| route | string | the new route for the user | - -###### Example - -```cs -lobbyManager.OnMemberUpdate += (lobbyId, userId) => -{ - var rawPeerId = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "metadata.peer_id"); - // Metadata is stored as a string, so we need to make it an integer for OpenChannel - var peerId = System.Convert.ToUInt64(rawPeerId); - var newRoute = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "metadata.route"); - networkManager.UpdatePeer(peerId, newRoute); -} -``` - -## SendMessage - -Sends data to a given peer ID through the given channel. - -Returns `void`. - -###### Parameters - -| name | type | description | -| --------- | ------ | ------------------------------- | -| peerId | UInt64 | the peer id to connect to | -| channelId | byte | the channel on which to connect | -| data | byte[] | the data to send | - -###### Example - -```cs -var userId = 53908232506183680; -var lobbyId = 290926798626357250; -var rawPeerId = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "metadata.peer_id"); -// Metadata is stored as a string, so we need to make it an integer for OpenChannel -var peerId = System.Convert.ToUInt64(rawPeerId); - -byte[] lootDrops = GameEngine.GetLootData(); -networkManager.SendMessage(peerId, 1, lootDrops); -``` - -## CloseChannel - -Close the connection to a given user by peerId on the given channel. - -Returns `void`. - -###### Parameters - -| name | type | description | -| --------- | ------ | ----------------------------------------- | -| peerId | UInt64 | the peerId of the user to disconnect from | -| channelId | byte | the route to close | - -###### Example - -```cs -var userId = 53908232506183680; -var lobbyId = 290926798626357250; - -var rawPeerId = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "metadata.peer_id"); -// Metadata is stored as a string, so we need to make it an integer for OpenChannel -var peerId = System.Convert.ToUInt64(rawPeerId); -networkManager.CloseChannel(peerId, 0); -Console.WriteLine("Channel {0} to {1} closed", 0, peerId); -``` - -## ClosePeer - -Disconnects the network session to another Discord user. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ------ | ------ | ----------------- | -| peerId | UInt64 | the user's peerId | - -###### Example - -```cs -var userId = 53908232506183680; -var lobbyId = 290926798626357250; - -var rawPeerId = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "metadata.peer_id"); -// Metadata is stored as a string, so we need to make it an integer for OpenChannel -var peerId = System.Convert.ToUInt64(rawPeerId); -networkManager.ClosePeer(peerId); -Console.WriteLine("Connection to {0} closed", peerId); -``` - -## OnMessage - -Fires when you receive data from another user. This callback will only fire if you already have an open channel with the user sending you data. Make sure you're running `RunCallbacks()` in your game loop, or you'll never get data! - -###### Parameters - -| name | type | description | -| --------- | ------ | -------------------------- | -| peerId | UInt64 | the peer id of the sender | -| channelId | byte | the channel it was sent on | -| data | byte[] | the data sent | - -###### Example - -```cs -networkManager.OnMessage += (peerId, channel, data) => -{ - var stringData = Encoding.UTF8.GetString(data); - Console.WriteLine("Message from {0}: {1}", peerId, stringData) -} -``` - -## OnRouteUpdate - -Fires when your networking route has changed. You should broadcast to other users to whom you are connected that this has changed, probably by updating your lobby member metadata for others to receive. - -###### Parameters - -| name | type | description | -| ----- | ------ | ------------------------- | -| route | string | the new route to the user | - -###### Example - -```cs -networkManager.OnRouteUpdate += route => -{ - var currentUser = userManager.GetCurrentUser(); - var lobbyId = 290926798626357250; - - var txn = lobbyManager.GetMemberUpdateTransaction(lobbyId, currentUser.Id); - txn.SetMetadata("route", route); - lobbyManager.UpdateMember(lobbyId, currentUser.Id, txn, (result) => - { - // Who needs error handling anyway - Console.WriteLine(result); - }); -} -``` - -## Flush vs RunCallbacks - -A quick note here may be helpful for the two functions that should be called continuously in your game loop: `discord.RunCallbacks()` and `networkManager.Flush()`. `RunCallbacks()` pumps the SDK's event loop, sending any newly-received data down the SDK tubes to your game. For this reason, you should call it at the beginning of your game loop; that way, any new data is handled immediately by callbacks you've registered. In Unity, for example, this goes in `Update()`. - -`Flush()` is specific to the network manager. It actually _writes_ the packets out to the stream. You should call this at the _end_ of your game loop as a way of saying "OK, I'm done with networking stuff, go send all the stuff to people who need it". In Unity, for example, this goes in `LateUpdate()`. - -## Connecting to Each Other - -This manager is built around the concept of routes between players, and then channels on those routes. Player A opens a route to Player B. This route will change, most commonly if the user's external IP address changes. As that route changes, the player will receive `OnRouteUpdate` events. They should then alert other lobby members that their route has changed by updating their lobbymetadata. Other lobby members will see those updates from the `OnLobbyMemberUpdate` event, and can call `UpdateRoute()` accordingly. A user's route could change frequently, so architect your system anticipating frequent route changes and updates. - -Once Player A has a route open to Player B, Player A then opens a channel to Player B, and Player B does the same to Player A. Channels are the pipes down which data is actually sent. These two users can now send data back and forth to each other with `SendMessage()` and receive it with `OnMessage`. - -In order to properly send and receive data between A and B, both users need to have **the same type of channel** open to each other **on the same channel number**. If A has Reliable Channel 4 open to B, B also needs Reliable Channel 4 open to A. - -## Example: Connecting to Another Player in a Lobby - -```cs -var discord = new Discord.Discord(clientId, (UInt64)Discord.CreateFlags.Default); - -// Join a lobby with another user in it -// Get their peer id, and connect to them - -var networkManager = discord.GetNetworkManager(); -var lobbyManager = discord.GetLobbyManager(); -var userManager = discord.GetUserManager(); - -Discord.User currentUser; -var otherUserPeerId; -var lobbyId; - -// Get yourself -currentUser = userManager.GetCurrentUser(); - -// This will fire once you connect to the lobby -// Telling you which route is yours -networkManager.OnRouteUpdate += route => -{ - var txn = lobbyManager.GetMemberUpdateTransaction(lobbyId, currentUser.Id); - txn.SetMetadata("route", route); - lobbyManager.UpdateMember(lobbyId, currentUser.Id, txn, (result => - { - // Who needs error handling anyway - Console.WriteLine(result); - })) -} - -// When other users get new routes, they'll update their metadata -// Fetch it and update their route -lobbyManager.OnMemberUpdate += (lobbyId, userId) => -{ - var rawPeerId = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "peer_id"); - // Metadata is stored as a string, so we need to make it an integer for OpenChannel - var peerId = System.Convert.ToUInt64(rawPeerId); - var newRoute = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "route"); - lobbyManager.UpdatePeer(peerId, newRoute); -} - -// Connect to lobby with an id of 12345 and a secret of "password" -// This may occur in a generated lobby search, when a user needs to input a password to connect -lobbyManager.ConnectLobby(12345, "password", (Discord.Result x, ref Discord.Lobby lobby) => -{ - lobbyId = lobby.Id; - - // Add our own peer id to our lobby member metadata - // So other users can get it to connect to us - var localPeerId = Convert.ToString(networkManager.GetPeerId()); - var txn = lobbyManager.GetMemberUpdateTransaction(lobby.Id, currentUser.Id); - txn.SetMetadata("peer_id", localPeerId); - lobbyManager.UpdateMember(lobby.Id, currentUser.Id, txn, (result) => - { - // Who needs error handling anyway - Console.WriteLine(result); - }); - - // Get the first member in the lobby, assuming someone is already there - var memberId = lobbyManager.GetMemberUserId(lobby.Id, 0); - - // Get their peer id and route from their metadata, added previously - var rawPeerId = lobbyManager.GetMemberMetadataValue(lobbyId, userId, "peer_id"); - // Metadata is stored as a string, so we need to make it an integer for OpenChannel - otherUserPeerId = System.Convert.ToUInt64(rawPeerId); - var otherRoute = lobbyManager.GetMemberMetadataValue(lobby.Id, memberId, "route"); - - // Connect to them - networkManager.OpenPeer(otherUserPeerId, otherRoute); - -} - -// Open an unreliable channel to the user on channel 0 -// And a reliable one on channel 1 -networkManager.OpenChannel(otherUserPeerId, 0, false); -networkManager.OpenChannel(otherUserPeerId, 1, true); - -// An important data packet from our game engine -byte[] data = GameEngine.GetImportantData(); - -// Determine if that data is about Player Loot Drops, if so send it on reliable, if not send it on unreliable -if (isDataAboutPlayerLootDrops(data)) -{ - // This is important and has to get there - // Send over reliable channel - networkManager.SendMessage(otherUserPeerId, 1, data); -} -else -{ - // This is eventually consistent data, like the player's position in the world - // It can be sent over the unreliable channel - networkManager.SendMessage(otherUserPeerId, 0, data); -} - -// Done; ship it! -networkManager.Flush(); -``` diff --git a/docs/game_sdk/Overlay.md b/docs/game_sdk/Overlay.md deleted file mode 100644 index 1c75d1ef80..0000000000 --- a/docs/game_sdk/Overlay.md +++ /dev/null @@ -1,195 +0,0 @@ -# Overlay - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -> warn -> The overlay is only supported on Windows for DirectX or OpenGL games. Linux, Mac, and games using Vulkan are not supported. [Click here for more info.](https://support.discord.com/hc/en-us/articles/217659737-Games-Overlay-101) - -Discord comes with an awesome built-in overlay, and you may want to make use of it for your game. This manager will help you do just that! It: - -- Gives you the current state of the overlay for the user - - Locked, enabled, unlocked, open, closed, etc. -- Allows you to change that state - -## Data Models - -###### ActivityActionType Enum - -| name | value | -| -------- | ----- | -| Join | 1 | -| Spectate | 2 | - -## IsEnabled - -Check whether the user has the overlay enabled or disabled. If the overlay is disabled, all the functionality in this manager will still work. The calls will instead focus the Discord client and show the modal there instead. - -Returns a `bool`. - -###### Parameters - -None - -###### Example - -```cs -if (!overlaymanager.IsEnabled()) -{ - Console.WriteLine("Overlay is not enabled. Modals will be shown in the Discord client instead"); -} -``` - -## IsLocked - -Check if the overlay is currently locked or unlocked - -###### Parameters - -None - -###### Example - -```cs -if (overlayManager.IsLocked()) -{ - overlayManager.SetLocked(true, (res) => - { - Console.WriteLine("Input in the overlay is now accessible again"); - }); -} -``` - -## SetLocked - -Locks or unlocks input in the overlay. Calling `SetLocked(true);` will also close any modals in the overlay or in-app from things like IAP purchase flows and disallow input. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ------ | ---- | -------------------------- | -| locked | bool | lock or unlock the overlay | - -###### Example - -```cs -overlayManager.SetLocked(true, (res) => -{ - Console.WriteLine("Overlay has been locked and modals have been closed"); -}); -``` - -## OpenActivityInvite - -Opens the overlay modal for sending game invitations to users, channels, and servers. If you do not have a valid activity with all the required fields, this call will error. See [Activity Action Field Requirements](#DOCS_GAME_SDK_ACTIVITIES/activity-action-field-requirements) for the fields required to have join and spectate invites function properly. - -Returns a `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ---- | ------------------ | --------------------------- | -| type | ActivityActionType | what type of invite to send | - -###### Example - -```cs -overlayManager.OpenActivityInvite(Discord.ActivityActionType.Join, (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("User is now inviting others to play!"); - } -}); -``` - -## OpenGuildInvite - -Opens the overlay modal for joining a Discord guild, given its invite code. An invite code for a server may look something like `fortnite` for a verified server—the full invite being `discord.gg/fortnite`—or something like `rjEeUJq` for a non-verified server, the full invite being `discord.gg/rjEeUJq`. - -Returns a `Discord.Result` via callback. Note that a successful `Discord.Result` response does not necessarily mean that the user has joined the guild. If you want more granular control over and knowledge about users joining your guild, you may want to look into implementing the [`guilds.join` OAuth2 scope in an authorization code grant](#DOCS_TOPICS_OAUTH2/authorization-code-grant) in conjunction with the [Add Guild Members](#DOCS_RESOURCES_GUILD/add-guild-member) endpoint. - -###### Parameters - -| name | type | description | -| ---- | ------ | -------------------------- | -| code | string | an invite code for a guild | - -###### Example - -```cs -overlayManager.OpenGuildInvite("rjEeUJq", (result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Invite was valid."); - } -}); -``` - -## OpenVoiceSettings - -Opens the overlay widget for voice settings for the currently connected application. These settings are unique to each user within the context of your application. That means that a user can have different favorite voice settings for each of their games! - -![Screenshot of the Voice Settings modal for an application](game-overlay-sdk-voice-settings.png) - -Also, when connected to a lobby's voice channel, the overlay will show a widget that allows users to locally mute, deafen, and adjust the volume of others. - -![Screenshot of the Voice Widget displayed in an application](game-overlay-sdk-voice-widget.png) - -Returns a `Discord.Result` via callback. - -###### Parameters - -None - -###### Example - -```cs -overlayManager.OpenVoiceSettings((result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Overlay is open to the voice settings for your application/") - } -}) -``` - -## OnToggle - -Fires when the overlay is locked or unlocked (a.k.a. opened or closed) - -###### Parameters - -| name | type | description | -| ------ | ---- | ------------------------------------- | -| locked | bool | is the overlay now locked or unlocked | - -###### Example - -overlayManager.OnToggle += overlayLock => -{ - Console.WriteLine("Overlay Locked: {0}", overlayLock); -}; - -## Example: Activate Overlay Invite Modal - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); -var overlayManager = discord.GetOverlayManager(); - -// Invite users to join your game -overlayManager.OpenActivityInvite(ActivityActionType.Join, (result) => -{ - Console.WriteLine("Overlay is now open!"); -}) -``` - -And that invite modal looks like this! - -![Screenshot of an Invitation Modal in an application](game-overlay-sdk-invite.gif) diff --git a/docs/game_sdk/Relationships.md b/docs/game_sdk/Relationships.md deleted file mode 100644 index cf3bbc4e03..0000000000 --- a/docs/game_sdk/Relationships.md +++ /dev/null @@ -1,234 +0,0 @@ -# Relationships - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -This manager helps you access the relationships your players have made on Discord. Unfortunately, it won't help them make relationships IRL. They're on their own for that. It lets you: - -- Access a user's relationships -- Filter those relationships based on a given criteria -- Build a user's friends list - -## First Notes - -Relationships on Discord change often; people start and stop playing games, go online, offline, invisible, or otherwise change state. Therefore, there are some important factors to remember when working with this manager. When you are first getting a list of a user's relationships, before you can `Filter()`, you need to wait for the `OnRefresh` callback to fire. This is your indicator that Discord has successfully taken a snapshot of the state of all your relationships at a given moment. Now that you have this snapshot, you can `Filter()` it to build the list that you want, and then iterate over that list to do whatever your game needs to do. Use this to build your initial social graph for a user. - -As relationships change, the `OnRelationshipUpdate` event will fire. You can use this to update the user's social graph, changing the status of the other Discord users that you chose to filter, e.g. someone is now online, or now playing the game, or no longer playing. - -An example of how to do this properly is at the end of this documentation page. - -## Data Models - -###### Relationship Struct - -| name | type | description | -| -------- | ---------------- | -------------------------------- | -| Type | RelationshipType | what kind of relationship it is | -| User | User | the user the relationship is for | -| Presence | Presence | that user's current presence | - -###### RelationshipType Enum - -| value | description | -| --------------- | -------------------------------------------------------------------------------- | -| None | user has no intrinsic relationship | -| Friend | user is a friend | -| Blocked | user is blocked | -| PendingIncoming | user has a pending incoming friend request to connected user | -| PendingOutgoing | current user has a pending outgoing friend request to user | -| Implicit | user is not friends, but interacts with current user often (frequency + recency) | - -###### Presence Struct - -| name | type | description | -| -------- | -------- | -------------------------------- | -| Status | Status | the user's current online status | -| Activity | Activity | the user's current activity | - -###### Status Enum - -| name | value | -| ------------ | ----- | -| Offline | 0 | -| Online | 1 | -| Idle | 2 | -| DoNotDisturb | 3 | - -## Filter - -Filters a user's relationship list by a boolean condition. - -Returns `void`. - -###### Parameters - -A function that takes a `Relationship` parameter. - -###### Example - -```cs -relationshipsManager.Filter(relationship => -{ - return relationship.Presence.Status == Discord.Status.Online; -}); -``` - -## Get - -Get the relationship between the current user and a given user by id. - -Returns a `Relationship`. - -###### Parameters - -| name | type | description | -| ------ | ----- | --------------------------- | -| userId | Int64 | the id of the user to fetch | - -###### Example - -```cs -var friend = relationshipsManager.Get(53908232506183680); -Console.WriteLine("This is my friend, {0}", friend.User.Username); -``` - -## GetAt - -Get the relationship at a given index when iterating over a list of relationships. - -Returns a `Relationship`. - -###### Parameters - -| name | type | description | -| ----- | ------ | ----------------- | -| index | UInt32 | index in the list | - -###### Example - -```cs -for (int i = 0; i < relationshipsManager.Count(); i++) -{ - var r = relationshipsManager.GetAt(i); - Console.WriteLine("This person is {0}", r.User.Username); -} -``` - -## Count - -Get the number of relationships that match your `Filter()`. - -Returns an `Int32`. - -###### Parameters - -None - -###### Example - -```cs -for (int i = 0; i < relationshipsManager.Count(); i++) -{ - var r = relationshipsManager.At(i); - Console.WriteLine("This person is {0}", r.User.Username); -} -``` - -## OnRefresh - -Fires at initialization when Discord has cached a snapshot of the current status of all your relationships. Wait for this to fire before calling `Filter` within its callback. - -###### Parameters - -None - -## OnRelationshipUpdate - -Fires when a relationship in the filtered list changes, like an updated presence or user attribute. - -###### Parameters - -| name | type | description | -| ------------ | ---------------- | ----------------------------- | -| relationship | ref Relationship | the relationship that changed | - -###### Example - -```cs -OnRelationshipUpdate += (ref Discord.Relationship relationship) => -{ - Console.WriteLine("Who changed? {0}", relationship.User.Id); -}; -``` - -## Example: Creating a Friends List - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); -var relationshipManager = discord.GetRelationshipManager(); - -// Assign this handle right away to get the initial relationships update. -// This callback will only be fired when the whole list is initially loaded or was reset - -// Wait for OnRefresh to fire to access a stable list -// Filter a user's relationship list to be just friends -// Use this list as your base -relationshipManager.OnRefresh += () => -{ - relationshipManager.Filter((relationship) => - { - return relationship.Type == Discord.RelationshipType.Friend; - }); - - // Loop over all friends a user has. - Console.WriteLine("relationships updated: {0}", relationshipManager.Count()); - - for (var i = 0; i < relationshipManager.Count(); i++) - { - // Get an individual relationship from the list - var r = relationshipManager.GetAt((uint)i); - Console.WriteLine("relationships: {0} {1}", r.Type, r.User.Username); - // Save r off to a list of user's relationships - } -} - -relationshipManager.OnRelationshipUpdate += (ref Discord.Relationship relationship) => -{ - Console.WriteLine("User is {0}", relationship.User.Username); - // Update the matching user in your previously created list -} -``` - -## Example: Invite Users Who Are Playing the Same Game - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); -var relationshipManager = discord.GetRelationshipManager(); -var activityManager = discord.GetActivityManager(); - -relationshipManager.OnRefresh += () => -{ - relationshipManager.Filter((relationship) => - { - // Filter for users who are playing the same game as the current user - // Is their activity application id the same as my client id? - return relationship.Presence.Activity.ApplicationId == clientId; - }); - - for (var i = 0; i < relationshipManager.Count(); i++) - { - // Get an individual relationship from the list - var r = relationshipManager.GetAt((uint)i); - Console.WriteLine("relationships: {0} {1}", r.Type, r.User.Username); - - // Send them a game invite! - activityManager.InviteUser(r.User.Id, Discord.ActivityActionType.Join, "Come play with me!", (result) => - { - Console.WriteLine("Invited user {0} to play with you", r.User.Username); - }); - }; -} -``` diff --git a/docs/game_sdk/SDK_Starter_Guide.md b/docs/game_sdk/SDK_Starter_Guide.md deleted file mode 100644 index cb015d6721..0000000000 --- a/docs/game_sdk/SDK_Starter_Guide.md +++ /dev/null @@ -1,311 +0,0 @@ -# SDK Starter Guide - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -Welcome to the Discord GameSDK! We're glad you made it. This SDK is here to solve all your problems, if your problems include finding an awesome SDK to help develop your game. Our SDK is like Clippy, if Clippy were built on a modern tech stack, talked less, and was an awesome game development SDK. - -## Step 0 - Some Notes - -Before we get off to the races, a couple notes on the SDK: - -- All strings in the SDK are UTF8 strings - - Make sure you've converted properly if necessary! -- The SDK is **_NOT_** threadsafe! - - Being callback-based, we thought that'd be confusing and inconsistent - -Now you know, and knowing is half the battle. - -## Step 1 - Get the Thing - -I know you're already convinced, so let's begin. First, get the SDK. Here it is: - -- [Discord GameSDK v3.2.1](https://dl-game-sdk.discordapp.net/3.2.1/discord_game_sdk.zip) - Latest version, includes Apple silicon (aarch64) support -- [Discord GameSDK v2.5.6](https://dl-game-sdk.discordapp.net/2.5.6/discord_game_sdk.zip) - Try this version if you encounter bugs on the latest version - -There's a few things in there, but let's quickly talk about what the SDK actually _is_. Inside the `lib/` folder, you'll see `x86/` and `x86_64/` that have some `.lib`, `.bundle`, and `.dll` files. These are the things you want to distribute with your game. - -These files are comprised of two parts: a "stub", and fallback modules. What that means is that when everything is running smoothly, the DLLs will just call back to the local running Discord client to do the heavy lifting. If, however, something is wrong, like a breaking change, the files also include "fallback" modules that reflect the native SDK modules in Discord at the time that version of the SDK was published. TLDR - you don't need to worry about breaking changes. - -## Get Set Up - -Next, we need to set up the application for your game. An application is the base "entity" in Discord for your game; it's what all the builds, branches, SKUs, store pages, assets, etc. will be filed under. - -Head over to our [developer site](https://discord.com/developers/) and create an account/log in if you haven't yet. The first thing we're going to do is create a Team. Teams are groups of developers working together on applications; you should create a team for your organization at [https://discord.com/developers/teams](https://discord.com/developers/teams). You can invite other users to join your team and work on applications together with you. - -Now that your team is created, you'll want to make an application. To do so, click on "Applications" at the top of the page and create an application. Make sure you pick your newly-created team in the `Team` dropdown. You want your team to own the application; this unlocks store functionality! Now that your app is made, let's dive into some more setup. - -> warn -> If you're integrating our SDK into an already-released game, there's a good chance that we may _already have_ an application in our database for your game! Reach out to our [Dev Support](https://dis.gd/devsupport) to learn more - -First, we'll need to set an OAuth2 redirect URL. You can add `http://127.0.0.1` in there for now; this powers some behind-the-scenes stuff you don't need to worry about. - -Next, copy the **Client ID** at the top of the page. This id, also referred to as an "application id", is your game's unique identifier across Discord. Keep it handy! - -While you're here, head to the "OAuth2" section of your application and add `http://127.0.0.1` as a redirect URI for your application. This will allow us to do the OAuth2 token exchange within the Discord client. - -Now we're gonna start coding. Didn't think we'd get there so fast, did ya? _Think again!_ The next sections are code primers for the main languages of the SDK: C#, C, and C++. They'll get you up and running with the most basic examples, and then you're off to the races. - -## Code Primer - Unity (Csharp) - -- Open up that SDK zip that you downloaded. -- Copy the contents of the `lib/` folder to `Assets/Plugins` in your Unity project -- Copy the contents of the `csharp/` folder to `Assets/Plugins/DiscordGameSDK` - -From there, you'll be able to reference functions in the DLL within your scripts. A basic example of a script can be found [in this example repo](https://github.com/msciotti/discord-game-sdk-test-apps/tree/master/cs-examples/unity-examples/Assets). In this example, we attach our `DiscordController.cs` script to the Main Camera object of the default created scene. We then instantiate the SDK with: - -```cs -/* - Grab that Client ID from earlier - Discord.CreateFlags.Default will require Discord to be running for the game to work - If Discord is not running, it will: - 1. Close your game - 2. Open Discord - 3. Attempt to re-open your game - Step 3 will fail when running directly from the Unity editor - Therefore, always keep Discord running during tests, or use Discord.CreateFlags.NoRequireDiscord -*/ -var discord = new Discord.Discord(CLIENT_ID, (UInt64)Discord.CreateFlags.Default); -``` - -You're now free to use other functionality in the SDK! Make sure to call `discord.RunCallbacks()` in your main game loop; that's your `Update()` function. - -You're ready to go! Check out the rest of the documentation for more info on how to use the other pieces of the SDK. See an example of everything it can do in `examples/Program.cs` in the SDK zip file. - -## Code Primer - Non-Unity Projects (Csharp) - -- Open up that SDK zip that you downloaded. -- Create a folder in your project directory called `DiscordGameSDK` and copy the contents of the `csharp/` folder to it -- Build your solution then place the `.dll` in the directory of the `.exe` (either x86 or x86_64 version depending on your compile platform). If you compile for Any CPU you may need to perform additional wrapping around DLL importing (like setting the DLL directory dynamically) to make sure you load the correct DLL. - -From there, you'll be able to reference functions in the DLL within your scripts. We then instantiate the SDK with: - -```cs -/* - Grab that Client ID from earlier - Discord.CreateFlags.Default will require Discord to be running for the game to work - If Discord is not running, it will: - 1. Close your game - 2. Open Discord - 3. Attempt to re-open your game - Step 3 may fail when running directly from your editor - Therefore, always keep Discord running during tests, or use Discord.CreateFlags.NoRequireDiscord -*/ -var discord = new Discord.Discord(CLIENT_ID, (UInt64)Discord.CreateFlags.Default); -``` - -You're now free to use other functionality in the SDK! Make sure to call `discord.RunCallbacks()` in your main game loop; that's your `Update()` function. - -You're ready to go! Check out the rest of the documentation for more info on how to use the other pieces of the SDK. See an example of everything it can do in `examples/Program.cs` in the SDK zip file. - -## Code Primer - Unreal Engine (C) - -Before jumping into the C binding, a word of caution. If you are using Unreal Engine 3, or need to support an older version of Visual Studio, you may at first see some unexpected crashes due to compile configurations. The way to fix this is to wrap the include statement for the Discord GameSDK header file like so: - -```c -#pragma pack(push, 8) -#include "discord_game_sdk.h" -#pragma pack(pop) -``` - -This should let you use the SDK without any further crashes. Now, on with the show! - -- Open up that SDK zip that you downloaded. -- Copy the contents of the `lib/` folder to the best location within your project for DLLs. -- Copy the contents of the `c/` folder to your source directory -- It's dangerous to go alone—take this small code block with you (to start)! - -```c -struct Application { - struct IDiscordCore* core; - struct IDiscordUsers* users; -}; - -struct Application app; -// Don't forget to memset or otherwise initialize your classes! -memset(&app, 0, sizeof(app)); - -struct IDiscordCoreEvents events; -memset(&events, 0, sizeof(events)); - -struct DiscordCreateParams params; -params.client_id = CLIENT_ID; -params.flags = DiscordCreateFlags_Default; -params.events = &events; -params.event_data = &app; - -DiscordCreate(DISCORD_VERSION, ¶ms, &app.core); -``` - -- Make sure to call `core->run_callbacks(core, 0)` in your game loop. - -You're ready to go! Check out the rest of the documentation for more info on how to use the other pieces of the SDK. See an example of everything it can do in `examples/c/main.c` in the SDK zip file. - -## Code Primer - Unreal Engine 4 (Cpp) - -Open up that SDK zip that you downloaded. There's a couple things in there that we care about. The first is the contents of the `cpp/` folder. These are our source files, including headers and `.cpp` file, that we'll need to reference in our build script. Second is the contents of the `lib/` folder. In this walkthrough we'll assume that we only care about win64, for ease of use. - -First, you'll want to copy the header files and `.cpp` files to a folder somewhere in your project directory. For ease of a quickstart example, you can put them right inside your `Source/your-project-name` folder; I'd put them in a containing folder called something like `discord-files/`. - -Second, you'll want to copy the `.dll` and `.lib` files from the `lib/x86_64` folder of the downloaded zip. These files should be put in `your-project-name/Binaries/Win64/`. For win32, take the files from `x86/` and put them, in `your-project-name/Binaries/Win32`. - -Next, we need to link these files within our project so that we can reference them. If you open up your project's `.sln` file in Visual Studio, you'll find a file called `your-project-name.Build.cs`. We're going to add the following lines of code to that file: - -```cpp -/* - ABSOLUTE_PATH_TO_DISCORD_FILES_DIRECTORY will look something like: - - "H:\\Unreal Projects\\gamesdktest\\Source\\gamesdktest\\discord-files\\" - - You should get this value programmatically -*/ -PublicIncludePaths.Add(ABSOLUTE_PATH_TO_DISCORD_FILES_DIRECTORY) - -/* - ABSOLUTE_PATH_TO_LIB_FILE will look something like: - - "H:\\Unreal Projects\\gamesdktest\\Binaries\\Win64\\discord_game_sdk.dll.lib" - - You should get this value programmatically -*/ -PublicAdditionalLibraries.Add(ABSOLUTE_PATH_TO_LIB_FILE) -``` - -Now that we've got our new dependencies properly linked, we can reference them in our code. In this example, we're going to make a new `Pawn` class called `MyPawn`. It will look something like this: - -```cpp -#include "MyPawn.h" -#include "discord-files/discord.h" - -discord::Core* core{}; - -AMyPawn::AMyPawn() -{ - // Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it. - PrimaryActorTick.bCanEverTick = true; -} - -// Called when the game starts or when spawned -void AMyPawn::BeginPlay() -{ - Super::BeginPlay(); - /* - Grab that Client ID from earlier - Discord.CreateFlags.Default will require Discord to be running for the game to work - If Discord is not running, it will: - 1. Close your game - 2. Open Discord - 3. Attempt to re-open your game - Step 3 will fail when running directly from the Unreal Engine editor - Therefore, always keep Discord running during tests, or use Discord.CreateFlags.NoRequireDiscord - */ - auto result = discord::Core::Create(461618159171141643, DiscordCreateFlags_Default, &core); - discord::Activity activity{}; - activity.SetState("Testing"); - activity.SetDetails("Fruit Loops"); - core->ActivityManager().UpdateActivity(activity, [](discord::Result result) { - - }); -} - -// Called every frame -void AMyPawn::Tick(float DeltaTime) -{ - Super::Tick(DeltaTime); - ::core->RunCallbacks(); -} - -// Called to bind functionality to input -void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) -{ - Super::SetupPlayerInputComponent(PlayerInputComponent); -} -``` - -Make sure you've got `core->RunCallbacks()` going every frame! - -You're ready to go! Check out the rest of the documentation for more info on how to use the other pieces of the SDK. See an example of everything it can do in `examples/cpp/main.cpp` in the SDK zip file. - -## Code Primer - No Engine (Cpp) - -In your project folder, you'll want to make something like a "discord-files" folder, for organization. In that folder, copy all the `.h` and `.cpp` files from the zip. -You want to include all the header and source files respectively in your project - -![Correct Files](cpp-files-sdk.png) - -In your project settings, you'll want to include the relevant library (e.g. `discord_game_sdk.dll.lib`) as an additional dependency. - -![Linked Library](lib-linked-sdk.png) - -- From there, you should be able to `#include "discord-files/discord.h"`, or whatever the path to that header file is, and have access to the code. - -## Testing Locally with Two Clients - -> info -> Value from environment variable `DISCORD_INSTANCE_ID` - -While integrating the Discord GameSDK, you will probably find yourself wanting to test functionality between two game clients locally, be it for networking, Rich Presence, etc. - -We also know that getting a test build of a game on two separate machines can be both difficult and cumbersome. So, we've got a solution for you! By using system environment variables, you can tell the SDK in a certain game client to connect to a specific Discord client. Here's how it works: - -1. Download Discord Canary. This is our most updated build, and is good to develop against: [Windows](https://discord.com/api/download/canary?platform=win) - [Mac](https://discord.com/api/download/canary?platform=osx) -2. Download a second Discord Build. Here's our Public Test Build: [Windows](https://discord.com/api/download/ptb?platform=win) - [Mac](https://discord.com/api/download/ptb?platform=osx) -3. Open up two Discord clients. We recommend you develop against Discord Canary, so you can use PTB or Stable for your test account -4. Log in with two separate users. Make sure any test account is added to the application's App Whitelist in the portal! - -Now, in your game code, you can tell the SDK which client to connect to via the environment variable `DISCORD_INSTANCE_ID` **before initializing the SDK**. The value of the variable corresponds to the order in which you opened the clients, so `0` would connect to the first opened client, `1` the second, etc. - -###### Environment Variable Example - -```cs -// This machine opened Discord Canary first, and Discord PTB second - -// This makes the SDK connect to Canary -System.Environment.SetEnvironmentVariable("DISCORD_INSTANCE_ID", "0"); -var discord = new Discord(applicationId, Discord.CreateFlags.Default); - -// This makes the SDK connect to PTB -System.Environment.SetEnvironmentVariable("DISCORD_INSTANCE_ID", "1"); -var discord = new Discord(applicationId, Discord.CreateFlags.Default); -``` - -This will set the environment variable only within the context of the running process, so don't worry about messing up global stuff. - -> danger -> If you test with this, make sure to remove this code before pushing a production build. It will interfere with the way that Discord launches games for users. - -## Section Checklist - -Think of these like those end of section review pages from your history textbooks, but we won't give you a pop quiz. I promise. By now, you should have the following: - -- The SDK downloaded -- An application created, and a Client ID you're keeping handy -- Rich Presence enabled on that application -- An OAuth2 redirect URL added to that application -- The proper libraries, DLLs, and header files in the right places for your game -- A working SDK, via the small code snippets above - -If you can check all those boxes, you are doing great! You're well-equipped to venture forth into the rest of the SDK and make full use of it's myriad of functionality. So, onwards, to game development! - -If you ever need help during the process, you can always reach out to us at our [Dev Support](https://dis.gd/devsupport). If you have feedback on things you'd like to see added to the SDK, drop us at a line at our [Dev Feedback](https://dis.gd/devfeedback). - -## Where...do I go... - -Oh, yeah. Pseudo Table of Contents: - -- [The Core - Start Here!](#DOCS_GAME_SDK_DISCORD/) -- [Activities, a.k.a. Rich Presence](#DOCS_GAME_SDK_ACTIVITIES/) -- [Relationships](#DOCS_GAME_SDK_RELATIONSHIPS/) -- [Users](#DOCS_GAME_SDK_USERS/) -- [Images](#DOCS_GAME_SDK_IMAGES/) -- [Lobbies](#DOCS_GAME_SDK_LOBBIES/) -- [Networking](#DOCS_GAME_SDK_NETWORKING/) -- [Storage](#DOCS_GAME_SDK_STORAGE/) -- [Applications](#DOCS_GAME_SDK_APPLICATIONS/) -- [Overlay](#DOCS_GAME_SDK_OVERLAY/) -- [Store](#DOCS_GAME_SDK_STORE/) -- [Discord Voice](#DOCS_GAME_SDK_DISCORD_VOICE/) -- [Achievements](#DOCS_GAME_SDK_ACHIEVEMENTS/) diff --git a/docs/game_sdk/Storage.md b/docs/game_sdk/Storage.md deleted file mode 100644 index 5a9cf5e24a..0000000000 --- a/docs/game_sdk/Storage.md +++ /dev/null @@ -1,312 +0,0 @@ -# Storage - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -We've been told that people playing games want to save their progress as they go, allowing them to come back where they left off and continue their epic journey of power. - -Yeah, roguelikes. Even you. - -Discord's storage manager lets you save data mapped to a key for easy reading, writing, and deleting both synchronously and asynchronously. It's saved to a super special directory, the Holy Grail of file mappings, that's unique per Discord user — no need to worry about your little brother overwriting your save file. - -Creating this manager will also spawn an IO thread for async reads and writes, so unless you really want to be blocking, you don't need to be! - -## Cloud Saves - -If you want to take that save data on your players' computers and save it to those **BIG COMPUTERS** in the cloud, look no further! All you need to do is head over to your manifest config file and set the following property: - -```js -{ - "storage": { - "sync": true - } -} -``` - -Yup, that's it! Don't know what this file is? Go read [Branches and Builds](#DOCS_DISPATCH_BRANCHES_AND_BUILDS/). - -## Data Models - -###### FileStat Struct - -| name | type | description | -| ------------ | ------ | -------------------------------------------- | -| Filename | string | the name of the file | -| Size | UInt64 | the size of the file | -| LastModified | UInt64 | timestamp of when the file was last modified | - -## GetPath - -> info -> Value from environment variable `DISCORD_STORAGE_PATH` - -Returns the filepath to which Discord saves files if you were to use the SDK's storage manager. Discord has branch-specific, user-specific saves, so you and your little brother will never overwrite each others' save files. If your game already has save file writing logic, you can use this method to get that user-specific path and help users protect their save files. - -Returns a `string`. - -###### Parameters - -None - -###### Example - -```cs -var savePath = storageManager.GetPath(); -Console.WriteLine("You should write your save files to {0}", savePath); -``` - -## Read - -Reads data synchronously from the game's allocated save file into a buffer. The file is mapped by key value pairs, and this function will read data that exists for the given key name. - -Returns a `UInt32`. - -###### Parameters - -| name | type | description | -| ---- | ------ | ---------------------------------- | -| name | string | the key name to read from the file | -| data | byte[] | the buffer to read into | - -## ReadAsync - -Reads data asynchronously from the game's allocated save file into a buffer. - -Returns a `Discord.Result` and a `byte[]` containing the data via callback. - -###### Parameters - -| name | type | description | -| ---- | ------ | ---------------------------------- | -| name | string | the key name to read from the file | - -###### Example - -```cs -storeManager.ReadAsync("high_score", (result, data) => -{ - if (result == Discord.Result.OK) { - LoadHighScore(data); - } -}); -``` - -## ReadAsyncPartial - -Reads data asynchronously from the game's allocated save file into a buffer, starting at a given offset and up to a given length. - -Returns a `Discord.Result` and `byte[]` containing the data via callback. - -###### Parameters - -| name | type | description | -| ------ | ------ | ------------------------------------ | -| name | string | the key name to read from the file | -| offset | UInt64 | the offset at which to start reading | -| length | UInt64 | the length to read | - -###### Example - -```cs -storeManager.ReadAsyncPartial("high_score", 10, 8, (result, data) => -{ - if (result == Discord.Result.OK) { - LoadHighScore(data); - } -}); -``` - -## Write - -Writes data synchronously to disk, under the given key name. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ---- | ------ | --------------------------- | -| name | string | the key name to write under | -| data | byte[] | the data to write | - -###### Example - -```cs -storageManager.Write("high_score", Encoding.UTF8.GetBytes("9999")); -``` - -## WriteAsync - -Writes data asynchronously to disk under the given keyname. - -Returns a `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ---- | ------ | --------------------------- | -| name | string | the key name to write under | -| data | byte[] | the data to write | - -###### Example - -```cs -storageManager.WriteAsync("high_score", Encoding.UTF8.GetBytes("9999"), (result) => -{ - if (result == Discord.Result.OK) - { - Console.WriteLine("Wrote data"); - } -}); -``` - -## Delete - -Deletes written data for the given key name. - -Returns `void`. - -###### Parameters - -| name | type | description | -| ---- | ------ | ---------------------- | -| name | string | the key name to delete | - -###### Example - -```cs -storageManager.Delete("high_score"); -// Because you cheated. Jerk. -``` - -## Exists - -Checks if data exists for a given key name. - -Returns `bool`. - -###### Parameters - -| name | type | description | -| ---- | ------ | --------------------- | -| name | string | the key name to check | - -###### Example - -```cs -var highScore = storageManager.Exists("high_score"); -if (!highScore) -{ - Console.WriteLine("Couldn't find any highscore for you. Did you cheat? Jerk."); -} -``` - -## Stat - -Returns file info for the given key name. - -Returns a `FileStat`. - -###### Parameters - -| name | type | description | -| ---- | ------ | ------------------------------ | -| name | string | the key name get file data for | - -###### Example - -```cs -var file = storageManager.Stat("high_score"); -Console.WriteLine("File {0} is {1} in size and was last edited at {2}", file.Name, file.Size, file.LastModified); -``` - -## Count - -Returns the count of files, for iteration. - -Returns an `Int32`. - -###### Parameters - -None - -###### Example - -```cs -var numFiles = storageManager.Count(); -for (int i = 0; i < numFiles; i++) -{ - Console.WriteLine("We're at file {0}", i); -} -``` - -## StatAt - -Returns file info for the given index when iterating over files. - -Returns a `FileStat`. - -###### Parameters - -| name | type | description | -| ----- | ----- | ------------------------------ | -| index | Int32 | the index to get file data for | - -###### Example - -```cs -var numFiles = storageManager.Count(); -for (int i = 0; i < numFiles; i++) -{ - var file = storageManager.StatAt(i); - Console.WriteLine("File is {0}", file.Name); -} -``` - -## Example: Saving, Reading, Deleting, and Checking Data - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); -var storageManager = discord.GetStorageManager(); - -// Create some nonsense data -var contents = new byte[20000]; -var random = new Random(); -random.NextBytes(contents); - -// Write the data asynchronously -storageManager.WriteAsync("foo", contents, res => -{ - // Get our list of files and iterate over it - for (int i = 0; i < storageManager.Count(); i++) - { - var file = storageManager.StatAt(i); - Console.WriteLine("file: {0} size: {1} last_modified: {2}", file.Filename, file.Size, file.LastModified); - } - - // Let's read just a small chunk of data from the "foo" key - storageManager.ReadAsyncPartial("foo", 400, 50, (result, data) => - { - Console.WriteLine("partial contents of foo match {0}", Enumerable.SequenceEqual(data, new ArraySegment(contents, 400, 50))); - }); - - // Now let's read all of "foo" - storageManager.ReadAsync("foo", (result, data) => - { - Console.WriteLine("length of contents {0} data {1}", contents.Length, data.Length); - Console.WriteLine("contents of foo match {0}", Enumerable.SequenceEqual(data, contents)); - - // We just read it, but let's make sure "foo" exists - Console.WriteLine("foo exists? {0}", storageManager.Exists("foo")); - - // Now delete it - storageManager.Delete("foo"); - - // Make sure it was deleted - Console.WriteLine("post-delete foo exists? {0}", storageManager.Exists("foo")); - }); -}); -``` diff --git a/docs/game_sdk/Store.md b/docs/game_sdk/Store.md deleted file mode 100644 index 0e7c393a95..0000000000 --- a/docs/game_sdk/Store.md +++ /dev/null @@ -1,555 +0,0 @@ -# Store - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -If your game has DLC or offers in-app purchases, this manager is for you! The Store Manager allows you to fetch a users' entitlements, as well as being notified when a user is granted an entitlement from a purchase flow for your game. - -## Application Test Mode - -With this new Store Manager comes a new fun toggle in the Discord app itself: Application Test Mode! While in Application Test Mode, you can freely make "purchases" of SKUs tied to your application. That means you can test buying your game, buying DLC, or going through an IAP flow without any credit card charges. - -> info -> You still need to have a valid payment method on file to "purchase" SKUs in Application Test Mode; it just won't be charged at checkout. - -To enable it, first make sure you have a payment method on file in User Settings -> Billing. Then: - -1. Open up the Discord app -2. Click on the settings cog in the bottom left corner -3. Go to Appearance -> allll the way at the bottom -4. Toggle "Developer Mode" **on** and "Application Test Mode" **on**, and enter your application ID -5. Exit user settings - -You should now see an orange bar across the top of your screen; this means it worked! The dropdown in the orange bar will show you all the available SKUs for that application; you can select one of them to go to its store page. You can also view your Library and see all the branches of your game automagically there waiting for you! - -If for some reason the "Install" button is greyed out, please check the following: - -1. Do you have a `LIVE_BUILD_ID` on that branch for this SKU? Check with `dispatch branch list `. -2. Do you have a price tier set for this SKU? If not, pick one! - -Once those two conditions are met, you should be good to go! Entitlements "purchased" with this mode enabled can be revoked with the `DELETE /entitlements` HTTP endpoint, documented below. - -## Checking DLC Entitlements - -If your game has DLC, and a user has purchased that DLC, you may want to check what they should have access to when the game launches. DLC entitlements will always be returned in a `FetchEntitlements()` call, so your game can check on each startup whether or not a user should have access to a certain new zone, raid, map, etc. based on their entitlements for DLC. - -## Checking Consumable Entitlements - -The `Discord.SkuType.Consumable` type is used for entitlements that may be "consumed" by a game's own server infrastructure. That is to say that if you have in-app purchases like gem bundles, skins, etc., they will be a `Consumable` SKU type. - -What that means is that your game is expected to "consume" these entitlements by doing something on your game server—giving the player a level, more coins, a skin, etc.—and then telling Discord that's been done by calling the `POST /entitlements//consume`, documented below. Then, Discord will mark that entitlement as `consumed` (the `consumed` field in the returned object will be set to `true`). - -Entitlements to consumable SKUs are intended to signal your game's server/service/database that the user should get something in-game, and that the entitlement should be invalidated afterwards. - -The same consumable SKU _can_ be purchased multiple times, but we have some safeguards in place to protect against possible abuse. If you purchase a consumable SKU, you cannot purchase a second one until the first one has been consumed; in the context of normal IAP transactions, your game will be auto-consuming entitlements as soon as they're created. If some malicious folks are somehow able to generate entitlements to your SKUs, they will not be able to consume them without your token, so you'll be safe! - -Non-consumable SKUs can only be purchased once. - -## Data Models - -###### SKU Struct - -| name | type | description | -| ----- | -------- | ------------------------ | -| Id | Int64 | the unique ID of the SKU | -| Type | SkuType | what sort of SKU it is | -| Name | string | the name of the SKU | -| Price | SkuPrice | the price of the SKU | - -###### SkuType Enum - -| name | value | description | -| ----------- | ----- | ---------------------------------------------- | -| Application | 1 | SKU is a game | -| DLC | 2 | SKU is a DLC | -| Consumable | 3 | SKU is a consumable (in-app purchase) | -| Bundle | 4 | SKU is a bundle (comprising the other 3 types) | - -###### SkuPrice Struct - -| name | type | description | -| -------- | ------ | --------------------------------- | -| Amount | UInt32 | the amount of money the SKU costs | -| Currency | string | the currency the amount is in | - -###### Entitlement Struct - -| name | type | description | -| ----- | --------------- | ----------------------------------------------- | -| Id | Int64 | the unique ID of the entitlement | -| Type | EntitlementType | the kind of entitlement it is | -| SkuId | Int64 | the ID of the SKU to which the user is entitled | - -###### EntitlementType Enum - -| name | value | description | -| ------------------- | ----- | -------------------------------------------------------------- | -| Purchase | 1 | entitlement was purchased | -| PremiumSubscription | 2 | entitlement for a Discord Nitro subscription | -| DeveloperGift | 3 | entitlement was gifted by a developer | -| TestModePurchase | 4 | entitlement was purchased by a dev in application test mode | -| FreePurchase | 5 | entitlement was granted when the SKU was free | -| UserGift | 6 | entitlement was gifted by another user | -| PremiumPurchase | 7 | entitlement was claimed by user for free as a Nitro Subscriber | - -## FetchSkus - -Fetches the list of SKUs for the connected application, readying them for iteration. - -> warn -> Only SKUs that have a price set will be fetched. If you aren't seeing any SKUs being returned, make sure they have a price set! - -Returns `Discord.Result` via callback. - -###### Parameters - -None - -###### Example - -```cs -storeManager.FetchSkus((result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Got skus! Now I can iterate over them!"); - } -}); -``` - -## CountSkus - -Get the number of SKUs readied by `FetchSkus()`. - -Returns `Int32`. - -###### Parameters - -None - -###### Example - -```cs -for (int i = 0; i < storeManager.CountSkus(); i++) -{ - var sku = storeManager.GetSkuAt(i); - Console.WriteLine("Sku is {0}", sku.Name); -} -``` - -## GetSku - -Gets a SKU by its ID. You must call `FetchSkus()` first before being able to access SKUs in this way. - -Returns `Discord.Sku`. - -###### Parameters - -| name | type | description | -| ----- | ----- | ------------------------ | -| skuId | Int64 | the ID of the SKU to get | - -###### Example - -```cs -storeManager.FetchSkus((_) => {}); -var sku = storeManager.GetSku(276467180839763999); -Console.WriteLine("Sku is {0}", sku.Name); -``` - -## GetSkuAt - -Gets a SKU by index when iterating over SKUs. You must call `FetchSkus()` first before being able to access SKUs in this way. - -Returns `Discord.Sku`. - -###### Parameters - -| name | type | description | -| ----- | ----- | ------------------------- | -| index | Int32 | the index at which to get | - -###### Example - -```cs -storeManager.FetchSkus((result) => -{ - for (int i = 0; i < storeManager.CountSkus(); i++) - { - var sku = storeManager.GetSkuAt(i); - Console.WriteLine("Sku is {0}", sku.Name); -} -}); -``` - -## FetchEntitlements - -Fetches a list of entitlements to which the user is entitled. Applications, DLC, and Bundles will always be returned. Consumables will be returned until they are consumed by the application via the HTTP endpoint. - -Returns `Discord.Result` via callback. - -###### Parameters - -None - -###### Example - -```cs -storeManager.FetchEntitlements((result) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("Got entitlements!"); - } -}); -``` - -## CountEntitlements - -Get the number of entitlements readied by `FetchEntitlements()`. You must call `FetchEntitlements()` first before being able to access SKUs in this way. - -Returns `Int32`. - -###### Parameters - -None - -###### Example - -```cs -storeManager.FetchEntitlements((result) => -{ - for (int i = 0; i < storeManager.CountEntitlements(); i++) - { - var entitlement = storeManager.GetEntitlementAt(i); - Console.WriteLine("Entitlement is {0}", entitlement.Name); - } -}); -``` - -## GetEntitlement - -Gets an entitlement by its id. You must call `FetchEntitlements()` first before being able to access SKUs in this way. - -Returns `Discord.Entitlement`. - -###### Parameters - -| name | type | description | -| ------------- | ----- | -------------------------------- | -| entitlementId | Int64 | the ID of the entitlement to get | - -###### Example - -```cs -storeManager.FetchEntitlements((result) => -{ - var entitlement = storeManager.GetEntitlement(276467180839763999); - Console.WriteLine("Entitlement is {0}", entitlement.Name); -}); -``` - -## GetEntitlementAt - -Gets an entitlement by index when iterating over a user's entitlements. You must call `FetchEntitlements()` first before being able to access SKUs in this way. - -Returns `Discord.Entitlement`. - -###### Parameters - -| name | type | description | -| ----- | ----- | ------------------------- | -| index | Int32 | the index at which to get | - -###### Example - -```cs -storeManager.FetchEntitlements((result) => -{ - for (int i = 0; i < storeManager.CountEntitlements(); i++) - { - var entitlement = storeManager.GetEntitlementAt(i); - Console.WriteLine("Entitlement is {0}", entitlement.Name); - } -}); -``` - -## HasSkuEntitlement - -Returns whether or not the user is entitled to the given SKU ID. You must call `FetchEntitlements()` first before being able to access SKUs in this way. - -Returns `bool`. - -###### Parameters - -| name | type | description | -| ----- | ----- | -------------------------- | -| skuId | Int64 | the ID of the SKU to check | - -###### Example - -```cs -storeManager.FetchEntitlements((result) => -{ - if (storeManager.HasSkuEntitlement(276467180839763999)) - { - Console.WriteLine("User has entitlement to this SKU"); - } - else - { - Console.WriteLine("How are you even running this right now..."); - } -}); -``` - -## StartPurchase - -Opens the overlay to begin the in-app purchase dialogue for the given SKU ID. You must call `FetchSkus()` first before being able to access SKUs in this way. If the user has enabled the overlay for your game, a purchase modal will appear in the overlay. Otherwise, the Discord client will be auto-focused with a purchase modal. - -Returns `Discord.Result` via callback. - -###### Parameters - -| name | type | description | -| ----- | ----- | ------------------------------------- | -| skuId | Int64 | the ID of the SKU to begin purchasing | - -###### Example - -```cs -storeManager.FetchSkus((result) => -{ - storeManager.StartPurchase(276467180839763999, (result) => - { - if (result == Discord.Result.Ok) - { - Console.WriteLine("User is in the flow!"); - } - }); -}); -``` - -## OnEntitlementCreate - -Fires when the connected user receives a new entitlement, either through purchase or through a developer grant. - -###### Parameters - -| name | type | description | -| ----------- | ------------------- | ----------------------------------------- | -| entitlement | Discord.Entitlement | the entitlement the user has been granted | - -## OnEntitlementDelete - -Fires when the connected user loses an entitlement, either by expiration, revocation, or consumption in the case of consumable entitlements. - -###### Parameters - -| name | type | description | -| ----------- | ------------------- | --------------------------------- | -| entitlement | Discord.Entitlement | the entitlement the user has lost | - -## HTTP APIs - -The following are HTTP requests, and should be handled by your game server, rather than a client. They require a token for an authorization header. This token should be the "Bot token" of your application. To get this token, go to your application in the Dev Portal. In the left sidebar, click the `Bot` navigation item. You should then click the `Add Bot` button. - -After that, you can copy the token on this page and use it in your HTTP requests. The format for the authorization header should be `Authorization: Bot `. - -Note that parameters with a `?` after the name denote optional fields. Parameters with a `?` before their type denote nullable fields. - -## HTTP-Specific Data Models - -###### Limited Payment Data Object - -| name | type | description | -| ------------- | ------ | ------------------------------------ | -| id | string | unique ID of the payment | -| currency | string | the currency the payment was made in | -| amount | int | the amount paid | -| tax | int | the amount of tax | -| tax_inclusive | bool | whether the amount is tax-inclusive | - -## Get Entitlements % GET /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/entitlements - -Gets entitlements for a given user. You can use this on your game backend to check entitlements of an arbitrary user, or perhaps in an administrative panel for your support team. - -###### Query Parameters - -| name | type | description | -| -------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| user_id? | snowflake | the user id to look up entitlements for | -| sku_ids? | comma-delimited set of snowflakes | (optional) the list SKU ids to check entitlements for | -| with_payments? | bool | returns [limited payment data](#DOCS_GAME_SDK_STORE/httpspecific-data-models-limited-payment-data-object) for each entitlement | -| before? | snowflake | retrieve entitlements before this time | -| after? | snowflake | retrieve entitlements after this time | -| limit? | int | number of entitlements to return, 1-100, default 100 | - -###### Example - -``` -curl https://discord.com/api/v6/applications/461618159171141643/entitlements?user_id=53908232506183680&sku_ids=53908232599983680&with_payments=true&limit=1 \ --H "Authorization: Bearer " \ --H "Accept: application/json" - -// Returns - -{ - [ - { - "user_id": "53908232506183680", - "sku_id": "53908232599983680", - "application_id": "461618159171141643", - "id": "53908232506183999", - "type": 1, - "payment": { - "id": "538491076055400999", - "currency": "usd", - "amount": 999, - "tax": 0, - "tax_inclusive": false - } - } - ] -} -``` - -## Get Entitlement % GET /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/entitlements/{entitlement.id#DOCS_GAME_SDK_STORE/data-models-entitlement-struct} - -Fetch an entitlement by its ID. This may be useful in confirming that a user has a given entitlement that another call or the SDK says they do. - -###### Query Parameters - -| name | type | description | -| ------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------ | -| with_payment? | bool | returns [limited payment data](#DOCS_GAME_SDK_STORE/httpspecific-data-models-limited-payment-data-object) for each entitlement | - -###### Example - -``` -curl https://discord.com/api/v6/applications/461618159171141643/entitlements/53908232506183999?with_payment=true \ --H "Authorization: Bearer " \ --H "Accept: application/json" - -// Returns - -{ - "user_id": "53908232506183680", - "sku_id": "53908232599983680", - "application_id": "461618159171141643", - "id": "53908232506183999", - "type": 3, - "payment": { - "id": "538491076055400999", - "currency": "usd", - "amount": 999, - "tax": 0, - "tax_inclusive": false - } -} -``` - -## Get SKUs % GET /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/skus - -Get all SKUs for an application. - -###### Example - -``` -curl https://discord.com/api/v6/applications/461618159171141643/skus \ --H "Authorization: Bearer " \ --H "Accept: application/json" - -// Returns - -{ - [ - { - "id": "53908232599983680", - "type": 1, - "dependent_sku_id": null, - "application_id": "461618159171141643", - "manifest_labels": ["461618159171111111"], - "name": "My Awesome Test Game", - "access_type": 1, - "features": [1, 2, 3], - "system_requirements": {}, - "content_ratings": {}, - "release_date": "1999-01-01", - "legal_notice": {}, - "price_tier": 1099, - "price": {}, - "premium": false, - "locales": ["en-US"], - "bundled_skus": null - } - ] -} -``` - -## Consume SKU % POST /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/entitlements/{entitlement.id#DOCS_GAME_SDK_STORE/data-models-entitlement-struct}/consume - -Marks a given entitlement for the user as consumed, meaning it will no longer be returned in an entitlements check. **Ensure the user was granted whatever items the entitlement was for before consuming it!** - -###### Example - -``` -curl -X POST https://discord.com/api/v6/applications/461618159171141643/entitlements/53908232506183999/consume \ --H "Authorization: Bearer " \ --H "Accept: application/json" - -// Returns 204 No Content -``` - -## Delete Test Entitlement % DELETE /applications/{application.id#DOCS_GAME_SDK_SDK_STARTER_GUIDE/get-set-up}/entitlements/{entitlement.id#DOCS_GAME_SDK_STORE/data-models-entitlement-struct} - -Deletes a test entitlement for an application. You can only delete entitlements that were "purchased" in developer test mode; these are entitlements of `type == TestModePurchase`. You cannot use this route to delete arbitrary entitlements that users actually purchased. - -###### Example - -``` -curl -X DELETE https://discord.com/api/v6/applications/461618159171141643/entitlements/53908232506183999 \ --H "Authorization: Bearer " \ --H "Accept: application/json" - -// Returns 204 No Content -``` - -## Create Purchase Discount % PUT /store/skus/{sku.id#DOCS_GAME_SDK_STORE/data-models-sku-struct}/discounts/{user.id#DOCS_RESOURCES_USER/user-object} - -Creates a discount for the given user on their next purchase of the given SKU. You should call this endpoint from your backend server just before calling [StartPurchase](#DOCS_GAME_SDK_STORE/startpurchase) for the SKU you wish to discount. The user will then see a discounted price for that SKU at time of payment. The discount is automatically consumed after successful purchase or if the TTL expires. - -###### Parameters - -| name | type | description | -| ----------- | ---- | -------------------------------------------------------------------------------------- | -| percent_off | int | the percentage to discount - max of 100, min of 1 | -| ttl? | int | the time to live for the discount, in seconds - max of 3600, min of 60, default of 600 | - -###### Example - -``` -curl -X PUT https://discord.com/api/v6/store/skus/461618229171141643/discounts/53908232522183999 \ --H "Authorization: Bearer " \ --H "Accept: application/json" \ --H "Content-type: application/json" \ --d '{"percent_off": 10, "ttl": 600}' - -// Returns 204 No Content -``` - -## Delete Purchase Discount % DELETE /store/skus/{sku.id#DOCS_GAME_SDK_STORE/data-models-sku-struct}/discounts/{user.id#DOCS_RESOURCES_USER/user-object} - -Deletes the currently active discount on the given SKU for the given user. You **do not need** to call this after a user has made a discounted purchase; successful discounted purchases will automatically remove the discount for that user for subsequent purchases. - -###### Example - -``` -curl -X DELETE https://discord.com/api/v6/store/skus/461618229171141643/discounts/53908232522183999 \ --H "Authorization: Bearer " \ --H "Accept: application/json" - -// Returns 204 No Content -``` diff --git a/docs/game_sdk/Users.md b/docs/game_sdk/Users.md deleted file mode 100644 index 2bb2f5c722..0000000000 --- a/docs/game_sdk/Users.md +++ /dev/null @@ -1,178 +0,0 @@ -# Users - -> info -> Need help with the SDK? Talk to us in the [Discord Developers Server](https://discord.gg/discord-developers)! - -> danger -> Selling SKUs on Discord has now been discontinued as of March 1, 2022. [Read here for more info.](https://support-dev.discord.com/hc/en-us/articles/6309018858647-Self-serve-Game-Selling-Deprecation) - -This manager helps retrieve basic user information for any user on Discord. - -## Data Models - -###### User Struct - -| name | type | description | -| ------------- | ------ | ----------------------------- | -| Id | Int64 | the user's id | -| Username | string | their name | -| Discriminator | string | the user's unique discrim | -| Avatar | string | the hash of the user's avatar | -| Bot | bool | if the user is a bot user | - -###### UserFlag Enum - -| name | value | description | -| --------------- | ----- | ---------------------------- | -| Partner | 2 | Discord Partner | -| HypeSquadEvents | 4 | HypeSquad Events participant | -| HypeSquadHouse1 | 64 | House Bravery | -| HypeSquadHouse2 | 128 | House Brilliance | -| HypeSquadHouse3 | 256 | House Balance | - -###### PremiumType Enum - -| name | value | description | -| ----- | ----- | ------------------------ | -| None | 0 | Not a Nitro subscriber | -| Tier1 | 1 | Nitro Classic subscriber | -| Tier2 | 2 | Nitro subscriber | - -## GetCurrentUser - -> info -> Before calling this function, you'll need to wait for the [OnCurrentUserUpdate](#DOCS_GAME_SDK_USERS/oncurrentuserupdate) callback to fire after instantiating the User manager. - -Fetch information about the currently connected user account. If you're interested in getting more detailed information about a user—for example, their email—check out our [GetCurrentUser](#DOCS_RESOURCES_USER/get-current-user) API endpoint. You'll want to call this with an authorization header of `Bearer `, where `` is the token retrieved from a standard [OAuth2 Authorization Code Grant](#DOCS_TOPICS_OAUTH2/authorization-code-grant) flow. - -Returns a `Discord.User`. - -###### Parameters - -None - -###### Example - -```cs -var user = userManager.GetCurrentUser(); -Console.WriteLine("Connected to user {0}", user.Id); -``` - -## GetUser - -Get user information for a given id. - -Returns a `Discord.Result` and `ref Discord.User` via callback. - -###### Parameters - -| name | type | description | -| ------ | ----- | --------------------------- | -| userId | Int64 | the id of the user to fetch | - -###### Example - -```cs -userManager.GetUser(userId, (Discord.Result result, ref Discord.User user) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("User {0} is {1}", user.Id, user.Username); - } -}); -``` - -## GetCurrentUserPremiumType - -Get the [PremiumType](#DOCS_GAME_SDK_USERS/data-models-premiumtype-enum) for the currently connected user. - -Returns `Discord.PremiumType`. - -###### Parameters - -None - -###### Example - -```cs -var userManager = discord.GetUserManager(); -var premiumType = userManager.GetCurrentUserPremiumType(); -switch (premiumType) -{ - case PremiumType.None: - Console.WriteLine("User is not a Nitro subscriber"); - - case PremiumType.Tier1: - Console.WriteLine("User has Nitro Classic"); - - case PremiumType.Tier2: - Console.WriteLine("User has Nitro"); - - default: - return; -} -``` - -## CurrentUserHasFlag - -See whether or not the current user has a certain [UserFlag](#DOCS_GAME_SDK_USERS/data-models-userflag-enum) on their account. - -Returns `bool`. - -###### Parameters - -| name | type | description | -| ---- | ---------------------------------------------------------- | --------------------------------------- | -| flag | [UserFlag](#DOCS_GAME_SDK_USERS/data-models-userflag-enum) | the flag to check on the user's account | - -###### Example - -```cs -var userManager = discord.GetUserManager(); -if (userManager.CurrentUserHasFlag(Discord.UserFlag.HypeSquadHouse1)) -{ - Console.WriteLine("User is a member of House Bravery!"); -} -``` - -## OnCurrentUserUpdate - -Fires when the `User` struct of the currently connected user changes. They may have changed their avatar, username, or something else. - -###### Parameters - -None - -###### Example - -```cs -var userManager = discord.GetUserManager(); -// GetCurrentUser will error until this fires once. -userManager.OnCurrentUserUpdate += () => { - var currentUser = userManager.GetCurrentUser(); - - Console.WriteLine(currentUser.Username); - Console.WriteLine(currentUser.Id); - Console.WriteLine(currentUser.Discriminator); - Console.WriteLine(currentUser.Avatar); -}; -``` - -## Example: Fetching Data About a Discord User - -```cs -var discord = new Discord.Discord(clientId, Discord.CreateFlags.Default); - -var userManager = discord.GetUserManager(); -userManager.GetUser(450795363658366976, (Discord.Result result, ref Discord.User user) => -{ - if (result == Discord.Result.Ok) - { - Console.WriteLine("user fetched: {0}", user.Username); - } - else - { - Console.WriteLine("user fetch error: {0}", result); - } -}); -``` diff --git a/docs/interactions/Application_Commands.md b/docs/interactions/Application_Commands.md deleted file mode 100644 index 1adc73a998..0000000000 --- a/docs/interactions/Application_Commands.md +++ /dev/null @@ -1,1206 +0,0 @@ -# Application Commands - -Application commands are native ways to interact with apps in the Discord client. There are 3 types of commands accessible in different interfaces: the chat input, a message's context menu (top-right menu or right-clicking in a message), and a user's context menu (right-clicking on a user). - -![Client interfaces showing the different types of application commands](command-types.png) - -## Application Command Object - -###### Application Command Naming - -`CHAT_INPUT` command names and command option names must match the following regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` with the unicode flag set. If there is a lowercase variant of any letters used, you must use those. Characters with no lowercase variants and/or uncased letters are still allowed. `USER` and `MESSAGE` commands may be mixed case and can include spaces. - -###### Application Command Structure - -| Field | Type | Description | Valid Types | -| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | -| id | snowflake | Unique ID of command | all | -| type? | one of [application command type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-types) | [Type of command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-types), defaults to `1` | all | -| application_id | snowflake | ID of the parent application | all | -| guild_id? | snowflake | guild id of the command, if not global | all | -| name | string | [Name of command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-naming), 1-32 characters | all | -| name_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for `name` field. Values follow the same restrictions as `name` | all | -| description | string | Description for `CHAT_INPUT` commands, 1-100 characters. Empty string for `USER` and `MESSAGE` commands | all | -| description_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for `description` field. Values follow the same restrictions as `description` | all | -| options? | array of [application command option](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) | Parameters for the command, max of 25 | CHAT_INPUT | -| default_member_permissions | ?string | Set of [permissions](#DOCS_TOPICS_PERMISSIONS) represented as a bit set | all | -| dm_permission? | boolean | Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | all | -| default_permission? | ?boolean | Not recommended for use as field will soon be deprecated. Indicates whether the command is enabled by default when the app is added to a guild, defaults to `true` | all | -| version | snowflake | Autoincrementing version identifier updated during substantial record changes | all | - -> danger -> `default_permission` will soon be deprecated. You can instead set `default_member_permissions` to `"0"` to disable the command for everyone except admins by default, and/or set `dm_permission` to `false` to disable globally-scoped commands inside of DMs with your app - -###### Application Command Types - -| Name | Type | Description | -| ---------- | ---- | ------------------------------------------------------------------------- | -| CHAT_INPUT | 1 | Slash commands; a text-based command that shows up when a user types `/` | -| USER | 2 | A UI-based command that shows up when you right click or tap on a user | -| MESSAGE | 3 | A UI-based command that shows up when you right click or tap on a message | - - -###### Application Command Option Structure - -> warn -> Required `options` must be listed before optional options - -| Field | Type | Description | -| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -| type | one of [application command option type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-type) | Type of option | -| name | string | [1-32 character name](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-naming) | -| name_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | -| description | string | 1-100 character description | -| description_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | -| required? | boolean | If the parameter is required or optional--default `false` | -| choices? | array of [application command option choice](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-choice-structure) | Choices for `STRING`, `INTEGER`, and `NUMBER` types for the user to pick from, max 25 | -| options? | array of [application command option](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) | If the option is a subcommand or subcommand group type, these nested options will be the parameters | -| channel_types? | array of [channel types](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) | If the option is a channel type, the channels shown will be restricted to these types | -| min_value? | integer for `INTEGER` options, double for `NUMBER` options | If the option is an `INTEGER` or `NUMBER` type, the minimum value permitted | -| max_value? | integer for `INTEGER` options, double for `NUMBER` options | If the option is an `INTEGER` or `NUMBER` type, the maximum value permitted | -| min_length? | integer | For option type `STRING`, the minimum allowed length (minimum of `0`, maximum of `6000`) | -| max_length? | integer | For option type `STRING`, the maximum allowed length (minimum of `1`, maximum of `6000`) | -| autocomplete? \* | boolean | If autocomplete interactions are enabled for this `STRING`, `INTEGER`, or `NUMBER` type option | - -\* `autocomplete` may not be set to true if `choices` are present. - -> info -> Options using `autocomplete` are not confined to only use choices given by the application. - -###### Application Command Option Type - -| Name | Value | Note | -| ----------------- | ----- | -------------------------------------------------------------- | -| SUB_COMMAND | 1 | | -| SUB_COMMAND_GROUP | 2 | | -| STRING | 3 | | -| INTEGER | 4 | Any integer between -2^53 and 2^53 | -| BOOLEAN | 5 | | -| USER | 6 | | -| CHANNEL | 7 | Includes all channel types + categories | -| ROLE | 8 | | -| MENTIONABLE | 9 | Includes users and roles | -| NUMBER | 10 | Any double between -2^53 and 2^53 | -| ATTACHMENT | 11 | [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) object | - -###### Application Command Option Choice Structure - -If you specify `choices` for an option, they are the **only** valid values for a user to pick - -| Field | Type | Description | -| ------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -| name | string | 1-100 character choice name | -| name_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | -| value | string, integer, or double \* | Value for the choice, up to 100 characters if string | - -\* Type of `value` depends on the [option type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-type) that the choice belongs to. - -## Authorizing Your Application - -Application commands do not depend on a bot user in the guild; they use the [interactions](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/) model. To create commands in a guild, your app must be authorized with the `applications.commands` scope. - -> danger -> **In order to make commands work within a guild, the guild must authorize your application with the `applications.commands` scope. The `bot` scope is not enough.** - -When requesting this scope, we "shortcut" the OAuth2 flow similar to adding a bot. You don't need to complete the flow, exchange for a token, or any of that. - -If your application does not require a bot user within the guild for its commands to work, **you no longer need to add for the bot scope or specific permissions**. - - -## Registering a Command - -> info -> Commands can only be registered via HTTP endpoint. - -Commands can be scoped either globally or to a specific guild. Global commands are available for every guild that adds your app. An individual app's global commands are also available in DMs if that app has a bot that shares a mutual guild with the user. - -Guild commands are specific to the guild you specify when making them. Guild commands are not available in DMs. Command names are unique per application, per type, within each scope (global and guild). That means: - -- Your app **cannot** have two global `CHAT_INPUT` commands with the same name -- Your app **cannot** have two guild `CHAT_INPUT` commands within the same name **on the same guild** -- Your app **cannot** have two global `USER` commands with the same name -- Your app **can** have a global and guild `CHAT_INPUT` command with the same name -- Your app **can** have a global `CHAT_INPUT` and `USER` command with the same name -- Multiple apps **can** have commands with the same names - -This list is non-exhaustive. In general, remember that command names must be unique per application, per type, and within each scope (global and guild). - -An app can have the following number of commands: - -- 100 global `CHAT_INPUT` commands -- 5 global `USER` commands -- 5 global `MESSAGE` commands - -As well as the same amount of guild-specific commands per guild. - -> danger -> There is a global rate limit of 200 application command creates per day, per guild - -### Making a Global Command - -Global commands are available on _all_ your app's guilds. - -Global commands have inherent read-repair functionality. That means that if you make an update to a global command, and a user tries to use that command before it has updated for them, Discord will do an internal version check and reject the command, and trigger a reload for that command. - -To make a **global** command, make an HTTP POST call like this: - -```py -import requests - - -url = "https://discord.com/api/v10/applications//commands" - -# This is an example CHAT_INPUT or Slash Command, with a type of 1 -json = { - "name": "blep", - "type": 1, - "description": "Send a random adorable animal photo", - "options": [ - { - "name": "animal", - "description": "The type of animal", - "type": 3, - "required": True, - "choices": [ - { - "name": "Dog", - "value": "animal_dog" - }, - { - "name": "Cat", - "value": "animal_cat" - }, - { - "name": "Penguin", - "value": "animal_penguin" - } - ] - }, - { - "name": "only_smol", - "description": "Whether to show only baby animals", - "type": 5, - "required": False - } - ] -} - -# For authorization, you can use either your bot token -headers = { - "Authorization": "Bot " -} - -# or a client credentials token for your app with the applications.commands.update scope -headers = { - "Authorization": "Bearer " -} - -r = requests.post(url, headers=headers, json=json) -``` - -### Making a Guild Command - -Guild commands are available only within the guild specified on creation. Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use. - -To make a **guild** command, make a similar HTTP POST call, but scope it to a specific `guild_id`: - -```py -import requests - - -url = "https://discord.com/api/v10/applications//guilds//commands" - -# This is an example USER command, with a type of 2 -json = { - "name": "High Five", - "type": 2 -} - -# For authorization, you can use either your bot token -headers = { - "Authorization": "Bot " -} - -# or a client credentials token for your app with the applications.commands.update scope -headers = { - "Authorization": "Bearer " -} - -r = requests.post(url, headers=headers, json=json) -``` - -## Updating and Deleting a Command - -Commands can be deleted and updated by making `DELETE` and `PATCH` calls to the command endpoint. Those endpoints are - -- `applications//commands/` for global commands, or -- `applications//guilds//commands/` for guild commands - -Because commands have unique names within a type and scope, we treat `POST` requests for new commands as upserts. That means **making a new command with an already-used name for your application will update the existing command**. - -Detailed documentation about application command endpoints and their parameters are [in the endpoints section](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/endpoints). - -## Permissions - -Application command permissions allow your app to enable or disable commands for up to 100 users, roles, and channels within a guild. Command permissions can also be updated by users in the client if they have the necessary permissions. - -> warn -> Command permissions can only be updated using a [Bearer token](#DOCS_TOPICS_OAUTH2/client-credentials-grant). Authenticating with a bot token will result in an error. - -A command's current permissions can be retrieved using the [`GET /applications/{application.id}/guilds/{guild.id}/commands/{command.id}/permissions`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/get-application-command-permissions) endpoint. The response will include an array called `permissions` with associated IDs and permission types. - -Command permissions can be updated with the [`PUT /applications/{application.id}/guilds/{guild.id}/commands/{command.id}/permissions`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/edit-application-command-permissions) endpoint. To call the endpoint, apps must use a Bearer token that's authorized with the [`applications.commands.permissions.update`](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes) scope from a user with sufficient permissions. For their permissions to be considered sufficient, all of the following must be true for **the authenticating user** (not your app or bot user): -- Has [permission to Manage Guild and Manage Roles](#DOCS_TOPICS_PERMISSIONS) in the guild where the command is being edited -- Has the ability to run the command being edited -- Has permission to manage the resources that will be affected (roles, users, and/or channels depending on the [permission types](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permission-type)) - -### Syncing and Unsyncing Permissions - -The command permissions interface can be accessed in the client by navigating to `Server Settings` > `Integrations`, then clicking `Manage` to the right of an installed app. At the top of the interface, users can edit permissions for a specific user, role, or channel. By default, these top-level permissions will apply to all of an app's commands. However, each permission can also be unsynced and customized for individual commands to provide more granular control. - -When the permissions for a specific command are unsynced, meaning it doesn't align with the top-level permissions, the interface will display "Not Synced" to users. - -### Application Command Permissions Object - -###### Guild Application Command Permissions Structure - -Returned when fetching the permissions for an app's command(s) in a guild. - -| Field | Type | Description | -| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -| id | snowflake | ID of the command or the application ID | -| application_id | snowflake | ID of the application the command belongs to | -| guild_id | snowflake | ID of the guild | -| permissions | array of [application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permissions-structure) | Permissions for the command in the guild, max of 100 | - -When the `id` field is the application ID instead of a command ID, the permissions apply to all commands that do not contain explicit overwrites. - -###### Application Command Permissions Structure - -Application command permissions allow you to enable or disable commands for specific users, roles, or channels within a guild. - -| Field | Type | Description | -| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| id | snowflake | ID of the role, user, or channel. It can also be a [permission constant](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permissions-constants) | -| type | [application command permission type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permission-type) | role (`1`), user (`2`), or channel (`3`) | -| permission | boolean | `true` to allow, `false`, to disallow | - -###### Application Command Permissions Constants - -The following constants can be used in the `id` field for command permissions payloads. - -| Permission | Value | Type | Description | -| ------------ | -------------- | --------- | ----------------------- | -| `@everyone` | `guild_id` | snowflake | All members in a guild | -| All Channels | `guild_id - 1` | snowflake | All channels in a guild | - -###### Application Command Permission Type - -| Name | Value | -| ------- | ----- | -| ROLE | 1 | -| USER | 2 | -| CHANNEL | 3 | - -To allow for fine-tuned access to commands, application command permissions are supported for guild and global commands of all types. Guild members and apps with the [necessary permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/permissions) can allow or deny specific users and roles from using a command, or toggle commands for entire channels. - -Similar to how threads [inherit user and role permissions from the parent channel](#DOCS_TOPICS_THREADS/permissions), any command permissions for a channel will apply to the threads it contains. - -> info -> If you don't have permission to use a command, it will not show up in the command picker. Members with the Administrator permission can use all commands. - -###### Using Default Permissions - -Default permissions can be added to a command during creation using the `default_member_permissions` and `dm_permission` fields. Adding default permissions doesn't require any Bearer token since it's configured during command creation and isn't targeting specific roles, users, or channels. - -The `default_member_permissions` field can be used when creating a command to set the permissions a user must have to use it. The value for `default_member_permissions` is a bitwise OR-ed set of [permissions](#DOCS_TOPICS_PERMISSIONS/permissions-bitwise-permission-flags), serialized as a string. Setting it to `"0"` will prohibit anyone in a guild from using the command unless a specific overwrite is configured or the user has admin permissions. - -You can also use the `dm_permission` flag to control whether a global command can be run in DMs with your app. The `dm_permission` flag isn't available for guild commands. - -###### Example of editing permissions - -As an example, the following command would not be usable by anyone except admins in any guilds by default: - -```json -{ - "name": "permissions_test", - "description": "A test of default permissions", - "type": 1, - "default_member_permissions": "0" -} -``` - -Or this would enable it just for users that have the `MANAGE_GUILD` permission: - -```py -permissions = str(1 << 5) - -command = { - "name": "permissions_test", - "description": "A test of default permissions", - "type": 1, - "default_member_permissions": permissions -} -``` - -And the following would disable a command for a specific channel: - -```py -A_SPECIFIC_CHANNEL = "" -url = "https://discord.com/api/v10/applications//guilds//commands//permissions" - -json = { - "permissions": [ - { - "id": A_SPECIFIC_CHANNEL, - "type": 3, - "permission": False - } - ] -} - -headers = { - "Authorization": "Bearer " -} - -r = requests.put(url, headers=headers, json=json) -``` - -## Slash Commands - -Slash commands—the `CHAT_INPUT` type—are a type of application command. They're made up of a name, description, and a block of `options`, which you can think of like arguments to a function. The name and description help users find your command among many others, and the `options` validate user input as they fill out your command. - -Slash commands can also have groups and subcommands to further organize commands. More on those later. - -> warn -> Slash commands can have a maximum of 4000 characters for combined name, description, and value properties for each command, its options (including subcommands and groups), and choices. When [localization fields](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/localization) are present, only the longest localization for each field (including the default value) is counted towards the size limit. - - -###### Example Slash Command - -```json -{ - "name": "blep", - "type": 1, - "description": "Send a random adorable animal photo", - "options": [ - { - "name": "animal", - "description": "The type of animal", - "type": 3, - "required": true, - "choices": [ - { - "name": "Dog", - "value": "animal_dog" - }, - { - "name": "Cat", - "value": "animal_cat" - }, - { - "name": "Penguin", - "value": "animal_penguin" - } - ] - }, - { - "name": "only_smol", - "description": "Whether to show only baby animals", - "type": 5, - "required": false - } - ] -} -``` - -When someone uses a slash command, your application will receive an interaction: - -###### Example Interaction - -```js -{ - "type": 2, - "token": "A_UNIQUE_TOKEN", - "member": { - "user": { - "id": "53908232506183680", - "username": "Mason", - "avatar": "a_d5efa99b3eeaa7dd43acca82f5692432", - "discriminator": "1337", - "public_flags": 131141 - }, - "roles": ["539082325061836999"], - "premium_since": null, - "permissions": "2147483647", - "pending": false, - "nick": null, - "mute": false, - "joined_at": "2017-03-13T19:19:14.040000+00:00", - "is_pending": false, - "deaf": false - }, - "id": "786008729715212338", - "guild_id": "290926798626357999", - "app_permissions": "442368", - "guild_locale": "en-US", - "locale": "en-US", - "data": { - "options": [{ - "type": 3, - "name": "cardname", - "value": "The Gitrog Monster" - }], - "type": 1, - "name": "cardsearch", - "id": "771825006014889984" - }, - "channel_id": "645027906669510667" -} -``` - -## Subcommands and Subcommand Groups - -> warn -> Currently, subcommands and subcommand groups all appear at the top level in the command explorer. This may change in the future to include them as nested autocomplete options. - -For those developers looking to make more organized and complex groups of commands, look no further than subcommands and groups. - -**Subcommands** organize your commands by **specifying actions within a command or group**. - -**Subcommand Groups** organize your **subcommands** by **grouping subcommands by similar action or resource within a command**. - -These are not enforced rules. You are free to use subcommands and groups however you'd like; it's just how we think about them. - -> danger -> Using subcommands or subcommand groups will make your base command unusable. You can't send the base `/permissions` command as a valid command if you also have `/permissions add | remove` as subcommands or subcommand groups - -We support nesting one level deep within a group, meaning your top level command can contain subcommand groups, and those groups can contain subcommands. **That is the only kind of nesting supported.** Here's some visual examples: - -``` -VALID - -command -| -|__ subcommand -| -|__ subcommand - ----- - -VALID - -command -| -|__ subcommand-group - | - |__ subcommand -| -|__ subcommand-group - | - |__ subcommand - ----- - -VALID - -command -| -|__ subcommand-group - | - |__ subcommand -| -|__ subcommand - -------- - -INVALID - -command -| -|__ subcommand-group - | - |__ subcommand-group -| -|__ subcommand-group - | - |__ subcommand-group - ----- - -INVALID - -command -| -|__ subcommand - | - |__ subcommand-group -| -|__ subcommand - | - |__ subcommand-group -``` - -### Example Walkthrough - -Let's look at an example. Let's imagine you run a moderation bot. You want to make a `/permissions` command that can do the following: - -- Get the guild permissions for a user or a role -- Get the permissions for a user or a role on a specific channel -- Change the guild permissions for a user or a role -- Change the permissions for a user or a role on a specific channel - -We'll start by defining the top-level information for `/permissions`: - -```js -{ - "name": "permissions", - "description": "Get or edit permissions for a user or a role", - "options": [] -} -``` - -![A command with no arguments. It says /permissions](command.png) - -Now we have a command named `permissions`. We want this command to be able to affect users and roles. Rather than making two separate commands, we can use subcommand groups. We want to use subcommand groups here because we are grouping commands on a similar resource: `user` or `role`. - -```js -{ - "name": "permissions", - "description": "Get or edit permissions for a user or a role", - "options": [ - { - "name": "user", - "description": "Get or edit permissions for a user", - "type": 2 // 2 is type SUB_COMMAND_GROUP - }, - { - "name": "role", - "description": "Get or edit permissions for a role", - "type": 2 - } - ] -} -``` - -You'll notice that a command like this **will not show up** in the command explorer. That's because groups are effectively "folders" for commands, and we've made two empty folders. So let's continue. - -Now that we've effectively made `user` and `role` "folders", we want to be able to either `get` and `edit` permissions. Within the subcommand groups, we can make subcommands for `get` and `edit`: - -```js -{ - "name": "permissions", - "description": "Get or edit permissions for a user or a role", - "options": [ - { - "name": "user", - "description": "Get or edit permissions for a user", - "type": 2, // 2 is type SUB_COMMAND_GROUP - "options": [ - { - "name": "get", - "description": "Get permissions for a user", - "type": 1 // 1 is type SUB_COMMAND - }, - { - "name": "edit", - "description": "Edit permissions for a user", - "type": 1 - } - ] - }, - { - "name": "role", - "description": "Get or edit permissions for a role", - "type": 2, - "options": [ - { - "name": "get", - "description": "Get permissions for a role", - "type": 1 - }, - { - "name": "edit", - "description": "Edit permissions for a role", - "type": 1 - } - ] - } - ] -} -``` - -![A command with grouped subcommands. It says /permissions user get](command-with-groups-subcommands.png) - -Now, we need some arguments! If we chose `user`, we need to be able to pick a user; if we chose `role`, we need to be able to pick a role. We also want to be able to pick between guild-level permissions and channel-specific permissions. For that, we can use optional arguments: - -```js -{ - "name": "permissions", - "description": "Get or edit permissions for a user or a role", - "options": [ - { - "name": "user", - "description": "Get or edit permissions for a user", - "type": 2, // 2 is type SUB_COMMAND_GROUP - "options": [ - { - "name": "get", - "description": "Get permissions for a user", - "type": 1, // 1 is type SUB_COMMAND - "options": [ - { - "name": "user", - "description": "The user to get", - "type": 6, // 6 is type USER - "required": true - }, - { - "name": "channel", - "description": "The channel permissions to get. If omitted, the guild permissions will be returned", - "type": 7, // 7 is type CHANNEL - "required": false - } - ] - }, - { - "name": "edit", - "description": "Edit permissions for a user", - "type": 1, - "options": [ - { - "name": "user", - "description": "The user to edit", - "type": 6, - "required": true - }, - { - "name": "channel", - "description": "The channel permissions to edit. If omitted, the guild permissions will be edited", - "type": 7, - "required": false - } - ] - } - ] - }, - { - "name": "role", - "description": "Get or edit permissions for a role", - "type": 2, - "options": [ - { - "name": "get", - "description": "Get permissions for a role", - "type": 1, - "options": [ - { - "name": "role", - "description": "The role to get", - "type": 8, // 8 is type ROLE - "required": true - }, - { - "name": "channel", - "description": "The channel permissions to get. If omitted, the guild permissions will be returned", - "type": 7, - "required": false - } - ] - }, - { - "name": "edit", - "description": "Edit permissions for a role", - "type": 1, - "options": [ - { - "name": "role", - "description": "The role to edit", - "type": 8, - "required": true - }, - { - "name": "channel", - "description": "The channel permissions to edit. If omitted, the guild permissions will be edited", - "type": 7, - "required": false - } - ] - } - ] - } - ] -} -``` - -And, done! The JSON looks a bit complicated, but what we've ended up with is a single command that can be scoped to multiple actions, and then further scoped to a particular resource, and then even _further_ scope with optional arguments. Here's what it looks like all put together. - -![A command with grouped subcommands and parameters. It says /permissions user get with arguments for a user and a channel.](command-with-groups-subcommands-parameters.png) - -## User Commands - -User commands are application commands that appear on the context menu (right click or tap) of users. They're a great way to surface quick actions for your app that target users. They don't take any arguments, and will return the user on whom you clicked or tapped in the interaction response. - -> warn -> A user must have permission to send text messages in the channel they invoke a user command in. If they don't have this permission, they will receive a 'Permission Denied' error from the interaction. - -> danger -> The `description` field is not allowed when creating user commands. However, to avoid breaking changes to data models, `description` will be an **empty string** (instead of `null`) when fetching commands. - -###### Example User Command - -```json -{ - "name": "High Five", - "type": 2 -} -``` - -![An example user command. The context menu has an Apps section open to a High Five command](user-command.png) - -When someone uses a user command, your application will receive an interaction: - -###### Example Interaction - -```json -{ - "application_id": "775799577604522054", - "channel_id": "772908445358620702", - "data": { - "id": "866818195033292850", - "name": "context-menu-user-2", - "resolved": { - "members": { - "809850198683418695": { - "avatar": null, - "is_pending": false, - "joined_at": "2021-02-12T18:25:07.972000+00:00", - "nick": null, - "pending": false, - "permissions": "246997699136", - "premium_since": null, - "roles": [] - } - }, - "users": { - "809850198683418695": { - "avatar": "afc428077119df8aabbbd84b0dc90c74", - "bot": true, - "discriminator": "7302", - "id": "809850198683418695", - "public_flags": 0, - "username": "VoltyDemo" - } - } - }, - "target_id": "809850198683418695", - "type": 2 - }, - "guild_id": "772904309264089089", - "guild_locale": "en-US", - "app_permissions": "442368", - "id": "867794291820986368", - "locale": "en-US", - "member": { - "avatar": null, - "deaf": false, - "is_pending": false, - "joined_at": "2020-11-02T20:46:57.364000+00:00", - "mute": false, - "nick": null, - "pending": false, - "permissions": "274877906943", - "premium_since": null, - "roles": ["785609923542777878"], - "user": { - "avatar": "a_f03401914fb4f3caa9037578ab980920", - "discriminator": "6538", - "id": "167348773423415296", - "public_flags": 1, - "username": "ian" - } - }, - "token": "UNIQUE_TOKEN", - "type": 2, - "version": 1 -} -``` - - -## Message Commands - -Message commands are application commands that appear on the context menu (right click or tap) of messages. They're a great way to surface quick actions for your app that target messages. They don't take any arguments, and will return the message on whom you clicked or tapped in the interaction response. - -> danger -> The `description` field is not allowed when creating message commands. However, to avoid breaking changes to data models, `description` will be an **empty string** (instead of `null`) when fetching commands. - -###### Example Message Command - -```json -{ - "name": "Bookmark", - "type": 3 -} -``` - -![An example message command. The context menu has an Apps section open to a Bookmark command](message-command.png) - -When someone uses a message command, your application will receive an interaction: - -###### Example Interaction - -```json -{ - "application_id": "775799577604522054", - "channel_id": "772908445358620702", - "data": { - "id": "866818195033292851", - "name": "context-menu-message-2", - "resolved": { - "messages": { - "867793854505943041": { - "attachments": [], - "author": { - "avatar": "a_f03401914fb4f3caa9037578ab980920", - "discriminator": "6538", - "id": "167348773423415296", - "public_flags": 1, - "username": "ian" - }, - "channel_id": "772908445358620702", - "components": [], - "content": "some message", - "edited_timestamp": null, - "embeds": [], - "flags": 0, - "id": "867793854505943041", - "mention_everyone": false, - "mention_roles": [], - "mentions": [], - "pinned": false, - "timestamp": "2021-07-22T15:42:57.744000+00:00", - "tts": false, - "type": 0 - } - } - }, - "target_id": "867793854505943041", - "type": 3 - }, - "guild_id": "772904309264089089", - "guild_locale": "en-US", - "app_permissions": "442368", - "id": "867793873336926249", - "locale": "en-US", - "member": { - "avatar": null, - "deaf": false, - "is_pending": false, - "joined_at": "2020-11-02T20:46:57.364000+00:00", - "mute": false, - "nick": null, - "pending": false, - "permissions": "274877906943", - "premium_since": null, - "roles": ["785609923542777878"], - "user": { - "avatar": "a_f03401914fb4f3caa9037578ab980920", - "discriminator": "6538", - "id": "167348773423415296", - "public_flags": 1, - "username": "ian" - } - }, - "token": "UNIQUE_TOKEN", - "type": 2, - "version": 1 -} -``` - -## Autocomplete - -Autocomplete interactions allow your application to dynamically return option suggestions to a user as they type. - -An autocomplete interaction **can return partial data** for option values. Your application will receive partial data for any existing user input, as long as that input passes client-side validation. For example, you may receive partial strings, but not invalid numbers. The option the user is currently typing will be sent with a `focused: true` boolean field and options the user has already filled will also be sent but without the `focused` field. This is a special case where options that are otherwise required might not be present, due to the user not having filled them yet. - -> warn -> This validation is **client-side only**. - -```json -{ - "type": 4, - "data": { - "id": "816437322781949972", - "name": "airhorn", - "type": 1, - "version": "847194950382780532", - "options": [ - { - "type": 3, - "name": "variant", - "value": "data a user is typ", - "focused": true - } - ] - } -} -``` - -## Localization - -Application commands can be localized, which will cause them to use localized names and descriptions depending on the client's selected language. This is entirely optional. Localization is available for names and descriptions of commands, subcommands, and options, as well as the names of choices, by submitting the appropriate `name_localizations` and `description_localizations` fields when creating or updating the application command. - -Application commands may be partially localized - not all [available locales](#DOCS_REFERENCE/locales) are required, nor do different fields within a command need to support the same set of locales. If a locale is not present in a localizations dictionary for a field, users in that locale will see the default value for that field. It's not necessary to fill out all locales with the default value. Any localized values that are identical to the default will be ignored. - -Localized option names are subject to an additional constraint, which is that they must be distinct from all other default option names of that command, as well as all other option names within that locale on that command. - -When taking advantage of command localization, the interaction payload received by your client will still use default command, subcommand, and option names. To localize your interaction response, you can determine the client's selected language by using the `locale` key in the interaction payload. - -An application command furnished with localizations might look like this: - -```json -{ - "name": "birthday", - "type": 1, - "description": "Wish a friend a happy birthday", - "name_localizations": { - "zh-CN": "生日", - "el": "γενέθλια" - }, - "description_localizations": { - "zh-CN": "祝你朋友生日快乐" - }, - "options": [ - { - "name": "age", - "type": 4, - "description": "Your friend's age", - "name_localizations": { - "zh-CN": "岁数" - }, - "description_localizations": { - "zh-CN": "你朋友的岁数" - } - } - ] -} -``` - -### Retrieving localized commands - -While most endpoints that return application command objects will return the `name_localizations` and `description_localizations` fields, some will not by default. This includes `GET` endpoints that return all of an application's guild or global commands. Instead, those endpoints will supply additional `name_localized` or `description_localized` fields, which only contain the localization relevant to the requester's locale. (The full dictionaries can still be obtained by supplying the appropriate query argument). - -For example, if a batch `GET` request were made with locale `zh-CN`, including the above command, the returned object would look as follows: - -```json -{ - "name": "birthday", - "type": 1, - "description": "Wish a friend a happy birthday", - "name_localized": "生日", - "description_localized": "祝你朋友生日快乐", - "options": [ - { - "name": "age", - "type": 4, - "description": "Your friend's age", - "name_localized": "岁数", - "description_localized": "你朋友的岁数", - } - ] -} -``` - -If the requester's locale is not found in a localizations dictionary, then the corresponding `name_localization` or `description_localization` for that field will also not be present. - -Locale is determined by looking at the `X-Discord-Locale` header, then the `Accept-Language` header if not present, then lastly the user settings locale. - -### Endpoints - -> info -> For authorization, all endpoints take either a [bot token](#DOCS_REFERENCE/authentication) or [client credentials token](#DOCS_TOPICS_OAUTH2/client-credentials-grant) for your application - -## Get Global Application Commands % GET /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/commands - -> warn -> The objects returned by this endpoint may be augmented with [additional fields if localization is active](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/retrieving-localized-commands). - -Fetch all of the global commands for your application. Returns an array of [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) objects. - -###### Query String Params - -| Field | Type | Description | -| ------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| with_localizations? | boolean | Whether to include full localization dictionaries (`name_localizations` and `description_localizations`) in the returned objects, instead of the `name_localized` and `description_localized` fields. Default `false`. | - -## Create Global Application Command % POST /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/commands - -> danger -> Creating a command with the same name as an existing command for your application will overwrite the old command. - -Create a new global command. Returns `201` and an [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) object. - -###### JSON Params - -| Field | Type | Description | -| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name | string | [Name of command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-naming), 1-32 characters | -| name_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | -| description | string | 1-100 character description | -| description_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | -| options? | array of [application command option](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) | the parameters for the command | -| default_member_permissions? | ?string | Set of [permissions](#DOCS_TOPICS_PERMISSIONS) represented as a bit set | -| dm_permission? | ?boolean | Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | -| default_permission? | boolean (default `true`) | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. | | -| type? | one of [application command type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-types) | Type of command, defaults `1` if not set | - -## Get Global Application Command % GET /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/commands/{command.id#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object} - -Fetch a global command for your application. Returns an [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) object. - -## Edit Global Application Command % PATCH /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/commands/{command.id#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object} - -> info -> All parameters for this endpoint are optional. - -Edit a global command. Returns `200` and an [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) object. All fields are optional, but any fields provided will entirely overwrite the existing values of those fields. - -###### JSON Params - -| Field | Type | Description | -| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name? | string | [Name of command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-naming), 1-32 characters | -| name_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | -| description? | string | 1-100 character description | -| description_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | -| options? | array of [application command option](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) | the parameters for the command | -| default_member_permissions? | ?string | Set of [permissions](#DOCS_TOPICS_PERMISSIONS) represented as a bit set | -| dm_permission? | ?boolean | Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | -| default_permission? | boolean (default `true`) | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. | | - -## Delete Global Application Command % DELETE /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/commands/{command.id#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object} - -Deletes a global command. Returns `204 No Content` on success. - -## Bulk Overwrite Global Application Commands % PUT /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/commands - -Takes a list of application commands, overwriting the existing global command list for this application. Returns `200` and a list of [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) objects. Commands that do not already exist will count toward daily application command create limits. - -> danger -> This will overwrite **all** types of application commands: slash commands, user commands, and message commands. - -## Get Guild Application Commands % GET /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands - -> warn -> The objects returned by this endpoint may be augmented with [additional fields if localization is active](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/retrieving-localized-commands). - -Fetch all of the guild commands for your application for a specific guild. Returns an array of [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) objects. - -###### Query String Params - -| Field | Type | Description | -| ------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| with_localizations? | boolean | Whether to include full localization dictionaries (`name_localizations` and `description_localizations`) in the returned objects, instead of the `name_localized` and `description_localized` fields. Default `false`. | - -## Create Guild Application Command % POST /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands - -> danger -> Creating a command with the same name as an existing command for your application will overwrite the old command. - -Create a new guild command. New guild commands will be available in the guild immediately. Returns `201` and an [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) object. If the command did not already exist, it will count toward daily application command create limits. - -###### JSON Params - -| Field | Type | Description | -| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name | string | [Name of command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-naming), 1-32 characters | -| name_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | -| description | string | 1-100 character description | -| description_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | -| options? | array of [application command option](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) | Parameters for the command | -| default_member_permissions? | ?string | Set of [permissions](#DOCS_TOPICS_PERMISSIONS) represented as a bit set | -| default_permission? | boolean (default `true`) | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. | | -| type? | one of [application command type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-types) | Type of command, defaults `1` if not set | - -## Get Guild Application Command % GET /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/{command.id#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object} - -Fetch a guild command for your application. Returns an [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) object. - -## Edit Guild Application Command % PATCH /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/{command.id#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object} - -> info -> All parameters for this endpoint are optional. - -Edit a guild command. Updates for guild commands will be available immediately. Returns `200` and an [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) object. All fields are optional, but any fields provided will entirely overwrite the existing values of those fields. - -###### JSON Params - -| Field | Type | Description | -| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name? | string | [Name of command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-naming), 1-32 characters | -| name_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | -| description? | string | 1-100 character description | -| description_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | -| options? | array of [application command option](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) | Parameters for the command | -| default_member_permissions? | ?string | Set of [permissions](#DOCS_TOPICS_PERMISSIONS) represented as a bit set | -| default_permission? | boolean (default `true`) | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. | - -## Delete Guild Application Command % DELETE /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/{command.id#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object} - -Delete a guild command. Returns `204 No Content` on success. - -## Bulk Overwrite Guild Application Commands % PUT /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands - -Takes a list of application commands, overwriting the existing command list for this application for the targeted guild. Returns `200` and a list of [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) objects. - -> danger -> This will overwrite **all** types of application commands: slash commands, user commands, and message commands. - -###### Bulk Application Command JSON Params - -| Field | Type | Description | -| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| id? | snowflake | ID of the command, if known | -| name | string | [Name of command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-naming), 1-32 characters | -| name_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `name` field. Values follow the same restrictions as `name` | -| description | string | 1-100 character description | -| description_localizations? | ?dictionary with keys in [available locales](#DOCS_REFERENCE/locales) | Localization dictionary for the `description` field. Values follow the same restrictions as `description` | -| options? | array of [application command option](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-structure) | Parameters for the command | -| default_member_permissions? | ?string | Set of [permissions](#DOCS_TOPICS_PERMISSIONS) represented as a bit set | -| dm_permission? | ?boolean | Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. | -| default_permission? | boolean (default `true`) | Replaced by `default_member_permissions` and will be deprecated in the future. Indicates whether the command is enabled by default when the app is added to a guild. | -| type? | one of [application command type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-types) | Type of command, defaults `1` if not set | - -## Get Guild Application Command Permissions % GET /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/permissions - -Fetches permissions for all commands for your application in a guild. Returns an array of [guild application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-guild-application-command-permissions-structure) objects. - -## Get Application Command Permissions % GET /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/{command.id#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object}/permissions - -Fetches permissions for a specific command for your application in a guild. Returns a [guild application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-guild-application-command-permissions-structure) object. - -## Edit Application Command Permissions % PUT /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/{command.id#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object}/permissions - -> warn -> This endpoint will overwrite existing permissions for the command in that guild - -Edits command permissions for a specific command for your application in a guild and returns a [guild application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-guild-application-command-permissions-structure) object. Fires an [Application Command Permissions Update](#DOCS_TOPICS_GATEWAY_EVENTS/application-command-permissions-update) Gateway event. - -You can add up to 100 permission overwrites for a command. - -> info -> This endpoint requires authentication with a Bearer token that has permission to manage the guild and its roles. For more information, read above about [application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/permissions). - -> warn -> Deleting or renaming a command will permanently delete all permissions for the command - -###### JSON Params - -| Field | Type | Description | -| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| permissions | array of [application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permissions-structure) | Permissions for the command in the guild | - -## Batch Edit Application Command Permissions % PUT /applications/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/commands/permissions - -> danger -> This endpoint has been disabled with [updates to command permissions (Permissions v2)](#DOCS_CHANGE_LOG/updated-command-permissions). Instead, you can [edit each application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/edit-application-command-permissions) (though you should be careful to handle any potential [rate limits](#DOCS_TOPICS_RATE_LIMITS)). diff --git a/docs/interactions/Message_Components.md b/docs/interactions/Message_Components.md deleted file mode 100644 index b9ba8109d5..0000000000 --- a/docs/interactions/Message_Components.md +++ /dev/null @@ -1,456 +0,0 @@ -# Message Components - -Message components—we'll call them "components" moving forward—are a framework for adding interactive elements to the messages your app or bot sends. They're accessible, customizable, and easy to use. - -There are several different types of components; this documentation will outline the basics of this new framework and each example. - -## What is a Component - -Components are a new field on the [message object](#DOCS_RESOURCES_CHANNEL/message-object), so you can use them whether you're sending messages or responding to a [slash command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/) or other interaction. - -The top-level `components` field is an array of [Action Row](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/action-rows) components. - -### Component Object - -###### Component Types - -| Type | Name | Description | -| ---- | ----------- | -------------------------------------- | -| 1 | Action Row | A container for other components | -| 2 | Button | A button object | -| 3 | Select Menu | A select menu for picking from choices | -| 4 | Text Input | A text input object | - -The structure of each component type is described in detail below. - -###### Example Component - -```json -{ - "content": "This is a message with components", - "components": [ - { - "type": 1, - "components": [] - } - ] -} -``` - -## Action Rows - -An Action Row is a non-interactive container component for other types of components. It has a `type: 1` and a sub-array of `components` of other types. - -- You can have up to 5 Action Rows per message -- An Action Row cannot contain another Action Row - -```json -{ - "content": "This is a message with components", - "components": [ - { - "type": 1, - "components": [ - { - "type": 2, - "label": "Click me!", - "style": 1, - "custom_id": "click_one" - } - ] - - } - ] -} -``` - -## Responding to a Component Interaction - -Responding to a user interacting with a component is the same as other interaction types, like application commands. You can simply ACK the request, send a followup message, or edit the original message to something new. Check out [Responding to An Interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/responding-to-an-interaction) and [interaction response](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object) for more. - -## Custom ID - -Components, aside from Action Rows, must have a `custom_id` field. This field is defined by the developer when sending the component payload, and is returned in the interaction payload sent when a user interacts with the component. For example, if you set `custom_id: click_me` on a button, you'll receive an interaction containing `custom_id: click_me` when a user clicks that button. - -`custom_id` must be unique per component; multiple buttons on the same message must not share the same `custom_id`. This field is a string of max 100 characters, and can be used flexibly to maintain state or pass through other important data. - -## Buttons - -Buttons are interactive components that render on messages. They can be clicked by users, and send an [interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object) to your app when clicked. - -- Buttons must be sent inside an Action Row -- An Action Row can contain up to 5 buttons -- An Action Row containing buttons cannot also contain a select menu - -### Button Object - -###### Button Structure - -| Field | Type | Description | -| ---------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| type | integer | `2` for a button | -| style | integer | one of [button styles](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/button-object-button-styles) | -| label? | string | text that appears on the button, max 80 characters | -| emoji? | partial [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) | `name`, `id`, and `animated` | -| custom_id? | string | a developer-defined identifier for the button, max 100 characters | -| url? | string | a url for link-style buttons | -| disabled? | boolean | whether the button is disabled (default `false`) | - -Buttons come in a variety of styles to convey different types of actions. These styles also define what fields are valid for a button. - -- Non-link buttons **must** have a `custom_id`, and cannot have a `url` -- Link buttons **must** have a `url`, and cannot have a `custom_id` -- Link buttons do not send an [interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object) to your app when clicked - -###### Button Styles - -| Name | Value | Color | Required Field | -| --------- | ----- | ------------------------ | -------------- | -| Primary | 1 | blurple | `custom_id` | -| Secondary | 2 | grey | `custom_id` | -| Success | 3 | green | `custom_id` | -| Danger | 4 | red | `custom_id` | -| Link | 5 | grey, navigates to a URL | `url` | - -![An image showing the different button styles](button-styles.png) - -When a user clicks on a button, your app will receive an [interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object) including the message the button was on: - -### Component Interaction Object - -###### Sample Component Interaction - -```json -{ - "version": 1, - "type": 3, - "token": "unique_interaction_token", - "message": { - "type": 0, - "tts": false, - "timestamp": "2021-05-19T02:12:51.710000+00:00", - "pinned": false, - "mentions": [], - "mention_roles": [], - "mention_everyone": false, - "id": "844397162624450620", - "flags": 0, - "embeds": [], - "edited_timestamp": null, - "content": "This is a message with components.", - "components": [ - { - "type": 1, - "components": [ - { - "type": 2, - "label": "Click me!", - "style": 1, - "custom_id": "click_one" - } - ] - } - ], - "channel_id": "345626669114982402", - "author": { - "username": "Mason", - "public_flags": 131141, - "id": "53908232506183680", - "discriminator": "1337", - "avatar": "a_d5efa99b3eeaa7dd43acca82f5692432" - }, - "attachments": [] - }, - "member": { - "user": { - "username": "Mason", - "public_flags": 131141, - "id": "53908232506183680", - "discriminator": "1337", - "avatar": "a_d5efa99b3eeaa7dd43acca82f5692432" - }, - "roles": [ - "290926798626357999" - ], - "premium_since": null, - "permissions": "17179869183", - "pending": false, - "nick": null, - "mute": false, - "joined_at": "2017-03-13T19:19:14.040000+00:00", - "is_pending": false, - "deaf": false, - "avatar": null - }, - "id": "846462639134605312", - "guild_id": "290926798626357999", - "data": { - "custom_id": "click_one", - "component_type": 2 - }, - "channel_id": "345626669114982999", - "application_id": "290926444748734465" -} -``` - -## Select Menus - -Select menus are another interactive component that renders on messages. On desktop, clicking on a select menu opens a dropdown-style UI; on mobile, tapping a select menu opens up a half-sheet with the options. - -![A select menu open on desktop](desktop-select.png) - -Select menus support single-select and multi-select behavior, meaning you can prompt a user to choose just one item from a list, or multiple. When a user finishes making their choice by clicking out of the dropdown or closing the half-sheet, your app will receive an [interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-structure). - -- Select menus must be sent inside an Action Row -- An Action Row can contain only one select menu -- An Action Row containing a select menu cannot also contain buttons - -###### Select Menu Example - -```json -// This is a message -{ - "content": "Mason is looking for new arena partners. What classes do you play?", - "components": [ - { - "type": 1, - "components": [ - { - "type": 3, - "custom_id": "class_select_1", - "options":[ - { - "label": "Rogue", - "value": "rogue", - "description": "Sneak n stab", - "emoji": { - "name": "rogue", - "id": "625891304148303894" - } - }, - { - "label": "Mage", - "value": "mage", - "description": "Turn 'em into a sheep", - "emoji": { - "name": "mage", - "id": "625891304081063986" - } - }, - { - "label": "Priest", - "value": "priest", - "description": "You get heals when I'm done doing damage", - "emoji": { - "name": "priest", - "id": "625891303795982337" - } - } - ], - "placeholder": "Choose a class", - "min_values": 1, - "max_values": 3 - } - ] - } - ] -} -``` - -### Select Menu Object - -###### Select Menu Structure - -| Field | Type | Description | -| ------------ | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -| type | integer | `3` for a select menu | -| custom_id | string | a developer-defined identifier for the select menu, max 100 characters | -| options | array of [select options](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/select-menu-object-select-option-structure) | the choices in the select, max 25 | -| placeholder? | string | custom placeholder text if nothing is selected, max 150 characters | -| min_values? | integer | the minimum number of items that must be chosen; default 1, min 0, max 25 | -| max_values? | integer | the maximum number of items that can be chosen; default 1, max 25 | -| disabled? | boolean | disable the select, default false | - -###### Select Option Structure - -| Field | Type | Description | -| ------------ | ---------------------------------------------------------- | ----------------------------------------------------------- | -| label | string | the user-facing name of the option, max 100 characters | -| value | string | the dev-defined value of the option, max 100 characters | -| description? | string | an additional description of the option, max 100 characters | -| emoji? | partial [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) object | `id`, `name`, and `animated` | -| default? | boolean | will render this option as selected by default | - -###### Select Menu Interaction - -```json -{ - "application_id": "845027738276462632", - "channel_id": "772908445358620702", - "data": { - "component_type":3, - "custom_id": "class_select_1", - "values": [ - "mage", - "rogue" - ] - }, - "guild_id": "772904309264089089", - "id": "847587388497854464", - "member": { - "avatar": null, - "deaf": false, - "is_pending": false, - "joined_at": "2020-11-02T19:25:47.248000+00:00", - "mute": false, - "nick": "Bot Man", - "pending": false, - "permissions": "17179869183", - "premium_since": null, - "roles": [ - "785609923542777878" - ], - "user":{ - "avatar": "a_d5efa99b3eeaa7dd43acca82f5692432", - "discriminator": "1337", - "id": "53908232506183680", - "public_flags": 131141, - "username": "Mason" - } - }, - "message":{ - "application_id": "845027738276462632", - "attachments": [], - "author": { - "avatar": null, - "bot": true, - "discriminator": "5284", - "id": "845027738276462632", - "public_flags": 0, - "username": "Interactions Test" - }, - "channel_id": "772908445358620702", - "components": [ - { - "components": [ - { - "custom_id": "class_select_1", - "max_values": 1, - "min_values": 1, - "options": [ - { - "description": "Sneak n stab", - "emoji":{ - "id": "625891304148303894", - "name": "rogue" - }, - "label": "Rogue", - "value": "rogue" - }, - { - "description": "Turn 'em into a sheep", - "emoji":{ - "id": "625891304081063986", - "name": "mage" - }, - "label": "Mage", - "value": "mage" - }, - { - "description": "You get heals when I'm done doing damage", - "emoji":{ - "id": "625891303795982337", - "name": "priest" - }, - "label": "Priest", - "value": "priest" - } - ], - "placeholder": "Choose a class", - "type": 3 - } - ], - "type": 1 - } - ], - "content": "Mason is looking for new arena partners. What classes do you play?", - "edited_timestamp": null, - "embeds": [], - "flags": 0, - "id": "847587334500646933", - "interaction": { - "id": "847587333942935632", - "name": "dropdown", - "type": 2, - "user": { - "avatar": "a_d5efa99b3eeaa7dd43acca82f5692432", - "discriminator": "1337", - "id": "53908232506183680", - "public_flags": 131141, - "username": "Mason" - } - }, - "mention_everyone": false, - "mention_roles":[], - "mentions":[], - "pinned": false, - "timestamp": "2021-05-27T21:29:27.956000+00:00", - "tts": false, - "type": 20, - "webhook_id": "845027738276462632" - }, - "token": "UNIQUE_TOKEN", - "type": 3, - "version": 1 -} -``` - -## Text Inputs - -Text inputs are an interactive component that render on modals. They can be used to collect short-form or long-form text. - -![A text input in a modal on desktop client](modal-desktop.png) - -###### Text Input Example - -```json -// this is a modal -{ - "title": "My Cool Modal", - "custom_id": "cool_modal", - "components": [{ - "type": 1, - "components": [{ - "type": 4, - "custom_id": "name", - "label": "Name", - "style": 1, - "min_length": 1, - "max_length": 4000, - "placeholder": "John", - "required": true - }] - }] -} -``` - -###### Text Input Structure - -| Field | Type | Description | -| ------------ | ------- | ------------------------------------------------------------------------------------------- | -| type | integer | `4` for a text input | -| custom_id | string | a developer-defined identifier for the input, max 100 characters | -| style | integer | the [Text Input Style](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/text-inputs-text-input-styles) | -| label | string | the label for this component, max 45 characters | -| min_length? | integer | the minimum input length for a text input, min 0, max 4000 | -| max_length? | integer | the maximum input length for a text input, min 1, max 4000 | -| required? | boolean | whether this component is required to be filled, default true | -| value? | string | a pre-filled value for this component, max 4000 characters | -| placeholder? | string | custom placeholder text if the input is empty, max 100 characters | - -###### Text Input Styles - -| Name | Value | Description | -| --------- | ----- | ------------------- | -| Short | 1 | A single-line input | -| Paragraph | 2 | A multi-line input | diff --git a/docs/interactions/Receiving_and_Responding.md b/docs/interactions/Receiving_and_Responding.md deleted file mode 100644 index a4561054a8..0000000000 --- a/docs/interactions/Receiving_and_Responding.md +++ /dev/null @@ -1,414 +0,0 @@ -# Interactions - -An **[Interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object)** is the message that your application receives when a user uses an application command or a message component. - -For [Slash Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/slash-commands), it includes the values that the user submitted. - -For [User Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/user-commands) and [Message Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/message-commands), it includes the resolved user or message on which the action was taken. - -For [Message Components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/) it includes identifying information about the component that was used. It will also include some metadata about how the interaction was triggered: the `guild_id`, `channel_id`, `member` and other fields. You can find all the values in our data models below. - -### Interaction Object - -###### Interaction Structure - -| Field | Type | Description | -| ---------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| id | snowflake | ID of the interaction | -| application_id | snowflake | ID of the application this interaction is for | -| type | [interaction type](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-type) | Type of interaction | -| data?\* | [interaction data](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-data) | Interaction data payload | -| guild_id? | snowflake | Guild that the interaction was sent from | -| channel_id? | snowflake | Channel that the interaction was sent from | -| member?\*\* | [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) object | Guild member data for the invoking user, including permissions | -| user? | [user](#DOCS_RESOURCES_USER/user-object) object | User object for the invoking user, if invoked in a DM | -| token | string | Continuation token for responding to the interaction | -| version | integer | Read-only property, always `1` | -| message? | [message](#DOCS_RESOURCES_CHANNEL/message-object) object | For components, the message they were attached to | -| app_permissions? | string | Bitwise set of permissions the app or bot has within the channel the interaction was sent from | -| locale?\*\*\* | string | Selected [language](#DOCS_REFERENCE/locales) of the invoking user | -| guild_locale? | string | [Guild's preferred locale](#DOCS_RESOURCES_GUILD/guild-object), if invoked in a guild | - -\* This is always present on application command, message component, and modal submit interaction types. It is optional for future-proofing against new interaction types - -\*\* `member` is sent when the interaction is invoked in a guild, and `user` is sent when invoked in a DM - -\*\*\* This is available on all interaction types except PING - -###### Interaction Type - -| Name | Value | -| -------------------------------- | ----- | -| PING | 1 | -| APPLICATION_COMMAND | 2 | -| MESSAGE_COMPONENT | 3 | -| APPLICATION_COMMAND_AUTOCOMPLETE | 4 | -| MODAL_SUBMIT | 5 | - -###### Interaction Data - -While the `data` field is guaranteed to be present for all [interaction types](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-type) besides `PING`, its structure will vary. The following tables detail the inner `data` payload for each interaction type. - -###### Application Command Data Structure - -> info -> Sent in `APPLICATION_COMMAND` and `APPLICATION_COMMAND_AUTOCOMPLETE` interactions. - -| Field | Type | Description | -| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| id | snowflake | the [`ID`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-structure) of the invoked command | -| name | string | the [`name`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-structure) of the invoked command | -| type | integer | the [`type`](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-structure) of the invoked command | -| resolved? | [resolved data](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-resolved-data-structure) | converted users + roles + channels + attachments | -| options?\* | array of [application command interaction data option](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-application-command-interaction-data-option-structure) | the params + values from the user | -| guild_id? | snowflake | the id of the guild the command is registered to | -| target_id? | snowflake | id of the user or message targeted by a [user](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/user-commands) or [message](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/message-commands) command | - -\* This [can be partial](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/autocomplete) when in response to `APPLICATION_COMMAND_AUTOCOMPLETE` - -###### Message Component Data Structure - -| Field | Type | Description | -| -------------- | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -| custom_id | string | the [`custom_id`](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/custom-id) of the component | -| component_type | integer | the [type](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/component-object-component-types) of the component | -| values?\* | array of [select option values](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/select-menu-object-select-option-structure) | values the user selected in a [select menu](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/select-menu-object) component | - -\* This is always present for select menu components - -###### Modal Submit Data Structure - -| Field | Type | Description | -| ---------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| custom_id | string | the [`custom_id`](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/custom-id) of the modal | -| components | array of [message components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/message-components) | the values submitted by the user | - -###### Resolved Data Structure - -> info -> If data for a Member is included, data for its corresponding User will also be included. - -| Field | Type | Description | -| ------------- | ---------------------------------------------------------------------------------------- | ----------------------------------- | -| users? | Map of Snowflakes to [user](#DOCS_RESOURCES_USER/user-object) objects | the ids and User objects | -| members?\* | Map of Snowflakes to [partial member](#DOCS_RESOURCES_GUILD/guild-member-object) objects | the ids and partial Member objects | -| roles? | Map of Snowflakes to [role](#DOCS_TOPICS_PERMISSIONS/role-object) objects | the ids and Role objects | -| channels?\*\* | Map of Snowflakes to [partial channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | the ids and partial Channel objects | -| messages? | Map of Snowflakes to [partial messages](#DOCS_RESOURCES_CHANNEL/message-object) objects | the ids and partial Message objects | -| attachments? | Map of Snowflakes to [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) objects | the ids and attachment objects | - -\* Partial `Member` objects are missing `user`, `deaf` and `mute` fields - -\*\* Partial `Channel` objects only have `id`, `name`, `type` and `permissions` fields. Threads will also have `thread_metadata` and `parent_id` fields. - -###### Application Command Interaction Data Option Structure - -All options have names, and an option can either be a parameter and input value--in which case `value` will be set--or it can denote a subcommand or group--in which case it will contain a top-level key and another array of `options`. - -`value` and `options` are mutually exclusive. - -| Field | Type | Description | -| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| name | string | Name of the parameter | -| type | integer | Value of [application command option type](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-type) | -| value? | string, integer, or double | Value of the option resulting from user input | -| options? | array of [application command interaction data option](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-application-command-interaction-data-option-structure) | Present if this option is a group or subcommand | -| focused? | boolean | `true` if this option is the currently focused option for autocomplete | - -### Message Interaction Object - -This is sent on the [message object](#DOCS_RESOURCES_CHANNEL/message-object) when the message is a response to an Interaction without an existing message. - -> info -> This means responses to [Message Components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/) do not include this property, instead including a [message reference](#DOCS_RESOURCES_CHANNEL/message-reference-object-message-reference-structure) object as components _always_ exist on preexisting messages. - -###### Message Interaction Structure - -| Field | Type | Description | -| ------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| id | snowflake | ID of the interaction | -| type | [interaction type](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-type) | Type of interaction | -| name | string | Name of the [application command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-structure), including subcommands and subcommand groups | -| user | [user object](#DOCS_RESOURCES_USER/user-object) | User who invoked the interaction | -| member? | [partial member](#DOCS_RESOURCES_GUILD/guild-member-object) object | Member who invoked the interaction in the guild | - - -## Interactions and Bot Users - -We're all used to the way that Discord bots have worked for a long time. You make an application in the Dev Portal, you add a bot user to it, and you copy the token. That token can be used to connect to the gateway and to make requests against our API. - -Interactions bring something entirely new to the table: the ability to interact with an application _without needing a bot user in the guild_. As you read through this documentation, you'll see that bot tokens are only referenced as a helpful alternative to doing a client credentials auth flow. Responding to interactions does not require a bot token. - -In many cases, you may still need a bot user. If you need to receive gateway events, or need to interact with other parts of our API (like fetching a guild, or a channel, or updating permissions on a user), those actions are all still tied to having a bot token. However, if you don't need any of those things, you never have to add a bot user to your application at all. - -Welcome to the new world. - -## Receiving an Interaction - -When a user interacts with your app, your app will receive an **[Interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object)**. Your app can receive an interaction in one of two ways: - -- Via [Interaction Create](#DOCS_TOPICS_GATEWAY_EVENTS/interaction-create) gateway event -- Via outgoing webhook - -These two methods are **mutually exclusive**; you can _only_ receive Interactions one of the two ways. The `INTERACTION_CREATE` [Gateway Event](#DOCS_TOPICS_GATEWAY_EVENTS/interaction-create) may be handled by connected clients, while the webhook method detailed below does not require a connected client. - -In your application in the Developer Portal, there is a field on the main page called "Interactions Endpoint URL". If you want to receive Interactions via outgoing webhook, you can set your URL in this field. In order for the URL to be valid, you must be prepared for two things ahead of time: - -> info -> These steps are only necessary for webhook-based Interactions. It is not required for receiving them over the gateway. - -1. Your endpoint must be prepared to ACK a `PING` message -2. Your endpoint must be set up to properly handle signature headers--more on that in [Security and Authorization](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/security-and-authorization) - -If either of these are not complete, we will not validate your URL and it will fail to save. - -When you attempt to save a URL, we will send a `POST` request to that URL with a `PING` payload. The `PING` payload has a `type: 1`. So, to properly ACK the payload, return a `200` response with a payload of `type: 1`: - -```py -@app.route('/', methods=['POST']) -def my_command(): - if request.json["type"] == 1: - return jsonify({ - "type": 1 - }) -``` - -You'll also need to properly set up [Security and Authorization](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/security-and-authorization) on your endpoint for the URL to be accepted. Once both of those are complete and your URL has been saved, you can start receiving Interactions via webhook! At this point, your app will **no longer receive Interactions over the gateway**. If you want to receive them over the gateway again, simply delete your URL. - -An [Interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object) includes metadata to aid your application in handling it as well as `data` specific to the interaction type. You can find samples for each interaction type on their respective pages: - -- [Slash Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/slash-commands-example-interaction) -- [User Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/user-commands-example-interaction) -- [Message Commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/message-commands-example-interaction) -- [Message Components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/component-interaction-object-sample-component-interaction) -- [Select Menu Message Components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/select-menu-object-select-menu-interaction) - -An explanation of all the fields can be found in our [data models](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object). - -Now that you've gotten the data from the user, it's time to respond to them. - -## Responding to an Interaction - -Interactions--both receiving and responding--are webhooks under the hood. So responding to an Interaction is just like sending a webhook request! - -There are a number of ways you can respond to an interaction: - -### Interaction Response Object - -###### Interaction Response Structure - -| Field | Type | Description | -| ----- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -| type | [interaction callback type](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-interaction-callback-type) | the type of response | -| data? | [interaction callback data](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object-interaction-callback-data-structure) | an optional response message | - -###### Interaction Callback Type - -| Name | Value | Description | -| --------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------- | -| PONG | 1 | ACK a `Ping` | -| CHANNEL_MESSAGE_WITH_SOURCE | 4 | respond to an interaction with a message | -| DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE | 5 | ACK an interaction and edit a response later, the user sees a loading state | -| DEFERRED_UPDATE_MESSAGE\* | 6 | for components, ACK an interaction and edit the original message later; the user does not see a loading state | -| UPDATE_MESSAGE\* | 7 | for components, edit the message the component was attached to | -| APPLICATION_COMMAND_AUTOCOMPLETE_RESULT | 8 | respond to an autocomplete interaction with suggested choices | -| MODAL\*\* | 9 | respond to an interaction with a popup modal | - -\* Only valid for [component-based](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/) interactions - -\*\* Not available for MODAL_SUBMIT and PING interactions. - -###### Interaction Callback Data Structure - -###### Messages - -Not all message fields are currently supported. - - -| Field | Type | Description | -| ----------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| tts? | boolean | is the response TTS | -| content? | string | message content | -| embeds? | array of [embeds](#DOCS_RESOURCES_CHANNEL/embed-object) | supports up to 10 embeds | -| allowed_mentions? | [allowed mentions](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) | [allowed mentions](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) object | -| flags? | integer | [message flags](#DOCS_RESOURCES_CHANNEL/message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS` and `EPHEMERAL` can be set) | -| components? | array of [components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/) | message components | -| attachments? \* | array of partial [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) objects | attachment objects with filename and description | - -\* See [Uploading Files](#DOCS_REFERENCE/uploading-files) for details. - -###### Autocomplete - -| Field | Type | Description | -| ------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| choices | array of [choices](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object-application-command-option-choice-structure) | autocomplete choices (max of 25 choices) | - -###### Modal - -> warn -> Support for components in modals is currently limited to type 4 (Text Input). - -| Field | Type | Description | -| ---------- | ------------------------------------------------------------- | -------------------------------------------------------------------- | -| custom_id | string | a developer-defined identifier for the component, max 100 characters | -| title | string | the title of the popup modal, max 45 characters | -| components | array of [components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/) | between 1 and 5 (inclusive) components that make up the modal | - -> warn -> While interaction responses and followups are webhooks, they respect @everyone's ability to ping @everyone / @here . Nonetheless if your application responds with user data, you should still use [`allowed_mentions`](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) to filter which mentions in the content actually ping. Other differences include the ability to send named links in the message content (`[text](url)`). - -When responding to an interaction received **via webhook**, your server can simply respond to the received `POST` request. You'll want to respond with a `200` status code (if everything went well), as well as specifying a `type` and `data`, which is an [Interaction Response](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object) object: - -```py -@app.route('/', methods=['POST']) -def my_command(): - if request.json["type"] == 1: - return jsonify({ - "type": 1 - }) - - else: - return jsonify({ - "type": 4, - "data": { - "tts": False, - "content": "Congrats on sending your command!", - "embeds": [], - "allowed_mentions": { "parse": [] } - } - }) -``` - -If you are receiving Interactions over the gateway, you will **also need to respond via HTTP**. Responses to Interactions **are not sent as commands over the gateway**. - -To respond to a gateway Interaction, make a `POST` request like this. `interaction_id` is the unique id of that individual Interaction from the received payload. `interaction_token` is the unique token for that interaction from the received payload. **This endpoint is only valid for Interactions received over the gateway. Otherwise, respond to the `POST` request to issue an initial response.** - -```py -import requests - -url = "https://discord.com/api/v10/interactions///callback" - -json = { - "type": 4, - "data": { - "content": "Congrats on sending your command!" - } -} -r = requests.post(url, json=json) -``` - -> info -> Interaction `tokens` are valid for **15 minutes** and can be used to send followup messages but you **must send an initial response within 3 seconds of receiving the event**. If the 3 second deadline is exceeded, the token will be invalidated. - -## Followup Messages - -Sometimes, your bot will want to send followup messages to a user after responding to an interaction. Or, you may want to edit your original response. Whether you receive Interactions over the gateway or by outgoing webhook, you can use the following endpoints to edit your initial response or send followup messages: - -- [`PATCH /webhooks///messages/@original`](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/edit-original-interaction-response) to edit your initial response to an Interaction -- [`DELETE /webhooks///messages/@original`](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/delete-original-interaction-response) to delete your initial response to an Interaction -- [`POST /webhooks//`](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/create-followup-message) to send a new followup message -- [`PATCH /webhooks///messages/`](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/edit-followup-message) to edit a message sent with that `token` - -> info -> Interactions webhooks share the same rate limit properties as normal webhooks. - -Interaction tokens are valid for **15 minutes**, meaning you can respond to an interaction within that amount of time. - -## Security and Authorization - -> info -> Check out our [Community Resources](#DOCS_TOPICS_COMMUNITY_RESOURCES/interactions) for libraries to help you with security in your language of choice. - -The internet is a scary place, especially for people hosting open, unauthenticated endpoints. If you are receiving Interactions via outgoing webhook, there are some security steps you **must** take before your app is eligible to receive requests. - -Every Interaction is sent with the following headers: - -- `X-Signature-Ed25519` as a signature -- `X-Signature-Timestamp` as a timestamp - -Using your favorite security library, you **must validate the request each time you receive an [interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object)**. If the signature fails validation, respond with a `401` error code. Here's a couple code examples: - -```js -const nacl = require('tweetnacl'); - -// Your public key can be found on your application in the Developer Portal -const PUBLIC_KEY = 'APPLICATION_PUBLIC_KEY'; - -const signature = req.get('X-Signature-Ed25519'); -const timestamp = req.get('X-Signature-Timestamp'); -const body = req.rawBody; // rawBody is expected to be a string, not raw bytes - -const isVerified = nacl.sign.detached.verify( - Buffer.from(timestamp + body), - Buffer.from(signature, 'hex'), - Buffer.from(PUBLIC_KEY, 'hex') -); - -if (!isVerified) { - return res.status(401).end('invalid request signature'); -} -``` - -```py -from nacl.signing import VerifyKey -from nacl.exceptions import BadSignatureError - -# Your public key can be found on your application in the Developer Portal -PUBLIC_KEY = 'APPLICATION_PUBLIC_KEY' - -verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY)) - -signature = request.headers["X-Signature-Ed25519"] -timestamp = request.headers["X-Signature-Timestamp"] -body = request.data.decode("utf-8") - -try: - verify_key.verify(f'{timestamp}{body}'.encode(), bytes.fromhex(signature)) -except BadSignatureError: - abort(401, 'invalid request signature') -``` - -If you are not properly validating this signature header, we will not allow you to save your interactions URL in the Dev Portal. We will also do automated, routine security checks against your endpoint, including purposefully sending you invalid signatures. If you fail the validation, we will remove your interactions URL in the future and alert you via email and System DM. - -We highly recommend checking out our [Community Resources](#DOCS_TOPICS_COMMUNITY_RESOURCES/interactions) and the libraries found there. They not only provide typing for Interactions data models, but also include decorators for API frameworks like Flask and Express to make validation easy. - -### Endpoints - -> info -> The endpoints below are not bound to the application's [Global Rate Limit](#DOCS_TOPICS_RATE_LIMITS/global-rate-limit). - -## Create Interaction Response % POST /interactions/{interaction.id#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object}/{interaction.token#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object}/callback - -Create a response to an Interaction from the gateway. Body is an [interaction response](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-response-object). Returns `204 No Content`. - -This endpoint also supports file attachments similar to the webhook endpoints. Refer to [Uploading Files](#DOCS_REFERENCE/uploading-files) for details on uploading files and `multipart/form-data` requests. - -## Get Original Interaction Response % GET /webhooks/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/{interaction.token#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object}/messages/@original - -Returns the initial Interaction response. Functions the same as [Get Webhook Message](#DOCS_RESOURCES_WEBHOOK/get-webhook-message). - -## Edit Original Interaction Response % PATCH /webhooks/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/{interaction.token#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object}/messages/@original - -Edits the initial Interaction response. Functions the same as [Edit Webhook Message](#DOCS_RESOURCES_WEBHOOK/edit-webhook-message). - -## Delete Original Interaction Response % DELETE /webhooks/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/{interaction.token#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object}/messages/@original - -Deletes the initial Interaction response. Returns `204 No Content` on success. Does not support ephemeral followups. - -## Create Followup Message % POST /webhooks/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/{interaction.token#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object} - -Create a followup message for an Interaction. Functions the same as [Execute Webhook](#DOCS_RESOURCES_WEBHOOK/execute-webhook), but `wait` is always true. The `thread_id`, `avatar_url`, and `username` parameters are not supported when using this endpoint for interaction followups. - -`flags` can be set to `64` to mark the message as ephemeral, except when it is the first followup message to a deferred Interactions Response. In that case, the `flags` field will be ignored, and the ephemerality of the message will be determined by the `flags` value in your original ACK. - -## Get Followup Message % GET /webhooks/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/{interaction.token#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Returns a followup message for an Interaction. Functions the same as [Get Webhook Message](#DOCS_RESOURCES_WEBHOOK/get-webhook-message). - -## Edit Followup Message % PATCH /webhooks/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/{interaction.token#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Edits a followup message for an Interaction. Functions the same as [Edit Webhook Message](#DOCS_RESOURCES_WEBHOOK/edit-webhook-message). - -## Delete Followup Message % DELETE /webhooks/{application.id#DOCS_RESOURCES_APPLICATION/application-object}/{interaction.token#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Deletes a followup message for an Interaction. Returns `204 No Content` on success. Does not support ephemeral followups. diff --git a/docs/policies_and_agreements/Developer_Policy.md b/docs/policies_and_agreements/Developer_Policy.md deleted file mode 100644 index db47446bbe..0000000000 --- a/docs/policies_and_agreements/Developer_Policy.md +++ /dev/null @@ -1,161 +0,0 @@ -# Discord Developer Policy - -> info -> We are updating our Developer Policy and Developer Terms of Service, effective October 1, 2022. Please review the updated terms here and policy below (for translated versions of the Discord Developer Policy in your selected language, as available, please [see here](https://support-dev.discord.com/hc/articles/8563934450327). If you access or use the APIs on or after October 1, 2022, it means you agree to these updated terms and policy. Until then, the current terms and [policy](#DOCS_POLICIES_AND_AGREEMENTS_DEVELOPER_POLICY/previous-discord-developer-policy) will apply. [Learn more here](https://support-dev.discord.com/hc/articles/8541214983959). - -## Effective date: October 1, 2022 - -## Last updated: September 1, 2022 - -*For a link to the previous policy, please see **[here](https://github.com/discord/discord-api-docs/blob/62c9a95b56d2f989d3eefe39a058d69189f6b4a6/docs/policies_and_agreements/Developer_Policy.md)**. For a translated version in your selected language, as available, please see **[here](https://support-dev.discord.com/hc/articles/8563934450327)**.* - -Discord is a place of belonging where developers come to build cool tools to further the way that people connect. As Discord continues to grow and support our developers, it’s important to be transparent about the responsibilities and expectations of our developers and their Applications. This document sets forth the policies developers will need to follow to develop and operate their Applications. - -## Introduction - -This Discord Developer Policy is incorporated into the [Discord Developer Terms of Service](#DOCS_POLICIES_AND_AGREEMENTS_TERMS_OF_SERVICE) (“Developer Terms”) and you agree that it applies to your access to and use of our APIs in addition to the Developer Terms and other Terms. Capitalized terms not otherwise defined herein (including API Data and Application) have the meaning assigned to them in the Developer Terms. For clarity, the term “including” as used herein means “including without limitation.” - -Please check back here regularly as we may update these policies from time to time and your continued access to or use of the APIs after such updates go into effect means you accept and agree to them. Additional terms and policies may also apply to your access to or use of certain APIs (including as described in or available via our [Developer Portal](#DOCS_INTRO) or [Help Center](https://support-dev.discord.com/hc/categories/360000656491)). - -## Protect Discord users - -- **Do not modify a Discord user’s account without explicit permission from the Discord user**. Functionality that intends to make any changes to a Discord account, for example adding the account to a server, must clearly and properly inform the Discord account owner of the changes and receive explicit permission to enact the changes. -- **Do not collect, solicit, or deceive users into providing user login credentials**. Under no circumstances may you or your Application solicit, obtain, or request login credentials from Discord users in any way. This includes information such as passwords or user access or login tokens. -- **Do not target users with advertisements or marketing**. Messaging to Discord users from any Application or developer team should be relevant to the function of the Application and may not contain material unrelated to an Application’s function or information. - -## Handle data with care - -- **Do not use API Data for any purpose outside of what is necessary to provide your stated functionality**. You may not request, access, or use any API Data for any purpose other than as necessary to provide your Application’s stated (and approved through App Review, as applicable) functionality; provided that you may also use API Data for the purpose of improving your Application only if it has been aggregated or de-identified such that it cannot be associated with, or used to identify, any individual. -- **You may not mine or scrape any data, content, or information available on or through Discord services (as defined in our [Terms of Service](https://discord.com/terms)).** -- **Do not use API Data to:** profile Discord users, their identities, or relationships with other users; to discriminate against anyone based on personal or protected classifications as defined in our [Community Guidelines](https://dis.gd/guidelines); or for eligibility considerations for benefits or for purposes such as employment, housing, insurance, or otherwise. - - Furthermore, you may not, and may not use your Application, to obtain API Data or transmit data to Discord (i) of persons under the age of 13 or the relevant age of digital consent in their jurisdiction (if older) or (ii) that includes protected health information, financial information, or other sensitive information under applicable law, except to the extent specifically allowed by our terms for a given Discord service or necessary to process a financial transaction as specifically enabled by a Discord service; -- **Do not disclose API data to any advertising network, data broker, or other advertising or monetization related service.** -- **Do not sell, license, or otherwise commercialize any API Data or Discord services (as defined in our [Terms of Service](https://discord.com/terms), e.g., Nitro subscriptions).** -- **Do not direct your Application to people who are under the age of 13 or the minimum age of digital consent in their applicable countries**. Our services are only for people who are at least 13 years old and meet the minimum age of digital consent in their applicable countries. -- **Do not attempt to re-identify, de-anonymize, unscramble, unencrypt, or reverse hash or reverse engineer API Data from the form in which you obtain it.** -- **Do not use API Data in any way that goes against Discord users’ expectations**. This includes frequently sending unsolicited direct messages, sending direct messages not directly related to maintaining or improving an Application's core functionality, or making unsolicited changes to user data or to a user’s access to Discord services. - -For the avoidance of doubt, the above policies apply in addition to the terms relating to API Data described in the Developer Terms (including Section 5 (User Privacy and Security)), and use of data includes how you access, collect, store, retain, transmit, share, and otherwise process it. - -## Apps Should Provide a Positive, Quality Experience - -- **Do not use the API for any dangerous or illegal activity.** This includes, but is not limited to, activities that facilitate or promote: - - Death, bodily harm, and/or personal injury; - - Environmental damage (such as the operation of nuclear facilities, air traffic control); or - - Unlawful online gambling. -- **Do not use the APIs in any way to violate, or to enable or promote others to violate, the [Discord Community Guidelines](https://dis.gd/guidelines)**. As described in the Developer Terms, you are responsible for ensuring that your Application is not used to violate any of the Terms. We take into consideration the abundance, explicit negligence, and intentional enabling of violating behavior when assessing appropriate enforcement. Prohibited behaviors and activities include those that: - - Defraud users; - - Deceive users via impersonation (outside of clearly labeled satire, parody, or fan accounts), including impersonating other Applications, Discord employees or partners, or Discord services. This includes deception via your and your Application’s account and identity; - - Distribute adult content to users under the age of 18, and without age-restricted labels where applicable and appropriate to users 18 and older; - - Unless your Application is labeled as age-restricted, you will make sure your Application is appropriate for users under the age of eighteen (18) and complies with all applicable laws (including those applicable to users under the age of eighteen (18)). - - Or enable, promote, or organize any on or off-platform harassment. -- **Do not promote offensive, vulgar, violent, or violent extremist content, messages, or images**. This includes the fields in an Application’s Settings page in the Discord Developer Portal. -- **Do not attempt to manipulate engagement**. You may not participate, enable, or promote the inflating of server membership with bot or user accounts or mass creation of user accounts to redeem rewards or send messages. You may not automate messages to be sent for the purpose of maintaining message activity in a Discord community. -- **Do not contact users outside of Discord without their explicit consent**. This means you should not contact Discord users outside of the Discord platform by using any API Data (including any data obtained, disclosed, or inferred through a user’s use of your Application). -- **Do not enable your developer team, Application, or other users to bypass or circumvent Discord’s privacy, safety, and security measures**. Your Application must respect user-initiated blocks, bans, kicks, mutes, visibility settings (such as users opting out of displaying connections or permission settings), and must not enable any form of masking account identities (such as obscuring Discord usernames). - -## Respect the Platform - -- **Obtain consent before initiating processes on a user or server’s behalf**. Your Application must respect user decisions to opt out of or block the Application, as applicable. On the server level, your Application must respect server members’ (with the appropriate permissions) ability to remove the Application. The manner of providing the option for consent/authentication must be clearly labeled and apparent, and contain an accurate description of the purpose or feature being enabled. For example, if your Application is providing a service in response to a user initiated request or command, this is an acceptable instance of consent here. -- **Use commercially reasonable efforts to promptly make yourself or your team available for feedback or user support regarding your Application.** -- **Notify Discord and affected users of potential unauthorized access to API Data, as described in Section 5 of the Developer Terms.** -- **Do not remove, obscure, or alter Discord’s links to any of the Terms.** - -For the avoidance of doubt, the above policies apply in addition to the terms relating to use of the APIs in the Developer Terms (including Section 2). - -## API Limits - -As described in the Developer Terms, Discord may set and enforce limits on your use of the APIs (for example, by limiting the number of API requests that you may make, the number of servers your Application is in, or the number of users you may serve) at our sole discretion. You agree to, and will not attempt to circumvent, such limitations documented with each API. - -If you would like to use any API beyond these limits, you must obtain Discord’s express written consent (and Discord may, at our discretion, decline such request or condition acceptance on your agreement to additional terms and/or charges for such use). - -As described in the Developer Terms (including Section 9), we may take enforcement actions for any Enforcement Reason, including if we believe you or your Application have violated this Developer Policy. If you come across an Application that you believe violates any of these policies, please [report it to us](https://support.discord.com/hc/en-us/requests/new?ticket_form_id=360005592534). - ---- - -# Previous Discord Developer Policy - -> warn -> The following is the previous Developer Policy, which will be in effect until October 1, 2022. - -## Effective until: October 1, 2022 - -## Last updated: July 1, 2020 - -This Discord Developer Policy is incorporated into the Discord Developer Terms of Service (“Terms”) and applies to all uses of our APIs. All capitalized terms not defined here have the meaning assigned to them in these Terms. Please check back from time to time as these policies are occasionally updated. - -**“Discord Data”** means any and all data you obtain through the APIs. - -## Protect Discord users. - -You may not use the APIs in any way to: - -- modify a Discord user’s account without explicit permission from the Discord user. For example, you may not add a Discord user to a Discord (also known as a “server”) unless that Discord user expressly approved joining that Discord (such as when using a "group finder" app); -- post messages, trigger notifications, or play audio on behalf of a Discord user except in response to such Discord user expressly opting-in to each instance of such action; -- obtain Discord passwords or credentials under any circumstance; or -- target users with advertisements or marketing. - -## Handle data with care. - -You may not use the APIs in any way to: - -- scrape any Discord Data; -- use Discord Data for any purpose other than as necessary to provide your application; -- disclose any user’s Discord Data without their specific, informed consent; -- disclose Discord Data to any ad network, data broker, or other advertising or monetization related service; -- retain data any longer than necessary for the operation of your application; -- contravene Discord’s Privacy Policy; -- obtain Discord User passwords to obtain access to Discord Data; -- sell, license or otherwise commercialize any Discord Data; -- provide or direct services to children under the age of thirteen (13) in the United States or, outside of the United States, the relevant age of digital consent; -- process Discord Data in a way that surprises or violates Discord users’ expectations. - -## Don’t Do Anything Illegal, Harmful, or Otherwise Not Cool. - -You may not use the APIs in any way: - -- to promote or facilitate unlawful online gambling; -- for any activities where the use or failure of the APIs could lead to death, personal injury, or environmental damage (such as the operation of nuclear facilities, air traffic control, or life support systems); -- that doesn’t comply with all applicable laws, regulations, and third party rights; -- to encourage or promote illegal activity or violation of third party rights; -- to use the APIs to process or store any data that is subject to the International Traffic in Arms Regulations maintained by the U.S. Department of State -- to distribute NSFW material without an NSFW tag; or -- to defame, abuse, harass, stalk, threaten others, or otherwise violate our [Community Guidelines](https://discord.com/guidelines). - -## Don’t abuse the platform. - -You will: - -- require your end users to comply with (and not knowingly enable them to violate) applicable law, regulation, and the Discord Terms of Service; and -- only access (or attempt to access) an API by the means described in the documentation of that API. - -You will not: - -- remove, obscure, or alter Discord’s [Terms of Service](https://discord.com/terms) or any links to or notices of those terms; -- encourage or create functionality for your users that violates Discord’s [Terms of Service](https://discord.com/terms); -- sublicense an API for use by a third party; -- create an application that functions substantially the same as the APIs and offer it for use by third parties; -- perform an action with the intent of introducing to Discord products and services any viruses, worms, defects, Trojan horses, malware, or any items of a destructive nature; -- reverse engineer or attempt to extract the source code from any API or any related software, except to the extent that this restriction is expressly prohibited by applicable law; -- misrepresent or mask either your identity or your application’s identity when using the APIs or developer accounts; -- permit or allow a third party to violate any of this Discord Developer Policy; or -- interfere with or disrupt the APIs or the servers or networks providing the APIs. - -## API Limits. - -Discord sets and enforces limits on your use of the APIs (for example, by limiting the number of API requests that you may make, the number of servers your application is in, or the number of users you may serve), in our sole discretion. You agree to, and will not attempt to circumvent, such limitations documented with each API. - -If you would like to use any API beyond these limits, you must obtain Discord’s express written consent (and Discord may decline such request or condition acceptance on your agreement to additional terms and/or charges for that use). -We support increases to the following rate limits if your app meets the criteria set forth below. If you are seeking approval for any of the following rate limit increases, -please follow the outlined steps: - -- 100 server limit for your bot - - Please see your bot’s settings and get verified and approved for Privileged Intents as needed. [Read more here](https://support.discord.com/hc/en-us/articles/360040720412) -- Large Bot Sharding - - If you are near or in over 150,000 guilds, please reach out to support at [https://dis.gd/contact](https://dis.gd/contact) about Large Bot Sharding. [Read more here](https://discord.com/developers/docs/topics/gateway#sharding-for-very-large-bots) -- Higher global rate limit - - If you are near or in over 150,000 guilds, please reach out to support at [https://dis.gd/contact](https://dis.gd/contact) about a higher global rate limit - -## HIPAA Rules. - -Discord does not intend use of the APIs to create obligations under the Health Insurance Portability and Accountability Act, as amended ("HIPAA"), and makes no representations that the APIs satisfy HIPAA requirements. If you are (or become) a "covered entity" or "business associate" as defined in HIPAA, you will not use the APIs for any purpose or in any manner involving transmitting protected health information to Discord. diff --git a/docs/policies_and_agreements/Store_Distribution_Agreement.md b/docs/policies_and_agreements/Store_Distribution_Agreement.md deleted file mode 100644 index 2dfc3df141..0000000000 --- a/docs/policies_and_agreements/Store_Distribution_Agreement.md +++ /dev/null @@ -1,179 +0,0 @@ -# Discord Store Distribution Agreement for Developers (Self-Service) - -This Discord Store Distribution Agreement for Developers (“Agreement”) is by and between Discord, Inc., with a principal office located at 444 De Haro Street, San Francisco, California 94107 (“Discord”), and the person or entity identified in the “Legal Name” field in connection with the process of agreeing to this Agreement (“Developer”). Such process is referred to in this Agreement as the “Agreement Process”. The effective date of this Agreement is date on which Discord provides Developer with notice of its acceptance (“Effective Date”). - -BY ELECTRONICALLY SIGNING THIS AGREEMENT OR CLICKING THE “I AGREE” OR SIMILAR BUTTON AS PART OF THE AGREEMENT PROCESS, DEVELOPER IS AGREEING TO BE BOUND BY THIS AGREEMENT. - -### 1\. Definitions - -1.1 “**Affiliate**” means, with respect to an entity, a person or entity that controls, is controlled by, or is under common control with such entity. - -1.2 “**App(s)**” means the Developer games or other applications specified in connection with the Agreement Process and provided by Developer to Discord for distribution via the Discord Store, including, upon delivery to Discord, any App Updates, Error corrections for, and any Localized Versions of such Apps. Apps include only the versions of such games or applications that operate on Windows, Macintosh, or Linux operating systems, or other operating systems supported by Discord Store in the future. - -1.3 “**App-Related Content**” means any online content, features or software specific to an App that is made available by Developer for purchase, download or online access separately from the App, whether through in- application purchase transactions or otherwise (for example, but without limiting the foregoing, App-themed virtual items, expansion packs, additional filters, codecs, stock multimedia, game scenarios or levels, additional functionality, etc.). App-Related Content also includes any services provided with respect to an App in exchange for a subscription payment. - -1.4 “**App Term**” means the time period during which an App may be made generally available in the entire Territory for use by and/or distribution to Discord Store Account Owners on the Discord Store. - -1.5 “**App Updates**” means any updates, corrections, and enhancements provided by Developer for use by any end user of the Apps and shall include any such updates, corrections, and enhancements made available to third parties or end users. - -1.6 “**Demo Version**” means any demonstration versions of the Apps, if any. - -1.7 “**Developer Marks**” means the trademarks, including logos, used by Developer for the Apps. - -1.8 “**Discord Marks**” means Discord’s trademarks and logos that Discord provides to Developer for use in accordance with this Agreement. - -1.9 “**Discord SDK**” means Discord’s rich presence or other software development kits made available by Discord to Developer for use in accordance with this Agreement. - -1.10 “**Discord Software**” means the Discord SDK, the Discord Store SDK, and any other Discord software made available by Discord to Developer (for example, the API as defined in the Developer TOS) for use in accordance with this Agreement. - -1.11 “**Discord Store**” means Discord’s online app distribution platform. For the purposes of this Agreement, “Discord Store” does not include any content from Developer, Discord, or other third parties. - -1.12 “**Discord Store Account Owner**” means an end user of one or more of the Apps who has obtained a Discord Store account, agreed to Discord’s Discord Store Subscriber Agreement, and licensed a version of the Apps via the Discord Store. - -1.13 “**Discord Store SDK**” means the software development toolkit for the Discord Store that is delivered to or made available to Developer for its use in accordance with this Agreement. - -1.14 “**General Availability Date**” or “GA Date” means, for each App, the date such App is first made generally available in the entire Territory for use by and/or distribution to Discord Store Account Owners on the Discord Store. For clarity, the GA Date shall not be the first date of any beta testing or other limited availability. - -1.15 “**Gross Revenue**” means revenue actually received and booked by Discord from its users in accordance with its standard accounting processes from Discord’s distribution of the Apps via the Discord Store pursuant to this Agreement. - -1.16 “**Localized Version**” means any versions of the Apps created for specific languages or jurisdictions. - -1.17 “**Net Revenue**” means Gross Revenue less (a) actual amounts attributed to returns, user fraud, charge-backs and refunds; (b) amounts collected for VAT (defined below); and (c) any amounts paid to third-party payment processors and money-transmitters. - -1.18 “**Primary Platform**” means the operating system on which the majority of usage occurred during the seven (7) day period following the date of purchase (or free download) of an App, provided that if no usage occurred during that period, the Primary Platform shall be the operating system on which the Discord Store Account Owner purchased (or downloaded for free) the App, - -1.19 “**Ratings Information**” means ratings information about the Apps obtained from ratings boards or government authorities (including, but not limited to, the ESRB). - -1.20 “**Sales Data**” means non-personally identifiable sales data for Discord Store Account Owners that is retained by Discord and typically analyzed by Discord for its own computer games, including but not limited to sales rates by country. - -1.21 “**Territory**” means (a) worldwide or (b) the jurisdictions authorized for distribution by Developer on Discord Store, if Developer has restricted an App’s distribution territory through the online tools provided by Discord for this purpose. - -1.22 “**VAT**” means any value added tax, goods and services tax or any other similar tax, including any sales tax, service tax, gross receipt tax, or use tax imposed by any governmental authority in any country at any level. - -### 2\. Rights and Licenses - -2.1 **Apps License.** Developer hereby grants to Discord a license: (a) to copy, perform, display, modify, create derivative works of, and use the Apps for general access and as necessary to (i) enable the use and distribution of the Apps (including Demo Versions, Localized Versions and App Updates) via Discord Store, and (ii) support Discord Store Account Owners as described in this Agreement; and (b) to copy, perform, display, modify, create derivative works of, and use, transmit, sell, license and otherwise distribute the Apps via Discord Store to Discord Store Account Owners in the Territory. The foregoing license (the “Apps License”) shall be non-exclusive and worldwide. The only fees payable for the Apps License shall be as set forth in Section 6.1 of this Agreement. Any Territory restrictions shall apply to the sale or initial distribution of the App. Developer acknowledges and agrees that Discord Store Account Owners may receive subsequent distributions of Apps from locations outside the Territory. - -2.2 **Appointment as Agent and Commissionaire.** Developer hereby appoints Discord as (i) its agent for the marketing and delivery of the Apps to Discord Store Account Owners located in the United States and (ii) its commissionaire (Agent with Developer as undisclosed Principal) for the marketing and delivery of Apps to Discord Store Account Owners located in the rest of the world. Developer hereby acknowledges that Discord will market and make the Apps available for download. In furtherance of Discord's appointment under this Section, Developer hereby authorizes and instructs Discord to: - -(a) be merchant of record at the Discord Store and issue invoices for the purchase price payable by Discord Store Account Owners for the Apps; and - -(b) otherwise use Apps, Information and associated metadata as may be reasonably necessary in the marketing and delivery of the Apps. - -The parties acknowledge and agree that their relationship is, and shall be, that of principal and agent (U.S.), or principal and commissionaire (non-U.S.), as the case may be, and that Developer, as principal, is, and shall be, solely responsible for any and all claims and liabilities involving or relating to, the Apps. For completeness' sake, (i) Discord Store Account Owners located in the United States will enter into a license agreement for Apps with Developer (effectuated by Discord in the name of and on behalf of Developer), and (ii) Discord Store Account Owners located in the rest of the world will enter into a license agreement for Apps with Discord (but for the risk and account of Developer). - -2.3 **Marketing License.** Developer hereby grants to Discord a royalty-free license, during the Term, to copy, perform, display, create derivative works of, use, and distribute (a) the Developer Marks and (b) materials from the Apps (for example, screenshots), marketing material (for example, videos), and ratings information in connection with the promotion, marketing, licensing, sale, and distribution of the Apps and the exercise of Discord’s other rights under this Agreement. The foregoing license shall be non-exclusive and worldwide. - -2.4 **Discord Store and Discord Software License.** Discord hereby grants to Developer a non-exclusive, royalty-free, license, during the Term, to use the Discord Store solely as necessary to fulfill Developer’s obligations under this Agreement. Developer’s use of the Discord Software shall be subject to the Discord Developer Terms of Service currently located at https://discord.com/developers/docs/legal (the “Developer TOS”). - -2.5 **Discord Trademark License.** Subject to Discord’s prior written approval in each case, Discord hereby grants to Developer a non-exclusive, royalty-free, license, during the Term, to publicly display the Discord Marks solely in marketing materials for the Apps. All such use of the Discord Marks must be in accordance with, Discord’s trademark and other brand guidelines made available to Developer. - -2.6 **Open Source Software.** Except as otherwise expressly agreed in writing, Developer shall not distribute via Discord Store, or combine any Discord materials with, open source or other software that is licensed under terms that purport to bind Discord to contractual obligations (e.g., the GNU General Public License or Lesser General Public License). - -2.7 **License Upon Termination.** In addition, upon termination of this Agreement, Developer hereby grants to Discord a non-exclusive, worldwide, perpetual, irrevocable, fully-paid-up license to use, reproduce, transmit and distribute the Apps and any error corrections in object code form via electronic delivery solely to Discord Store Account Owners that have licensed a Discord distributed version of the Apps prior to the date of any termination of this Agreement, and Developer will continue to give Discord Store Account Owners access to online features of the App on an equal, non-discriminatory basis with other users, for so long as Developer supports such online features. - -2.8 **Reservation of Rights.** Except as expressly provided herein, Developer retains all right, title and interest in and to the Apps and the Developer Marks, and Discord retains all right, title and interest in and to Discord Store, the Discord Software, and the Discord Marks. Each party reserves all rights not expressly granted in this Agreement. - -### 3\. App Submissions - -3.1 **Submission.** Developer shall submit each App (or Localized Version) to Discord for distribution via the Discord Store concurrently with the first commercial release of each App (or Localized Version), or, if already commercially released as of the Effective Date, within thirty (30) days of the Effective Date. Developer shall also submit to Discord any Localized Versions when generally available, but in no event later than they are provided to any other third party for commercial release. All submissions must be in object code form, and in the format reasonably requested by Discord. Developer will ensure that any password needed to access its Discord Store account is treated as Confidential Information, and agrees that it will be responsible for any use of that password, whether by Developer or any third party. - -3.2 **Testing.** Before submitting any software to Discord, Developer shall perform quality assurance and other error and bug testing of the Apps (including any Localized Versions and all App Updates), in a professional manner consistent with industry standards. Discord may also conduct hardware and software compatibility and performance testing on the Apps, either itself or via a third party. - -3.3 **Compatibility.** Developer shall make the Apps compatible with Discord Store services that Developer may choose to use. Developer shall use reasonable efforts to maintain compatibility of the Apps with future versions of Discord Store. - -3.4 **App-Related Content.** If Developer distributes an App and App-Related Content for that App via any channel other than Discord Store, Developer will deliver the App-Related Content to Discord simultaneously to enable Discord to make such App-Related Content available to Discord Store Account Owners. In addition, to the extent any App supports the sale of digital items or digital currency for use in the App, Developer shall not allow or facilitate the redemption or exchange of such digital items or digital currency for real-world currency. - -3.5 **No Links to Other Stores.** Developer will ensure that Apps distributed via Discord Store will not include functionality from or links or references to any store other than Discord Store, or any other payment or purchase facility. - -3.6 **User Reviews.** Discord may, in its discretion, make available a user review system for Apps. Developer shall not manipulate the user review system for Apps, including but not limited to soliciting positive reviews in exchange for value. - -3.7 **Ratings Information.** Discord will facilitate Developer’s inclusion of Ratings Information for Apps in the Discord Store. Developer agrees to provide via the Discord Store current and accurate Ratings Information for each App. - -3.8 **Privacy and App Sales Data.** Subject to Discord’s privacy policy, the current version of which is located at discord.com/privacy, (the “Discord Privacy Policy”) the Discord Store Subscriber Agreement, applicable laws and any other obligations Discord has to a third party or otherwise, Discord will provide Developer with Sales Data for the Apps. The Discord Privacy Policy is hereby incorporated into this Agreement. - -### 4\. Marketing and Publicity - -4.1 **Marketing.** Discord may, at its own expense and sole discretion, market and promote the Apps via Discord Store and other channels, including but not limited to the Discord and Discord Store web sites and shall include the right to distribute promotional copies of the App(s). - -4.2 **Press Releases and Press Accounts.** Discord and Developer may reference in public statements the fact that the Apps are or will be available on Discord Store; however, any press release about the other Party shall be subject to the prior approval of the other Party. Discord may make the Apps available to journalists via press subscription accounts free-of- charge in order to enable such journalists to evaluate and review the Apps. - -4.3 **Demo Versions.** If Developer releases a Demo Version outside of the Discord Store that includes a reference regarding the method(s) to purchase the App, Developer shall cooperate with Discord to include a reference to Discord Store in that version. - -### 5\. Support - -5.1 **Developer Support to Discord.** Developer shall with respect to each App: (a) correct within a reasonable period of time all material bugs, errors, or defects in the Apps (collectively, “Errors”) of which Discord informs Developer; (b) deliver all App Updates when made available to any other third party; (c) provide such other reasonable additional support as Discord may reasonably request. If, in Discord's sole judgment, an App contains Errors or is otherwise of insufficient quality to meet Discord’s standards, it may suspend the availability of such App in the Discord Store.  In the event Discord has suspended the availability of all Apps under this Agreement, it may terminate this Agreement for convenience. In addition, Developer must implement the Discord Store SDK in a manner that is safe and secure for the Discord Store Account Owner.  Discord may, but shall not be obligated to, correct any security vulnerabilities in Developer’s implementation of the Discord Store SDK and Developer shall provide assistance with such corrections as requested by Discord.  If Discord cannot reasonably correct any such vulnerabilities, or Discord determines that Developer should correct them, Developer shall correct such vulnerabilities. - -5.2 **Developer Support to End Users.** Developer will provide support to Discord Store Account Owners who have acquired a copy of the Apps, at the same level that Developer provides customer support for such applications made available directly or via other channels. Such support shall include but not be limited to responding to questions and assisting customers in the diagnosis and correction of issues encountered in using the Apps. - -5.3 **Discord End User Support.** Provided that Developer has complied with its support obligations in this Section 5, Discord will provide a reasonable level of support for issues related to Discord Store reported by Discord Store Account Owners. - -### 6\. Revenue, Reporting, and Payments - -6.1 **Licensee Fees or Other Fees.** Within thirty (30) days after the end of each calendar month, Discord will provide to Developer a report of Gross Revenue and Net Revenue for such month (each a “Monthly Report”). Each Monthly Report will contain activity for the current month as well as any Net Revenue adjustments for prior months reported by payment processors in the current month. Unless mutually agreed by Discord and Developer in a separate written agreement executed by both parties, Discord will pay to Developer ninety percent (90%) of the Net Revenue on the Monthly Report (the “License Fees”) within thirty (30) days after the Monthly Report is published, in no event later than sixty (60) days from the end of the of the calendar month covered by the Monthly Report. For completeness' sake, the remaining ten percent (10%) of the Net Revenue retained by Discord shall be payment of the consideration by Developer to Discord for the services and activities performed by Discord under this Agreement. Developer acknowledges and agrees that Discord, in the course of acting as agent or commissionaire of Developer, is hosting the Apps, and is allowing the download of those Apps by Discord Store Account Owners, on behalf of or respectively for the risk and account of Developer. All of the Apps and App-Related Content shall be marketed by Discord, on behalf of Developer, to Discord Store Account Owners at the prices established by Developer. Developer may change the price for any Apps or App-Related Content at any time, at Developer’s discretion, upon notice to Discord. As agent or commissionaire for Developer, Discord shall be solely responsible for the collection of all prices payable by Discord Store Account Owners for Apps acquired by those Discord Store Account Owners. - -6.2 **Taxes.** In the event that any remittance made by Discord to Developer is subject to any VAT, the full amount of such VAT shall be solely for the account of Developer, and will not reduce Discord's fee amount to which Discord is entitled on such transaction. To the extent required under applicable law, Discord will deduct the full amount of such VAT from the amount owed to Developer, and will pay the full amount withheld to the competent tax authorities. Discord will use commercially reasonably efforts to obtain, and to furnish to Developer, copies of official tax receipts or similar evidence of payment, confirming payment of such VAT to the competent tax authorities. Developer will indemnify and hold Discord harmless against any and all claims by any competent tax authorities for any underpayment of any VAT, and any penalties and/or interest thereon, including, but not limited to, underpayments attributable to any erroneous claim or representation by Developer as to its entitlement to, or any disqualification of Developer for, the benefit of a reduced rate of withholding tax.. - -6.3 **Minimum Monthly Payment and Negative Amounts.** In the event amounts due to Developer for a given month do not exceed one hundred U.S. dollars ($100), Discord may elect to not remit payment for that month, and may instead accumulate payments due until they exceed one hundred U.S. dollars ($100) as of the end of a future monthly payment cycle. Also, to the extent that the payment calculation in a Monthly Report results in a negative amount, that negative amount will be carried forward and deducted from any future amounts otherwise payable to Developer by Discord under this Agreement. - -6.4 **Multi-Platform Apps.** To the extent that Developer has submitted to Discord Store versions of an App for multiple operating systems (for example, a Mac version, and a Windows version) such versions shall be sold together as a single "hybrid" App unit on Discord Store. Accordingly, a Mac version of any App, a Windows version of an App, and a Linux version of an App shall not be considered separate sales when calculating the payments due to Developer hereunder. - -### 7\. Term - -7.1 **Term.** The term of this Agreement (“Term”) shall begin on the Effective Date and continue until the end of the latest App Term for any App unless earlier terminated in accordance with this Agreement. - -7.2 **App Term.** With respect to each App, the App Term shall commence on the GA Date for such App and continue for one (1) year (the “Initial App Term”); and shall automatically renew for successive one (1) year periods ("Renewal Term") until terminated by either party pursuant to Section 7.3 or Section 7.4 below. - -7.3 **Termination for Cause.** Either party may terminate this Agreement upon written notice at any time if the other party is in breach of this Agreement and has failed to cure that breach within thirty (30) days after written notice of that breach. In addition, either party may terminate this Agreement immediately upon written notice if the other party (a) becomes insolvent or makes an assignment for the benefit of creditors; (b) files a petition, or has a third party file a petition with respect to it, under any bankruptcy or similar statute; or (c) materially breaches this Agreement and such breach is not capable of cure. - -7.4 **Termination for Convenience.** Either party may terminate this Agreement for convenience (with or without cause), during any Renewal Term, by providing the other party with thirty (30) days prior written notice of such termination, provided that during such period the parties have reasonably discussed and agreed on a process for sunsetting the Apps (a “Sunset Process”). In the event that a Sunset Process has not been agreed upon within such 30-day period, or the Sunset Process requires that this Agreement remain fully in force for longer than such 30-day period, this Agreement will not terminate until the completion of the Sunset Process.. In addition, Discord may terminate this Agreement immediately at any time upon written notice to Developer in the event that Discord (a) determines in its discretion that any or all Apps are not suitable for distribution via the Discord Store or (b) ceases to operate the Discord Store or the self-service portion of the Discord Store. - -7.5 **Survival.** Sections 2.5, 2.6, 3.1 - 3.7, 5.2 (only with respect to supporting Discord Store Account Owners that have licensed an App prior to termination or expiration), 6 (only with respect to amounts that accrued prior to termination or expiration), 7.5, and 8 - 13, and any then-existing Discord Store Account Owner licenses to Apps, shall survive any termination or expiration of this Agreement. - -### 8\. Warranties and Disclaimer - -8.1 **Mutual Representations and Warranties.** Each party represents and warrants that (a) this Agreement has been duly and validly executed and delivered by such party; (b) it has all necessary power and authority to execute and perform its obligations under this Agreement; and (c) its execution and performance of this Agreement will not violate any applicable law or regulation or violate any rights of, or breach any obligation owed to, a third party. - -8.2 **Developer Representations and Warranties.** Developer represents and warrants that (a) it originally created the Apps (which, for purposes of this Section 8.2, include Demo Versions, Localized Versions and App Updates) and the Developer Marks, or otherwise has the rights necessary to grant the licenses and to fulfill its obligations under this Agreement; (b) the Apps will conform to any documentation for the Apps; (c) the Apps and the Developer Marks do not infringe any right of any third party, including but not limited to any intellectual property right or right of publicity or privacy; (d) the Apps do not violate any terms of the Discord Store Subscriber Agreement; (e) it is in compliance with and will comply with all applicable laws and regulations, and its privacy policy, in connection with this Agreement, including but not limited to its use of any end user data supplied by Discord to Developer under this Agreement; (f) it has all necessary rights to any content or information it submits to Discord through the Discord Store partner portal or using any Discord Store-provided partner or publishing tools; and (g) the Apps do not contain any software viruses, trojan horses, or any other harmful or malicious code. Developer further represents and warrants that neither it nor its agents are on any list maintained by the United States Treasury Department’s Office of Foreign Assets Control (“OFAC”) of persons, entities, or prohibited or restricted jurisdictions (“Restricted List”) and Developer shall promptly notify Discord in writing if it is no longer in compliance with, or is likely to become noncompliant with, the foregoing representation or warranty. In such event, or if Developer or any of its agents are on any OFAC Restricted List, Discord, in addition to any other remedies, shall have the right to terminate this Agreement immediately without providing Developer an opportunity to cure. - -8.3 **Disclaimer. EXCEPT AS OTHERWISE EXPRESSLY PROVIDED HEREIN, EACH PARTY DISCLAIMS ALL WARRANTIES, INCLUDING THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, TITLE, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE, AND ALL MATERIAL PROVIDED BY SUCH PARTY HEREUNDER IS PROVIDED “AS IS” AND WITHOUT WARRANTY OR REPRESENTATION.** - -### 9\. Indemnification - -Developer will indemnify, defend (upon Discord’s written request), and hold harmless Discord and its Affiliates, successors, officers, directors and employees from any loss, liability, expense or damage (including attorneys’ fees) (“Liabilities”) arising out of any third party action, cause of action, claim or demand (each, a “Claim”) alleging a breach by Developer of its representations and warranties under Section 8 of this Agreement. Discord will promptly notify Developer of a Claim, provided that any delay in providing such notice shall relieve Developer of its obligations only to the extent that such delay materially prejudiced Developer. If Discord requests Developer to defend a Claim, Discord shall give Developer sole control of the defense of the Claim, provided that Developer shall not settle or otherwise dispose of any Claim in any manner that imposes fault or other liability on Discord. Discord may, at its own expense, participate in any such defense with its own counsel. - -### 10\. Limitation of Liability - -EXCEPT FOR BREACH OF SECTION 12 (CONFIDENTIALITY), IN NO EVENT SHALL DISCORD BE LIABLE FOR (A) ANY SPECIAL, INCIDENTAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES OR (B) ANY DAMAGES IN EXCESS OF (IN THE AGGREGATE) THE AMOUNTS PAID OR PAYABLE UNDER THIS AGREEMENT ARISING OUT OF OR IN CONNECTION WITH THIS AGREEMENT OR ANY PROVISION HEREIN, REGARDLESS OF THE LEGAL THEORY UPON WHICH ANY CLAIM FOR SUCH DAMAGES IS BASED. THE FOREGOING EXCLUSIONS AND LIMITATIONS SHALL APPLY TO THE EXTENT PERMITTED BY APPLICABLE LAW. - -### 11\. Governing Law, Jurisdiction, and Venue - -This Agreement will be governed by and construed in accordance with the laws of the State of California, without reference to conflicts of laws principles. Any claim or dispute between the parties that arises in whole or in part from this Agreement or Developer’s use of the Discord Store shall be decided exclusively by a court of competent jurisdiction located in Santa Clara County, California. Developer hereby consents to, and waives all defenses with respect to, venue and jurisdiction in the state and federal courts of Santa Clara County, California. - -### 12\. Confidentiality - -12.1 "**Confidential Information**" means all non-public information that is identified as being confidential or which should reasonably be understood to be confidential, which includes without limitation, information relating to released or unreleased products, marketing or promotion of any product, financial information, business policies or practices, personnel, pricing and sales information, technology, computer programs, unpublished works of original authorship, trade secrets, or information received from third parties that the disclosing party is obligated to treat as confidential. Without limiting the foregoing, the terms of this Agreement and Sales Data shall be Discord’s Confidential Information. Confidential Information shall not include information that: (i) is or becomes generally known or available by publication, commercial use or otherwise through no fault of the receiving party; (ii) is known by the receiving party without restriction at the time of disclosure; (iii) is independently developed by the receiving party without the use of any Confidential Information received from the disclosing party; or (iv) is lawfully obtained without restriction from a third party that has the right to make such disclosure. - -12.2 The receiving party shall use Confidential Information it receives solely in connection with its performance under this Agreement, and shall disclose such Confidential Information only to employees and contractors who have a need to know such Confidential Information for purposes of this Agreement and are bound by written confidentiality obligations at least as restrictive as those herein. The receiving party shall protect Confidential Information from unauthorized disclosure and use with the same degree of care that it uses to protect its own Confidential Information, but in no event less than reasonable care. - -12.3 Notwithstanding the foregoing, the receiving party may disclose Confidential Information as necessary to comply with any court order or applicable law or regulation, provided that such party to the extent reasonably possible gives prior written notice to the other party of the possibility of such disclosure and to resist or limit disclosure, including without limitation reasonably cooperating with the other party in seeking a protective order or other similar relief. - -12.4 After termination of this Agreement or receiving earlier written request from the other party, the receiving party shall promptly return or delete the Confidential Information received from the disclosing party. - -### 13\. General Provisions - -13.1 **Independent Contractor.** The parties to this Agreement are independent contractors. This Agreement does not create any joint venture, partnership, or other business entity. - -13.2 **Notices.** Any notices required by this Agreement must be sent in writing via overnight delivery by a nationally recognized courier service (e.g., FedEx) or first-class mail, signature required and shall be deemed effective upon signature acknowledging receipt by the party receiving notice. All such notices must be addressed to the receiving party at the address stated at the top of this Agreement with attention to the individual executing this Agreement on behalf of such party. - -13.3 **Assignment.** Neither party may assign or otherwise transfer this Agreement or its rights hereunder without the other party’s prior written consent, except that such consent shall not be required for (a) a party to transfer this Agreement to an Affiliate or in connection with a merger, acquisition, or other corporate combination, or the sale of all or substantially all of such party’s assets, provided that the successor party is not a competitor of the non-transferring party and the transferring party provides prompt notice of such transfer; or (b) Discord to assign, or sublicense some or all of, its rights under this Agreement to any of its Affiliates or to a subcontractor. Such transfer or assignment shall not relieve the transferring or assigning party of its obligations hereunder. Any attempted transfer or assignment by either party in violation of this Section shall be void and of no effect and will constitute a breach by the party attempting such transfer or assignment. This Agreement will be binding upon any permitted successor. - -13.4 **Headings.** The section headings used herein are for convenience only and shall not be given any legal import. - -13.5 **Severability.** If any provision of this Agreement is found by a court of competent jurisdiction to be invalid, the parties nevertheless agree that the court should endeavor to give effect to the parties' intentions as reflected in the provision, and the other provisions of this Agreement shall remain in full force and effect. - -13.6 **No Waiver.** The failure of a party to exercise or enforce any right or provision of this Agreement shall not constitute a waiver of such right or provision, and no waiver by either party of any breach or default hereunder shall be deemed to be a waiver of any preceding or subsequent breach or default. - -13.7 **Modifications.** This Agreement may not be modified except by a written agreement dated signed on behalf of Discord and Developer by their respective duly authorized representatives. - -13.8 **Complete Agreement.** This Agreement constitutes the entire agreement between the parties with respect to the subject matter hereof and supersedes all prior and contemporaneous communications and agreements between the parties with respect to such subject matter. Neither party has relied in entering into this Agreement on any statement, inducement or representation that is not set forth in this Agreement. Each party agrees that electronic signatures may be used to authenticate this writing and, if so used, will have the same force and effect as manual signatures. diff --git a/docs/policies_and_agreements/Terms_of_Service.md b/docs/policies_and_agreements/Terms_of_Service.md deleted file mode 100644 index d1424b2452..0000000000 --- a/docs/policies_and_agreements/Terms_of_Service.md +++ /dev/null @@ -1,393 +0,0 @@ -# Discord Developer Terms of Service - -> info -> We are updating our Developer Policy and Developer Terms of Service, effective October 1, 2022. Please review the updated terms here and policy below (for translated versions of the Discord Developer Terms of Service in your selected language, as available, please [see here](https://support-dev.discord.com/hc/articles/8562894815383). If you access or use the APIs on or after October 1, 2022, it means you agree to these updated terms and policy. Until then, the current [terms](#DOCS_POLICIES_AND_AGREEMENTS_TERMS_OF_SERVICE/previous-discord-developer-terms-of-service) and policy will apply. [Learn more here](https://support-dev.discord.com/hc/articles/8541214983959). - -## Effective date: October 1, 2022 - -## Posted date: September 1, 2022 - -*For a link to the previous terms, please see **[here](https://github.com/discord/discord-api-docs/blob/62c9a95b56d2f989d3eefe39a058d69189f6b4a6/docs/policies_and_agreements/Terms_of_Service.md)**. For a translated version in your selected language, as available, please see **[here](https://support-dev.discord.com/hc/articles/8562894815383)**.* - -Welcome! Thank you for your interest in Discord’s APIs, software development kits (“SDKs”), and other developer products and services and associated software (including those described in or available via our [Developer Portal](#DOCS_INTRO)) (collectively, "APIs" or “Developer Platform”). - -These Developer Terms of Service (“Developer Terms”) apply to your access to and use of the APIs and set forth the legal obligations between us and you. - -When we say “Discord,” “we,” “us,” and “our” in these Developer Terms of Service, we mean Discord Inc., unless otherwise set forth in additional terms applicable to a given API. - -When we say “Terms” in these Developer Terms, we mean, collectively, these Developer Terms, our [Developer Documentation](#DOCS_INTRO) and any other applicable documentation we make available (“Documentation”), our [Discord Developer Policy](#DOCS_POLICIES_AND_AGREEMENTS_DEVELOPER_POLICY), our [Terms of Service](https://discord.com/terms) (including our [Discord Community Guidelines](https://discord.com/guidelines)), and any other applicable terms, policies, and guidelines we make available (including in our [Developer Portal](#DOCS_INTRO) or [Help Center](https://support-dev.discord.com/hc)). If there is a conflict between these Developer Terms and any additional terms, then these Developer Terms will control for that conflict. If you use other Discord services in connection with the APIs, then the terms for those other services also apply to you. Additional defined terms are described in Section 13 (Definitions) below. - -**Important Note: Section 12(e) (Choice of Law; Dispute Resolution) incorporates the [dispute resolution provision](https://discord.com/terms#settling-disputes-between-you-and-discord) from our [Terms of Service](https://discord.com/terms), which includes an arbitration clause and class action waiver that applies to all U.S.-based Discord users. Please read this section carefully as it may significantly affect your legal rights, including your right to file a lawsuit in court.** - -# Section 1: Acceptance of the Terms and Registration - -## a. Accepting the Terms - -By accepting these Developer Terms of Service, creating or operating an Application, or otherwise accessing or using our APIs, you agree to comply with the Terms and that the Terms control your relationship with us. You also confirm and agree that (i) you are at least 13 years of age and meet the minimum age of digital consent in your country, and (ii) if you are not old enough to have authority to consent to the Terms in your country, that your parent or legal guardian must agree to the Terms on your behalf. If you are a parent or legal guardian, and your teenager accesses or uses the APIs, then the Terms also apply to you and you are responsible for your teenager’s activity. Please read all the Terms carefully. - -You may not use the APIs and may not accept the Terms if you are barred from using or receiving the APIs under the applicable laws of the United States or other countries, including the country in which you are a resident or from which you use the APIs. - -If you are using the APIs on behalf of an entity, you represent and warrant that you have authority to bind that entity to the Terms and by accepting the Terms, you are doing so on behalf of that entity (and all references to "you" in the Terms refer to that entity). - -## b. Registration & Application Information - -You may be required or requested to provide certain information about yourself (such as identification or contact details or materials) and your Application (such as details about its functionality) as part of the registration process for access to the APIs (including when you create your Application), or as part of your continued use of the APIs (including in the Developer Portal or during App Review). You will ensure such information is accurate, complete, and up-to-date at all times. Without limiting the foregoing, you will maintain a clear and accurate description of your Application in its user-facing profiles. You will not misrepresent or mask your, or your Application’s, identity in the Developer Portal, as part of any registration process or App Review, or when using the APIs. We will use information about you to confirm your identity and as otherwise described in our [Privacy Policy](https://discord.com/privacy). - -# Section 2: Use of the APIs - -## a. License to you - -Subject to your compliance with the Terms, we grant you a limited, non-exclusive, non-sublicensable, non-transferable, non-assignable, revocable license to access and use the APIs and Documentation we make available to you solely as necessary to integrate with, develop, and operate your Application to the extent permitted under the Terms (including the [Developer Policy](#DOCS_POLICIES_AND_AGREEMENTS_DEVELOPER_POLICY)). We reserve all rights, title, and interest not expressly granted in the Terms and you may not access or use the APIs or Documentation except as set forth in the Terms. - -## b. Restrictions - -You will not (and will not attempt to or permit or enable others to): (a) reverse engineer or otherwise derive source code, trade secrets, or know-how from the APIs, except to the extent this restriction is prohibited by applicable laws; (b) modify, create derivative works, copy, reproduce, redistribute, rent, lease, sell, or syndicate access to the APIs; or (c) access or use the APIs in any way that (i) is not in accordance with the applicable Documentation, (ii) compromises, breaks, or circumvents any of our technical processes or security measures, (iii) poses a security vulnerability to users, us (including our systems or networks), or any third party, or (iv) exceeds any API rate, call, or other usage limits we set in our sole discretion (including as set forth in the Developer Policy or the applicable Documentation) or that we believe constitutes excessive or abusive usage. - -## c. Open Source Software - -Some of the software required by or included in our APIs may be offered under an open source license, as may be listed in the Documentation. Open source software licenses constitute separate written agreements. To the limited extent the open source software license expressly supersedes the Terms, the open source license instead sets forth your agreement with Discord solely for the applicable open source software. - -## d. Developer Credentials - -You will use any developer credentials (such as your Application ID, passwords, keys, tokens, and client secrets) we assign to you solely with your Application and the applicable APIs (and will not permit or enable any other Application to use them) and will treat them as Discord confidential information (as described below). Without limiting the foregoing, you will keep API keys and tokens encrypted in any files or other materials accessible by third parties (other than your Service Providers, subject to Section 12(a)). For the avoidance of doubt, developer credentials may not be embedded in open source projects. - -## e. Eligibility - -As part of the APIs, we may make certain features available to you that require you to meet additional criteria in order to be eligible to access them (e.g., to enable your Application to be discoverable on or through Discord services). For the avoidance of doubt, your access to and use of such features will be subject to your compliance with the then-current applicable eligibility criteria and other policies (in addition to all other Terms). - -# Section 3: Use of App Content and Your Application - -## a. License - -You grant us a non-exclusive, transferable, sublicensable, royalty-free, worldwide license to run, publicly display and perform, distribute, reproduce, modify, host, translate, store, and otherwise use your Application and App Content in connection with operating, developing, and improving the APIs and other Discord services; provided that the right to reproduce does not apply to the Application. This license is to allow us to, among other things: enable users to install and use your Application in Discord servers; display and provide information about you (including your name and Brand Features) and your Application to users (including in profile and product pages); frame and link to, place content around, and limit commands from your Application; make your Application discoverable by users on or through Discord services (including in search results, categories, and collections with other Applications); attribute you as the source of App Content; and analyze your Application and App Content for performance and compliance. For clarity, the things we do under this license will depend in part on your Application, App Content, and how you use the APIs. For example, if your Application is a bot integration that you host and deploy on your own, then we would not need to host your Application. This license will remain in effect with respect to App Content after the Terms end. We will not be subject to any terms or policies associated with your Application or App Content (even if we click to agree). - -## b. Users - -You are solely responsible for your Application (including its development, operation, maintenance, support, distribution, use, and content). You will require users to comply with all applicable laws and regulations and the Terms (including our [Terms of Service](https://discord.com/terms)) in connection with their use of your Application (and will not permit or enable them to violate any of the foregoing). If you have a separate terms of service or other agreement to apply to users’ use of your Application, you agree (i) such agreement is solely between you and the user and we will not have any responsibility or liability thereunder and (ii) such agreement may not and will not supersede, modify, or be inconsistent with the Terms, which will supersede if there is any conflict or inconsistency with your agreement. - -# Section 4: Compliance with Laws & Third-Party Rights - -You and your Application will (and you will require those acting on your behalf and your users to): (i) comply with all applicable laws and regulations; (ii) not infringe or violate any third-party rights (including intellectual or other proprietary rights or rights of privacy or publicity); (iii) not access or use the APIs in a manner that is deceptive, unethical, false, misleading, or encourages or promotes illegal activity or infringement or violation of third-party rights; and (iv) not violate any other terms or policies with Discord. - -You represent and warrant that you have obtained and will maintain all necessary rights (including from your users) to grant us the licenses, permissions, and other rights in the Terms (including those in Sections 3(a) and 8(d)) and to operate your Application (including to display, distribute, and provide all information, data, and other content therein). - -# Section 5: User Privacy and Security - -## a. Implement Good Privacy Practices - -Without limiting Section 4, you will comply with all applicable privacy laws and regulations, including the European Union’s General Data Protection Regulation (GDPR) and the ePrivacy Directive, the UK General Data Protection Regulation, Brazil’s Lei Geral de Proteção de Dados (LGPD), the California Consumer Privacy Act (CCPA), and the California Privacy Rights Act (CPRA). You will provide and adhere to a privacy policy for your Application that is compliant with applicable privacy laws and clearly, accurately, and fully describes to users of your Application what data you collect, how you use and share such data with us and third parties, and how users can request deletion of such data. For the avoidance of doubt, how you use data includes how you access, collect, store, retain, transmit, share, and otherwise process it. You may only use API Data in accordance with your privacy policy, applicable laws and regulations, and the Terms. You agree that your privacy policy may not and will not supersede, modify, or be inconsistent with the Terms, which will supersede if there is any conflict or inconsistency with your privacy policy. You will maintain publicly available, up-to-date links to your privacy policy in the Developer Portal. - -## b. API Data Sharing & Retention - -You will not share API Data with any third party, except in the following circumstances, subject to compliance with the Terms and applicable laws and regulations: (i) with a Service Provider; (ii) to the extent required under applicable laws or regulations; and (iii) when a user of your Application expressly directs you to share their API Data with the third party (and you will provide us proof thereof upon request). - -Except to the extent you are required to retain API Data under applicable laws or regulations, you will (1) promptly update the API Data upon request from us or the applicable user, and (2) promptly delete the API Data when: (a) retaining it is no longer necessary for your Application’s stated (and approved through App Review, as applicable) functionality that is permitted under the Terms; (b) you stop operating your Application (whether on your own, due to an enforcement action by us, or otherwise); (c) we request you delete it; (d) the applicable user requests you delete it; or (e) required by applicable laws or regulations. You will give users an easily accessible way to ask for their API Data to be modified and deleted. If you have received API Data in error, you will immediately tell us, delete it, and provide proof of deletion upon our request. - -## c. Implement Good Security - -You will, and will require those acting on your behalf to, use commercially reasonable efforts to protect API Data from unauthorized access or use (including, for the avoidance of doubt, destruction, loss, alteration, disclosure, distribution, or other compromise). These efforts will include: (i) encryption of the data at rest and (ii) maintaining administrative, physical, and technical safeguards that are designed to prevent unauthorized access and use and comply with applicable laws and regulations (including relating to data security and privacy). You will promptly report to your users any unauthorized access or use of their data to the extent required by applicable laws or regulations. You will also promptly notify us and provide us with requested information about any incidents of unauthorized access or use of any API Data or any incidents that are reasonably likely to compromise the security, confidentiality, or integrity of your systems (or the systems of those acting on your behalf) that process API Data. In the event of any of the foregoing incidents, you will immediately begin to remediate it and reasonably cooperate with us, including by keeping us regularly updated about the incident and its impact on API Data, remediation actions being taken, and your compliance with any notification or other requirements under applicable laws and regulations. - -# Section 6: Monitoring & App Review - -The APIs are designed to help you enhance your Application. You agree to respond promptly to our requests to verify and audit your compliance with these Terms. YOU AGREE THAT DISCORD MAY MONITOR USE OF THE APIS TO ENSURE QUALITY, IMPROVE DISCORD APIs AND OTHER SERVICES, AND VERIFY YOUR COMPLIANCE WITH THE TERMS. You will not interfere with this monitoring. Discord may use any technical means to overcome such interference. - -Without limiting the foregoing, we may also require that you submit your Application for our review and approval in various circumstances (e.g., to approve access to certain APIs or API Data, when use of your Application reaches certain thresholds, or to review compliance with the Terms) (“App Review”). You will cooperate with App Review and provide and maintain accurate and complete information as we request (including in the Developer Portal). If your Application has been approved through App Review, you will not materially change your Application’s functionality (including the scope of API Data you collect or how you use or share it) beyond its reviewed stated purpose, unless you first re-submit your Application, and receive our approval, through App Review. - -Periodically, we may also request (including through the Developer Portal) information, certifications, and attestations relating to your access to or use of the APIs or API Data, which you will provide to us in the requested time frame and form and which you will ensure are accurate and complete. If you are an entity, such certifications and attestations must be provided by an authorized representative. - -# Section 7: IP and Confidentiality - -## a. IP Ownership and Feedback - -Discord does not acquire ownership in your Application, and you do not acquire ownership of any rights in our APIs, API Data, Documentation, or Discord confidential information or any derivative works of any of the foregoing. - -Our APIs and API Data contain some third-party content (such as text, images, videos, audio, or software). This content is the sole responsibility of the person that makes it available and you are solely responsible for your decision to use or distribute any of this content. While we may sometimes review content to determine whether it is illegal or violates our policies or the Terms and we may remove or refuse to display content, we make no representations or warranties about the content or any decisions we make regarding it. Finally, API Data may be subject to intellectual property rights, and, if so, you may not use it unless you are licensed to do so by the owner of that API Data or are otherwise permitted by law. Your access to the API Data may be restricted, limited, or filtered in accordance with applicable law, regulation, and policy. - -If you provide suggestions or other feedback about our APIs, Documentation, or any other Discord services, then you grant us a non-exclusive, perpetual, irrevocable, sublicensable, transferable license to use the feedback and ideas generated from the feedback without any restrictions, attribution, or compensation to you. - -## b. Discord Copyright and IP Policy - -We respond to notices of alleged IP infringement and terminate the accounts of repeat infringers according to the process set out in the U.S. Digital Millennium Copyright Act and [Discord’s Copyright & IP Policy](https://support.discord.com/hc/en-us/articles/4410339349655-Discord-s-Copyright-IP-Policy). If you think somebody is violating your IP and want to notify us, you can find information about submitting notices in the foregoing policy. - -## c. Confidential Matters - -Our communications to you and our APIs may contain Discord confidential information. Discord confidential information includes any materials, communications, and information that are marked or designated confidential or that would normally be considered confidential under the circumstances (including any nonpublic information). Without limiting the foregoing, your participation in any research or feedback surveys or sessions or in any experimentation, testing, or use of APIs we make available to you that are not generally publicly available (including any information you receive, and feedback you provide us, in connection therewith) are Discord confidential information. If you receive any Discord confidential information, then you (i) will keep it confidential using no less than reasonable care to protect it and will only use it as necessary for your permitted use of the applicable APIs under the Terms and to provide us feedback, and (ii) will not disclose it to any third party without Discord's prior written consent, except to the extent (1) required by law with reasonable prior notice to us unless prohibited by court order or (2) necessary for a Service Provider to provide you services subject to Section 12(a). - -# Section 8: Brand Features; Attribution - -## a. Brand Features - -"Brand Features" means the trade names, trademarks, service marks, logos, domain names, and other distinctive brand features of each party. Except where expressly stated, the Terms do not grant either party any right, title, or interest in or to the other party's Brand Features. All use by you of Discord’s Brand Features (including any goodwill associated therewith) will inure to the benefit of Discord. All use by us of your Brand Features (including any goodwill associated therewith) will inure to your benefit. - -## b. Attribution - -All Applications will provide proper attribution(s) as required by Discord (including as may be described in the applicable Documentation). Subject to your compliance with the Terms, Discord grants you a limited, revocable, non-transferable, non-assignable, non-sublicensable, non-exclusive license while the Terms are in effect to display Discord’s Brand Features for required attribution(s) or in an incidental manner while promoting or advertising your Application, only in accordance with the Terms and Discord’s Brand Guidelines. You understand and agree that Discord has the sole discretion to determine whether your attribution(s) and use of Discord's Brand Features are in accordance with the above requirements and guidelines. - -## c. Publicity - -You will not make any statement (including in any promotions or advertisements of your Application) regarding your access to or use of the APIs which suggests partnership with, sponsorship by, or endorsement by Discord without Discord prior written approval in each instance. - -## d. Promotional and Marketing Use - -You grant us a non-exclusive, transferable, sublicensable, royalty-free, worldwide license to use your name and Brand Features, your Application’s name and icon, and produce and distribute App Content and incidental depictions (including screenshots, videos, or other content) from your Application, in the course of promoting, marketing, or demonstrating the APIs or other Discord services you are using or your Application (including in our discovery surfaces). This license will remain in effect for existing materials and instances even if the Terms are terminated. - -# Section 9: Termination - -## a. Termination - -You may terminate the Terms by discontinuing your (including your Application’s and those acting on your behalf) access to and use of the APIs at any time with or without notice. Discord reserves the right to terminate the Terms with you, or discontinue, suspend, or limit the APIs or any portion or feature or your access thereto, if there is an Enforcement Reason or at our convenience upon notice to you. If there is an Enforcement Reason, we may, at our discretion, also take other enforcement actions, including the following: issue warnings or notifications to you or the users; identify and require or request corrective actions from you; remove, suspend, or limit your Application or App Content; remove any approval we have provided for your Application through App Review; remove badges we’ve issued to you or your Application; require you to stop using and delete API Data; terminate, suspend, or limit your user or developer account with Discord or your developer credentials; terminate your other agreements with Discord or your ability to use other Discord services; report you or your Application to law enforcement (as we deem appropriate); or take legal action or any other action we consider to be appropriate. Enforcement actions may be automated or manual and we may take them with or without notice to you, including while we investigate. - -Without limiting any of the foregoing, we may also limit, suspend, or terminate your Application’s access tokens or any other means of access to the APIs that have not been used or accessed within at least the prior 30-day period, with or without notice to you. - -## b. Your Obligations Post-Termination - -Upon any termination of the Terms or discontinuation of your access to the APIs, you will (and will require those acting on your behalf to) immediately stop using the APIs and developer credentials, cease all use of the Discord Brand Features, and delete any cached or stored API Data you have in your possession or control or was otherwise collected by your Application, and all rights granted to you under the Terms (including in Sections 2 and 8(b)) will immediately cease. Discord may independently communicate with any account owner whose account(s) are associated with your Application and developer credentials to provide notice of the termination of your right to use an API. - -## c. Surviving Provisions - -When the Terms come to an end, those terms that by their nature are intended to continue indefinitely will continue to apply, including Sections 2(b), 3 - 7, 8(c) and (d), 9(b) and (c), and 10-13. - -# Section 10: Liability for our APIs - -## a. WARRANTIES - -NEITHER DISCORD NOR ITS AFFILIATES, SUPPLIERS, OR DISTRIBUTORS MAKE ANY SPECIFIC PROMISES ABOUT THE APIs, API DATA, DOCUMENTATION, OR ANY DISCORD SERVICES. FOR EXAMPLE, WE DON'T MAKE ANY COMMITMENTS OR WARRANTIES ABOUT THE SPECIFIC FUNCTIONS OF THE APIs OR THE RELIABILITY, AVAILABILITY, ACCURACY, QUALITY, APPROPRIATENESS, LEGALITY, ORIGIN, SAFETY, USEFULNESS, OR ABILITY TO MEET YOUR NEEDS OF THE APIs, API DATA, OR DOCUMENTATION. YOU ACKNOWLEDGE THAT WE DO NOT WARRANT THAT THE APIs WILL BE UNINTERRUPTED, TIMELY, SECURE, OR ERROR-FREE, AND WE MAY CHANGE, SUSPEND, OR DISCONTINUE THE AVAILABILITY OF THE APIs AT ANY TIME. WE PROVIDE THE APIs, API DATA, AND DOCUMENTATION "AS IS" AND YOU USE THEM AT YOUR SOLE RISK. - -SOME JURISDICTIONS PROVIDE FOR CERTAIN WARRANTIES, LIKE THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. EXCEPT AS EXPRESSLY PROVIDED FOR IN THE TERMS, TO THE EXTENT PERMITTED BY LAW, WE EXCLUDE ALL WARRANTIES, GUARANTEES, CONDITIONS, REPRESENTATIONS, AND UNDERTAKINGS. - -## b. LIMITATION OF LIABILITY - -TO THE EXTENT PERMITTED BY LAW, DISCORD, AND DISCORD’S SUPPLIERS AND DISTRIBUTORS, WILL NOT BE RESPONSIBLE FOR LOST PROFITS, REVENUES, OR DATA; FINANCIAL LOSSES; OR INDIRECT, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES. - -TO THE EXTENT PERMITTED BY LAW, THE TOTAL LIABILITY OF DISCORD, AND ITS AFFILIATES, SUPPLIERS, AND DISTRIBUTORS, UNDER OR ARISING OUT OF THE TERMS, INCLUDING FOR ANY IMPLIED WARRANTIES, IS LIMITED TO THE GREATER OF (I) AMOUNT YOU PAID US TO USE THE APPLICABLE APIS (OR, IF WE CHOOSE, TO SUPPLYING YOU THE APIS AGAIN) DURING THE SIX MONTHS BEFORE YOU FIRST ASSERT A CLAIM OR (II) $100 (OR THE EQUIVALENT IN YOUR LOCAL CURRENCY). - -DISCORD IS NOT LIABLE FOR THE CONDUCT OR CONTENT, WHETHER ONLINE OR OFFLINE, OF ANY USER OF DISCORD SERVICES. IN ALL CASES, DISCORD, AND ITS AFFILIATES, SUPPLIERS, AND DISTRIBUTORS, WILL NOT BE LIABLE FOR ANY EXPENSE, LOSS, OR DAMAGE THAT IS NOT REASONABLY FORESEEABLE. - -## c. Indemnification - -Unless prohibited by applicable law, you will defend (at our option), indemnify, and hold harmless Discord, and its affiliates, directors, officers, employees, and users, from and against all claims, liabilities, damages, losses, costs, fees (including legal and accounting fees), and expenses relating to: -- access to or use of the APIs or API Data by you, those acting on your behalf, your Application, or your users (including any incidents of unauthorized access to or use of API Data); -- actual or alleged violation the Terms by you, those acting on your behalf, your Application, or your users; -- actual or alleged infringement of any third-party right (including intellectual property or other proprietary rights or rights to privacy or publicity) by you, those acting on your behalf, your Application, or your users; or -- your Application, App Content, your Brand Features, or any content, data, technology, information, or materials provided or contributed by or through you, those acting on your behalf, your Application, or your users. - -You will not settle any claim without our prior written consent. We may, at our sole discretion, elect to assume control of the defense and settlement of any claim. - -# Section 11: International Transfers - -This section applies to the extent that the API Data includes personal data controlled by Discord Netherlands BV (“Discord EU Data”) and you transfer the Discord EU Data to a territory outside of the European Economic Area that, at the time of such transfer, does not have a positive adequacy decision from the European Commission under Article 45 GDPR (each, an “EEA Data Transfer”). - -In each instance of an EEA Data Transfer, your use of Discord EU Data is subject to your compliance with the standard contractual clauses in European Commission Decision (EU) 2021/914 (“Clauses”) as they relate to controller-to-controller transfers as described in Module One. - -In each instance, you agree that for the purposes of Section IV, Clauses 17 and 18 in the Clauses, Option 1 and Option (b) shall apply respectively and the Member State shall be the Netherlands. Nothing in this Section 11 (International Transfers) is intended to vary or modify the Clauses. For the purposes of the Appendix to the Clauses, the following will apply: -- For the purposes of Annex I(A) to the Clauses, Discord Netherlands BV is the "data exporter" and you are the "data importer" as defined in the Clauses. -- For the purposes of Annex I(B) to the Clauses: - - “Categories of data subjects” are the users who visit, access, use, or otherwise interact with your Application and the services of Discord Netherlands BV; - - "Categories of personal data" are Discord EU Data, which includes profile information, communications between users, photos and videos, location information, information about use of your Application and other products and services, payment information, device information, information from third-party partners or Discord, or as otherwise set forth in the Discord Privacy Policy; - - "Sensitive data" is personal data about a person's racial or ethnic origin, political opinions, religious or philosophical beliefs, trade-union membership, genetic data, biometric data for the purpose of uniquely identifying a natural person, data concerning health or data concerning a natural person's sex life or sexual orientation, criminal convictions or alleged commission of an offence; - - "Frequency of the transfer" is on a continuous basis to the extent required to fulfill the purpose set forth in Section 11(b)(v) directly below; - - “Nature and purpose of the data transfer(s)" is the provision of your Application by you to users pursuant to these Terms and your applicable terms and privacy policy; and - - "Period for which personal data will be retained" is as described in the Terms and in any case not longer than necessary for a legitimate business purpose in accordance with the Terms and applicable laws or regulations (including rights of erasure and similar rights for individuals), unless applicable laws require the Discord EU Data be retained for a longer period, in which case you shall only retain such Discord EU Data for the period required by such applicable laws and subject always to Section 11(d) below. -- For the purposes of Annex I(C) to the Clauses, the competent supervisory authority will be the Dutch Data Protection Authority (*Autoriteit Persoonsgegevens*); and -- For the purposes of Annex II to the Clauses, you will implement and maintain the technical and organisational security measures set out in the Terms, including those in Section 5 (“User Privacy and Security”) and such other measures as we may require from time to time. - -# Section 12: General Provisions - -## a. Service Providers - -Before using a Service Provider, you will require them to first agree in writing to only access and use the APIs and API Data for you and at your direction to provide you services to develop or operate your Application in compliance with the Terms, and for no other purpose (including their own). For the avoidance of doubt, Service Providers are acting on your behalf. You will ensure that each Service Provider complies with the Terms as if they are in your place, and you are solely responsible and liable for their acts and omissions, including noncompliance. When you stop using a Service Provider, you will ensure they immediately cease using the APIs and API Data and promptly delete all API Data in their possession or control. Upon notice, we may prohibit your use of any Service Provider if we reasonably believe that they have violated the Terms or they are negatively impacting us, the APIs or our other services, API Data, or the users, and you will promptly stop using them. - -## b. Modification - -We may change these Developer Terms from time to time. If we make a material change to these Developer Terms, we will provide you with reasonable notice prior to the change taking effect (e.g., at the email address or Discord ID we have on file for you or in the Developer Portal), unless the changes are urgent. If you continue to access or use the APIs or API Data after the changes have taken effect, it means that you agree to the changes. If you do not agree, you must stop using the APIs and API Data. You can review the most current version of these Developer Terms at any time by visiting this page. - -## c. U.S. Federal Agency Entities - -The APIs and Documentation were developed solely at private expense and are commercial computer software and related documentation within the meaning of the applicable U.S. Federal Acquisition Regulation and agency supplements thereto. - -## d. Export Control - -You represent and warrant that you are not located in, or affiliated with the government of North Korea and are not included on any U.S. or E.U. list of prohibited or restricted parties. If you are located in any other country, including those subject to a comprehensive U.S. or E.U. sanction or embargo, your use of the APIs and any other Discord services must be in compliance with U.S. and E.U. laws and must not cause Discord to be in violation of such laws. If you request access to, download, or use any APIs that are not generally publicly available, you also represent and warrant that you are not located in Syria or the Crimea region of Ukraine, and are not affiliated with a government of such regions or the governments of Cuba or Iran. In addition, if your Application incorporates Discord software or SDKs, it may be subject to U.S. export controls governing its export, reexport, and transfer. You are responsible for determining the applicable restrictions and obtaining any necessary export approvals for your Application under the applicable laws. You may not permit your Application to be hosted, accessed, or used (i) in North Korea or by any U.S. or E.U. list of prohibited or restricted parties; or (ii) in any other country or region subject to comprehensive U.S. or E.U. government embargo (including Cuba, Iran, Syria, and the Crimea, Donetsk People's Republic, or the Luhansk People's Republic regions of Ukraine) unless that use is authorized by the U.S. and other relevant authorities, for example the general licenses issued by the U.S. Office of Foreign Assets Control for services incident to the exchange of personal communications over the Internet (such as instant messaging, chat and email, social networking, sharing of photos and movies, web browsing, and blogging). - -## e. Choice of Law; Dispute Resolution - -Subject to Section 11 above with respect to the governing law that applies to EEA Data Transfers, the [“Settling disputes between you and Discord” section in our Terms of Service](https://discord.com/terms#settling-disputes-between-you-and-discord) applies, where references to “the services” or “our services” will mean the APIs, “these terms” will mean these Developer Terms of Service, and the date for emailing an opt-out notice is within 30 days of October 1, 2022 or when you first create an Application, whichever is later. - -## f. General Legal Terms - -To the extent permitted by law, we each agree to contract in the English language and if we provide a translation of the Terms, we do so for your convenience only and the English Terms will solely govern our relationship. The term “including” means “including without limitation.” The Terms do not create any third party beneficiary rights or any agency, partnership, or joint venture. Nothing in the Terms will limit either party's ability to seek injunctive relief. We are not liable for failure or delay in performance to the extent caused by circumstances beyond our reasonable control. If you do not comply with the Terms, and Discord does not take action right away, this does not mean that Discord is giving up any rights that it may have (such as taking action in the future). If it turns out that a particular term is not enforceable, this will not affect any other terms. The Terms are the entire agreement between you and Discord relating to its subject and supersede any prior or contemporaneous agreements on that subject. For information about how to contact Discord, please visit our [Terms of Service](https://discord.com/terms). - -You may not assign, sublicense, or delegate any of your rights or obligations under the Terms (including any sale, license, or other transfer of ownership of your Application) to any third party without the prior written consent of Discord. Any assignment, sublicense, or delegation in violation of this provision is null and void. Any change of control or material ownership of you is hereby deemed an assignment. These Terms are non-exclusive. You acknowledge that Discord may develop products or services that may compete with, or are similar to, Applications or any other products or services. - -# Section 13: Definitions - -- “APIs” (or “Developer Platform”) means, collectively, Discord’s APIs, software development kits (“SDKs”), and other developer services and associated software (including those described in or available via our [Developer Portal](#DOCS_INTRO)). -- “API Data” means any data, information, or other content you obtain through the APIs (including personal data). API Data includes your developer credentials and access tokens. -- “App Content” means any data, information, technology, materials, or other content that you or those acting on your behalf add to our services or otherwise make available to us in connection with the APIs or your Application (including as submitted, posted, or displayed by or through your Application). -- “App Review” has the meaning set forth in Section 6. -- “Application” (or “app”) means any application (including any bot, game, activity, website, or other client) that accesses or uses our APIs or to which we have assigned an Application ID. “your Application” means any Application that you own or operate. References made to API Client in existing terms, policies, agreements, or Documentation will now mean Application. -- “Brand Features” has the meaning set forth in Section 8(a). -- “Clauses” has the meaning set forth in Section 11. -- “Discord EU Data” has the meaning set forth in Section 11. -- “Discord services” (or “our services") has the meaning set forth in our [Terms of Service](https://discord.com/terms). -- “EEA Data Transfer” has the meaning set forth in Section 11. -- “Enforcement Reasons” mean if we believe (i) you or your API Client have or may have violated any of the Terms (including by enabling or permitting violations of our [Terms of Service](https://discord.com/terms) or other Terms by users) or is negatively impacting us, the APIs or other Discord services, or our users; (ii) your Discord user account has been compromised or it is otherwise suspended or terminated; (iii) it is necessary to comply with applicable laws or regulations or otherwise required by court order or other governmental authority; or (iv) it is necessary to protect ourselves from legal or regulatory liability. -- “Service Provider” means a person or entity you use to provide you services to develop or operate your Application. - ---- - -# Previous Discord Developer Terms of Service - -> warn -> The following is the previous Developer Terms of Service, which will be in effect until October 1, 2022. - -## Effective until: October 1, 2022 - -## Posted date: July 15, 2020 - -*For a link to the previous terms, please see [here](https://github.com/discord/discord-api-docs/blob/b9edace323c9df64c79f104d85984690ae4e2977/docs/Legal.md).* - -Welcome to Discord’s APIs, software development kits (**“SDKs”**), and associated documentation (collectively, **"APIs"**). - -Throughout this document, we use the word **_“Terms”_** to refer to the terms below, terms within the accompanying API and SDK documentation, our [Discord Developer Policy](#DOCS_POLICIES_AND_AGREEMENTS_DEVELOPER_POLICY), and any applicable policies and guidelines. If there is a conflict between these terms and additional terms applicable to a given API, the additional terms will control for that conflict. - -# Section 1: Acceptance of the Terms and Registration - -## a. Accepting the Terms - -By accessing or using our APIs you are agreeing to comply with the Terms and that the Terms control your relationship with us. You are also agreeing that (i) you are 13 years of age and the minimum age of digital consent in your country, (ii) if you are the age of majority in your jurisdiction or over, that you have read, understood, and accept to be bound by the Terms, and (iii) if you are between 13 (or the minimum age of digital consent, as applicable) and the age of majority in your jurisdiction, that your legal guardian has reviewed and agrees to these Terms. Please read all the Terms carefully. - -You may not use the APIs and may not accept the Terms if you are a person barred from using or receiving the APIs under the applicable laws of the United States or other countries including the country in which you are resident or from which you use the APIs. - -If you are using the APIs on behalf of an entity, you represent and warrant that you have authority to bind that entity to the Terms and by accepting the Terms, you are doing so on behalf of that entity (and all references to "you" in the Terms refer to that entity). - -## b. Registration - -In order to access certain APIs you may be required to provide certain information (such as identification or contact details) as part of the registration process for the APIs, or as part of your continued use of the APIs. You must provide accurate and up-to-date registration information at all times. We will use the information you submit in accordance with our [Privacy Policy](https://discord.com/privacy). - -# Section 2: User Privacy and Security. - -## a. Implement Good Privacy Practices. - -You will comply with all applicable privacy laws and regulations including those applying to personally identifiable information ("PII"). You will provide and adhere to a privacy policy for your application that uses the API (your “API Client”) that clearly and accurately describes to users of your API Client what user information you collect and how you use and share such information with Discord and third parties. - -## b. Implement Good Security. - -You will use commercially reasonable efforts to protect data collected by your API Client, including PII, from unauthorized access or use. These efforts will include, but are not limited to, encryption of this data at rest. You will promptly report to your users any unauthorized access or use of such information to the extent required by applicable law. - -# Section 3: API Clients and Monitoring - -The APIs are designed to help you enhance your API Client. You agree to respond promptly to our requests to verify and audit your compliance with these Terms. YOU AGREE THAT DISCORD MAY MONITOR USE OF THE APIS TO ENSURE QUALITY, IMPROVE DISCORD PRODUCTS AND SERVICES, AND VERIFY YOUR COMPLIANCE WITH THE TERMS. This monitoring may include Discord accessing and using your API Client, for example to identify security issues that could affect Discord or its users. You will not interfere with this monitoring. Discord may use any technical means to overcome such interference. - -# Section 4: IP, DMCA, and Confidentiality - -## a. IP Ownership, Open Source, and Feedback - -Discord does not acquire ownership in your API Clients, and by using our APIs, you do not acquire ownership of any rights in our APIs or the content that is accessed through our APIs. - -Some of the software required by or included in our APIs may be offered under an open source license. Open source software licenses constitute separate written agreements. For certain APIs, open source software is listed in the documentation. To the limited extent the open source software license expressly supersedes the Terms, the open source license instead sets forth your agreement with Discord for the applicable open source software. - -If you provide feedback or suggestions about our APIs, then we (and those we allow) may use such information without obligation to you. - -## b. Discord DMCA Policy - -We can't determine whether something is being used legally or not without copyright holder’s input. We respond to notices of alleged copyright infringement and terminate accounts of repeat infringers according to the process set out in the U.S. Digital Millennium Copyright Act. If you think somebody is violating your copyrights and want to notify us, you can find information about submitting notices and Discord's policy about responding to notices in our [Terms of Service](https://discord.com/terms). - -## c. Confidential Matters - -1. Developer credentials (such as passwords, keys, and client secrets) are intended to be used by you and identify your API Client. You will keep your credentials confidential and make reasonable efforts to prevent and discourage other API Clients from using your credentials. Developer credentials may not be embedded in open source projects. -2. Our communications to you and our APIs may contain Discord confidential information. Discord confidential information includes any materials, communications, and information that are marked confidential or that would normally be considered confidential under the circumstances. If you receive any such information, then you will not disclose it to any third party without Discord's prior written consent unless required by law with reasonable prior notice to us (unless a court orders that we not receive notice). - -# Section 5: Content - -## a. Content Accessible Through our APIs - -Our APIs contain some third-party content (such as text, images, videos, audio, or software). This content is the sole responsibility of the person that makes it available and you are solely responsible for your decision to use or distribute any of this content. While we may sometimes review content to determine whether it is illegal or violates our policies or the Terms, and we may remove or refuse to display content, we make no representations or warranties about the content, its appropriateness, legality, pedigree or other details. Finally, content accessible through our APIs may be subject to intellectual property rights, and, if so, you may not use it unless you are licensed to do so by the owner of that content or are otherwise permitted by law. Your access to the content provided by the API may be restricted, limited, or filtered in accordance with applicable law, regulation, and policy. - -## b. Submission of Content - -Some of our APIs allow the submission of content. Discord does not acquire any ownership of any intellectual property rights in the content that you submit to our APIs through your API Client, except as expressly provided in these Terms. For the sole purpose of enabling Discord to provide, secure, and improve the APIs (and the related service(s)) and only in accordance with the applicable Discord privacy policies, you give Discord a perpetual, irrevocable, worldwide, sublicensable, royalty-free, and non-exclusive license to Use content submitted, posted, or displayed to or from the APIs through your API Client. **_"Use"_** means use, host, store, modify, communicate, and publish. Before you submit content to our APIs through your API Client, you will ensure that you have the necessary rights (including the necessary rights from your end users) to grant us the license. - -# Section 6: Brand Features; Attribution - -## a. Brand Features - -**_"Brand Features"_** is defined as the trade names, trademarks, service marks, logos, domain names, and other distinctive brand features of each party. Except where expressly stated, the Terms do not grant either party any right, title, or interest in or to the other party's Brand Features. All use by you of Discord’s Brand Features (including any goodwill associated therewith) will inure to the benefit of Discord. All use by us of your Brand Features (including any goodwill associated therewith) will inure to your benefit. - -## b. Attribution - -You agree to display any attribution(s) required by Discord as described in the documentation for the API. Discord hereby grants to you a nontransferable, nonsublicenseable, nonexclusive license while the Terms are in effect to display Discord’s Brand Features for the purpose of promoting or advertising that you use the APIs. You must only use the Discord Brand Features in accordance with the Terms and for the purpose of fulfilling your obligations under this Section. In using Discord’s Brand Features, you must follow the [Discord Brand Features Use Guidelines](https://discord.com/branding). You understand and agree that Discord has the sole discretion to determine whether your attribution(s) and use of Discord's Brand Features are in accordance with the above requirements and guidelines. - -## c. Publicity - -You will not make any statement regarding your use of an API which suggests partnership with, sponsorship by, or endorsement by Discord without a Discord officer’s prior written approval. - -## d. Promotional and Marketing Use - -In the course of promoting, marketing, or demonstrating the APIs you are using and the associated Discord products, Discord may produce and distribute incidental depictions, including screenshots, video, or other content from your API Client, and may use your company or product name. You grant us all necessary rights for the above purposes. - -# Section 7: Termination - -## a. Termination - -You may stop using our APIs at any time with or without notice. Discord reserves the right to terminate the Terms with you or discontinue the APIs or any portion or feature or your access thereto if you violate any of these Terms, if we believe your Application harms us or our users, or at our convenience upon notice to you. - -## b. Your Obligations Post-Termination - -Upon any termination of the Terms or discontinuation of your access to an API, you will immediately stop using the API, cease all use of the Discord Brand Features, and delete any cached or stored data collected by your API Client. Discord may independently communicate with any account owner whose account(s) are associated with your API Client and developer credentials to provide notice of the termination of your right to use an API. - -## c. Surviving Provisions - -When the Terms come to an end, those terms that by their nature are intended to continue indefinitely will continue to apply, including but not limited to: Sections 4b, 5, 7, 8, and 9. - -# Section 8: Liability for our APIs - -## a. WARRANTIES - -EXCEPT AS EXPRESSLY SET OUT IN THE TERMS, NEITHER DISCORD NOR ITS SUPPLIERS OR DISTRIBUTORS MAKE ANY SPECIFIC PROMISES ABOUT THE APIS. FOR EXAMPLE, WE DON'T MAKE ANY COMMITMENTS ABOUT THE CONTENT ACCESSED OR MADE AVAILABLE THROUGH THE APIS, THE SPECIFIC FUNCTIONS OF THE APIS, OR THEIR RELIABILITY, AVAILABILITY, OR ABILITY TO MEET YOUR NEEDS. WE PROVIDE THE APIs "AS IS". - -SOME JURISDICTIONS PROVIDE FOR CERTAIN WARRANTIES, LIKE THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. EXCEPT AS EXPRESSLY PROVIDED FOR IN THE TERMS, TO THE EXTENT PERMITTED BY LAW, WE EXCLUDE ALL WARRANTIES, GUARANTEES, CONDITIONS, REPRESENTATIONS, AND UNDERTAKINGS. - -## b. LIMITATION OF LIABILITY - -WHEN PERMITTED BY LAW, DISCORD, AND DISCORD’S SUPPLIERS AND DISTRIBUTORS, WILL NOT BE RESPONSIBLE FOR LOST PROFITS, REVENUES, OR DATA; FINANCIAL LOSSES; OR INDIRECT, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES. - -TO THE EXTENT PERMITTED BY LAW, THE TOTAL LIABILITY OF DISCORD, AND ITS SUPPLIERS AND DISTRIBUTORS, FOR ANY CLAIM UNDER THE TERMS, INCLUDING FOR ANY IMPLIED WARRANTIES, IS LIMITED TO THE AMOUNT YOU PAID US TO USE THE APPLICABLE APIS (OR, IF WE CHOOSE, TO SUPPLYING YOU THE APIS AGAIN) DURING THE SIX MONTHS PRIOR TO THE EVENT GIVING RISE TO THE LIABILITY. - -IN ALL CASES, DISCORD, AND ITS SUPPLIERS AND DISTRIBUTORS, WILL NOT BE LIABLE FOR ANY EXPENSE, LOSS, OR DAMAGE THAT IS NOT REASONABLY FORESEEABLE. - -## c. Indemnification - -Unless prohibited by applicable law, you will defend and indemnify Discord, and its affiliates, directors, officers, employees, and users, against all liabilities, damages, losses, costs, fees (including legal fees), and expenses relating to any allegation or third-party legal proceeding to the extent arising from: - -1. your actual or alleged misuse or your end users’ misuse of the APIs; -2. your actual or alleged violation or your end users’ violation of the Terms; -3. your actual or alleged infringement of any third party’s intellectual property rights or rights to privacy or publicity; or -4. any content or data routed into or used with the APIs by you, those acting on your behalf, or your end users. - -# Section 9: General Provisions - -## a. Modification - -We may change these Terms from time to time. If we make a material change to the Terms, we will provide you with reasonable notice prior to the change taking effect at the email address or Discord ID we have on file for you. You can review the most current version of these Terms at any time by visiting this page. The materially revised Terms will become effective on the date set forth in our notice, and all other changes will become effective upon posting of the change. Also, changes addressing new functions for an API or changes made for legal reasons will be effective immediately. If you do not agree to the modified Terms for an API, you should discontinue your use of that API. Your continued use of the API constitutes your acceptance of the modified Terms. - -## b. U.S. Federal Agency Entities - -The APIs were developed solely at private expense and are commercial computer software and related documentation within the meaning of the applicable U.S. Federal Acquisition Regulation and agency supplements thereto. - -## c. Choice of Law; Dispute Resolution - -Except as set forth below the laws of California, U.S.A., excluding California's conflict of laws rules, will apply to any disputes arising out of or related to the Terms or the APIs. - -This Agreement shall be interpreted, construed and enforced in all respects in accordance with the internal laws of the State of California, U.S.A., without regard to its principles of conflicts of law. If you have a dispute with Discord arising out of or in connection with the subject matter covered by these Terms, you agree to first contact us and attempt to resolve the dispute with us informally. If we aren’t able to resolve the dispute informally, you and we each agree that any claim or dispute between you and the Company that arises in whole or in part from these Terms or your use of the API shall be decided exclusively by a court of competent jurisdiction located in San Francisco County, California, and you hereby consent to, and waive all defenses of lack of personal jurisdiction and forum non conveniens with respect to venue and jurisdiction in the state and federal courts of San Francisco County, California. - -If you are accepting the Terms on behalf of a United States city, county, or state government entity, then the following applies instead of the paragraph above: the parties agree to remain silent regarding governing law and venue. - -## d. General Legal Terms - -We each agree to contract in the English language. If we provide a translation of the Terms, we do so for your convenience only and the English Terms will solely govern our relationship. The Terms do not create any third party beneficiary rights or any agency, partnership, or joint venture. Nothing in the Terms will limit either party's ability to seek injunctive relief. We are not liable for failure or delay in performance to the extent caused by circumstances beyond our reasonable control. If you do not comply with the Terms, and Discord does not take action right away, this does not mean that Discord is giving up any rights that it may have (such as taking action in the future). If it turns out that a particular term is not enforceable, this will not affect any other terms. The Terms are the entire agreement between you and Discord relating to its subject and supersede any prior or contemporaneous agreements on that subject. For information about how to contact Discord, please visit our [Terms of Service](https://discord.com/terms). - -You may not assign, sublicense or delegate any of your rights or obligations under these Terms to any third party without the prior written consent of Discord. Any assignment, sublicense or delegation in violation of this provision is null and void. Any change of control or material ownership of you is hereby deemed an assignment. -These Terms are non-exclusive. You acknowledge that Discord may develop products or services that may compete with the API Clients or any other products or services. - -**_"Discord"_** means Discord Inc., with offices in San Francisco, California unless set forth otherwise in additional terms applicable for a given API. We may refer to "Discord" as "we", "our", or "us" in the Terms. diff --git a/docs/resources/Application.md b/docs/resources/Application.md deleted file mode 100644 index 780fd8be3a..0000000000 --- a/docs/resources/Application.md +++ /dev/null @@ -1,95 +0,0 @@ -# Application Resource - -### Application Object - -###### Application Structure - -| Field | Type | Description | -| ---------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| id | snowflake | the id of the app | -| name | string | the name of the app | -| icon | ?string | the [icon hash](#DOCS_REFERENCE/image-formatting) of the app | -| description | string | the description of the app | -| rpc_origins? | array of strings | an array of rpc origin urls, if rpc is enabled | -| bot_public | boolean | when false only app owner can join the app's bot to guilds | -| bot_require_code_grant | boolean | when true the app's bot will only join upon completion of the full oauth2 code grant flow | -| terms_of_service_url? | string | the url of the app's terms of service | -| privacy_policy_url? | string | the url of the app's privacy policy | -| owner? | partial [user](#DOCS_RESOURCES_USER/user-object) object | partial user object containing info on the owner of the application | -| summary *(deprecated)* | string | **deprecated and will be removed in v11.** An empty string. | -| verify_key | string | the hex encoded key for verification in interactions and the GameSDK's [GetTicket](#DOCS_GAME_SDK_APPLICATIONS/getticket) | -| team | ?[team](#DOCS_TOPICS_TEAMS/data-models-team-object) object | if the application belongs to a team, this will be a list of the members of that team | -| guild_id? | snowflake | if this application is a game sold on Discord, this field will be the guild to which it has been linked | -| primary_sku_id? | snowflake | if this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists | -| slug? | string | if this application is a game sold on Discord, this field will be the URL slug that links to the store page | -| cover_image? | string | the application's default rich presence invite [cover image hash](#DOCS_REFERENCE/image-formatting) | -| flags? | integer | the application's public [flags](#DOCS_RESOURCES_APPLICATION/application-object-application-flags) | -| tags? | array of strings | up to 5 tags describing the content and functionality of the application | -| install_params? | [install params](#DOCS_RESOURCES_APPLICATION/install-params-object) object | settings for the application's default in-app authorization link, if enabled | -| custom_install_url? | string | the application's default custom authorization link, if enabled | - -###### Example Application Object - -```json -{ - "bot_public": true, - "bot_require_code_grant": false, - "cover_image": "31deabb7e45b6c8ecfef77d2f99c81a5", - "description": "Test", - "guild_id": "290926798626357260", - "icon": null, - "id": "172150183260323840", - "name": "Baba O-Riley", - "owner": { - "avatar": null, - "discriminator": "1738", - "flags": 1024, - "id": "172150183260323840", - "username": "i own a bot" - }, - "primary_sku_id": "172150183260323840", - "slug": "test", - "summary": "", - "team": { - "icon": "dd9b7dcfdf5351b9c3de0fe167bacbe1", - "id": "531992624043786253", - "members": [ - { - "membership_state": 2, - "permissions": ["*"], - "team_id": "531992624043786253", - "user": { - "avatar": "d9e261cd35999608eb7e3de1fae3688b", - "discriminator": "0001", - "id": "511972282709709995", - "username": "Mr Owner" - } - } - ] - }, - "verify_key": "1e0a356058d627ca38a5c8c9648818061d49e49bd9da9e3ab17d98ad4d6bg2u8" -} -``` - -###### Application Flags - -| Value | Name | Description | -| ------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| 1 << 12 | GATEWAY_PRESENCE | Intent required for bots in **100 or more servers** to receive [`presence_update` events](#DOCS_TOPICS_GATEWAY_EVENTS/presence-update) | -| 1 << 13 | GATEWAY_PRESENCE_LIMITED | Intent required for bots in under 100 servers to receive [`presence_update` events](#DOCS_TOPICS_GATEWAY_EVENTS/presence-update), found in Bot Settings | -| 1 << 14 | GATEWAY_GUILD_MEMBERS | Intent required for bots in **100 or more servers** to receive member-related events like `guild_member_add`. See list of member-related events [under `GUILD_MEMBERS`](#DOCS_TOPICS_GATEWAY/list-of-intents) | -| 1 << 15 | GATEWAY_GUILD_MEMBERS_LIMITED | Intent required for bots in under 100 servers to receive member-related events like `guild_member_add`, found in Bot Settings. See list of member-related events [under `GUILD_MEMBERS`](#DOCS_TOPICS_GATEWAY/list-of-intents) | -| 1 << 16 | VERIFICATION_PENDING_GUILD_LIMIT | Indicates unusual growth of an app that prevents verification | -| 1 << 17 | EMBEDDED | Indicates if an app is embedded within the Discord client (currently unavailable publicly) | -| 1 << 18 | GATEWAY_MESSAGE_CONTENT | Intent required for bots in **100 or more servers** to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055) | -| 1 << 19 | GATEWAY_MESSAGE_CONTENT_LIMITED | Intent required for bots in under 100 servers to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055), found in Bot Settings | -| 1 << 23 | APPLICATION_COMMAND_BADGE | Indicates if an app has registered global [application commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS) | - -### Install Params Object - -###### Install Params Structure - -| Field | Type | Description | -| ----------- | ---------------- | ---------------------------------------------------------------------------------------------------------- | -| scopes | array of strings | the [scopes](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes) to add the application to the server with | -| permissions | string | the [permissions](#DOCS_TOPICS_PERMISSIONS) to request for the bot role | diff --git a/docs/resources/Audit_Log.md b/docs/resources/Audit_Log.md deleted file mode 100644 index 9e3a98f2b2..0000000000 --- a/docs/resources/Audit_Log.md +++ /dev/null @@ -1,192 +0,0 @@ -# Audit Logs Resource - -## Audit Logs - -When an administrative action is performed in a guild, an entry is added to its audit log. Viewing audit logs requires the `VIEW_AUDIT_LOG` permission and can be fetched by apps using the [`GET /guilds/{guild.id}/audit-logs` endpoint](#DOCS_RESOURCES_AUDIT_LOG/get-guild-audit-log), or seen by users in the guild's **Server Settings**. All audit log entries are stored for 45 days. - -When an app is performing an eligible action using the APIs, it can pass an `X-Audit-Log-Reason` header to indicate why the action was taken. More information is in the [audit log entry](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object) section. - -### Audit Log Object - -###### Audit Log Structure - -| Field | Type | Description | -| ---------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- | -| application_commands | array of [application commands](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-object) objects | List of application commands referenced in the audit log | -| audit_log_entries | array of [audit log entry](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object) objects | List of audit log entries, sorted from most to least recent | -| auto_moderation_rules | array of [auto moderation rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) objects | List of auto moderation rules referenced in the audit log | -| guild_scheduled_events | array of [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) objects | List of guild scheduled events referenced in the audit log | -| integrations | array of partial [integration](#DOCS_RESOURCES_GUILD/integration-object) objects | List of partial integration objects | -| threads | array of thread-specific [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | List of threads referenced in the audit log\* | -| users | array of [user](#DOCS_RESOURCES_USER/user-object) objects | List of users referenced in the audit log | -| webhooks | array of [webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object) objects | List of webhooks referenced in the audit log | - -\* Threads referenced in `THREAD_CREATE` and `THREAD_UPDATE` events are included in the threads map since archived threads might not be kept in memory by clients. - -###### Example Partial Integration Object - -```json -{ - "id": "33590653072239123", - "name": "A Name", - "type": "twitch", - "account": { - "name": "twitchusername", - "id": "1234567" - }, - "application_id": "94651234501213162" -} -``` - -### Audit Log Entry Object - -Each audit log entry represents a single administrative action (or [event](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object-audit-log-events)), indicated by `action_type`. Most entries contain one to many changes in the `changes` array that affected an entity in Discord—whether that's a user, channel, guild, emoji, or something else. - -The information (and structure) of an entry's changes will be different depending on its type. For example, in `MEMBER_ROLE_UPDATE` events there is only one change: a member is either added or removed from a specific role. However, in `CHANNEL_CREATE` events there are many changes, including (but not limited to) the channel's name, type, and permission overwrites added. More details are in the [change object](#DOCS_RESOURCES_AUDIT_LOG/audit-log-change-object) section. - -Apps can specify why an administrative action is being taken by passing an `X-Audit-Log-Reason` request header, which will be stored as the audit log entry's `reason` field. The `X-Audit-Log-Reason` header supports 1-512 URL-encoded UTF-8 characters. Reasons are visible to users in the client and to apps when fetching audit log entries with the API. - -###### Audit Log Entry Structure - -| Field | Type | Description | -| ----------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -| target_id | ?string | ID of the affected entity (webhook, user, role, etc.) | -| changes? | array of [audit log change](#DOCS_RESOURCES_AUDIT_LOG/audit-log-change-object) objects | Changes made to the target_id | -| user_id | ?snowflake | User or app that made the changes | -| id | snowflake | ID of the entry | -| action_type | [audit log event](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object-audit-log-events) | Type of action that occurred | -| options? | [optional audit entry info](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object-optional-audit-entry-info) | Additional info for certain event types | -| reason? | string | Reason for the change (1-512 characters) | - -> warn -> For `APPLICATION_COMMAND_PERMISSION_UPDATE` events, the `target_id` is the command ID or the app ID since the `changes` array represents the entire `permissions` property on the [guild permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-guild-application-command-permissions-structure) object. - -###### Audit Log Events - -The table below lists audit log events and values (the `action_type` field) that your app may receive. - -The **Object Changed** column notes which object's values may be included in the entry. Though there are exceptions, possible keys in the `changes` array typically correspond to the object's fields. The descriptions and types for those fields can be found in the linked documentation for the object. - -If no object is noted, there won't be a `changes` array in the entry, though other fields like the `target_id` still exist and many have fields in the [`options` array](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object-optional-audit-entry-info). - -> info -> You should assume that your app may run into any field for the changed object, though none are guaranteed to be present. In most cases only a subset of the object's fields will be in the `changes` array. - -| Event | Value | Description | Object Changed | -| ------------------------------------------- | ----- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| GUILD_UPDATE | 1 | Server settings were updated | [Guild](#DOCS_RESOURCES_GUILD/guild-object) | -| CHANNEL_CREATE | 10 | Channel was created | [Channel](#DOCS_RESOURCES_CHANNEL/channel-object) | -| CHANNEL_UPDATE | 11 | Channel settings were updated | [Channel](#DOCS_RESOURCES_CHANNEL/channel-object) | -| CHANNEL_DELETE | 12 | Channel was deleted | [Channel](#DOCS_RESOURCES_CHANNEL/channel-object) | -| CHANNEL_OVERWRITE_CREATE | 13 | Permission overwrite was added to a channel | [Channel Overwrite](#DOCS_RESOURCES_CHANNEL/overwrite-object) | -| CHANNEL_OVERWRITE_UPDATE | 14 | Permission overwrite was updated for a channel | [Channel Overwrite](#DOCS_RESOURCES_CHANNEL/overwrite-object) | -| CHANNEL_OVERWRITE_DELETE | 15 | Permission overwrite was deleted from a channel | [Channel Overwrite](#DOCS_RESOURCES_CHANNEL/overwrite-object) | -| MEMBER_KICK | 20 | Member was removed from server | | -| MEMBER_PRUNE | 21 | Members were pruned from server | | -| MEMBER_BAN_ADD | 22 | Member was banned from server | | -| MEMBER_BAN_REMOVE | 23 | Server ban was lifted for a member | | -| MEMBER_UPDATE | 24 | Member was updated in server | [Member](#DOCS_RESOURCES_GUILD/guild-member-object) | -| MEMBER_ROLE_UPDATE | 25 | Member was added or removed from a role | [Partial Role](#DOCS_TOPICS_PERMISSIONS/role-object)\* | -| MEMBER_MOVE | 26 | Member was moved to a different voice channel | | -| MEMBER_DISCONNECT | 27 | Member was disconnected from a voice channel | | -| BOT_ADD | 28 | Bot user was added to server | | -| ROLE_CREATE | 30 | Role was created | [Role](#DOCS_TOPICS_PERMISSIONS/role-object) | -| ROLE_UPDATE | 31 | Role was edited | [Role](#DOCS_TOPICS_PERMISSIONS/role-object) | -| ROLE_DELETE | 32 | Role was deleted | [Role](#DOCS_TOPICS_PERMISSIONS/role-object) | -| INVITE_CREATE | 40 | Server invite was created | [Invite](#DOCS_RESOURCES_INVITE/invite-object) and [Invite Metadata](#DOCS_RESOURCES_INVITE/invite-metadata-object)* | -| INVITE_UPDATE | 41 | Server invite was updated | [Invite](#DOCS_RESOURCES_INVITE/invite-object) and [Invite Metadata](#DOCS_RESOURCES_INVITE/invite-metadata-object)* | -| INVITE_DELETE | 42 | Server invite was deleted | [Invite](#DOCS_RESOURCES_INVITE/invite-object) and [Invite Metadata](#DOCS_RESOURCES_INVITE/invite-metadata-object)* | -| WEBHOOK_CREATE | 50 | Webhook was created | [Webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object)\* | -| WEBHOOK_UPDATE | 51 | Webhook properties or channel were updated | [Webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object)\* | -| WEBHOOK_DELETE | 52 | Webhook was deleted | [Webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object)\* | -| EMOJI_CREATE | 60 | Emoji was created | [Emoji](#DOCS_RESOURCES_EMOJI/emoji-object) | -| EMOJI_UPDATE | 61 | Emoji name was updated | [Emoji](#DOCS_RESOURCES_EMOJI/emoji-object) | -| EMOJI_DELETE | 62 | Emoji was deleted | [Emoji](#DOCS_RESOURCES_EMOJI/emoji-object) | -| MESSAGE_DELETE | 72 | Single message was deleted | | -| MESSAGE_BULK_DELETE | 73 | Multiple messages were deleted | | -| MESSAGE_PIN | 74 | Message was pinned to a channel | | -| MESSAGE_UNPIN | 75 | Message was unpinned from a channel | | -| INTEGRATION_CREATE | 80 | App was added to server | [Integration](#DOCS_RESOURCES_GUILD/integration-object) | -| INTEGRATION_UPDATE | 81 | App was updated (as an example, its scopes were updated) | [Integration](#DOCS_RESOURCES_GUILD/integration-object) | -| INTEGRATION_DELETE | 82 | App was removed from server | [Integration](#DOCS_RESOURCES_GUILD/integration-object) | -| STAGE_INSTANCE_CREATE | 83 | Stage instance was created (stage channel becomes live) | [Stage Instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object) | -| STAGE_INSTANCE_UPDATE | 84 | Stage instance details were updated | [Stage Instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object) | -| STAGE_INSTANCE_DELETE | 85 | Stage instance was deleted (stage channel no longer live) | [Stage Instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object) | -| STICKER_CREATE | 90 | Sticker was created | [Sticker](#DOCS_RESOURCES_STICKER/sticker-object) | -| STICKER_UPDATE | 91 | Sticker details were updated | [Sticker](#DOCS_RESOURCES_STICKER/sticker-object) | -| STICKER_DELETE | 92 | Sticker was deleted | [Sticker](#DOCS_RESOURCES_STICKER/sticker-object) | -| GUILD_SCHEDULED_EVENT_CREATE | 100 | Event was created | [Guild Scheduled Event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) | -| GUILD_SCHEDULED_EVENT_UPDATE | 101 | Event was updated | [Guild Scheduled Event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) | -| GUILD_SCHEDULED_EVENT_DELETE | 102 | Event was cancelled | [Guild Scheduled Event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) | -| THREAD_CREATE | 110 | Thread was created in a channel | [Thread](#DOCS_RESOURCES_CHANNEL/thread-metadata-object) | -| THREAD_UPDATE | 111 | Thread was updated | [Thread](#DOCS_RESOURCES_CHANNEL/thread-metadata-object) | -| THREAD_DELETE | 112 | Thread was deleted | [Thread](#DOCS_RESOURCES_CHANNEL/thread-metadata-object) | -| APPLICATION_COMMAND_PERMISSION_UPDATE | 121 | Permissions were updated for a command | [Command Permission](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permissions-structure)\* | -| AUTO_MODERATION_RULE_CREATE | 140 | Auto Moderation rule was created | [Auto Moderation Rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) | -| AUTO_MODERATION_RULE_UPDATE | 141 | Auto Moderation rule was updated | [Auto Moderation Rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) | -| AUTO_MODERATION_RULE_DELETE | 142 | Auto Moderation rule was deleted | [Auto Moderation Rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) | -| AUTO_MODERATION_BLOCK_MESSAGE | 143 | Message was blocked by AutoMod | | -| AUTO_MODERATION_FLAG_TO_CHANNEL | 144 | Message was flagged by AutoMod | | -| AUTO_MODERATION_USER_COMMUNICATION_DISABLED | 145 | Member was timed out by AutoMod | | - -\* Object has exception(s) to available keys. See the [exceptions](#DOCS_RESOURCES_AUDIT_LOG/audit-log-change-object-audit-log-change-exceptions) section below for details. - -###### Optional Audit Entry Info - -| Field | Type | Description | Event Types | -| --------------------------------- | --------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| application_id | snowflake | ID of the app whose permissions were targeted | APPLICATION_COMMAND_PERMISSION_UPDATE | -| auto_moderation_rule_name | string | Name of the Auto Moderation rule that was triggered | AUTO_MODERATION_BLOCK_MESSAGE & AUTO_MODERATION_FLAG_TO_CHANNEL & AUTO_MODERATION_USER_COMMUNICATION_DISABLED | -| auto_moderation_rule_trigger_type | string | Trigger type of the Auto Moderation rule that was triggered | AUTO_MODERATION_BLOCK_MESSAGE & AUTO_MODERATION_FLAG_TO_CHANNEL & AUTO_MODERATION_USER_COMMUNICATION_DISABLED | -| channel_id | snowflake | Channel in which the entities were targeted | MEMBER_MOVE & MESSAGE_PIN & MESSAGE_UNPIN & MESSAGE_DELETE & STAGE_INSTANCE_CREATE & STAGE_INSTANCE_UPDATE & STAGE_INSTANCE_DELETE & AUTO_MODERATION_BLOCK_MESSAGE & AUTO_MODERATION_FLAG_TO_CHANNEL & AUTO_MODERATION_USER_COMMUNICATION_DISABLED | -| count | string | Number of entities that were targeted | MESSAGE_DELETE & MESSAGE_BULK_DELETE & MEMBER_DISCONNECT & MEMBER_MOVE | -| delete_member_days | string | Number of days after which inactive members were kicked | MEMBER_PRUNE | -| id | snowflake | ID of the overwritten entity | CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE | -| members_removed | string | Number of members removed by the prune | MEMBER_PRUNE | -| message_id | snowflake | ID of the message that was targeted | MESSAGE_PIN & MESSAGE_UNPIN | -| role_name | string | Name of the role if type is `"0"` (not present if type is `"1"`) | CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE | -| type | string | Type of overwritten entity - role (`"0"`) or member (`"1"`) | CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE | - -### Audit Log Change Object - -Many audit log events include a `changes` array in their [entry object](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object-audit-log-entry-structure). The [structure for the individual changes](#DOCS_RESOURCES_AUDIT_LOG/audit-log-change-object-audit-log-change-structure) varies based on the event type and its changed objects, so apps shouldn't depend on a single pattern of handling audit log events. - -###### Audit Log Change Structure - -Some events don't follow the same pattern as other audit log events. Details about these exceptions are explained in [the next section](#DOCS_RESOURCES_AUDIT_LOG/audit-log-change-object-audit-log-change-exceptions). - -> info -> If `new_value` is not present in the change object while `old_value` is, it indicates that the property has been reset or set to `null`. If `old_value` isn't included, it indicated that the property was previously `null`. - - -| Field | Type | Description | -| ---------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| new_value? | mixed (matches object field's type) | New value of the key | -| old_value? | mixed (matches object field's type) | Old value of the key | -| key | string | Name of the changed entity, with a few [exceptions](#DOCS_RESOURCES_AUDIT_LOG/audit-log-change-object-audit-log-change-exceptions) | - -###### Audit Log Change Exceptions - -For most objects, the change keys may be any field on the changed object. The following table details the exceptions to this pattern. - -| Object Changed | Change Key Exceptions | Change Object Exceptions | -| ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Command Permission](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-application-command-permissions-structure) | snowflake as key | The `changes` array contains objects with a `key` field representing the entity whose command was affected (role, channel, or user ID), a previous permissions object (with an `old_value` key), and an updated permissions object (with a `new_value` key) | -| [Invite](#DOCS_RESOURCES_INVITE/invite-object) and [Invite Metadata](#DOCS_RESOURCES_INVITE/invite-metadata-object) | Additional `channel_id` key (instead of object's `channel.id`) | | -| [Partial Role](#DOCS_TOPICS_PERMISSIONS/role-object) | `$add` and `$remove` as keys | `new_value` is an array of objects that contain the role `id` and `name` | -| [Webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object) | `avatar_hash` key (instead of `avatar`) | | - -## Get Guild Audit Log % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/audit-logs - -Returns an [audit log](#DOCS_RESOURCES_AUDIT_LOG/audit-log-object) object for the guild. Requires the [`VIEW_AUDIT_LOG`](#DOCS_TOPICS_PERMISSIONS/permissions-bitwise-permission-flags) permission. - -###### Query String Params - -The following parameters can be used to filter which and how many audit log entries are returned. - -| Field | Type | Description | -| ------------ | --------- | ----------------------------------------------------------------------------------------------------------- | -| user_id? | snowflake | Entries from a specific user ID | -| action_type? | integer | Entries for a specific [audit log event](#DOCS_RESOURCES_AUDIT_LOG/audit-log-entry-object-audit-log-events) | -| before? | snowflake | Entries that preceded a specific audit log entry ID | -| limit? | integer | Maximum number of entries (between 1-100) to return, defaults to 50 | diff --git a/docs/resources/Auto_Moderation.md b/docs/resources/Auto_Moderation.md deleted file mode 100644 index aacb7e1ca4..0000000000 --- a/docs/resources/Auto_Moderation.md +++ /dev/null @@ -1,258 +0,0 @@ -# Auto Moderation - -Auto Moderation is a feature which allows each [guild](#DOCS_RESOURCES_GUILD/) to set up rules that trigger based on some criteria. For example, a rule can trigger whenever a message contains a specific keyword. - -Rules can be configured to automatically execute actions whenever they trigger. For example, if a user tries to send a message which contains a certain keyword, a rule can trigger and block the message before it is sent. - -### Auto Moderation Rule Object - -###### Auto Moderation Rule Structure - -| Field | Type | Description | -| ---------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| id | snowflake | the id of this rule | -| guild_id | snowflake | the id of the guild which this rule belongs to | -| name | string | the rule name | -| creator_id | snowflake | the user which first created this rule | -| event_type | integer | the rule [event type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-event-types) | -| trigger_type | integer | the rule [trigger type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-types) | -| trigger_metadata | object | the rule [trigger metadata](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-metadata) | -| actions | array of [action](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object) objects | the actions which will execute when the rule is triggered | -| enabled | boolean | whether the rule is enabled | -| exempt_roles | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) | -| exempt_channels | array of snowflakes | the channel ids that should not be affected by the rule (Maximum of 50) | - -###### Example Auto Moderation Rule - -```json -{ - "id": "969707018069872670", - "guild_id": "613425648685547541", - "name": "Keyword Filter 1", - "creator_id": "423457898095789043", - "trigger_type": 1, - "event_type": 1, - "actions": [ - { - "type": 1, - "metadata": {} - }, - { - "type": 2, - "metadata": { "channel_id": "123456789123456789" } - } - ], - "trigger_metadata": { - "keyword_filter": ["cat*", "*dog", "*ana*", "i like javascript"] - }, - "enabled": true, - "exempt_roles": ["323456789123456789", "423456789123456789"], - "exempt_channels": ["523456789123456789"] -} -``` - -###### Trigger Types -Characterizes the type of content which can trigger the rule. - -| Type | Value | Description | Max per Guild | -| -------------- | ----- | -------------------------------------------------------------------- | ------------- | -| KEYWORD | 1 | check if content contains words from a user defined list of keywords | 3 | -| SPAM | 3 | check if content represents generic spam | 1 | -| KEYWORD_PRESET | 4 | check if content contains words from internal pre-defined wordsets | 1 | -| MENTION_SPAM | 5 | check if content contains more unique mentions than allowed | 1 | - -###### Trigger Metadata - -Additional data used to determine whether a rule should be triggered. Different fields are relevant based on the -value of [trigger_type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-types). - -| Field | Type | Associated Trigger Types | Description | -| ------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------ | -------------------------------------------------------------------------- | -| keyword_filter | array of strings * | KEYWORD | substrings which will be searched for in content | -| presets | array of [keyword preset types](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-keyword-preset-types) | KEYWORD_PRESET | the internally pre-defined wordsets which will be searched for in content | -| allow_list | array of strings * | KEYWORD_PRESET | substrings which will be exempt from triggering the preset trigger type | -| mention_total_limit | integer | MENTION_SPAM | total number of unique role and user mentions allowed per message (Maximum of 50) | - -\* A keyword can be a phrase which contains multiple words. Wildcard symbols (not available to allow lists) can be used to customize how each keyword will be matched. -See [keyword matching strategies](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-keyword-matching-strategies). - - -###### Keyword Preset Types - -| Type | Value | Description | -| -------------- | ----- | ------------------------------------------------------------ | -| PROFANITY | 1 | Words that may be considered forms of swearing or cursing | -| SEXUAL_CONTENT | 2 | Words that refer to sexually explicit behavior or activity | -| SLURS | 3 | Personal insults or words that may be considered hate speech | - - -###### Event Types - -Indicates in what event context a rule should be checked. - -| Type | Value | Description | -| ------------ | ----- | --------------------------------------------------- | -| MESSAGE_SEND | 1 | when a member sends or edits a message in the guild | - - -###### Keyword Matching Strategies - -Use the wildcard symbol (`*`) at the beginning or end of a keyword to define how it should be matched. All keywords are case insensitive. - -**Prefix** - word must start with the keyword - -| Keyword | Matches | -| --------- | ------------------------------------- | -| cat\* | **cat**ch, **Cat**apult, **CAt**tLE | -| tra\* | **tra**in, **tra**de, **TRA**ditional | -| the mat\* | **the mat**rix | - - -**Suffix** - word must end with the keyword - -| Keyword | Matches | -| --------- | ----------------------------------- | -| \*cat | wild**cat**, copy**Cat** | -| \*tra | ex**tra**, ul**tra**, orches**TRA** | -| \*the mat | brea**the mat** | - - -**Anywhere** - keyword can appear anywhere in the content - -| Keyword | Matches | -| ----------- | --------------------------- | -| \*cat\* | lo**cat**ion, edu**Cat**ion | -| \*tra\* | abs**tra**cted, ou**tra**ge | -| \*the mat\* | brea**the mat**ter | - - -**Whole Word** - keyword is a full word or phrase and must be surrounded by whitespace - -| Keyword | Matches | -| ------- | ----------- | -| cat | **cat** | -| train | **train** | -| the mat | **the mat** | - - -### Auto Moderation Action Object - -An action which will execute whenever a rule is triggered. - -###### Auto Moderation Action Structure - -| Field | Type | Description | -| ----------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | -| type | [action type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object-action-types) | the type of action | -| metadata? * | [action metadata](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object-action-metadata) | additional metadata needed during execution for this specific action type | - -\* Can be omitted based on `type`. See the `Associated Action Types` column in [action metadata](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object-action-metadata) to understand which `type` values require `metadata` to be set. - -###### Action Types - -| Type | Value | Description | -| ------------------ | ----- | ----------------------------------------------------- | -| BLOCK_MESSAGE | 1 | blocks the content of a message according to the rule | -| SEND_ALERT_MESSAGE | 2 | logs user content to a specified channel | -| TIMEOUT | 3 | timeout user for a specified duration * | - -\* A `TIMEOUT` action can only be set up for `KEYWORD` and `MENTION_SPAM` rules. The `MODERATE_MEMBERS` permission is required to use the `TIMEOUT` action type. - - -###### Action Metadata - -Additional data used when an action is executed. Different fields are relevant based on the -value of [action type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object-action-types). - -| Field | Type | Associated Action Types | Description | -| ---------------- | --------- | ----------------------- | ---------------------------------------------- | -| channel_id | snowflake | SEND_ALERT_MESSAGE | channel to which user content should be logged | -| duration_seconds | integer | TIMEOUT | timeout duration in seconds * | - -\* Maximum of 2419200 seconds (4 weeks) - - -### Auto Moderation Permission Requirements - -Users are required to have the `MANAGE_GUILD` permission to access all Auto Moderation resources. -Some [action types](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object-action-types) require additional permissions, e.g. the `TIMEOUT` action type requires an additional `MODERATE_MEMBERS` permission. - -## List Auto Moderation Rules for Guild % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/auto-moderation/rules - -Get a list of all rules currently configured for the guild. Returns a list of [auto moderation rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) objects for the given guild. - -> info -> This endpoint requires the `MANAGE_GUILD` [permission](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-permission-requirements). - -## Get Auto Moderation Rule % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/auto-moderation/rules/{auto_moderation_rule.id#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object} - -Get a single rule. Returns an [auto moderation rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) object. - -> info -> This endpoint requires the `MANAGE_GUILD` [permission](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-permission-requirements). - -## Create Auto Moderation Rule % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/auto-moderation/rules - -Create a new rule. Returns an [auto moderation rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) on success. Fires an [Auto Moderation Rule Create](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-create) Gateway event. - -> info -> This endpoint requires the `MANAGE_GUILD` [permission](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-permission-requirements). - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -| name | string | the rule name | -| event_type | integer | the [event type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-event-types) | -| trigger_type | integer | the [trigger type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-types) | -| trigger_metadata? * | object | the [trigger metadata](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-metadata) | -| actions | array of [action](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object) objects | the actions which will execute when the rule is triggered | -| enabled? | boolean | whether the rule is enabled (False by default) | -| exempt_roles? | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) | -| exempt_channels? | array of snowflakes | the channel ids that should not be affected by the rule (Maximum of 50) | - -\* Can be omitted based on `trigger_type`. See the `Associated Trigger Types` column in [trigger metadata](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-metadata) to understand which `trigger_type` values require `trigger_metadata` to be set. - -> info -> See [Trigger Types](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-types) for limits on how many rules of each trigger type can be created per guild. - - -## Modify Auto Moderation Rule % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/auto-moderation/rules/{auto_moderation_rule.id#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object} - -Modify an existing rule. Returns an [auto moderation rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) on success. Fires an [Auto Moderation Rule Update](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-update) Gateway event. - -> info -> Requires `MANAGE_GUILD` [permissions](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-permission-requirements). - -> info -> All parameters for this endpoint are optional. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -| name | string | the rule name | -| event_type | integer | the [event type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-event-types) | -| trigger_metadata? * | object | the [trigger metadata](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-metadata) | -| actions | array of [action](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object) objects | the actions which will execute when the rule is triggered | -| enabled | boolean | whether the rule is enabled | -| exempt_roles | array of snowflakes | the role ids that should not be affected by the rule (Maximum of 20) | -| exempt_channels | array of snowflakes | the channel ids that should not be affected by the rule (Maximum of 50) | - -\* Can be omitted based on `trigger_type`. See the `Associated Trigger Types` column in [trigger metadata](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-metadata) to understand which `trigger_type` values require `trigger_metadata` to be set. - -## Delete Auto Moderation Rule % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/auto-moderation/rules/{auto_moderation_rule.id#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object} - -Delete a rule. Returns a `204` on success. Fires an [Auto Moderation Rule Delete](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-delete) Gateway event. - -> info -> This endpoint requires the `MANAGE_GUILD` [permission](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-permission-requirements). - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. diff --git a/docs/resources/Channel.md b/docs/resources/Channel.md deleted file mode 100644 index 1765d9623c..0000000000 --- a/docs/resources/Channel.md +++ /dev/null @@ -1,1349 +0,0 @@ -# Channels Resource - -### Channel Object - -Represents a guild or DM channel within Discord. - -###### Channel Structure - -| Field | Type | Description | -| ----------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| id | snowflake | the id of this channel | -| type | integer | the [type of channel](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) | -| guild_id? | snowflake | the id of the guild (may be missing for some channel objects received over gateway guild dispatches) | -| position? | integer | sorting position of the channel | -| permission_overwrites? | array of [overwrite](#DOCS_RESOURCES_CHANNEL/overwrite-object) objects | explicit permission overwrites for members and roles | -| name? | ?string | the name of the channel (1-100 characters) | -| topic? | ?string | the channel topic (0-4096 characters for `GUILD_FORUM` channels, 0-1024 characters for all others) | -| nsfw? | boolean | whether the channel is nsfw | -| last_message_id? | ?snowflake | the id of the last message sent in this channel (or thread for `GUILD_FORUM` channels) (may not point to an existing or valid message or thread) | -| bitrate? | integer | the bitrate (in bits) of the voice channel | -| user_limit? | integer | the user limit of the voice channel | -| rate_limit_per_user?\* | integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages` or `manage_channel`, are unaffected | -| recipients? | array of [user](#DOCS_RESOURCES_USER/user-object) objects | the recipients of the DM | -| icon? | ?string | icon hash of the group DM | -| owner_id? | snowflake | id of the creator of the group DM or thread | -| application_id? | snowflake | application id of the group DM creator if it is bot-created | -| parent_id? | ?snowflake | for guild channels: id of the parent category for a channel (each parent category can contain up to 50 channels), for threads: id of the text channel this thread was created | -| last_pin_timestamp? | ?ISO8601 timestamp | when the last pinned message was pinned. This may be `null` in events such as `GUILD_CREATE` when a message is not pinned. | -| rtc_region? | ?string | [voice region](#DOCS_RESOURCES_VOICE/voice-region-object) id for the voice channel, automatic when set to null | -| video_quality_mode? | integer | the camera [video quality mode](#DOCS_RESOURCES_CHANNEL/channel-object-video-quality-modes) of the voice channel, 1 when not present | -| message_count?\*\* | integer | number of messages (not including the initial message or deleted messages) in a thread. | -| member_count? | integer | an approximate count of users in a thread, stops counting at 50 | -| thread_metadata? | a [thread metadata](#DOCS_RESOURCES_CHANNEL/thread-metadata-object) object | thread-specific fields not needed by other channels | -| member? | a [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object | thread member object for the current user, if they have joined the thread, only included on certain API endpoints | -| default_auto_archive_duration? | integer | default duration, copied onto newly created threads, in minutes, threads will stop showing in the channel list after the specified period of inactivity, can be set to: 60, 1440, 4320, 10080 | -| permissions? | string | computed permissions for the invoking user in the channel, including overwrites, only included when part of the `resolved` data received on a slash command interaction | -| flags? | integer | [channel flags](#DOCS_RESOURCES_CHANNEL/channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | -| total_message_sent? | integer | number of messages ever sent in a thread, it's similar to `message_count` on message creation, but will not decrement the number when a message is deleted | -| available_tags? | array of [tag](#DOCS_RESOURCES_CHANNEL/forum-tag-object) objects | the set of tags that can be used in a `GUILD_FORUM` channel | -| applied_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` channel | -| default_reaction_emoji? | ?[default reaction](#DOCS_RESOURCES_CHANNEL/default-reaction-object) object | the emoji to show in the add reaction button on a thread in a `GUILD_FORUM` channel | -| default_thread_rate_limit_per_user? | integer | the initial `rate_limit_per_user` to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update. | -| default_sort_order? | ?integer | the [default sort order type](#DOCS_RESOURCES_CHANNEL/channel-object-sort-order-types) used to order posts in `GUILD_FORUM` channels. Defaults to `null`, which indicates a preferred sort order hasn't been set by a channel admin | - -\* `rate_limit_per_user` also applies to thread creation. Users can send one message and create one thread during each `rate_limit_per_user` interval. -\*\* For threads created before July 1, 2022, the message count is inaccurate when it's greater than 50. - -###### Channel Types - -> warn -> Type 10, 11 and 12 are only available in API v9 and above. - -| Type | ID | Description | -| ------------------- | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| GUILD_TEXT | 0 | a text channel within a server | -| DM | 1 | a direct message between users | -| GUILD_VOICE | 2 | a voice channel within a server | -| GROUP_DM | 3 | a direct message between multiple users | -| GUILD_CATEGORY | 4 | an [organizational category](https://support.discord.com/hc/en-us/articles/115001580171-Channel-Categories-101) that contains up to 50 channels | -| GUILD_ANNOUNCEMENT | 5 | a channel that [users can follow and crosspost into their own server](https://support.discord.com/hc/en-us/articles/360032008192) (formerly news channels) | -| ANNOUNCEMENT_THREAD | 10 | a temporary sub-channel within a GUILD_ANNOUNCEMENT channel | -| PUBLIC_THREAD | 11 | a temporary sub-channel within a GUILD_TEXT channel | -| PRIVATE_THREAD | 12 | a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission | -| GUILD_STAGE_VOICE | 13 | a voice channel for [hosting events with an audience](https://support.discord.com/hc/en-us/articles/1500005513722) | -| GUILD_DIRECTORY | 14 | the channel in a [hub](https://support.discord.com/hc/en-us/articles/4406046651927-Discord-Student-Hubs-FAQ) containing the listed servers | -| GUILD_FORUM | 15 | Channel that can only contain threads | - -###### Video Quality Modes - -| Mode | Value | Description | -| ---- | ----- | --------------------------------------------------- | -| AUTO | 1 | Discord chooses the quality for optimal performance | -| FULL | 2 | 720p | - -###### Channel Flags - -| Flag | Value | Description | -| ----------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------- | -| PINNED | 1 << 1 | this thread is pinned to the top of its parent `GUILD_FORUM` channel | -| REQUIRE_TAG | 1 << 4 | whether a tag is required to be specified when creating a thread in a `GUILD_FORUM` channel. Tags are specified in the `applied_tags` field. | - -###### Sort Order Types - -| Flag | Value | Description | -| --------------- | ----- | -------------------------------------------------------------- | -| LATEST_ACTIVITY | 0 | Sort forum posts by activity | -| CREATION_DATE | 1 | Sort forum posts by creation time (from most recent to oldest) | - -###### Example Guild Text Channel - -```json -{ - "id": "41771983423143937", - "guild_id": "41771983423143937", - "name": "general", - "type": 0, - "position": 6, - "permission_overwrites": [], - "rate_limit_per_user": 2, - "nsfw": true, - "topic": "24/7 chat about how to gank Mike #2", - "last_message_id": "155117677105512449", - "parent_id": "399942396007890945", - "default_auto_archive_duration": 60 -} -``` - -###### Example Guild Announcement Channel - -Bots can post or publish messages in this type of channel if they have the proper permissions. - -```json -{ - "id": "41771983423143937", - "guild_id": "41771983423143937", - "name": "important-news", - "type": 5, - "position": 6, - "permission_overwrites": [], - "nsfw": true, - "topic": "Rumors about Half Life 3", - "last_message_id": "155117677105512449", - "parent_id": "399942396007890945", - "default_auto_archive_duration": 60 -} -``` - -###### Example Guild Voice Channel - -```json -{ - "id": "155101607195836416", - "last_message_id": "174629835082649376", - "type": 2, - "name": "ROCKET CHEESE", - "position": 5, - "parent_id": null, - "bitrate": 64000, - "user_limit": 0, - "rtc_region": null, - "guild_id": "41771983423143937", - "permission_overwrites": [], - "rate_limit_per_user": 0, - "nsfw": false, -} -``` - -###### Example DM Channel - -```json -{ - "last_message_id": "3343820033257021450", - "type": 1, - "id": "319674150115610528", - "recipients": [ - { - "username": "test", - "discriminator": "9999", - "id": "82198898841029460", - "avatar": "33ecab261d4681afa4d85a04691c4a01" - } - ] -} -``` - -###### Example Group DM Channel - -```json -{ - "name": "Some test channel", - "icon": null, - "recipients": [ - { - "username": "test", - "discriminator": "9999", - "id": "82198898841029460", - "avatar": "33ecab261d4681afa4d85a04691c4a01" - }, - { - "username": "test2", - "discriminator": "9999", - "id": "82198810841029460", - "avatar": "33ecab261d4681afa4d85a10691c4a01" - } - ], - "last_message_id": "3343820033257021450", - "type": 3, - "id": "319674150115710528", - "owner_id": "82198810841029460" -} -``` - -###### Example Channel Category - -```json -{ - "permission_overwrites": [], - "name": "Test", - "parent_id": null, - "nsfw": false, - "position": 0, - "guild_id": "290926798629997250", - "type": 4, - "id": "399942396007890945" -} -``` - -###### Example Thread Channel - -[Threads](#DOCS_TOPICS_THREADS) can be either `archived` or `active`. Archived threads are generally immutable. To send a message or add a reaction, a thread must first be unarchived. The API will helpfully automatically unarchive a thread when sending a message in that thread. - -Unlike with channels, the API will only sync updates to users about threads the current user can view. When receiving a [guild create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) payload, the API will only include active threads the current user can view. Threads inside of private channels are completely private to the members of that private channel. As such, when _gaining_ access to a channel the API sends a [thread list sync](#DOCS_TOPICS_GATEWAY_EVENTS/thread-list-sync), which includes all active threads in that channel. - -Threads also track membership. Users must be added to a thread before sending messages in them. The API will helpfully automatically add users to a thread when sending a message in that thread. - -Guilds have limits on the number of active threads and members per thread. Once these are reached additional threads cannot be created or unarchived, and users cannot be added. Threads do not count against the per-guild channel limit. - -The [threads](#DOCS_TOPICS_THREADS) topic has some more information. - -```json -{ - "id": "41771983423143937", - "guild_id": "41771983423143937", - "parent_id": "41771983423143937", - "owner_id": "41771983423143937", - "name": "don't buy dota-2", - "type": 11, - "last_message_id": "155117677105512449", - "message_count": 1, - "member_count": 5, - "rate_limit_per_user": 2, - "thread_metadata": { - "archived": false, - "auto_archive_duration": 1440, - "archive_timestamp": "2021-04-12T23:40:39.855793+00:00", - "locked": false - }, - "total_message_sent": 1 -} -``` - -### Message Object - -Represents a message sent in a channel within Discord. - -###### Message Structure - -> info -> Fields specific to the `MESSAGE_CREATE` and `MESSAGE_UPDATE` events are listed in the [Gateway documentation](#DOCS_TOPICS_GATEWAY_EVENTS/message-create). - -> warn -> `content`, `embeds`, `attachments`, and `components` require the [`MESSAGE_CONTENT` intent](#DOCS_TOPICS_GATEWAY/message-content-intent) to receive non-empty values. - -| Field | Type | Description | -| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| id | snowflake | id of the message | -| channel_id | snowflake | id of the channel the message was sent in | -| author\* | [user](#DOCS_RESOURCES_USER/user-object) object | the author of this message (not guaranteed to be a valid user, see below) | -| content\*\* | string | contents of the message | -| timestamp | ISO8601 timestamp | when this message was sent | -| edited_timestamp | ?ISO8601 timestamp | when this message was edited (or null if never) | -| tts | boolean | whether this was a TTS message | -| mention_everyone | boolean | whether this message mentions everyone | -| mentions | array of [user](#DOCS_RESOURCES_USER/user-object) objects | users specifically mentioned in the message | -| mention_roles | array of [role](#DOCS_TOPICS_PERMISSIONS/role-object) object ids | roles specifically mentioned in this message | -| mention_channels?\*\*\* | array of [channel mention](#DOCS_RESOURCES_CHANNEL/channel-mention-object) objects | channels specifically mentioned in this message | -| attachments\*\* | array of [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) objects | any attached files | -| embeds\*\* | array of [embed](#DOCS_RESOURCES_CHANNEL/embed-object) objects | any embedded content | -| reactions? | array of [reaction](#DOCS_RESOURCES_CHANNEL/reaction-object) objects | reactions to the message | -| nonce? | integer or string | used for validating a message was sent | -| pinned | boolean | whether this message is pinned | -| webhook_id? | snowflake | if the message is generated by a webhook, this is the webhook's id | -| type | integer | [type of message](#DOCS_RESOURCES_CHANNEL/message-object-message-types) | -| activity? | [message activity](#DOCS_RESOURCES_CHANNEL/message-object-message-activity-structure) object | sent with Rich Presence-related chat embeds | -| application? | partial [application](#DOCS_RESOURCES_APPLICATION/application-object) object | sent with Rich Presence-related chat embeds | -| application_id? | snowflake | if the message is an [Interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/) or application-owned webhook, this is the id of the application | -| message_reference? | [message reference](#DOCS_RESOURCES_CHANNEL/message-reference-object-message-reference-structure) object | data showing the source of a crosspost, channel follow add, pin, or reply message | -| flags? | integer | [message flags](#DOCS_RESOURCES_CHANNEL/message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) | -| referenced_message?\*\*\*\* | ?[message object](#DOCS_RESOURCES_CHANNEL/message-object) | the message associated with the message_reference | -| interaction? | [message interaction object](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/message-interaction-object-message-interaction-structure) | sent if the message is a response to an [Interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/) | -| thread? | [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object | the thread that was started from this message, includes [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object | -| components?\*\* | array of [message components](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/component-object) | sent if the message contains components like buttons, action rows, or other interactive components | -| sticker_items? | array of [message sticker item objects](#DOCS_RESOURCES_STICKER/sticker-item-object) | sent if the message contains stickers | -| stickers? | array of [sticker](#DOCS_RESOURCES_STICKER/sticker-object) objects | **Deprecated** the stickers sent with the message | -| position? | integer | A generally increasing integer (there may be gaps or duplicates) that represents the approximate position of the message in a thread, it can be used to estimate the relative position of the message in a thread in company with `total_message_sent` on parent thread | - -\* The author object follows the structure of the user object, but is only a valid user in the case where the message is generated by a user or bot user. If the message is generated by a webhook, the author object corresponds to the webhook's id, username, and avatar. You can tell if a message is generated by a webhook by checking for the `webhook_id` on the message object. - -\*\* An app will receive empty values in the `content`, `embeds`, `attachments`, and `components` fields if they have not configured (or been approved for) the [`MESSAGE_CONTENT` privileged intent (`1 << 15`)](#DOCS_TOPICS_GATEWAY/message-content-intent). - -\*\*\* Not all channel mentions in a message will appear in `mention_channels`. Only textual channels that are visible to everyone in a lurkable guild will ever be included. Only crossposted messages (via Channel Following) currently include `mention_channels` at all. If no mentions in the message meet these requirements, this field will not be sent. - -\*\*\*\* This field is only returned for messages with a `type` of `19` (REPLY) or `21` (THREAD_STARTER_MESSAGE). If the message is a reply but the `referenced_message` field is not present, the backend did not attempt to fetch the message that was being replied to, so its state is unknown. If the field exists but is null, the referenced message was deleted. - - -###### Message Types - -> warn -> Type `19` and `20` are only available in API v8 and above. In v6, they are represented as type `0`. Additionally, type `21` is only available in API v9 and above. - -| Type | Value | Deletable | -|----------------------------------------------|-------|-----------| -| DEFAULT | 0 | true | -| RECIPIENT_ADD | 1 | false | -| RECIPIENT_REMOVE | 2 | false | -| CALL | 3 | false | -| CHANNEL_NAME_CHANGE | 4 | false | -| CHANNEL_ICON_CHANGE | 5 | false | -| CHANNEL_PINNED_MESSAGE | 6 | true | -| USER_JOIN | 7 | true | -| GUILD_BOOST | 8 | true | -| GUILD_BOOST_TIER_1 | 9 | true | -| GUILD_BOOST_TIER_2 | 10 | true | -| GUILD_BOOST_TIER_3 | 11 | true | -| CHANNEL_FOLLOW_ADD | 12 | true | -| GUILD_DISCOVERY_DISQUALIFIED | 14 | false | -| GUILD_DISCOVERY_REQUALIFIED | 15 | false | -| GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING | 16 | false | -| GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING | 17 | false | -| THREAD_CREATED | 18 | true | -| REPLY | 19 | true | -| CHAT_INPUT_COMMAND | 20 | true | -| THREAD_STARTER_MESSAGE | 21 | false | -| GUILD_INVITE_REMINDER | 22 | true | -| CONTEXT_MENU_COMMAND | 23 | true | -| AUTO_MODERATION_ACTION | 24 | true* | - -\* Can only be deleted by members with `MANAGE_MESSAGES` permission - -###### Message Activity Structure - -| Field | Type | Description | -| --------- | ------- | ------------------------------------------------------------------------------------------------------------------ | -| type | integer | [type of message activity](#DOCS_RESOURCES_CHANNEL/message-object-message-activity-types) | -| party_id? | string | party_id from a [Rich Presence event](#DOCS_RICH_PRESENCE_HOW_TO/updating-presence-update-presence-payload-fields) | - -###### Message Activity Types - -| Type | Value | -| ------------ | ----- | -| JOIN | 1 | -| SPECTATE | 2 | -| LISTEN | 3 | -| JOIN_REQUEST | 5 | - -###### Message Flags - -| Flag | Value | Description | -| -------------------------------------- | ------ | --------------------------------------------------------------------------------- | -| CROSSPOSTED | 1 << 0 | this message has been published to subscribed channels (via Channel Following) | -| IS_CROSSPOST | 1 << 1 | this message originated from a message in another channel (via Channel Following) | -| SUPPRESS_EMBEDS | 1 << 2 | do not include any embeds when serializing this message | -| SOURCE_MESSAGE_DELETED | 1 << 3 | the source message for this crosspost has been deleted (via Channel Following) | -| URGENT | 1 << 4 | this message came from the urgent message system | -| HAS_THREAD | 1 << 5 | this message has an associated thread, with the same id as the message | -| EPHEMERAL | 1 << 6 | this message is only visible to the user who invoked the Interaction | -| LOADING | 1 << 7 | this message is an Interaction Response and the bot is "thinking" | -| FAILED_TO_MENTION_SOME_ROLES_IN_THREAD | 1 << 8 | this message failed to mention some roles and add their members to the thread | - -###### Example Message - -```json -{ - "reactions": [ - { - "count": 1, - "me": false, - "emoji": { - "id": null, - "name": "🔥" - } - } - ], - "attachments": [], - "tts": false, - "embeds": [], - "timestamp": "2017-07-11T17:27:07.299000+00:00", - "mention_everyone": false, - "id": "334385199974967042", - "pinned": false, - "edited_timestamp": null, - "author": { - "username": "Mason", - "discriminator": "9999", - "id": "53908099506183680", - "avatar": "a_bab14f271d565501444b2ca3be944b25" - }, - "mention_roles": [], - "content": "Supa Hot", - "channel_id": "290926798999357250", - "mentions": [], - "type": 0 -} -``` - -###### Example Crossposted Message - -```json -{ - "reactions": [ - { - "count": 1, - "me": false, - "emoji": { - "id": null, - "name": "🔥" - } - } - ], - "attachments": [], - "tts": false, - "embeds": [], - "timestamp": "2017-07-11T17:27:07.299000+00:00", - "mention_everyone": false, - "id": "334385199974967042", - "pinned": false, - "edited_timestamp": null, - "author": { - "username": "Mason", - "discriminator": "9999", - "id": "53908099506183680", - "avatar": "a_bab14f271d565501444b2ca3be944b25" - }, - "mention_roles": [], - "mention_channels": [ - { - "id": "278325129692446722", - "guild_id": "278325129692446720", - "name": "big-news", - "type": 5 - } - ], - "content": "Big news! In this <#278325129692446722> channel!", - "channel_id": "290926798999357250", - "mentions": [], - "type": 0, - "flags": 2, - "message_reference": { - "channel_id": "278325129692446722", - "guild_id": "278325129692446720", - "message_id": "306588351130107906" - } -} -``` - -### Message Reference Object - -###### Message Reference Structure - -| Field | Type | Description | -| ------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------- | -| message_id? | snowflake | id of the originating message | -| channel_id? \* | snowflake | id of the originating message's channel | -| guild_id? | snowflake | id of the originating message's guild | -| fail_if_not_exists? | boolean | when sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default true | - -\* `channel_id` is optional when creating a reply, but will always be present when receiving an event/response that includes this data model. - -#### Message Types - -There are multiple message types that have a message_reference object. Since message references are generic attribution to a previous message, there will be more types of messages which have this information in the future. - -###### Crosspost messages - -- These are messages that originated from another channel (IS_CROSSPOST flag). -- These messages have all three fields, which point to the original message that was crossposted. - -###### Channel Follow Add messages - -- These are automatic messages sent when a channel is followed into the current channel (type 12). -- These messages have the `channel_id` and `guild_id` fields, which point to the followed announcement channel. - -###### Pin messages - -- These are automatic messages sent when a message is pinned (type 6). -- These messages have `message_id` and `channel_id`, and `guild_id` if it is in a guild, which point to the message that was pinned. - -###### Replies - -- These are messages replying to a previous message (type 19). -- These messages have `message_id` and `channel_id`, and `guild_id` if it is in a guild, which point to the message that was replied to. The channel_id and guild_id will be the same as the reply. -- Replies are created by including a message_reference when sending a message. When sending, only `message_id` is required. - -###### Thread Created messages - -- These are automatic messages sent when a public thread is created from an old message or without a message (type 18). -- These messages have the `channel_id` and `guild_id` fields, which point to the created thread channel. - -###### Thread starter messages - -- These are the first message in public threads created from messages. They point back to the message in the parent channel from which the thread was started. (type 21) -- These messages have `message_id`, `channel_id`, and `guild_id`. -- These messages will never have content, embeds, or attachments, mainly just the `message_reference` and `referenced_message` fields. - -### Followed Channel Object - -###### Followed Channel Structure - -| Field | Type | Description | -| ---------- | --------- | ------------------------- | -| channel_id | snowflake | source channel id | -| webhook_id | snowflake | created target webhook id | - -### Reaction Object - -###### Reaction Structure - -| Field | Type | Description | -| ----- | ---------------------------------------------------------- | ------------------------------------------------- | -| count | integer | times this emoji has been used to react | -| me | boolean | whether the current user reacted using this emoji | -| emoji | partial [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) object | emoji information | - -### Overwrite Object - -See [permissions](#DOCS_TOPICS_PERMISSIONS/permissions) for more information about the `allow` and `deny` fields. - -###### Overwrite Structure - -| Field | Type | Description | -| ----- | --------- | ----------------------------- | -| id | snowflake | role or user id | -| type | int | either 0 (role) or 1 (member) | -| allow | string | permission bit set | -| deny | string | permission bit set | - -### Thread Metadata Object - -The thread metadata object contains a number of thread-specific channel fields that are not needed by other channel types. - -###### Thread Metadata Structure - -| Field | Type | Description | -| --------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | -| archived | boolean | whether the thread is archived | -| auto_archive_duration | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 | -| archive_timestamp | ISO8601 timestamp | timestamp when the thread's archive status was last changed, used for calculating recent activity | -| locked | boolean | whether the thread is locked; when a thread is locked, only users with MANAGE_THREADS can unarchive it | -| invitable? | boolean | whether non-moderators can add other non-moderators to a thread; only available on private threads | -| create_timestamp? | ?ISO8601 timestamp | timestamp when the thread was created; only populated for threads created after 2022-01-09 | - -### Thread Member Object - -A thread member is used to indicate whether a user has joined a thread or not. - -###### Thread Member Structure - -| Field | Type | Description | -| -------------- | ----------------- | --------------------------------------------------------------- | -| id? \* | snowflake | the id of the thread | -| user_id? \* | snowflake | the id of the user | -| join_timestamp | ISO8601 timestamp | the time the current user last joined the thread | -| flags | integer | any user-thread settings, currently only used for notifications | - -** \* These fields are omitted on the member sent within each thread in the [GUILD_CREATE](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) event ** - -### Default Reaction Object - -An object that specifies the emoji to use as the default way to react to a forum post. Exactly one of `emoji_id` and `emoji_name` must be set. - -###### Default Reaction Structure - -| Field | Type | Description | -| ---------- | ---------- | ---------------------------------- | -| emoji_id | ?snowflake | the id of a guild's custom emoji | -| emoji_name | ?string | the unicode character of the emoji | - -### Forum Tag Object - -An object that represents a tag that is able to be applied to a thread in a `GUILD_FORUM` channel. - -###### Forum Tag Structure - -> info -> When updating a `GUILD_FORUM` channel, tag objects in `available_tags` only require the `name` field. - -| Field | Type | Description | -| ---------- | --------- | -------------------------------------------------------------------------------------------------------------- | -| id | snowflake | the id of the tag | -| name | string | the name of the tag (0-20 characters) | -| moderated | boolean | whether this tag can only be added to or removed from threads by a member with the `MANAGE_THREADS` permission | -| emoji_id | snowflake | the id of a guild's custom emoji \* | -| emoji_name | ?string | the unicode character of the emoji \* | - -\* At most one of `emoji_id` and `emoji_name` may be set. - -### Embed Object - -###### Embed Structure - -| Field | Type | Description | -| ------------ | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | -| title? | string | title of embed | -| type? | string | [type of embed](#DOCS_RESOURCES_CHANNEL/embed-object-embed-types) (always "rich" for webhook embeds) | -| description? | string | description of embed | -| url? | string | url of embed | -| timestamp? | ISO8601 timestamp | timestamp of embed content | -| color? | integer | color code of the embed | -| footer? | [embed footer](#DOCS_RESOURCES_CHANNEL/embed-object-embed-footer-structure) object | footer information | -| image? | [embed image](#DOCS_RESOURCES_CHANNEL/embed-object-embed-image-structure) object | image information | -| thumbnail? | [embed thumbnail](#DOCS_RESOURCES_CHANNEL/embed-object-embed-thumbnail-structure) object | thumbnail information | -| video? | [embed video](#DOCS_RESOURCES_CHANNEL/embed-object-embed-video-structure) object | video information | -| provider? | [embed provider](#DOCS_RESOURCES_CHANNEL/embed-object-embed-provider-structure) object | provider information | -| author? | [embed author](#DOCS_RESOURCES_CHANNEL/embed-object-embed-author-structure) object | author information | -| fields? | array of [embed field](#DOCS_RESOURCES_CHANNEL/embed-object-embed-field-structure) objects | fields information | - -###### Embed Types - -Embed types are "loosely defined" and, for the most part, are not used by our clients for rendering. Embed attributes power what is rendered. Embed types should be considered deprecated and might be removed in a future API version. - -| Type | Description | -| ------- | -------------------------------------------------- | -| rich | generic embed rendered from embed attributes | -| image | image embed | -| video | video embed | -| gifv | animated gif image embed rendered as a video embed | -| article | article embed | -| link | link embed | - -###### Embed Thumbnail Structure - -| Field | Type | Description | -| ---------- | ------- | --------------------------------------------------------------- | -| url | string | source url of thumbnail (only supports http(s) and attachments) | -| proxy_url? | string | a proxied url of the thumbnail | -| height? | integer | height of thumbnail | -| width? | integer | width of thumbnail | - -###### Embed Video Structure - -| Field | Type | Description | -| ---------- | ------- | -------------------------- | -| url? | string | source url of video | -| proxy_url? | string | a proxied url of the video | -| height? | integer | height of video | -| width? | integer | width of video | - -###### Embed Image Structure - -| Field | Type | Description | -| ---------- | ------- | ----------------------------------------------------------- | -| url | string | source url of image (only supports http(s) and attachments) | -| proxy_url? | string | a proxied url of the image | -| height? | integer | height of image | -| width? | integer | width of image | - -###### Embed Provider Structure - -| Field | Type | Description | -| ----- | ------ | ---------------- | -| name? | string | name of provider | -| url? | string | url of provider | - -###### Embed Author Structure - -| Field | Type | Description | -| --------------- | ------ | ---------------------------------------------------------- | -| name | string | name of author | -| url? | string | url of author | -| icon_url? | string | url of author icon (only supports http(s) and attachments) | -| proxy_icon_url? | string | a proxied url of author icon | - -###### Embed Footer Structure - -| Field | Type | Description | -| --------------- | ------ | ---------------------------------------------------------- | -| text | string | footer text | -| icon_url? | string | url of footer icon (only supports http(s) and attachments) | -| proxy_icon_url? | string | a proxied url of footer icon | - -###### Embed Field Structure - -| Field | Type | Description | -| ------- | ------- | ----------------------------------------------- | -| name | string | name of the field | -| value | string | value of the field | -| inline? | boolean | whether or not this field should display inline | - -###### Embed Limits - -To facilitate showing rich content, rich embeds do not follow the traditional limits of message content. However, some limits are still in place to prevent excessively large embeds. The following table describes the limits: - -All of the following limits are measured inclusively. Leading and trailing whitespace characters are not included (they are trimmed automatically). - -| Field | Limit | -| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| title | 256 characters | -| description | 4096 characters | -| fields | Up to 25 [field](#DOCS_RESOURCES_CHANNEL/embed-object-embed-field-structure) objects | -| [field.name](#DOCS_RESOURCES_CHANNEL/embed-object-embed-field-structure) | 256 characters | -| [field.value](#DOCS_RESOURCES_CHANNEL/embed-object-embed-field-structure) | 1024 characters | -| [footer.text](#DOCS_RESOURCES_CHANNEL/embed-object-embed-footer-structure) | 2048 characters | -| [author.name](#DOCS_RESOURCES_CHANNEL/embed-object-embed-author-structure) | 256 characters | - -Additionally, the combined sum of characters in all `title`, `description`, `field.name`, `field.value`, `footer.text`, and `author.name` fields across all embeds attached to a message must not exceed 6000 characters. Violating any of these constraints will result in a `Bad Request` response. - -### Attachment Object - -###### Attachment Structure - -> info -> For the `attachments` array in Message Create/Edit requests, only the `id` is required. - -| Field | Type | Description | -| ------------- | --------- | ----------------------------------------------------------------------- | -| id | snowflake | attachment id | -| filename | string | name of file attached | -| description? | string | description for the file (max 1024 characters) | -| content_type? | string | the attachment's [media type](https://en.wikipedia.org/wiki/Media_type) | -| size | integer | size of file in bytes | -| url | string | source url of file | -| proxy_url | string | a proxied url of file | -| height? | ?integer | height of file (if image) | -| width? | ?integer | width of file (if image) | -| ephemeral? \* | boolean | whether this attachment is ephemeral | - -\* Ephemeral attachments will automatically be removed after a set period of time. Ephemeral attachments on messages are guaranteed to be available as long as the message itself exists. - -### Channel Mention Object - -###### Channel Mention Structure - -| Field | Type | Description | -| -------- | --------- | --------------------------------------------------------------------------- | -| id | snowflake | id of the channel | -| guild_id | snowflake | id of the guild containing the channel | -| type | integer | the [type of channel](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) | -| name | string | the name of the channel | - -### Allowed Mentions Object - -The allowed mention field allows for more granular control over mentions without various hacks to the message content. This will always validate against message content to avoid phantom pings (e.g. to ping everyone, you must still have `@everyone` in the message content), and check against user/bot permissions. - -###### Allowed Mention Types - -| Type | Value | Description | -| ----------------- | ---------- | ------------------------------------- | -| Role Mentions | "roles" | Controls role mentions | -| User Mentions | "users" | Controls user mentions | -| Everyone Mentions | "everyone" | Controls @everyone and @here mentions | - -###### Allowed Mentions Structure - -| Field | Type | Description | -| ------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -| parse | array of allowed mention types | An array of [allowed mention types](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object-allowed-mention-types) to parse from the content. | -| roles | list of snowflakes | Array of role_ids to mention (Max size of 100) | -| users | list of snowflakes | Array of user_ids to mention (Max size of 100) | -| replied_user | boolean | For replies, whether to mention the author of the message being replied to (default false) | - -###### Allowed Mentions Reference - -Due to the complexity of possibilities, we have included a set of examples and behavior for the allowed mentions field. - -If `allowed_mentions` is _not_ passed in (i.e. the key does not exist), the mentions will be parsed via the content. This corresponds with existing behavior. - -In the example below we would ping @here (and also @role124 and @user123) - -```json -{ - "content": "@here Hi there from <@123>, cc <@&124>" -} -``` - -To suppress all mentions in a message use: - -```json -{ - "content": "@everyone hi there, <@&123>", - "allowed_mentions": { - "parse": [] - } -} -``` - -This will suppress _all_ mentions in the message (no @everyone or user mention). - -The `parse` field is mutually exclusive with the other fields. In the example below, we would ping users `123` and role `124`, but _not_ @everyone. Note that passing a `Falsy` value ([], null) into the "users" field does not trigger a validation error. - -```json -{ - "content": "@everyone <@123> <@&124>", - "allowed_mentions": { - "parse": ["users", "roles"], - "users": [] - } -} -``` - -In the next example, we would ping @everyone, (and also users `123` and `124` if they suppressed -@everyone mentions), but we would not ping any roles. - -```json -{ - "content": "@everyone <@123> <@124> <@125> <@&200>", - "allowed_mentions": { - "parse": ["everyone"], - "users": ["123", "124"] - } -} -``` - -Due to possible ambiguities, not all configurations are valid. An _invalid_ configuration is as follows - -```json -{ - "content": "@everyone <@123> <@124> <@125> <@&200>", - "allowed_mentions": { - "parse": ["users"], - "users": ["123", "124"] - } -} -``` - -Because `parse: ["users"]` and `users: [123, 124]` are both present, we would throw a validation error. -This is because the conditions cannot be fulfilled simultaneously (they are mutually exclusive). - -Any entities with an ID included in the list of IDs can be mentioned. Note that the IDs of entities not present in the message's content will simply be ignored. -e.g. The following example is valid, and would mention user 123, but _not_ user 125 since there is no mention of -user 125 in the content. - -```json -{ - "content": "<@123> Time for some memes.", - "allowed_mentions": { - "users": ["123", "125"] - } -} -``` - -## Get Channel % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object} - -Get a channel by ID. Returns a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object. If the channel is a thread, a [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object is included in the returned result. - -## Modify Channel % PATCH /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object} - -Update a channel's settings. Returns a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) on success, and a 400 BAD REQUEST on invalid parameters. All JSON parameters are optional. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params (Group DM) - -Fires a [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update) Gateway event. - -| Field | Type | Description | -| ----- | ------ | ---------------------------- | -| name | string | 1-100 character channel name | -| icon | binary | base64 encoded icon | - -###### JSON Params (Guild channel) - -Requires the `MANAGE_CHANNELS` permission for the guild. Fires a [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update) Gateway event. If modifying a category, individual [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update) events will fire for each child channel that also changes. If modifying permission overwrites, the `MANAGE_ROLES` permission is required. Only permissions your bot has in the guild or parent channel (if applicable) can be allowed/denied (unless your bot has a `MANAGE_ROLES` overwrite in the channel). - -| Field | Type | Description | Channel Type | -| ----------------------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | -| name | string | 1-100 character channel name | All | -| type | integer | the [type of channel](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types); only conversion between text and announcement is supported and only in guilds with the "NEWS" feature | Text, Announcement | -| position | ?integer | the position of the channel in the left-hand listing | All | -| topic | ?string | 0-1024 character channel topic (0-4096 characters for `GUILD_FORUM` channels) | Text, Announcement, Forum | -| nsfw | ?boolean | whether the channel is nsfw | Text, Voice, Announcement, Forum | -| rate_limit_per_user | ?integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages` or `manage_channel`, are unaffected | Text, Forum | -| bitrate\* | ?integer | the bitrate (in bits) of the voice or stage channel; min 8000 | Voice, Stage | -| user_limit | ?integer | the user limit of the voice channel; 0 refers to no limit, 1 to 99 refers to a user limit | Voice | -| permission_overwrites\*\* | ?array of partial [overwrite](#DOCS_RESOURCES_CHANNEL/overwrite-object) objects | channel or category-specific permissions | All | -| parent_id | ?snowflake | id of the new parent category for a channel | Text, Voice, Announcement, Forum | -| rtc_region | ?string | channel [voice region](#DOCS_RESOURCES_VOICE/voice-region-object) id, automatic when set to null | Voice, Stage | -| video_quality_mode | ?integer | the camera [video quality mode](#DOCS_RESOURCES_CHANNEL/channel-object-video-quality-modes) of the voice channel | Voice | -| default_auto_archive_duration | ?integer | the default duration that the clients use (not the API) for newly created threads in the channel, in minutes, to automatically archive the thread after recent activity | Text, Announcement, Forum | -| flags? | integer | [channel flags](#DOCS_RESOURCES_CHANNEL/channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field). Currently only `REQUIRE_TAG` (`1 << 4`) is supported. | Forum | -| available_tags? | array of [tag](#DOCS_RESOURCES_CHANNEL/forum-tag-object) objects | the set of tags that can be used in a `GUILD_FORUM` channel | Forum | -| default_reaction_emoji? | ?[default reaction](#DOCS_RESOURCES_CHANNEL/default-reaction-object) object | the emoji to show in the add reaction button on a thread in a `GUILD_FORUM` channel | Forum | -| default_thread_rate_limit_per_user? | integer | the initial `rate_limit_per_user` to set on newly created threads in a channel. this field is copied to the thread at creation time and does not live update. | Text, Forum | -| default_sort_order? | ?integer | the [default sort order type](#DOCS_RESOURCES_CHANNEL/channel-object-sort-order-types) used to order posts in `GUILD_FORUM` channels | Forum | - -\* For voice channels, normal servers can set bitrate up to 96000, servers with Boost level 1 can set up to 128000, servers with Boost level 2 can set up to 256000, and servers with Boost level 3 or the `VIP_REGIONS` [guild feature](#DOCS_RESOURCES_GUILD/guild-object-guild-features) can set up to 384000. For stage channels, bitrate can be set up to 64000. - -\*\* In each overwrite object, the `allow` and `deny` keys can be omitted or set to `null`, which both default to `"0"`. - -###### JSON Params (Thread) - -When setting `archived` to `false`, when `locked` is also `false`, only the `SEND_MESSAGES` permission is required. - -Otherwise, requires the `MANAGE_THREADS` permission. Fires a [Thread Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-update) Gateway event. Requires the thread to have `archived` set to `false` or be set to `false` in the request. - -| Field | Type | Description | -| --------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name | string | 1-100 character channel name | -| archived | boolean | whether the thread is archived | -| auto_archive_duration | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 | -| locked | boolean | whether the thread is locked; when a thread is locked, only users with MANAGE_THREADS can unarchive it | -| invitable | boolean | whether non-moderators can add other non-moderators to a thread; only available on private threads | -| rate_limit_per_user | ?integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages`, `manage_thread`, or `manage_channel`, are unaffected | -| flags? | integer | [channel flags](#DOCS_RESOURCES_CHANNEL/channel-object-channel-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field); `PINNED` can only be set for threads in forum channels | -| applied_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` channel | - -## Delete/Close Channel % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object} - -Delete a channel, or close a private message. Requires the `MANAGE_CHANNELS` permission for the guild, or `MANAGE_THREADS` if the channel is a thread. Deleting a category does not delete its child channels; they will have their `parent_id` removed and a [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update) Gateway event will fire for each of them. Returns a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object on success. Fires a [Channel Delete](#DOCS_TOPICS_GATEWAY_EVENTS/channel-delete) Gateway event (or [Thread Delete](#DOCS_TOPICS_GATEWAY_EVENTS/thread-delete) if the channel was a thread). - -> warn -> Deleting a guild channel cannot be undone. Use this with caution, as it is impossible to undo this action when performed on a guild channel. In contrast, when used with a private message, it is possible to undo the action by opening a private message with the recipient again. - -> info -> For Community guilds, the Rules or Guidelines channel and the Community Updates channel cannot be deleted. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Get Channel Messages % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages - -Returns the messages for a channel. If operating on a guild channel, this endpoint requires the `VIEW_CHANNEL` permission to be present on the current user. If the current user is missing the `READ_MESSAGE_HISTORY` permission in the channel then this will return no messages (since they cannot read the message history). Returns an array of [message](#DOCS_RESOURCES_CHANNEL/message-object) objects on success. - -> info -> The `before`, `after`, and `around` parameters are mutually exclusive, only one may be passed at a time. - -###### Query String Params - -| Field | Type | Description | Default | -| ------- | --------- | ---------------------------------------- | ------- | -| around? | snowflake | Get messages around this message ID | absent | -| before? | snowflake | Get messages before this message ID | absent | -| after? | snowflake | Get messages after this message ID | absent | -| limit? | integer | Max number of messages to return (1-100) | 50 | - -## Get Channel Message % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Returns a specific message in the channel. If operating on a guild channel, this endpoint requires the `READ_MESSAGE_HISTORY` permission to be present on the current user. Returns a [message](#DOCS_RESOURCES_CHANNEL/message-object) object on success. - -## Create Message % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages - -> warn -> Discord may strip certain characters from message content, like invalid unicode characters or characters which cause unexpected message formatting. If you are passing user-generated strings into message content, consider sanitizing the data to prevent unexpected behavior and utilizing `allowed_mentions` to prevent unexpected mentions. - -Post a message to a guild text or DM channel. Returns a [message](#DOCS_RESOURCES_CHANNEL/message-object) object. Fires a [Message Create](#DOCS_TOPICS_GATEWAY_EVENTS/message-create) Gateway event. See [message formatting](#DOCS_REFERENCE/message-formatting) for more information on how to properly format messages. - -To create a message as a reply to another message, apps can include a [`message_reference`](#DOCS_RESOURCES_CHANNEL/message-reference-object-message-reference-structure) with a `message_id`. The `channel_id` and `guild_id` in the `message_reference` are optional, but will be validated if provided. - -Files must be attached using a `multipart/form-data` body as described in [Uploading Files](#DOCS_REFERENCE/uploading-files). - -###### Limitations - -- When operating on a guild channel, the current user must have the `SEND_MESSAGES` permission. -- When sending a message with `tts` (text-to-speech) set to `true`, the current user must have the `SEND_TTS_MESSAGES` permission. -- When creating a message as a reply to another message, the current user must have the `READ_MESSAGE_HISTORY` permission. - - The referenced message must exist and cannot be a system message. -- The maximum request size when sending a message is **8MiB** -- For the embed object, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images. - -###### JSON/Form Params - -> info -> When creating a message, apps must provide a value for **at least one of** `content`, `embeds`, `sticker_ids`, `components`, or `files[n]`. - -| Field | Type | Description | -| ------------------ | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| content?\* | string | Message contents (up to 2000 characters) | -| nonce? | integer or string | Can be used to verify a message was sent (up to 25 characters). Value will appear in the [Message Create event](#DOCS_TOPICS_GATEWAY_EVENTS/message-create). | -| tts? | boolean | `true` if this is a TTS message | -| embeds?\* | array of [embed](#DOCS_RESOURCES_CHANNEL/embed-object) objects | Embedded `rich` content (up to 6000 characters) | -| allowed_mentions? | [allowed mention object](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) | Allowed mentions for the message | -| message_reference? | [message reference](#DOCS_RESOURCES_CHANNEL/message-reference-object-message-reference-structure) | Include to make your message a reply | -| components?\* | array of [message component](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/component-object) objects | Components to include with the message | -| sticker_ids?\* | array of snowflakes | IDs of up to 3 [stickers](#DOCS_RESOURCES_STICKER/sticker-object) in the server to send in the message | -| files[n]?\* | file contents | Contents of the file being sent. See [Uploading Files](#DOCS_REFERENCE/uploading-files) | -| payload_json? | string | JSON-encoded body of non-file params, only for `multipart/form-data` requests. See [Uploading Files](#DOCS_REFERENCE/uploading-files) | -| attachments? | array of partial [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) objects | Attachment objects with filename and description. See [Uploading Files](#DOCS_REFERENCE/uploading-files) | -| flags? | integer | [Message flags](#DOCS_RESOURCES_CHANNEL/message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS` can be set) | - -\* At least one of `content`, `embeds`, `sticker_ids`, `components`, or `files[n]` is required. - -###### Example Request Body (application/json) - -```json -{ - "content": "Hello, World!", - "tts": false, - "embeds": [{ - "title": "Hello, Embed!", - "description": "This is an embedded message." - }] -} -``` - -Examples for file uploads are available in [Uploading Files](#DOCS_REFERENCE/uploading-files). - -## Crosspost Message % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object}/crosspost - -Crosspost a message in an Announcement Channel to following channels. This endpoint requires the `SEND_MESSAGES` permission, if the current user sent the message, or additionally the `MANAGE_MESSAGES` permission, for all other messages, to be present for the current user. - -Returns a [message](#DOCS_RESOURCES_CHANNEL/message-object) object. - -## Create Reaction % PUT /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object}/reactions/{emoji#DOCS_RESOURCES_EMOJI/emoji-object}/@me - -Create a reaction for the message. This endpoint requires the `READ_MESSAGE_HISTORY` permission to be present on the current user. Additionally, if nobody else has reacted to the message using this emoji, this endpoint requires the `ADD_REACTIONS` permission to be present on the current user. Returns a 204 empty response on success. Fires a [Message Reaction Add](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-add) Gateway event. -The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. - -## Delete Own Reaction % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object}/reactions/{emoji#DOCS_RESOURCES_EMOJI/emoji-object}/@me - -Delete a reaction the current user has made for the message. Returns a 204 empty response on success. Fires a [Message Reaction Remove](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-remove) Gateway event. -The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. - -## Delete User Reaction % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object}/reactions/{emoji#DOCS_RESOURCES_EMOJI/emoji-object}/{user.id#DOCS_RESOURCES_USER/user-object} - -Deletes another user's reaction. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Returns a 204 empty response on success. Fires a [Message Reaction Remove](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-remove) Gateway event. -The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. - -## Get Reactions % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object}/reactions/{emoji#DOCS_RESOURCES_EMOJI/emoji-object} - -Get a list of users that reacted with this emoji. Returns an array of [user](#DOCS_RESOURCES_USER/user-object) objects on success. -The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. - -###### Query String Params - -| Field | Type | Description | Default | -| ------ | --------- | ------------------------------------- | ------- | -| after? | snowflake | Get users after this user ID | absent | -| limit? | integer | Max number of users to return (1-100) | 25 | - -## Delete All Reactions % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object}/reactions - -Deletes all reactions on a message. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Fires a [Message Reaction Remove All](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-remove-all) Gateway event. - -## Delete All Reactions for Emoji % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object}/reactions/{emoji#DOCS_RESOURCES_EMOJI/emoji-object} - -Deletes all the reactions for a given emoji on a message. This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user. Fires a [Message Reaction Remove Emoji](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-remove-emoji) Gateway event. -The `emoji` must be [URL Encoded](https://en.wikipedia.org/wiki/Percent-encoding) or the request will fail with `10014: Unknown Emoji`. To use custom emoji, you must encode it in the format `name:id` with the emoji name and emoji id. - -## Edit Message % PATCH /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Edit a previously sent message. The fields `content`, `embeds`, and `flags` can be edited by the original message author. Other users can only edit `flags` and only if they have the `MANAGE_MESSAGES` permission in the corresponding channel. When specifying flags, ensure to include all previously set flags/bits in addition to ones that you are modifying. Only `flags` documented in the table below may be modified by users (unsupported flag changes are currently ignored without error). - -When the `content` field is edited, the `mentions` array in the message object will be reconstructed from scratch based on the new content. The `allowed_mentions` field of the edit request controls how this happens. If there is no explicit `allowed_mentions` in the edit request, the content will be parsed with _default_ allowances, that is, without regard to whether or not an `allowed_mentions` was present in the request that originally created the message. - -Returns a [message](#DOCS_RESOURCES_CHANNEL/message-object) object. Fires a [Message Update](#DOCS_TOPICS_GATEWAY_EVENTS/message-update) Gateway event. - -Refer to [Uploading Files](#DOCS_REFERENCE/uploading-files) for details on attachments and `multipart/form-data` requests. -Any provided files will be **appended** to the message. To remove or replace files you will have to supply the `attachments` field which specifies the files to retain on the message after edit. - -> warn -> Starting with API v10, the `attachments` array must contain all attachments that should be present after edit, including **retained and new** attachments provided in the request body. - -> info -> All parameters to this endpoint are optional and nullable. - -###### JSON/Form Params - -| Field | Type | Description | -| ---------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | -| content | string | Message contents (up to 2000 characters) | -| embeds | array of [embed](#DOCS_RESOURCES_CHANNEL/embed-object) objects | Embedded `rich` content (up to 6000 characters) | -| flags | integer | Edit the [flags](#DOCS_RESOURCES_CHANNEL/message-object-message-flags) of a message (only `SUPPRESS_EMBEDS` can currently be set/unset) | -| allowed_mentions | [allowed mention object](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) | Allowed mentions for the message | -| components | array of [message component](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/component-object) | Components to include with the message | -| files[n] | file contents | Contents of the file being sent/edited. See [Uploading Files](#DOCS_REFERENCE/uploading-files) | -| payload_json | string | JSON-encoded body of non-file params (multipart/form-data only). See [Uploading Files](#DOCS_REFERENCE/uploading-files) | -| attachments | array of [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) objects | Attached files to keep and possible descriptions for new files. See [Uploading Files](#DOCS_REFERENCE/uploading-files) | - -## Delete Message % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Delete a message. If operating on a guild channel and trying to delete a message that was not sent by the current user, this endpoint requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Message Delete](#DOCS_TOPICS_GATEWAY_EVENTS/message-delete) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Bulk Delete Messages % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/bulk-delete - -Delete multiple messages in a single request. This endpoint can only be used on guild channels and requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Message Delete Bulk](#DOCS_TOPICS_GATEWAY_EVENTS/message-delete-bulk) Gateway event. - -Any message IDs given that do not exist or are invalid will count towards the minimum and maximum message count (currently 2 and 100 respectively). - -> warn -> This endpoint will not delete messages older than 2 weeks, and will fail with a 400 BAD REQUEST if any message provided is older than that or if any duplicate message IDs are provided. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| -------- | ------------------- | ----------------------------------------- | -| messages | array of snowflakes | an array of message ids to delete (2-100) | - -## Edit Channel Permissions % PUT /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/permissions/{overwrite.id#DOCS_RESOURCES_CHANNEL/overwrite-object} - -Edit the channel permission overwrites for a user or role in a channel. Only usable for guild channels. Requires the `MANAGE_ROLES` permission. Only permissions your bot has in the guild or parent channel (if applicable) can be allowed/denied (unless your bot has a `MANAGE_ROLES` overwrite in the channel). Returns a 204 empty response on success. Fires a [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update) Gateway event. For more information about permissions, see [permissions](#DOCS_TOPICS_PERMISSIONS/permissions). - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ------ | ------- | --------------------------------------------------------------- | -| allow? | string? | the bitwise value of all allowed permissions (default `"0"`) | -| deny? | string? | the bitwise value of all disallowed permissions (default `"0"`) | -| type | integer | 0 for a role or 1 for a member | - -## Get Channel Invites % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/invites - -Returns a list of [invite](#DOCS_RESOURCES_INVITE/invite-object) objects (with [invite metadata](#DOCS_RESOURCES_INVITE/invite-metadata-object)) for the channel. Only usable for guild channels. Requires the `MANAGE_CHANNELS` permission. - -## Create Channel Invite % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/invites - -Create a new [invite](#DOCS_RESOURCES_INVITE/invite-object) object for the channel. Only usable for guild channels. Requires the `CREATE_INSTANT_INVITE` permission. All JSON parameters for this route are optional, however the request body is not. If you are not sending any fields, you still have to send an empty JSON object (`{}`). Returns an [invite](#DOCS_RESOURCES_INVITE/invite-object) object. Fires an [Invite Create](#DOCS_TOPICS_GATEWAY_EVENTS/invite-create) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | Default | -| --------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | -| max_age | integer | duration of invite in seconds before expiry, or 0 for never. between 0 and 604800 (7 days) | 86400 (24 hours) | -| max_uses | integer | max number of uses or 0 for unlimited. between 0 and 100 | 0 | -| temporary | boolean | whether this invite only grants temporary membership | false | -| unique | boolean | if true, don't try to reuse a similar invite (useful for creating many unique one time use invites) | false | -| target_type | integer | the [type of target](#DOCS_RESOURCES_INVITE/invite-object-invite-target-types) for this voice channel invite | | -| target_user_id | snowflake | the id of the user whose stream to display for this invite, required if `target_type` is 1, the user must be streaming in the channel | | -| target_application_id | snowflake | the id of the embedded application to open for this invite, required if `target_type` is 2, the application must have the `EMBEDDED` flag | | - -## Delete Channel Permission % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/permissions/{overwrite.id#DOCS_RESOURCES_CHANNEL/overwrite-object} - -Delete a channel permission overwrite for a user or role in a channel. Only usable for guild channels. Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update) Gateway event. For more information about permissions, see [permissions](#DOCS_TOPICS_PERMISSIONS/permissions) - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Follow Announcement Channel % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/followers - -Follow an Announcement Channel to send messages to a target channel. Requires the `MANAGE_WEBHOOKS` permission in the target channel. Returns a [followed channel](#DOCS_RESOURCES_CHANNEL/followed-channel-object) object. Fires a [Webhooks Update](#DOCS_TOPICS_GATEWAY_EVENTS/webhooks-update) Gateway event for the target channel. - -###### JSON Params - -| Field | Type | Description | -| ------------------ | --------- | -------------------- | -| webhook_channel_id | snowflake | id of target channel | - -## Trigger Typing Indicator % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/typing - -Post a typing indicator for the specified channel. Generally bots should **not** implement this route. However, if a bot is responding to a command and expects the computation to take a few seconds, this endpoint may be called to let the user know that the bot is processing their message. Returns a 204 empty response on success. Fires a [Typing Start](#DOCS_TOPICS_GATEWAY_EVENTS/typing-start) Gateway event. - -## Get Pinned Messages % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/pins - -Returns all pinned messages in the channel as an array of [message](#DOCS_RESOURCES_CHANNEL/message-object) objects. - -## Pin Message % PUT /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/pins/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Pin a message in a channel. Requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Channel Pins Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-pins-update) Gateway event. - -> warn -> The max pinned messages is 50. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Unpin Message % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/pins/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Unpin a message in a channel. Requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. Fires a [Channel Pins Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-pins-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Group DM Add Recipient % PUT /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/recipients/{user.id#DOCS_RESOURCES_USER/user-object} - -Adds a recipient to a Group DM using their access token. - -###### JSON Params - -| Field | Type | Description | -| ------------ | ------ | --------------------------------------------------------------------- | -| access_token | string | access token of a user that has granted your app the `gdm.join` scope | -| nick | string | nickname of the user being added | - -## Group DM Remove Recipient % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/recipients/{user.id#DOCS_RESOURCES_USER/user-object} - -Removes a recipient from a Group DM. - -## Start Thread from Message % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object}/threads - -Creates a new thread from an existing message. Returns a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) Gateway event. - -When called on a `GUILD_TEXT` channel, creates a `PUBLIC_THREAD`. When called on a `GUILD_ANNOUNCEMENT` channel, creates a `ANNOUNCEMENT_THREAD`. Does not work on a [`GUILD_FORUM`](#DOCS_RESOURCES_CHANNEL/start-thread-in-forum-channel) channel. The id of the created thread will be the same as the id of the source message, and as such a message can only have a single thread created from it. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -| name | string | 1-100 character channel name | -| auto_archive_duration? | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 | -| rate_limit_per_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) | - -## Start Thread without Message % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/threads - -Creates a new thread that is not connected to an existing message. Returns a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -> info -> Creating a private thread requires the server to be boosted. The [guild features](#DOCS_RESOURCES_GUILD/guild-object-guild-features) will indicate if that is possible for the guild. - -###### JSON Params - -| Field | Type | Description | -| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -| name | string | 1-100 character channel name | -| auto_archive_duration? | integer | the thread will stop showing in the channel list after `auto_archive_duration` minutes of inactivity, can be set to: 60, 1440, 4320, 10080 | -| type?\* | integer | the [type of thread](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) to create | -| invitable? | boolean | whether non-moderators can add other non-moderators to a thread; only available when creating a private thread | -| rate_limit_per_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) | - -\* `type` currently defaults to `GUILD_PRIVATE_THREAD` in order to match the behavior when thread documentation was first published. In a future API version this will be changed to be a required field, with no default. - -## Start Thread in Forum Channel % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/threads - -Creates a new thread in a forum channel, and sends a message within the created thread. Returns a [channel](#DOCS_RESOURCES_CHANNEL/channel-object), with a nested [message](#DOCS_RESOURCES_CHANNEL/message-object) object, on success, and a 400 BAD REQUEST on invalid parameters. Fires a [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) and [Message Create](#DOCS_TOPICS_GATEWAY_EVENTS/message-create) Gateway event. - -- The type of the created thread is `PUBLIC_THREAD`. -- See [message formatting](#DOCS_REFERENCE/message-formatting) for more information on how to properly format messages. -- The current user must have the `SEND_MESSAGES` permission (`CREATE_PUBLIC_THREADS` is ignored). -- The maximum request size when sending a message is **8MiB**. -- For the embed object, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images. -- Examples for file uploads are available in [Uploading Files](#DOCS_REFERENCE/uploading-files). -- Files must be attached using a `multipart/form-data` body as described in [Uploading Files](#DOCS_REFERENCE/uploading-files). -- Note that when sending a message, you must provide a value for at **least one of** `content`, `embeds`, `sticker_ids`, `components`, or `files[n]`. - -> warn -> Discord may strip certain characters from message content, like invalid unicode characters or characters which cause unexpected message formatting. If you are passing user-generated strings into message content, consider sanitizing the data to prevent unexpected behavior and utilizing `allowed_mentions` to prevent unexpected mentions. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON/Form Params - -| Field | Type | Description | -| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| name | string | 1-100 character channel name | -| auto_archive_duration?\* | integer | duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 | -| rate_limit_per_user? | ?integer | amount of seconds a user has to wait before sending another message (0-21600) | -| message | a [forum thread message params](#DOCS_RESOURCES_CHANNEL/start-thread-in-forum-channel-forum-thread-message-params-object) object | contents of the first message in the forum thread | -| applied_tags? | array of snowflakes | the IDs of the set of tags that have been applied to a thread in a `GUILD_FORUM` channel | - - -###### Forum Thread Message Params Object - -> info -> When sending a message, apps must provide a value for **at least one of** `content`, `embeds`, `sticker_ids`, `components`, or `files[n]`. - -| Field | Type | Description | -| ----------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| content?\* | string | Message contents (up to 2000 characters) | -| embeds?\* | array of [embed](#DOCS_RESOURCES_CHANNEL/embed-object) objects | Embedded `rich` content (up to 6000 characters) | -| allowed_mentions? | [allowed mention object](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) | Allowed mentions for the message | -| components?\* | array of [message component](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/component-object) objects | Components to include with the message | -| sticker_ids?\* | array of snowflakes | IDs of up to 3 [stickers](#DOCS_RESOURCES_STICKER/sticker-object) in the server to send in the message | -| files[n]\* | file contents | Contents of the file being sent. See [Uploading Files](#DOCS_REFERENCE/uploading-files) | -| payload_json? | string | JSON-encoded body of non-file params, only for `multipart/form-data` requests. See [Uploading Files](#DOCS_REFERENCE/uploading-files) | -| attachments? | array of partial [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) objects | Attachment objects with `filename` and `description`. See [Uploading Files](#DOCS_REFERENCE/uploading-files) | -| flags? | integer | [Message flags](#DOCS_RESOURCES_CHANNEL/message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS` can be set) | - -\* At least one of `content`, `embeds`, `sticker_ids`, `components`, or `files[n]` is required. - -## Join Thread % PUT /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/thread-members/@me - -Adds the current user to a thread. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) Gateway event. - -## Add Thread Member % PUT /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/thread-members/{user.id#DOCS_RESOURCES_USER/user-object} - -Adds another member to a thread. Requires the ability to send messages in the thread. Also requires the thread is not archived. Returns a 204 empty response if the member is successfully added or was already a member of the thread. Fires a [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) Gateway event. - -## Leave Thread % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/thread-members/@me - -Removes the current user from a thread. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) Gateway event. - -## Remove Thread Member % DELETE /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/thread-members/{user.id#DOCS_RESOURCES_USER/user-object} - -Removes another member from a thread. Requires the `MANAGE_THREADS` permission, or the creator of the thread if it is a `GUILD_PRIVATE_THREAD`. Also requires the thread is not archived. Returns a 204 empty response on success. Fires a [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) Gateway event. - -## Get Thread Member % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/thread-members/{user.id#DOCS_RESOURCES_USER/user-object} - -Returns a [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object for the specified user if they are a member of the thread, returns a 404 response otherwise. - -## List Thread Members % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/thread-members - -Returns array of [thread members](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects that are members of the thread. - -> warn -> This endpoint is restricted according to whether the `GUILD_MEMBERS` [Privileged Intent](#DOCS_TOPICS_GATEWAY/privileged-intents) is enabled for your application. - -## List Public Archived Threads % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/threads/archived/public - -Returns archived threads in the channel that are public. When called on a `GUILD_TEXT` channel, returns threads of [type](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) `PUBLIC_THREAD`. When called on a `GUILD_ANNOUNCEMENT` channel returns threads of [type](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) `ANNOUNCEMENT_THREAD`. Threads are ordered by `archive_timestamp`, in descending order. Requires the `READ_MESSAGE_HISTORY` permission. - -###### Query String Params - -| Field | Type | Description | -| ------- | ----------------- | -------------------------------------------- | -| before? | ISO8601 timestamp | returns threads before this timestamp | -| limit? | integer | optional maximum number of threads to return | - -###### Response Body - -| Field | Type | Description | -| -------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| threads | array of [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | the public, archived threads | -| members | array of [thread members](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects | a thread member object for each returned thread the current user has joined | -| has_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call | - -## List Private Archived Threads % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/threads/archived/private - -Returns archived threads in the channel that are of [type](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) `GUILD_PRIVATE_THREAD`. Threads are ordered by `archive_timestamp`, in descending order. Requires both the `READ_MESSAGE_HISTORY` and `MANAGE_THREADS` permissions. - -###### Query String Params - -| Field | Type | Description | -| ------- | ----------------- | -------------------------------------------- | -| before? | ISO8601 timestamp | returns threads before this timestamp | -| limit? | integer | optional maximum number of threads to return | - -###### Response Body - -| Field | Type | Description | -| -------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| threads | array of [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | the private, archived threads | -| members | array of [thread members](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects | a thread member object for each returned thread the current user has joined | -| has_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call | - -## List Joined Private Archived Threads % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/users/@me/threads/archived/private - -Returns archived threads in the channel that are of [type](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) `GUILD_PRIVATE_THREAD`, and the user has joined. Threads are ordered by their `id`, in descending order. Requires the `READ_MESSAGE_HISTORY` permission. - -###### Query String Params - -| Field | Type | Description | -| ------- | --------- | -------------------------------------------- | -| before? | snowflake | returns threads before this id | -| limit? | integer | optional maximum number of threads to return | - -###### Response Body - -| Field | Type | Description | -| -------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| threads | array of [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | the private, archived threads the current user has joined | -| members | array of [thread members](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects | a thread member object for each returned thread the current user has joined | -| has_more | boolean | whether there are potentially additional threads that could be returned on a subsequent call | diff --git a/docs/resources/Emoji.md b/docs/resources/Emoji.md deleted file mode 100644 index 4f1391f78d..0000000000 --- a/docs/resources/Emoji.md +++ /dev/null @@ -1,122 +0,0 @@ -# Emoji Resource - -> warn -> Routes for controlling emojis do not follow the normal rate limit conventions. These routes are specifically limited on a per-guild basis to prevent abuse. This means that the quota returned by our APIs may be inaccurate, and you may encounter 429s. - -### Emoji Object - -###### Emoji Structure - -| Field | Type | Description | -| --------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------- | -| id | ?snowflake | [emoji id](#DOCS_REFERENCE/image-formatting) | -| name | ?string (can be null only in reaction emoji objects) | emoji name | -| roles? | array of [role](#DOCS_TOPICS_PERMISSIONS/role-object) object ids | roles allowed to use this emoji | -| user? | [user](#DOCS_RESOURCES_USER/user-object) object | user that created this emoji | -| require_colons? | boolean | whether this emoji must be wrapped in colons | -| managed? | boolean | whether this emoji is managed | -| animated? | boolean | whether this emoji is animated | -| available? | boolean | whether this emoji can be used, may be false due to loss of Server Boosts | - - -###### Emoji Example - -```json -{ - "id": "41771983429993937", - "name": "LUL", - "roles": ["41771983429993000", "41771983429993111"], - "user": { - "username": "Luigi", - "discriminator": "0002", - "id": "96008815106887111", - "avatar": "5500909a3274e1812beb4e8de6631111", - "public_flags": 131328 - }, - "require_colons": true, - "managed": false, - "animated": false -} -``` - -###### Standard Emoji Example - -```json -{ - "id": null, - "name": "🔥" -} -``` - -###### Custom Emoji Examples - ->info ->In `MESSAGE_REACTION_ADD` gateway events `animated` will be returned for animated emoji. - ->info ->In `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE` gateway events `name` may be `null` when custom emoji data is not available (for example, if it was deleted from the guild). - -```json -{ - "id": "41771983429993937", - "name": "LUL", - "animated": true -} -``` - -```json -{ - "id": "41771983429993937", - "name": null -} -``` - -## List Guild Emojis % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/emojis - -Returns a list of [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) objects for the given guild. - -## Get Guild Emoji % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/emojis/{emoji.id#DOCS_RESOURCES_EMOJI/emoji-object} - -Returns an [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) object for the given guild and emoji IDs. - -## Create Guild Emoji % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/emojis - -Create a new emoji for the guild. Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. Returns the new [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) object on success. Fires a [Guild Emojis Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-emojis-update) Gateway event. - -> warn -> Emojis and animated emojis have a maximum file size of 256kb. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a [JSON status code](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/json). - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ----- | ---------------------------------------- | ---------------------------------------------- | -| name | string | name of the emoji | -| image | [image data](#DOCS_REFERENCE/image-data) | the 128x128 emoji image | -| roles | array of snowflakes | roles allowed to use this emoji | - -## Modify Guild Emoji % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/emojis/{emoji.id#DOCS_RESOURCES_EMOJI/emoji-object} - -Modify the given emoji. Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. Returns the updated [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) object on success. Fires a [Guild Emojis Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-emojis-update) Gateway event. - -> info -> All parameters to this endpoint are optional. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ----- | -------------------- | --------------------------------------------- | -| name | string | name of the emoji | -| roles | ?array of snowflakes | roles allowed to use this emoji | - -## Delete Guild Emoji % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/emojis/{emoji.id#DOCS_RESOURCES_EMOJI/emoji-object} - -Delete the given emoji. Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. Returns `204 No Content` on success. Fires a [Guild Emojis Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-emojis-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. diff --git a/docs/resources/Guild.md b/docs/resources/Guild.md deleted file mode 100644 index 84d75157bb..0000000000 --- a/docs/resources/Guild.md +++ /dev/null @@ -1,1185 +0,0 @@ -# Guild Resource - -Guilds in Discord represent an isolated collection of users and channels, and are often referred to as "servers" in the UI. - -### Guild Object - -###### Guild Structure - -> info -> Fields specific to the `GUILD_CREATE` event are listed in the [Gateway Events documentation](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create). - -| Field | Type | Description | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| id | snowflake | guild id | -| name | string | guild name (2-100 characters, excluding trailing and leading whitespace) | -| icon | ?string | [icon hash](#DOCS_REFERENCE/image-formatting) | -| icon_hash? | ?string | [icon hash](#DOCS_REFERENCE/image-formatting), returned when in the template object | -| splash | ?string | [splash hash](#DOCS_REFERENCE/image-formatting) | -| discovery_splash | ?string | [discovery splash hash](#DOCS_REFERENCE/image-formatting); only present for guilds with the "DISCOVERABLE" feature | -| owner? \* | boolean | true if [the user](#DOCS_RESOURCES_USER/get-current-user-guilds) is the owner of the guild | -| owner_id | snowflake | id of owner | -| permissions? \* | string | total permissions for [the user](#DOCS_RESOURCES_USER/get-current-user-guilds) in the guild (excludes overwrites) | -| region? \*\* | ?string | [voice region](#DOCS_RESOURCES_VOICE/voice-region-object) id for the guild (deprecated) | -| afk_channel_id | ?snowflake | id of afk channel | -| afk_timeout | integer | afk timeout in seconds, can be set to: 60, 300, 900, 1800, 3600 | -| widget_enabled? | boolean | true if the server widget is enabled | -| widget_channel_id? | ?snowflake | the channel id that the widget will generate an invite to, or `null` if set to no invite | -| verification_level | integer | [verification level](#DOCS_RESOURCES_GUILD/guild-object-verification-level) required for the guild | -| default_message_notifications | integer | default [message notifications level](#DOCS_RESOURCES_GUILD/guild-object-default-message-notification-level) | -| explicit_content_filter | integer | [explicit content filter level](#DOCS_RESOURCES_GUILD/guild-object-explicit-content-filter-level) | -| roles | array of [role](#DOCS_TOPICS_PERMISSIONS/role-object) objects | roles in the guild | -| emojis | array of [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) objects | custom guild emojis | -| features | array of [guild feature](#DOCS_RESOURCES_GUILD/guild-object-guild-features) strings | enabled guild features | -| mfa_level | integer | required [MFA level](#DOCS_RESOURCES_GUILD/guild-object-mfa-level) for the guild | -| application_id | ?snowflake | application id of the guild creator if it is bot-created | -| system_channel_id | ?snowflake | the id of the channel where guild notices such as welcome messages and boost events are posted | -| system_channel_flags | integer | [system channel flags](#DOCS_RESOURCES_GUILD/guild-object-system-channel-flags) | -| rules_channel_id | ?snowflake | the id of the channel where Community guilds can display rules and/or guidelines | -| max_presences? | ?integer | the maximum number of presences for the guild (`null` is always returned, apart from the largest of guilds) | -| max_members? | integer | the maximum number of members for the guild | -| vanity_url_code | ?string | the vanity url code for the guild | -| description | ?string | the description of a guild | -| banner | ?string | [banner hash](#DOCS_REFERENCE/image-formatting) | -| premium_tier | integer | [premium tier](#DOCS_RESOURCES_GUILD/guild-object-premium-tier) (Server Boost level) | -| premium_subscription_count? | integer | the number of boosts this guild currently has | -| preferred_locale | string | the preferred [locale](#DOCS_REFERENCE/locales) of a Community guild; used in server discovery and notices from Discord, and sent in interactions; defaults to "en-US" | -| public_updates_channel_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive notices from Discord | -| max_video_channel_users? | integer | the maximum amount of users in a video channel | -| approximate_member_count? | integer | approximate number of members in this guild, returned from the `GET /guilds/` endpoint when `with_counts` is `true` | -| approximate_presence_count? | integer | approximate number of non-offline members in this guild, returned from the `GET /guilds/` endpoint when `with_counts` is `true` | -| welcome_screen? | [welcome screen](#DOCS_RESOURCES_GUILD/welcome-screen-object) object | the welcome screen of a Community guild, shown to new members, returned in an [Invite](#DOCS_RESOURCES_INVITE/invite-object)'s guild object | -| nsfw_level | integer | [guild NSFW level](#DOCS_RESOURCES_GUILD/guild-object-guild-nsfw-level) | -| stickers? | array of [sticker](#DOCS_RESOURCES_STICKER/sticker-object) objects | custom guild stickers | -| premium_progress_bar_enabled | boolean | whether the guild has the boost progress bar enabled | - -\* These fields are only sent when using the [GET Current User Guilds](#DOCS_RESOURCES_USER/get-current-user-guilds) endpoint and are relative to the requested user - -\*\* This field is deprecated and is replaced by [channel.rtc_region](#DOCS_RESOURCES_CHANNEL/channel-object-channel-structure) - -###### Default Message Notification Level - -| Key | Value | Description | -| ------------- | ----- | ---------------------------------------------------------------------------------- | -| ALL_MESSAGES | 0 | members will receive notifications for all messages by default | -| ONLY_MENTIONS | 1 | members will receive notifications only for messages that @mention them by default | - -###### Explicit Content Filter Level - -| Level | Integer | Description | -| --------------------- | ------- | ----------------------------------------------------------- | -| DISABLED | 0 | media content will not be scanned | -| MEMBERS_WITHOUT_ROLES | 1 | media content sent by members without roles will be scanned | -| ALL_MEMBERS | 2 | media content sent by all members will be scanned | - -###### MFA Level - -| Level | Integer | Description | -| -------- | ------- | ------------------------------------------------------- | -| NONE | 0 | guild has no MFA/2FA requirement for moderation actions | -| ELEVATED | 1 | guild has a 2FA requirement for moderation actions | - -###### Verification Level - -| Level | Integer | Description | -| --------- | ------- | --------------------------------------------------------- | -| NONE | 0 | unrestricted | -| LOW | 1 | must have verified email on account | -| MEDIUM | 2 | must be registered on Discord for longer than 5 minutes | -| HIGH | 3 | must be a member of the server for longer than 10 minutes | -| VERY_HIGH | 4 | must have a verified phone number | - -###### Guild NSFW Level - -| Level | Value | -| -------------- | ------- | -| DEFAULT | 0 | -| EXPLICIT | 1 | -| SAFE | 2 | -| AGE_RESTRICTED | 3 | - -###### Premium Tier - -| Level | Integer | Description | -| ------ | ------- | --------------------------------------------- | -| NONE | 0 | guild has not unlocked any Server Boost perks | -| TIER_1 | 1 | guild has unlocked Server Boost level 1 perks | -| TIER_2 | 2 | guild has unlocked Server Boost level 2 perks | -| TIER_3 | 3 | guild has unlocked Server Boost level 3 perks | - -###### System Channel Flags - -| Flag | Value | Description | -| ------------------------------------- | ------ | -------------------------------------- | -| SUPPRESS_JOIN_NOTIFICATIONS | 1 << 0 | Suppress member join notifications | -| SUPPRESS_PREMIUM_SUBSCRIPTIONS | 1 << 1 | Suppress server boost notifications | -| SUPPRESS_GUILD_REMINDER_NOTIFICATIONS | 1 << 2 | Suppress server setup tips | -| SUPPRESS_JOIN_NOTIFICATION_REPLIES | 1 << 3 | Hide member join sticker reply buttons | - -###### Guild Features - -| Feature | Description | -| -------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| ANIMATED_BANNER | guild has access to set an animated guild banner image | -| ANIMATED_ICON | guild has access to set an animated guild icon | -| AUTO_MODERATION | guild has set up auto moderation rules | -| BANNER | guild has access to set a guild banner image | -| COMMUNITY | guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates | -| DISCOVERABLE | guild is able to be discovered in the directory | -| FEATURABLE | guild is able to be featured in the directory | -| INVITES_DISABLED | guild has paused invites, preventing new users from joining | -| INVITE_SPLASH | guild has access to set an invite splash background | -| MEMBER_VERIFICATION_GATE_ENABLED | guild has enabled [Membership Screening](#DOCS_RESOURCES_GUILD/membership-screening-object) | -| MONETIZATION_ENABLED | guild has enabled monetization | -| MORE_STICKERS | guild has increased custom sticker slots | -| NEWS | guild has access to create announcement channels | -| PARTNERED | guild is partnered | -| PREVIEW_ENABLED | guild can be previewed before joining via Membership Screening or the directory | -| PRIVATE_THREADS | guild has access to create private threads | -| ROLE_ICONS | guild is able to set role icons | -| TICKETED_EVENTS_ENABLED | guild has enabled ticketed events | -| VANITY_URL | guild has access to set a vanity URL | -| VERIFIED | guild is verified | -| VIP_REGIONS | guild has access to set 384kbps bitrate in voice (previously VIP voice servers) | -| WELCOME_SCREEN_ENABLED | guild has enabled the welcome screen | - -###### Mutable Guild Features - -| Features | Required Permissions | Effects | -| ---------------- | --------------------- | --------------------------------------------------------- | -| COMMUNITY | Administrator | Enables Community Features in the guild | -| INVITES_DISABLED | Manage Guild | Pauses all invites/access to the server | -| DISCOVERABLE | Administrator* | Enables discovery in the guild, making it publicly listed | - -\* Server also must be passing all discovery requirements - -###### Example Guild - -```json -{ - "id": "197038439483310086", - "name": "Discord Testers", - "icon": "f64c482b807da4f539cff778d174971c", - "description": "The official place to report Discord Bugs!", - "splash": null, - "discovery_splash": null, - "features": [ - "ANIMATED_ICON", - "VERIFIED", - "NEWS", - "VANITY_URL", - "DISCOVERABLE", - "MORE_EMOJI", - "INVITE_SPLASH", - "BANNER", - "COMMUNITY" - ], - "emojis": [], - "banner": "9b6439a7de04f1d26af92f84ac9e1e4a", - "owner_id": "73193882359173120", - "application_id": null, - "region": null, - "afk_channel_id": null, - "afk_timeout": 300, - "system_channel_id": null, - "widget_enabled": true, - "widget_channel_id": null, - "verification_level": 3, - "roles": [], - "default_message_notifications": 1, - "mfa_level": 1, - "explicit_content_filter": 2, - "max_presences": 40000, - "max_members": 250000, - "vanity_url_code": "discord-testers", - "premium_tier": 3, - "premium_subscription_count": 33, - "system_channel_flags": 0, - "preferred_locale": "en-US", - "rules_channel_id": "441688182833020939", - "public_updates_channel_id": "281283303326089216" -} -``` - -### Unavailable Guild Object - -A partial [guild](#DOCS_RESOURCES_GUILD/guild-object) object. Represents an Offline Guild, or a Guild whose information has not been provided through [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) events during the Gateway connect. - -###### Example Unavailable Guild - -```json -{ - "id": "41771983423143937", - "unavailable": true -} -``` - -### Guild Preview Object - -###### Guild Preview Structure - -| Field | Type | Description | -| -------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------- | -| id | snowflake | guild id | -| name | string | guild name (2-100 characters) | -| icon | ?string | [icon hash](#DOCS_REFERENCE/image-formatting) | -| splash | ?string | [splash hash](#DOCS_REFERENCE/image-formatting) | -| discovery_splash | ?string | [discovery splash hash](#DOCS_REFERENCE/image-formatting) | -| emojis | array of [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) objects | custom guild emojis | -| features | array of [guild feature](#DOCS_RESOURCES_GUILD/guild-object-guild-features) strings | enabled guild features | -| approximate_member_count | integer | approximate number of members in this guild | -| approximate_presence_count | integer | approximate number of online members in this guild | -| description | ?string | the description for the guild | -| stickers | array of [sticker](#DOCS_RESOURCES_STICKER/sticker-object) objects | custom guild stickers | - -###### Example Guild Preview - -```json -{ - "id": "197038439483310086", - "name": "Discord Testers", - "icon": "f64c482b807da4f539cff778d174971c", - "splash": null, - "discovery_splash": null, - "emojis": [], - "features": [ - "DISCOVERABLE", - "VANITY_URL", - "ANIMATED_ICON", - "INVITE_SPLASH", - "NEWS", - "COMMUNITY", - "BANNER", - "VERIFIED", - "MORE_EMOJI" - ], - "approximate_member_count": 60814, - "approximate_presence_count": 20034, - "description": "The official place to report Discord Bugs!", - "stickers": [] -} -``` - -### Guild Widget Settings Object - -###### Guild Widget Settings Structure - -| Field | Type | Description | -| ---------- | ---------- | ----------------------------- | -| enabled | boolean | whether the widget is enabled | -| channel_id | ?snowflake | the widget channel id | - -###### Example Guild Widget Settings - -```json -{ - "enabled": true, - "channel_id": "41771983444115456" -} -``` - -### Guild Widget Object - -###### Guild Widget Structure - -| Field | Type | Description | -| -------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------- | -| id | snowflake | guild id | -| name | string | guild name (2-100 characters) | -| instant_invite | ?string | instant invite for the guilds specified widget invite channel | -| channels | array of partial [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | voice and stage channels which are accessible by @everyone | -| members | array of partial [user](#DOCS_RESOURCES_USER/user-object) objects | special widget user objects that includes users presence (Limit 100) | -| presence_count | integer | number of online members in this guild | - -> warn -> The fields `id`, `discriminator` and `avatar` are anonymized to prevent abuse. - -###### Example Guild Widget - -```json -{ - "id": "290926798626999250", - "name": "Test Server", - "instant_invite": "https://discord.com/invite/abcdefg", - "channels": [ - { - "id": "705216630279993882", - "name": "elephant", - "position": 2 - }, - { - "id": "669583461748992190", - "name": "groovy-music", - "position": 1 - } - ], - "members": [ - { - "id": "0", - "username": "1234", - "discriminator": "0000", - "avatar": null, - "status": "online", - "avatar_url": "https://cdn.discordapp.com/widget-avatars/FfvURgcr3Za92K3JtoCppqnYMppMDc5B-Rll74YrGCU/C-1DyBZPQ6t5q2RuATFuMFgq0_uEMZVzd_6LbGN_uJKvZflobA9diAlTjhf6CAESLLeTuu4dLuHFWOb_PNLteooNfhC4C6k5QgAGuxEOP12tVVVCvX6t64k14PMXZrGTDq8pWZhukP40Wg" - } - ], - "presence_count": 1 -} -``` - -### Guild Member Object - -###### Guild Member Structure - -| Field | Type | Description | -| ----------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| user? | [user](#DOCS_RESOURCES_USER/user-object) object | the user this guild member represents | -| nick? | ?string | this user's guild nickname | -| avatar? | ?string | the member's [guild avatar hash](#DOCS_REFERENCE/image-formatting) | -| roles | array of snowflakes | array of [role](#DOCS_TOPICS_PERMISSIONS/role-object) object ids | -| joined_at | ISO8601 timestamp | when the user joined the guild | -| premium_since? | ?ISO8601 timestamp | when the user started [boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-) the guild | -| deaf | boolean | whether the user is deafened in voice channels | -| mute | boolean | whether the user is muted in voice channels | -| pending? | boolean | whether the user has not yet passed the guild's [Membership Screening](#DOCS_RESOURCES_GUILD/membership-screening-object) requirements | -| permissions? | string | total permissions of the member in the channel, including overwrites, returned when in the interaction object | -| communication_disabled_until? | ?ISO8601 timestamp | when the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out | - -> info -> The field `user` won't be included in the member object attached to `MESSAGE_CREATE` and `MESSAGE_UPDATE` gateway events. - -> info -> In `GUILD_` events, `pending` will always be included as true or false. In non `GUILD_` events which can only be triggered by non-`pending` users, `pending` will not be included. - -###### Example Guild Member - -```json -{ - "user": {}, - "nick": "NOT API SUPPORT", - "avatar": null, - "roles": [], - "joined_at": "2015-04-26T06:26:56.936000+00:00", - "deaf": false, - "mute": false -} -``` - -### Integration Object - -###### Integration Structure - -| Field | Type | Description | -| ----------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| id | snowflake | integration id | -| name | string | integration name | -| type | string | integration type (twitch, youtube, or discord) | -| enabled? \* | boolean | is this integration enabled | -| syncing? \* | boolean | is this integration syncing | -| role_id? \* | snowflake | id that this integration uses for "subscribers" | -| enable_emoticons? \* | boolean | whether emoticons should be synced for this integration (twitch only currently) | -| expire_behavior? \* | [integration expire behavior](#DOCS_RESOURCES_GUILD/integration-object-integration-expire-behaviors) | the behavior of expiring subscribers | -| expire_grace_period? \* | integer | the grace period (in days) before expiring subscribers | -| user? \* | [user](#DOCS_RESOURCES_USER/user-object) object | user for this integration | -| account | [account](#DOCS_RESOURCES_GUILD/integration-account-object) object | integration account information | -| synced_at? \* | ISO8601 timestamp | when this integration was last synced | -| subscriber_count? \* | integer | how many subscribers this integration has | -| revoked? \* | boolean | has this integration been revoked | -| application? | [application](#DOCS_RESOURCES_GUILD/integration-application-object) object | The bot/OAuth2 application for discord integrations | -| scopes? | array of [OAuth2 scopes](#DOCS_TOPICS_OAUTH2/shared-resources-oauth2-scopes) | the scopes the application has been authorized for | - -\* These fields are not provided for discord bot integrations. - -###### Integration Expire Behaviors - -| Value | Name | -| ----- | ----------- | -| 0 | Remove role | -| 1 | Kick | - -### Integration Account Object - -###### Integration Account Structure - -| Field | Type | Description | -| ----- | ------ | ------------------- | -| id | string | id of the account | -| name | string | name of the account | - -### Integration Application Object - -###### Integration Application Structure - -| Field | Type | Description | -| ----------- | ----------------------------------------------- | ---------------------------------------------------------------------- | -| id | snowflake | the id of the app | -| name | string | the name of the app | -| icon | ?string | the [icon hash](#DOCS_REFERENCE/image-formatting) of the app | -| description | string | the description of the app | -| bot? | [user](#DOCS_RESOURCES_USER/user-object) object | the bot associated with this application | - -### Ban Object - -###### Ban Structure - -| Field | Type | Description | -| ------ | ----------------------------------------------- | ---------------------- | -| reason | ?string | the reason for the ban | -| user | [user](#DOCS_RESOURCES_USER/user-object) object | the banned user | - -###### Example Ban - -```json -{ - "reason": "mentioning b1nzy", - "user": { - "username": "Mason", - "discriminator": "9999", - "id": "53908099506183680", - "avatar": "a_bab14f271d565501444b2ca3be944b25", - "public_flags": 131141 - } -} -``` - -### Welcome Screen Object - -###### Welcome Screen Structure - -| Field | Type | Description | -| ---------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | -| description | ?string | the server description shown in the welcome screen | -| welcome_channels | array of [welcome screen channel](#DOCS_RESOURCES_GUILD/welcome-screen-object-welcome-screen-channel-structure) objects | the channels shown in the welcome screen, up to 5 | - -###### Welcome Screen Channel Structure - -| Field | Type | Description | -| ----------- | ---------- | ----------------------------------------------------------------------------------------- | -| channel_id | snowflake | the channel's id | -| description | string | the description shown for the channel | -| emoji_id | ?snowflake | the [emoji id](#DOCS_REFERENCE/image-formatting), if the emoji is custom | -| emoji_name | ?string | the emoji name if custom, the unicode character if standard, or `null` if no emoji is set | - -###### Example Welcome Screen - -```json -{ - "description": "Discord Developers is a place to learn about Discord's API, bots, and SDKs and integrations. This is NOT a general Discord support server.", - "welcome_channels": [ - { - "channel_id": "697138785317814292", - "description": "Follow for official Discord API updates", - "emoji_id": null, - "emoji_name": "📡" - }, - { - "channel_id": "697236247739105340", - "description": "Get help with Bot Verifications", - "emoji_id": null, - "emoji_name": "📸" - }, - { - "channel_id": "697489244649816084", - "description": "Create amazing things with Discord's API", - "emoji_id": null, - "emoji_name": "🔬" - }, - { - "channel_id": "613425918748131338", - "description": "Integrate Discord into your game", - "emoji_id": null, - "emoji_name": "🎮" - }, - { - "channel_id": "646517734150242346", - "description": "Find more places to help you on your quest", - "emoji_id": null, - "emoji_name": "🔦" - } - ] -} -``` - -### Membership Screening Object - -In guilds with [Membership Screening](https://support.discord.com/hc/en-us/articles/1500000466882) enabled, when a member joins, [Guild Member Add](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-add) will be emitted but they will initially be restricted from doing any actions in the guild, and `pending` will be true in the [member object](#DOCS_RESOURCES_GUILD/guild-member-object). When the member completes the screening, [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) will be emitted and `pending` will be false. - -Giving the member a role will bypass Membership Screening as well as the guild's verification level, giving the member immediate access to chat. Therefore, instead of giving a role when the member joins, it is recommended to not give the role until the user is no longer `pending`. - -> warn -> We are making significant changes to the Membership Screening API specifically related to getting and editing the Membership Screening object. Long story short is that it can be improved. As such, we have removed those documentation. There will **not be** any changes to how pending members work, as outlined above. That behavior will stay the same. - -## Create Guild % POST /guilds - -Create a new guild. Returns a [guild](#DOCS_RESOURCES_GUILD/guild-object) object on success. Fires a [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) Gateway event. - -> warn -> This endpoint can be used only by bots in less than 10 guilds. - -###### JSON Params - -| Field | Type | Description | -| ------------------------------ | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -| name | string | name of the guild (2-100 characters) | -| region? | ?string | [voice region](#DOCS_RESOURCES_VOICE/voice-region-object) id (deprecated) | -| icon? | [image data](#DOCS_REFERENCE/image-data) | base64 128x128 image for the guild icon | -| verification_level? | integer | [verification level](#DOCS_RESOURCES_GUILD/guild-object-verification-level) | -| default_message_notifications? | integer | default [message notification level](#DOCS_RESOURCES_GUILD/guild-object-default-message-notification-level) | -| explicit_content_filter? | integer | [explicit content filter level](#DOCS_RESOURCES_GUILD/guild-object-explicit-content-filter-level) | -| roles? | array of [role](#DOCS_TOPICS_PERMISSIONS/role-object) objects | new guild roles | -| channels? | array of partial [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | new guild's channels | -| afk_channel_id? | snowflake | id for afk channel | -| afk_timeout? | integer | afk timeout in seconds, can be set to: 60, 300, 900, 1800, 3600 | -| system_channel_id? | snowflake | the id of the channel where guild notices such as welcome messages and boost events are posted | -| system_channel_flags? | integer | [system channel flags](#DOCS_RESOURCES_GUILD/guild-object-system-channel-flags) | - -> warn -> When using the `roles` parameter, the first member of the array is used to change properties of the guild's `@everyone` role. If you are trying to bootstrap a guild with additional roles, keep this in mind. - -> info -> When using the `roles` parameter, the required `id` field within each role object is an integer placeholder, and will be replaced by the API upon consumption. Its purpose is to allow you to [overwrite](#DOCS_RESOURCES_CHANNEL/overwrite-object) a role's permissions in a channel when also passing in channels with the channels array. - -> warn -> When using the `channels` parameter, the `position` field is ignored, and none of the default channels are created. - -> info -> When using the `channels` parameter, the `id` field within each channel object may be set to an integer placeholder, and will be replaced by the API upon consumption. Its purpose is to allow you to create `GUILD_CATEGORY` channels by setting the `parent_id` field on any children to the category's `id` field. Category channels must be listed before any children. - -> warn -> The `region` field is deprecated and is replaced by [channel.rtc_region](#DOCS_RESOURCES_CHANNEL/channel-object-channel-structure). - -###### Example Partial Channel Object - -```json -{ - "name": "naming-things-is-hard", - "type": 0 -} -``` - -###### Example Category Channel - -```json -{ - "name": "my-category", - "type": 4, - "id": 1 -} -{ - "name": "naming-things-is-hard", - "type": 0, - "id": 2, - "parent_id": 1 -} -``` - -## Get Guild % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object} - -Returns the [guild](#DOCS_RESOURCES_GUILD/guild-object) object for the given id. If `with_counts` is set to `true`, this endpoint will also return `approximate_member_count` and `approximate_presence_count` for the guild. - -###### Query String Params - -| Field | Type | Description | Required | Default | -| ------------ | ------- | ----------------------------------------------------------------------------- | -------- | ------- | -| with_counts? | boolean | when `true`, will return approximate member and presence counts for the guild | false | false | - -###### Example Response - -```json -{ - "id": "2909267986263572999", - "name": "Mason's Test Server", - "icon": "389030ec9db118cb5b85a732333b7c98", - "description": null, - "splash": "75610b05a0dd09ec2c3c7df9f6975ea0", - "discovery_splash": null, - "approximate_member_count": 2, - "approximate_presence_count": 2, - "features": [ - "INVITE_SPLASH", - "VANITY_URL", - "COMMERCE", - "BANNER", - "NEWS", - "VERIFIED", - "VIP_REGIONS" - ], - "emojis": [ - { - "name": "ultrafastparrot", - "roles": [], - "id": "393564762228785161", - "require_colons": true, - "managed": false, - "animated": true, - "available": true - } - ], - "banner": "5c3cb8d1bc159937fffe7e641ec96ca7", - "owner_id": "53908232506183680", - "application_id": null, - "region": null, - "afk_channel_id": null, - "afk_timeout": 300, - "system_channel_id": null, - "widget_enabled": true, - "widget_channel_id": "639513352485470208", - "verification_level": 0, - "roles": [ - { - "id": "2909267986263572999", - "name": "@everyone", - "permissions": "49794752", - "position": 0, - "color": 0, - "hoist": false, - "managed": false, - "mentionable": false - } - ], - "default_message_notifications": 1, - "mfa_level": 0, - "explicit_content_filter": 0, - "max_presences": null, - "max_members": 250000, - "max_video_channel_users": 25, - "vanity_url_code": "no", - "premium_tier": 0, - "premium_subscription_count": 0, - "system_channel_flags": 0, - "preferred_locale": "en-US", - "rules_channel_id": null, - "public_updates_channel_id": null -} -``` - -## Get Guild Preview % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/preview - -Returns the [guild preview](#DOCS_RESOURCES_GUILD/guild-preview-object) object for the given id. If the user is not in the guild, then the guild must be lurkable. - -## Modify Guild % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object} - -Modify a guild's settings. Requires the `MANAGE_GUILD` permission. Returns the updated [guild](#DOCS_RESOURCES_GUILD/guild-object) object on success. Fires a [Guild Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-update) Gateway event. - -> info -> All parameters to this endpoint are optional - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -> warn -> Attempting to add or remove the `COMMUNITY` [guild feature](#DOCS_RESOURCES_GUILD/guild-object-guild-features) requires the `ADMINISTRATOR` permission. - -###### JSON Params - -| Field | Type | Description | -| ----------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name | string | guild name | -| region | ?string | guild [voice region](#DOCS_RESOURCES_VOICE/voice-region-object) id (deprecated) | -| verification_level | ?integer | [verification level](#DOCS_RESOURCES_GUILD/guild-object-verification-level) | -| default_message_notifications | ?integer | default [message notification level](#DOCS_RESOURCES_GUILD/guild-object-default-message-notification-level) | -| explicit_content_filter | ?integer | [explicit content filter level](#DOCS_RESOURCES_GUILD/guild-object-explicit-content-filter-level) | -| afk_channel_id | ?snowflake | id for afk channel | -| afk_timeout | integer | afk timeout in seconds, can be set to: 60, 300, 900, 1800, 3600 | -| icon | ?[image data](#DOCS_REFERENCE/image-data) | base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has the `ANIMATED_ICON` feature) | -| owner_id | snowflake | user id to transfer guild ownership to (must be owner) | -| splash | ?[image data](#DOCS_REFERENCE/image-data) | base64 16:9 png/jpeg image for the guild splash (when the server has the `INVITE_SPLASH` feature) | -| discovery_splash | ?[image data](#DOCS_REFERENCE/image-data) | base64 16:9 png/jpeg image for the guild discovery splash (when the server has the `DISCOVERABLE` feature) | -| banner | ?[image data](#DOCS_REFERENCE/image-data) | base64 16:9 png/jpeg image for the guild banner (when the server has the `BANNER` feature; can be animated gif when the server has the `ANIMATED_BANNER` feature) | -| system_channel_id | ?snowflake | the id of the channel where guild notices such as welcome messages and boost events are posted | -| system_channel_flags | integer | [system channel flags](#DOCS_RESOURCES_GUILD/guild-object-system-channel-flags) | -| rules_channel_id | ?snowflake | the id of the channel where Community guilds display rules and/or guidelines | -| public_updates_channel_id | ?snowflake | the id of the channel where admins and moderators of Community guilds receive notices from Discord | -| preferred_locale | ?string | the preferred [locale](#DOCS_REFERENCE/locales) of a Community guild used in server discovery and notices from Discord; defaults to "en-US" | -| features | array of [guild feature](#DOCS_RESOURCES_GUILD/guild-object-guild-features) strings | enabled guild features | -| description | ?string | the description for the guild | -| premium_progress_bar_enabled | boolean | whether the guild's boost progress bar should be enabled | - -## Delete Guild % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object} - -Delete a guild permanently. User must be owner. Returns `204 No Content` on success. Fires a [Guild Delete](#DOCS_TOPICS_GATEWAY_EVENTS/guild-delete) Gateway event. - -## Get Guild Channels % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/channels - -Returns a list of guild [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects. Does not include threads. - -## Create Guild Channel % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/channels - -Create a new [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object for the guild. Requires the `MANAGE_CHANNELS` permission. If setting permission overwrites, only permissions your bot has in the guild can be allowed/denied. Setting `MANAGE_ROLES` permission in channels is only possible for guild administrators. Returns the new [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object on success. Fires a [Channel Create](#DOCS_TOPICS_GATEWAY_EVENTS/channel-create) Gateway event. - -> info -> All parameters to this endpoint are optional and nullable excluding `name` - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ----------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name | string | channel name (1-100 characters) | -| type | integer | the [type of channel](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) | -| topic | string | channel topic (0-1024 characters) | -| bitrate\* | integer | the bitrate (in bits) of the voice or stage channel; min 8000 | -| user_limit | integer | the user limit of the voice channel | -| rate_limit_per_user | integer | amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages` or `manage_channel`, are unaffected | -| position | integer | sorting position of the channel | -| permission_overwrites\*\* | array of partial [overwrite](#DOCS_RESOURCES_CHANNEL/overwrite-object) objects | the channel's permission overwrites | -| parent_id | snowflake | id of the parent category for a channel | -| nsfw | boolean | whether the channel is nsfw | -| rtc_region | string | channel [voice region](#DOCS_RESOURCES_VOICE/voice-region-object) id of the voice or stage channel, automatic when set to null | -| video_quality_mode | integer | the camera [video quality mode](#DOCS_RESOURCES_CHANNEL/channel-object-video-quality-modes) of the voice channel | -| default_auto_archive_duration | integer | the default duration that the clients use (not the API) for newly created threads in the channel, in minutes, to automatically archive the thread after recent activity | -| default_reaction_emoji | [default reaction](#DOCS_RESOURCES_CHANNEL/default-reaction-object) object | emoji to show in the add reaction button on a thread in a `GUILD_FORUM` channel | -| available_tags | array of [tag](#DOCS_RESOURCES_CHANNEL/forum-tag-object) objects | set of tags that can be used in a `GUILD_FORUM` channel | -| default_sort_order | integer | the [default sort order type](#DOCS_RESOURCES_CHANNEL/channel-object-sort-order-types) used to order posts in `GUILD_FORUM` channels | - -\* For voice channels, normal servers can set bitrate up to 96000, servers with Boost level 1 can set up to 128000, servers with Boost level 2 can set up to 256000, and servers with Boost level 3 or the `VIP_REGIONS` [guild feature](#DOCS_RESOURCES_GUILD/guild-object-guild-features) can set up to 384000. For stage channels, bitrate can be set up to 64000. - -\*\* In each overwrite object, the `allow` and `deny` keys can be omitted or set to `null`, which both default to `"0"`. - -## Modify Guild Channel Positions % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/channels - -Modify the positions of a set of [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects for the guild. Requires `MANAGE_CHANNELS` permission. Returns a 204 empty response on success. Fires multiple [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update) Gateway events. - -> info -> Only channels to be modified are required. - -This endpoint takes a JSON array of parameters in the following format: - -###### JSON Params - -| Field | Type | Description | -| ---------------- | ---------- | -------------------------------------------------------------------------------- | -| id | snowflake | channel id | -| position | ?integer | sorting position of the channel | -| lock_permissions | ?boolean | syncs the permission overwrites with the new parent, if moving to a new category | -| parent_id | ?snowflake | the new parent ID for the channel that is moved | - -## List Active Guild Threads % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/threads/active - -Returns all active threads in the guild, including public and private threads. Threads are ordered by their `id`, in descending order. - -###### Response Body - -| Field | Type | Description | -|----------|---------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------| -| threads | array of [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | the active threads | -| members | array of [thread members](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects | a thread member object for each returned thread the current user has joined | - -## Get Guild Member % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/{user.id#DOCS_RESOURCES_USER/user-object} - -Returns a [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) object for the specified user. - -## List Guild Members % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members - -Returns a list of [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) objects that are members of the guild. - -> warn -> This endpoint is restricted according to whether the `GUILD_MEMBERS` [Privileged Intent](#DOCS_TOPICS_GATEWAY/privileged-intents) is enabled for your application. - -> info -> All parameters to this endpoint are optional - -###### Query String Params - -| Field | Type | Description | Default | -| ----- | --------- | ---------------------------------------- | ------- | -| limit | integer | max number of members to return (1-1000) | 1 | -| after | snowflake | the highest user id in the previous page | 0 | - -## Search Guild Members % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/search - -Returns a list of [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) objects whose username or nickname starts with a provided string. - -> info -> All parameters to this endpoint except for `query` are optional - -###### Query String Params - -| Field | Type | Description | Default | -| ----- | ------- | ---------------------------------------------------------- | ------- | -| query | string | Query string to match username(s) and nickname(s) against. | | -| limit | integer | max number of members to return (1-1000) | 1 | - -## Add Guild Member % PUT /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/{user.id#DOCS_RESOURCES_USER/user-object} - -Adds a user to the guild, provided you have a valid oauth2 access token for the user with the `guilds.join` scope. Returns a 201 Created with the [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) as the body, or 204 No Content if the user is already a member of the guild. Fires a [Guild Member Add](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-add) Gateway event. - -For guilds with [Membership Screening](#DOCS_RESOURCES_GUILD/membership-screening-object) enabled, this endpoint will default to adding new members as `pending` in the [guild member object](#DOCS_RESOURCES_GUILD/guild-member-object). Members that are `pending` will have to complete membership screening before they become full members that can talk. - -> info -> All parameters to this endpoint except for `access_token` are optional. - -> info -> The Authorization header must be a Bot token (belonging to the same application used for authorization), and the bot must be a member of the guild with `CREATE_INSTANT_INVITE` permission. - -###### JSON Params - -| Field | Type | Description | Permission | -| ------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------- | -| access_token | string | an oauth2 access token granted with the `guilds.join` to the bot's application for the user you want to add to the guild | | -| nick | string | value to set user's nickname to | MANAGE_NICKNAMES | -| roles | array of snowflakes | array of role ids the member is assigned | MANAGE_ROLES | -| mute | boolean | whether the user is muted in voice channels | MUTE_MEMBERS | -| deaf | boolean | whether the user is deafened in voice channels | DEAFEN_MEMBERS | - -> warn -> For guilds with Membership Screening enabled, assigning a role using the `roles` parameter will add the user to the guild as a full member (`pending` is false in the [member object](#DOCS_RESOURCES_GUILD/guild-member-object)). A member with a role will bypass membership screening and the guild's verification level, and get immediate access to chat. Therefore, instead of assigning a role when the member joins, it is recommended to grant roles only after the user completes screening. - -## Modify Guild Member % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/{user.id#DOCS_RESOURCES_USER/user-object} - -Modify attributes of a [guild member](#DOCS_RESOURCES_GUILD/guild-member-object). Returns a 200 OK with the [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) as the body. Fires a [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) Gateway event. If the `channel_id` is set to null, this will force the target user to be disconnected from voice. - -> info -> All parameters to this endpoint are optional and nullable. When moving members to channels, the API user _must_ have permissions to both connect to the channel and have the `MOVE_MEMBERS` permission. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | Permission | -| ---------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | -| nick | string | value to set user's nickname to | MANAGE_NICKNAMES | -| roles | array of snowflakes | array of role ids the member is assigned | MANAGE_ROLES | -| mute | boolean | whether the user is muted in voice channels. Will throw a 400 error if the user is not in a voice channel | MUTE_MEMBERS | -| deaf | boolean | whether the user is deafened in voice channels. Will throw a 400 error if the user is not in a voice channel | DEAFEN_MEMBERS | -| channel_id | snowflake | id of channel to move user to (if they are connected to voice) | MOVE_MEMBERS | -| communication_disabled_until | ISO8601 timestamp | when the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again (up to 28 days in the future), set to null to remove timeout. Will throw a 403 error if the user has the ADMINISTRATOR permission or is the owner of the guild | MODERATE_MEMBERS | - -## Modify Current Member % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/@me - -Modifies the current member in a guild. Returns a 200 with the updated member object on success. Fires a [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | Permission | -| ----- | ------- | ------------------------------- | --------------- | -| nick? | ?string | value to set user's nickname to | CHANGE_NICKNAME | - -## Modify Current User Nick % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/@me/nick - -> danger -> Deprecated in favor of [Modify Current Member](#DOCS_RESOURCES_GUILD/modify-current-member). - -Modifies the nickname of the current user in a guild. Returns a 200 with the nickname on success. Fires a [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | Permission | -| ----- | ------- | ------------------------------- | --------------- | -| nick? | ?string | value to set user's nickname to | CHANGE_NICKNAME | - -## Add Guild Member Role % PUT /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/{user.id#DOCS_RESOURCES_USER/user-object}/roles/{role.id#DOCS_TOPICS_PERMISSIONS/role-object} - -Adds a role to a [guild member](#DOCS_RESOURCES_GUILD/guild-member-object). Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Remove Guild Member Role % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/{user.id#DOCS_RESOURCES_USER/user-object}/roles/{role.id#DOCS_TOPICS_PERMISSIONS/role-object} - -Removes a role from a [guild member](#DOCS_RESOURCES_GUILD/guild-member-object). Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Remove Guild Member % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/members/{user.id#DOCS_RESOURCES_USER/user-object} - -Remove a member from a guild. Requires `KICK_MEMBERS` permission. Returns a 204 empty response on success. Fires a [Guild Member Remove](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-remove) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Get Guild Bans % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/bans - -Returns a list of [ban](#DOCS_RESOURCES_GUILD/ban-object) objects for the users banned from this guild. Requires the `BAN_MEMBERS` permission. - -###### Query String Params - -| Field | Type | Description | Default | -| ------------ | ------- | ------------------------------------------------------------------------------ | ------- | -| limit? | number | number of users to return (up to maximum 1000) | 1000 | -| before? * | snowflake | consider only users before given user id | null | -| after? * | snowflake | consider only users after given user id | null | - -\* Provide a user id to `before` and `after` for pagination. Users will always be returned in ascending order by `user.id`. If both `before` and `after` are provided, only `before` is respected. - -## Get Guild Ban % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/bans/{user.id#DOCS_RESOURCES_USER/user-object} - -Returns a [ban](#DOCS_RESOURCES_GUILD/ban-object) object for the given user or a 404 not found if the ban cannot be found. Requires the `BAN_MEMBERS` permission. - -## Create Guild Ban % PUT /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/bans/{user.id#DOCS_RESOURCES_USER/user-object} - -Create a guild ban, and optionally delete previous messages sent by the banned user. Requires the `BAN_MEMBERS` permission. Returns a 204 empty response on success. Fires a [Guild Ban Add](#DOCS_TOPICS_GATEWAY_EVENTS/guild-ban-add) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | Default | -| ----------------------- | ------- | ----------------------------------------------------------------------- | ------- | -| delete_message_days? | integer | number of days to delete messages for (0-7) (deprecated) | 0 | -| delete_message_seconds? | integer | number of seconds to delete messages for, between 0 and 604800 (7 days) | 0 | - -## Remove Guild Ban % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/bans/{user.id#DOCS_RESOURCES_USER/user-object} - -Remove the ban for a user. Requires the `BAN_MEMBERS` permissions. Returns a 204 empty response on success. Fires a [Guild Ban Remove](#DOCS_TOPICS_GATEWAY_EVENTS/guild-ban-remove) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Get Guild Roles % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/roles - -Returns a list of [role](#DOCS_TOPICS_PERMISSIONS/role-object) objects for the guild. - -## Create Guild Role % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/roles - -Create a new [role](#DOCS_TOPICS_PERMISSIONS/role-object) for the guild. Requires the `MANAGE_ROLES` permission. Returns the new [role](#DOCS_TOPICS_PERMISSIONS/role-object) object on success. Fires a [Guild Role Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-role-create) Gateway event. All JSON params are optional. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | Default | -| ------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ | -| name | string | name of the role | "new role" | -| permissions | string | bitwise value of the enabled/disabled permissions | @everyone permissions in guild | -| color | integer | RGB color value | 0 | -| hoist | boolean | whether the role should be displayed separately in the sidebar | false | -| icon | ?[image data](#DOCS_REFERENCE/image-data) | the role's icon image (if the guild has the `ROLE_ICONS` feature) | null | -| unicode_emoji | ?string | the role's unicode emoji as a [standard emoji](#DOCS_REFERENCE/message-formatting) (if the guild has the `ROLE_ICONS` feature) | null | -| mentionable | boolean | whether the role should be mentionable | false | - -## Modify Guild Role Positions % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/roles - -Modify the positions of a set of [role](#DOCS_TOPICS_PERMISSIONS/role-object) objects for the guild. Requires the `MANAGE_ROLES` permission. Returns a list of all of the guild's [role](#DOCS_TOPICS_PERMISSIONS/role-object) objects on success. Fires multiple [Guild Role Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-role-update) Gateway events. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -This endpoint takes a JSON array of parameters in the following format: - -###### JSON Params - -| Field | Type | Description | -| --------- | --------- | ---------------------------- | -| id | snowflake | role | -| position? | ?integer | sorting position of the role | - -## Modify Guild Role % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/roles/{role.id#DOCS_TOPICS_PERMISSIONS/role-object} - -Modify a guild role. Requires the `MANAGE_ROLES` permission. Returns the updated [role](#DOCS_TOPICS_PERMISSIONS/role-object) on success. Fires a [Guild Role Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-role-update) Gateway event. - -> info -> All parameters to this endpoint are optional and nullable. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| name | string | name of the role | -| permissions | string | bitwise value of the enabled/disabled permissions | -| color | integer | RGB color value | -| hoist | boolean | whether the role should be displayed separately in the sidebar | -| icon | [image data](#DOCS_REFERENCE/image-data) | the role's icon image (if the guild has the `ROLE_ICONS` feature) | -| unicode_emoji | string | the role's unicode emoji as a [standard emoji](#DOCS_REFERENCE/message-formatting) (if the guild has the `ROLE_ICONS` feature) | -| mentionable | boolean | whether the role should be mentionable | - -## Modify Guild MFA Level % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/mfa - -Modify a guild's MFA level. Requires guild ownership. Returns the updated [level](#DOCS_RESOURCES_GUILD/guild-object-mfa-level) on success. Fires a [Guild Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| level | integer | [MFA level](#DOCS_RESOURCES_GUILD/guild-object-mfa-level) | - -## Delete Guild Role % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/roles/{role.id#DOCS_TOPICS_PERMISSIONS/role-object} - -Delete a guild role. Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success. Fires a [Guild Role Delete](#DOCS_TOPICS_GATEWAY_EVENTS/guild-role-delete) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Get Guild Prune Count % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/prune - -Returns an object with one `pruned` key indicating the number of members that would be removed in a prune operation. Requires the `KICK_MEMBERS` permission. - -By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` parameter. Any inactive user that has a subset of the provided role(s) will be counted in the prune and users with additional roles will not. - -###### Query String Params - -| Field | Type | Description | Default | -| ------------- | ------------------------------------------- | ---------------------------------------- | ------- | -| days | integer | number of days to count prune for (1-30) | 7 | -| include_roles | string; comma-delimited array of snowflakes | role(s) to include | none | - -## Begin Guild Prune % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/prune - -Begin a prune operation. Requires the `KICK_MEMBERS` permission. Returns an object with one `pruned` key indicating the number of members that were removed in the prune operation. For large guilds it's recommended to set the `compute_prune_count` option to `false`, forcing `pruned` to `null`. Fires multiple [Guild Member Remove](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-remove) Gateway events. - -By default, prune will not remove users with roles. You can optionally include specific roles in your prune by providing the `include_roles` parameter. Any inactive user that has a subset of the provided role(s) will be included in the prune and users with additional roles will not. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | Default | -| ------------------- | ------------------- | ---------------------------------------------------------- | ------- | -| days | integer | number of days to prune (1-30) | 7 | -| compute_prune_count | boolean | whether `pruned` is returned, discouraged for large guilds | true | -| include_roles | array of snowflakes | role(s) to include | none | -| reason? | string | reason for the prune (deprecated) | | - -## Get Guild Voice Regions % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/regions - -Returns a list of [voice region](#DOCS_RESOURCES_VOICE/voice-region-object) objects for the guild. Unlike the similar `/voice` route, this returns VIP servers when the guild is VIP-enabled. - -## Get Guild Invites % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/invites - -Returns a list of [invite](#DOCS_RESOURCES_INVITE/invite-object) objects (with [invite metadata](#DOCS_RESOURCES_INVITE/invite-metadata-object)) for the guild. Requires the `MANAGE_GUILD` permission. - -## Get Guild Integrations % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/integrations - -Returns a list of [integration](#DOCS_RESOURCES_GUILD/integration-object) objects for the guild. Requires the `MANAGE_GUILD` permission. - -## Delete Guild Integration % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/integrations/{integration.id#DOCS_RESOURCES_GUILD/integration-object} - -Delete the attached [integration](#DOCS_RESOURCES_GUILD/integration-object) object for the guild. Deletes any associated webhooks and kicks the associated bot if there is one. Requires the `MANAGE_GUILD` permission. Returns a 204 empty response on success. Fires [Guild Integrations Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-integrations-update) and [Integration Delete](#DOCS_TOPICS_GATEWAY_EVENTS/integration-delete) Gateway events. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Get Guild Widget Settings % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/widget - -Returns a [guild widget settings](#DOCS_RESOURCES_GUILD/guild-widget-settings-object) object. Requires the `MANAGE_GUILD` permission. - -## Modify Guild Widget % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/widget - -Modify a [guild widget settings](#DOCS_RESOURCES_GUILD/guild-widget-settings-object) object for the guild. All attributes may be passed in with JSON and modified. Requires the `MANAGE_GUILD` permission. Returns the updated [guild widget](#DOCS_RESOURCES_GUILD/guild-widget-settings-object) object. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Get Guild Widget % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/widget.json - -Returns the [widget](#DOCS_RESOURCES_GUILD/guild-widget-object) for the guild. - -## Get Guild Vanity URL % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/vanity-url - -Returns a partial [invite](#DOCS_RESOURCES_INVITE/invite-object) object for guilds with that feature enabled. Requires the `MANAGE_GUILD` permission. `code` will be null if a vanity url for the guild is not set. - -###### Example Partial Invite Object - -```json -{ - "code": "abc", - "uses": 12 -} -``` - -## Get Guild Widget Image % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/widget.png - -Returns a PNG image widget for the guild. Requires no permissions or authentication. - -> info -> All parameters to this endpoint are optional. - -###### Query String Params - -| Field | Type | Description | Default | -| ----- | ------ | ---------------------------------------------- | ------- | -| style | string | style of the widget image returned (see below) | shield | - -###### Widget Style Options - -| Value | Description | Example | -| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| shield | shield style widget with Discord icon and guild members online count | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=shield) | -| banner1 | large image with guild icon, name and online count. "POWERED BY DISCORD" as the footer of the widget | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner1) | -| banner2 | smaller widget style with guild icon, name and online count. Split on the right with Discord logo | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner2) | -| banner3 | large image with guild icon, name and online count. In the footer, Discord logo on the left and "Chat Now" on the right | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner3) | -| banner4 | large Discord logo at the top of the widget. Guild icon, name and online count in the middle portion of the widget and a "JOIN MY SERVER" button at the bottom | [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner4) | - -## Get Guild Welcome Screen % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/welcome-screen - -Returns the [Welcome Screen](#DOCS_RESOURCES_GUILD/welcome-screen-object) object for the guild. If the welcome screen is not enabled, the `MANAGE_GUILD` permission is required. - -## Modify Guild Welcome Screen % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/welcome-screen - -Modify the guild's [Welcome Screen](#DOCS_RESOURCES_GUILD/welcome-screen-object). Requires the `MANAGE_GUILD` permission. Returns the updated [Welcome Screen](#DOCS_RESOURCES_GUILD/welcome-screen-object) object. - -> info -> All parameters to this endpoint are optional and nullable - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ---------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -| enabled | boolean | whether the welcome screen is enabled | -| welcome_channels | array of [welcome screen channel](#DOCS_RESOURCES_GUILD/welcome-screen-object-welcome-screen-channel-structure) objects | channels linked in the welcome screen and their display options | -| description | string | the server description to show in the welcome screen | - -## Modify Current User Voice State % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/voice-states/@me - -Updates the current user's voice state. Returns `204 No Content` on success. Fires a [Voice State Update](#DOCS_TOPICS_GATEWAY_EVENTS/voice-state-update) Gateway event. - -###### JSON Params - -| Field | Type | Description | -| --------------------------- | ------------------ | ---------------------------------------------- | -| channel_id? | snowflake | the id of the channel the user is currently in | -| suppress? | boolean | toggles the user's suppress state | -| request_to_speak_timestamp? | ?ISO8601 timestamp | sets the user's request to speak | - -###### Caveats - -There are currently several caveats for this endpoint: - -- `channel_id` must currently point to a stage channel. -- current user must already have joined `channel_id`. -- You must have the `MUTE_MEMBERS` permission to unsuppress yourself. You can always suppress yourself. -- You must have the `REQUEST_TO_SPEAK` permission to request to speak. You can always clear your own request to speak. -- You are able to set `request_to_speak_timestamp` to any present or future time. - -## Modify User Voice State % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/voice-states/{user.id#DOCS_RESOURCES_USER/user-object} - -Updates another user's voice state. Fires a [Voice State Update](#DOCS_TOPICS_GATEWAY_EVENTS/voice-state-update) Gateway event. - -###### JSON Params - -| Field | Type | Description | -| ---------- | --------- | ---------------------------------------------- | -| channel_id | snowflake | the id of the channel the user is currently in | -| suppress? | boolean | toggles the user's suppress state | - -###### Caveats - -There are currently several caveats for this endpoint: - -- `channel_id` must currently point to a stage channel. -- User must already have joined `channel_id`. -- You must have the `MUTE_MEMBERS` permission. (Since suppression is the only thing that is available currently.) -- When unsuppressed, non-bot users will have their `request_to_speak_timestamp` set to the current time. Bot users will not. -- When suppressed, the user will have their `request_to_speak_timestamp` removed. diff --git a/docs/resources/Guild_Scheduled_Event.md b/docs/resources/Guild_Scheduled_Event.md deleted file mode 100644 index 8b89b99ff9..0000000000 --- a/docs/resources/Guild_Scheduled_Event.md +++ /dev/null @@ -1,273 +0,0 @@ -# Guild Scheduled Event - -A representation of a scheduled event in a [guild](#DOCS_RESOURCES_GUILD/). - -### Guild Scheduled Event Object - -###### Guild Scheduled Event Structure - -| Field | Type | Description | -| --------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| id | snowflake | the id of the scheduled event | -| guild_id | snowflake | the guild id which the scheduled event belongs to | -| channel_id ** | ?snowflake | the channel id in which the scheduled event will be hosted, or `null` if [scheduled entity type](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-entity-types) is `EXTERNAL` | -| creator_id? * | ?snowflake | the id of the user that created the scheduled event * | -| name | string | the name of the scheduled event (1-100 characters) | -| description? | ?string | the description of the scheduled event (1-1000 characters) | -| scheduled_start_time | ISO8601 timestamp | the time the scheduled event will start | -| scheduled_end_time ** | ?ISO8601 timestamp | the time the scheduled event will end, required if entity_type is `EXTERNAL` | -| privacy_level | [privacy level](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event | -| status | [event status](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-status) | the status of the scheduled event | -| entity_type | [scheduled entity type](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-entity-types) | the type of the scheduled event | -| entity_id | ?snowflake | the id of an entity associated with a guild scheduled event | -| entity_metadata ** | ?[entity metadata](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | additional metadata for the guild scheduled event | -| creator? | [user](#DOCS_RESOURCES_USER/user-object) object | the user that created the scheduled event | -| user_count? | integer | the number of users subscribed to the scheduled event | -| image? | ?string | the [cover image hash](#DOCS_REFERENCE/image-formatting) of the scheduled event | - - -\* `creator_id` will be null and `creator` will not be included for events created before October 25th, 2021, when the concept of `creator_id` was introduced and tracked. - -\** See [field requirements by entity type](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-field-requirements-by-entity-type) to understand the relationship between `entity_type` and the following fields: `channel_id`, `entity_metadata`, and `scheduled_end_time` - -###### Guild Scheduled Event Privacy Level - -| Level | Value | Description | -| ---------- | ----- | -------------------------------------------------------- | -| GUILD_ONLY | 2 | the scheduled event is only accessible to guild members | - -###### Guild Scheduled Event Entity Types - -| Type | Value | -| -------------- | ----- | -| STAGE_INSTANCE | 1 | -| VOICE | 2 | -| EXTERNAL | 3 | - -###### Field Requirements By Entity Type - -The following table shows field requirements based on current entity type. - -`value` : This field is required to be a non-null value - -`null` : This field is required to be null - -`-` : No strict requirements - -| Entity Type | channel_id | entity_metadata | scheduled_end_time | -| -------------- | ---------- | --------------- | ------------------ | -| STAGE_INSTANCE | value | null | - | -| VOICE | value | null | - | -| EXTERNAL | null | value * | value | - -\* `entity_metadata` with a non-null `location` must be provided - - -###### Guild Scheduled Event Status - -| Type | Value | -| ----------- | ----- | -| SCHEDULED | 1 | -| ACTIVE | 2 | -| COMPLETED * | 3 | -| CANCELED * | 4 | - -\* Once `status` is set to `COMPLETED` or `CANCELED`, the `status` can no longer be updated - -###### Valid Guild Scheduled Event Status Transitions - -SCHEDULED --> ACTIVE - -ACTIVE --------> COMPLETED - -SCHEDULED --> CANCELED - - -###### Guild Scheduled Event Entity Metadata - -| Field | Type | Description | -| ------------ | ------------------- | ---------------------------------------- | -| location? * | string | location of the event (1-100 characters) | - -\* [required](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-entity-metadata) for events with `'entity_type': EXTERNAL` - -### Guild Scheduled Event User Object - -###### Guild Scheduled Event User Structure - -| Field | Type | Description | -| -------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | -| guild_scheduled_event_id | snowflake | the scheduled event id which the user subscribed to | -| user | [user](#DOCS_RESOURCES_USER/user-object) | user which subscribed to an event | -| member? | [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) | guild member data for this user for the guild which this event belongs to, if any | - - -## List Scheduled Events for Guild % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/scheduled-events - -Returns a list of [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) objects for the given guild. - -###### Query String Params - -| Field | Type | Description | -| ---------------- | ------- | ------------------------------------------------ | -| with_user_count? | boolean | include number of users subscribed to each event | - -## Create Guild Scheduled Event % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/scheduled-events - -Create a guild scheduled event in the guild. Returns a [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) object on success. Fires a [Guild Scheduled Event Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-scheduled-event-create) Gateway event. - -> info -> A guild can have a maximum of 100 events with `SCHEDULED` or `ACTIVE` status at any time. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -| channel_id? * | snowflake * | the channel id of the scheduled event. | -| entity_metadata? ** | [entity metadata](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | the entity metadata of the scheduled event | -| name | string | the name of the scheduled event | -| privacy_level | [privacy level](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event | -| scheduled_start_time | ISO8601 timestamp | the time to schedule the scheduled event | -| scheduled_end_time? ** | ISO8601 timestamp | the time when the scheduled event is scheduled to end | -| description? | string | the description of the scheduled event | -| entity_type | [entity type](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-entity-types) | the entity type of the scheduled event | -| image? | [image data](#DOCS_REFERENCE/image-data) | the cover image of the scheduled event | - -\* Optional for events with `'entity_type': EXTERNAL` - -\*\* Required for events with `'entity_type': EXTERNAL` - -## Get Guild Scheduled Event % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/scheduled-events/{guild_scheduled_event.id#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object} - -Get a guild scheduled event. Returns a [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) object on success. - -###### Query String Params - -| Field | Type | Description | -| ---------------- | ------- | ------------------------------------------------ | -| with_user_count? | boolean | include number of users subscribed to this event | - -## Modify Guild Scheduled Event % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/scheduled-events/{guild_scheduled_event.id#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object} - -Modify a guild scheduled event. Returns the modified [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) object on success. Fires a [Guild Scheduled Event Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-scheduled-event-update) Gateway event. - -> info -> To start or end an event, use this endpoint to modify the event's [status](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-status) field. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -> info -> This endpoint silently discards `entity_metadata` for non-`EXTERNAL` events. - -###### JSON Params - -| Field | Type | Description | -| --------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -| channel_id? * | ?snowflake | the channel id of the scheduled event, set to `null` if changing entity type to `EXTERNAL` | -| entity_metadata? | ?[entity metadata](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-entity-metadata) | the entity metadata of the scheduled event | -| name? | string | the name of the scheduled event | -| privacy_level? | [privacy level](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-privacy-level) | the privacy level of the scheduled event | -| scheduled_start_time? | ISO8601 timestamp | the time to schedule the scheduled event | -| scheduled_end_time? * | ISO8601 timestamp | the time when the scheduled event is scheduled to end | -| description? | ?string | the description of the scheduled event | -| entity_type? * | [event entity type](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-entity-types) | the entity type of the scheduled event | -| status? | [event status](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-guild-scheduled-event-status) | the status of the scheduled event | -| image? | [image data](#DOCS_REFERENCE/image-data) | the cover image of the scheduled event | - -\* If updating `entity_type` to `EXTERNAL`: - -- `channel_id` is required and [must be set to null](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object-field-requirements-by-entity-type) -- `entity_metadata` with a `location` field must be provided -- `scheduled_end_time` must be provided - -## Delete Guild Scheduled Event % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/scheduled-events/{guild_scheduled_event.id#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object} - -Delete a guild scheduled event. Returns a `204` on success. Fires a [Guild Scheduled Event Delete](#DOCS_TOPICS_GATEWAY_EVENTS/guild-scheduled-event-delete) Gateway event. - -## Get Guild Scheduled Event Users % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/scheduled-events/{guild_scheduled_event.id#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object}/users - -Get a list of guild scheduled event users subscribed to a guild scheduled event. Returns a list of [guild scheduled event user](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-user-object) objects on success. Guild member data, if it exists, is included if the `with_member` query parameter is set. - -###### Query String Params - -| Field | Type | Description | Default | -| ------------ | ------- | ------------------------------------------------------------------------------ | ------- | -| limit? | number | number of users to return (up to maximum 100) | 100 | -| with_member? | boolean | include guild member data if it exists | false | -| before? * | snowflake | consider only users before given user id | null | -| after? * | snowflake | consider only users after given user id | null | - -\* Provide a user id to `before` and `after` for pagination. Users will always be returned in ascending order by `user_id`. If both `before` and `after` are provided, only `before` is respected. Fetching users in-between `before` and `after` is not supported. - - -## Guild Scheduled Event Status Update Automation - -NOTE: `status` and `entity_type` here are expressed by name rather than their value for readability - -### An active scheduled event for a stage channel where all users have left the stage channel will automatically end a few minutes after the last user leaves the channel - -When an event with `'status': ACTIVE` and `'entity_type': STAGE_INSTANCE` has no users connected to the stage channel for a certain period of time (on the order of minutes), the event `status` will be automatically set to `COMPLETED`. - -### An active scheduled event for a voice channel where all users have left the voice channel will automatically end a few minutes after the last user leaves the channel - -When an event with `'status': ACTIVE` and `'entity_type': VOICE` has no users connected to the voice channel for a certain period of time (on the order of minutes), the event `status` will be automatically set to `COMPLETED`. - -### An external event will automatically begin at its scheduled start time - -An event with `'entity_type': EXTERNAL` at its `scheduled_start_time` will automatically have `status` set to `ACTIVE`. - -### An external event will automatically end at its scheduled end time - -An event with `'entity_type': EXTERNAL` at its `scheduled_end_time` will automatically have `status` set to `COMPLETED`. - -### Any scheduled event which has not begun after its scheduled start time will be automatically cancelled after a few hours - -Any event with `'status': SCHEDULED` after a certain time interval (on the order of hours) beyond its `scheduled_start_time` will have its `status` automatically set to `CANCELLED`. - -## Guild Scheduled Event Permissions Requirements - -NOTE: `entity_type` is expressed by name rather than value for readability - -> info -> A user must be a member of the guild in order to access events for that guild unless the guild is lurkable. If a guild is lurkable, -> events in that guild may be visible to lurkers depending on the event type and the permissions of any channels associated with the event. - -### Permissions to create an event with entity_type: STAGE_INSTANCE - -#### Write Permissions (CREATE / UPDATE) - -- `MANAGE_EVENTS` at the guild level or at least `MANAGE_EVENTS` for the `channel_id` associated with the event -- `MANAGE_CHANNELS` -- `MUTE_MEMBERS` -- `MOVE_MEMBERS` - -#### Read Permissions (GET) - -- `VIEW_CHANNEL` for `channel_id` associated with the event - -### Permissions to create an event with entity_type: VOICE - -#### Write Permissions (CREATE / UPDATE) - -- `MANAGE_EVENTS` at the guild level or `MANAGE_EVENTS` for the `channel_id` associated with the event -- `VIEW_CHANNEL` for `channel_id` associated with event -- `CONNECT` for `channel_id` associated with event - -#### Read Permissions (GET) - -- `VIEW_CHANNEL` for `channel_id` associated with the event - - -### Permissions to create an event with entity_type: EXTERNAL - -#### Write Permissions (CREATE / UPDATE) - -- `MANAGE_EVENTS` at the guild level - -#### Read Permissions (GET) - -* *No other permissions required* diff --git a/docs/resources/Guild_Template.md b/docs/resources/Guild_Template.md deleted file mode 100644 index 7dc434d570..0000000000 --- a/docs/resources/Guild_Template.md +++ /dev/null @@ -1,148 +0,0 @@ -# Guild Template Resource - -### Guild Template Object - -Represents a code that when used, creates a guild based on a snapshot of an existing guild. - -###### Guild Template Structure - -| Field | Type | Description | -|-------------------------|------------------------------------------------------------|--------------------------------------------------------| -| code | string | the template code (unique ID) | -| name | string | template name | -| description | ?string | the description for the template | -| usage_count | integer | number of times this template has been used | -| creator_id | snowflake | the ID of the user who created the template | -| creator | [user](#DOCS_RESOURCES_USER/user-object) object | the user who created the template | -| created_at | ISO8601 timestamp | when this template was created | -| updated_at | ISO8601 timestamp | when this template was last synced to the source guild | -| source_guild_id | snowflake | the ID of the guild this template is based on | -| serialized_source_guild | partial [guild](#DOCS_RESOURCES_GUILD/guild-object) object | the guild snapshot this template contains | -| is_dirty | ?boolean | whether the template has unsynced changes | - -###### Example Guild Template Object - -```json -{ - "code": "hgM48av5Q69A", - "name": "Friends & Family", - "description": "", - "usage_count": 49605, - "creator_id": "132837293881950208", - "creator": { - "id": "132837293881950208", - "username": "hoges", - "avatar": "79b0d9f8c340f2d43e1f78b09f175b62", - "discriminator": "0001", - "public_flags": 129 - }, - "created_at": "2020-04-02T21:10:38+00:00", - "updated_at": "2020-05-01T17:57:38+00:00", - "source_guild_id": "678070694164299796", - "serialized_source_guild": { - "name": "Friends & Family", - "description": null, - "region": "us-west", - "verification_level": 0, - "default_message_notifications": 0, - "explicit_content_filter": 0, - "preferred_locale": "en-US", - "afk_timeout": 300, - "roles": [ - { - "id": 0, - "name": "@everyone", - "permissions": 104324689, - "color": 0, - "hoist": false, - "mentionable": false - } - ], - "channels": [ - { - "name": "Text Channels", - "position": 1, - "topic": null, - "bitrate": 64000, - "user_limit": 0, - "nsfw": false, - "rate_limit_per_user": 0, - "parent_id": null, - "permission_overwrites": [], - "id": 1, - "type": 4 - }, - { - "name": "general", - "position": 1, - "topic": null, - "bitrate": 64000, - "user_limit": 0, - "nsfw": false, - "rate_limit_per_user": 0, - "parent_id": 1, - "permission_overwrites": [], - "id": 2, - "type": 0 - } - ], - "afk_channel_id": null, - "system_channel_id": 2, - "system_channel_flags": 0, - "icon_hash": null - }, - "is_dirty": null -} -``` - -## Get Guild Template % GET /guilds/templates/{template.code#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object} - -Returns a [guild template](#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object) object for the given code. - -## Create Guild from Guild Template % POST /guilds/templates/{template.code#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object} - -Create a new guild based on a template. Returns a [guild](#DOCS_RESOURCES_GUILD/guild-object) object on success. Fires a [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) Gateway event. - -> warn -> This endpoint can be used only by bots in less than 10 guilds. - -###### JSON Params - -| Field | Type | Description | -|-------|------------------------------------------|-----------------------------------------| -| name | string | name of the guild (2-100 characters) | -| icon? | [image data](#DOCS_REFERENCE/image-data) | base64 128x128 image for the guild icon | - -## Get Guild Templates % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/templates - -Returns an array of [guild template](#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object) objects. Requires the `MANAGE_GUILD` permission. - -## Create Guild Template % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/templates - -Creates a template for the guild. Requires the `MANAGE_GUILD` permission. Returns the created [guild template](#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object) object on success. - -###### JSON Params - -| Field | Type | Description | -|--------------|---------|-------------------------------------------------| -| name | string | name of the template (1-100 characters) | -| description? | ?string | description for the template (0-120 characters) | - -## Sync Guild Template % PUT /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/templates/{template.code#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object} - -Syncs the template to the guild's current state. Requires the `MANAGE_GUILD` permission. Returns the [guild template](#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object) object on success. - -## Modify Guild Template % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/templates/{template.code#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object} - -Modifies the template's metadata. Requires the `MANAGE_GUILD` permission. Returns the [guild template](#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object) object on success. - -###### JSON Params - -| Field | Type | Description | -|--------------|---------|-------------------------------------------------| -| name? | string | name of the template (1-100 characters) | -| description? | ?string | description for the template (0-120 characters) | - -## Delete Guild Template % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/templates/{template.code#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object} - -Deletes the template. Requires the `MANAGE_GUILD` permission. Returns the deleted [guild template](#DOCS_RESOURCES_GUILD_TEMPLATE/guild-template-object) object on success. diff --git a/docs/resources/Invite.md b/docs/resources/Invite.md deleted file mode 100644 index fce074eb08..0000000000 --- a/docs/resources/Invite.md +++ /dev/null @@ -1,150 +0,0 @@ -# Invite Resource - -### Invite Object - -Represents a code that when used, adds a user to a guild or group DM channel. - -###### Invite Structure - -| Field | Type | Description | -| --------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| code | string | the invite code (unique ID) | -| guild? | partial [guild](#DOCS_RESOURCES_GUILD/guild-object) object | the guild this invite is for | -| channel | ?partial [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object | the channel this invite is for | -| inviter? | [user](#DOCS_RESOURCES_USER/user-object) object | the user who created the invite | -| target_type? | integer | the [type of target](#DOCS_RESOURCES_INVITE/invite-object-invite-target-types) for this voice channel invite | -| target_user? | [user](#DOCS_RESOURCES_USER/user-object) object | the user whose stream to display for this voice channel stream invite | -| target_application? | partial [application](#DOCS_RESOURCES_APPLICATION/application-object) object | the embedded application to open for this voice channel embedded application invite | -| approximate_presence_count? | integer | approximate count of online members, returned from the `GET /invites/` endpoint when `with_counts` is `true` | -| approximate_member_count? | integer | approximate count of total members, returned from the `GET /invites/` endpoint when `with_counts` is `true` | -| expires_at? | ?ISO8601 timestamp | the expiration date of this invite, returned from the `GET /invites/` endpoint when `with_expiration` is `true` | -| stage_instance? | [invite stage instance](#DOCS_RESOURCES_INVITE/invite-stage-instance-object) object | stage instance data if there is a [public Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE) in the Stage channel this invite is for (deprecated) | -| guild_scheduled_event? | [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) object | guild scheduled event data, only included if `guild_scheduled_event_id` contains a valid guild scheduled event id | - -###### Invite Target Types - -| Type | Value | -|----------------------|-------| -| STREAM | 1 | -| EMBEDDED_APPLICATION | 2 | - -###### Example Invite Object - -```json -{ - "code": "0vCdhLbwjZZTWZLD", - "guild": { - "id": "165176875973476352", - "name": "CS:GO Fraggers Only", - "splash": null, - "banner": null, - "description": "Very good description", - "icon": null, - "features": ["NEWS", "DISCOVERABLE"], - "verification_level": 2, - "vanity_url_code": null, - "nsfw_level": 0, - "premium_subscription_count": 5 - }, - "channel": { - "id": "165176875973476352", - "name": "illuminati", - "type": 0 - }, - "inviter": { - "id": "115590097100865541", - "username": "speed", - "avatar": "deadbeef", - "discriminator": "7653", - "public_flags": 131328 - }, - "target_type": 1, - "target_user": { - "id": "165176875973476352", - "username": "bob", - "avatar": "deadbeef", - "discriminator": "1234", - "public_flags": 64 - } -} -``` - -### Invite Metadata Object - -Extra information about an invite, will extend the [invite](#DOCS_RESOURCES_INVITE/invite-object) object. - -###### Invite Metadata Structure - -| Field | Type | Description | -| ---------- | ----------------------------------------------- | ---------------------------------------------------- | -| uses | integer | number of times this invite has been used | -| max_uses | integer | max number of times this invite can be used | -| max_age | integer | duration (in seconds) after which the invite expires | -| temporary | boolean | whether this invite only grants temporary membership | -| created_at | ISO8601 timestamp | when this invite was created | - -###### Example Invite Metadata - -```json -{ - "uses": 0, - "max_uses": 0, - "max_age": 0, - "temporary": false, - "created_at": "2016-03-31T19:15:39.954000+00:00" -} -``` - -### Invite Stage Instance Object - -> warn -> This is deprecated. - -###### Invite Stage Instance Structure - -| Field | Type | Description | -|-------------------|------------------------------------------------------------------------------------|----------------------------------------------------| -| members | array of partial [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) objects | the members speaking in the Stage | -| participant_count | integer | the number of users in the Stage | -| speaker_count | integer | the number of users speaking in the Stage | -| topic | string | the topic of the Stage instance (1-120 characters) | - -###### Example Invite Stage Instance - -```json -{ - "topic": "The debate is over: diet is better than regular", - "participant_count": 200, - "speaker_count": 5 , - "members": [ - { - "roles": [], - "nick": "NOT API SUPPORT", - "avatar": null, - "premium_since": null, - "joined_at": "2015-04-26T06:26:56.936000+00:00", - "pending": false, - "user": {} - } - ] -} -``` - -## Get Invite % GET /invites/{invite.code#DOCS_RESOURCES_INVITE/invite-object} - -Returns an [invite](#DOCS_RESOURCES_INVITE/invite-object) object for the given code. - -###### Query String Params - -| Field | Type | Description | -| ------------------------- | --------- | ----------------------------------------------------------- | -| with_counts? | boolean | whether the invite should contain approximate member counts | -| with_expiration? | boolean | whether the invite should contain the expiration date | -| guild_scheduled_event_id? | snowflake | the guild scheduled event to include with the invite | - -## Delete Invite % DELETE /invites/{invite.code#DOCS_RESOURCES_INVITE/invite-object} - -Delete an invite. Requires the `MANAGE_CHANNELS` permission on the channel this invite belongs to, or `MANAGE_GUILD` to remove any invite across the guild. Returns an [invite](#DOCS_RESOURCES_INVITE/invite-object) object on success. Fires an [Invite Delete](#DOCS_TOPICS_GATEWAY_EVENTS/invite-delete) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. diff --git a/docs/resources/Sticker.md b/docs/resources/Sticker.md deleted file mode 100644 index c7a619d872..0000000000 --- a/docs/resources/Sticker.md +++ /dev/null @@ -1,166 +0,0 @@ -# Sticker Resource - -### Sticker Object - -Represents a sticker that can be sent in messages. - -###### Sticker Structure - -| Field | Type | Description | -| ----------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| id | snowflake | [id of the sticker](#DOCS_REFERENCE/image-formatting) | -| pack_id? | snowflake | for standard stickers, id of the pack the sticker is from | -| name | string | name of the sticker | -| description | ?string | description of the sticker | -| tags\* | string | autocomplete/suggestion tags for the sticker (max 200 characters) | -| asset? | string | **Deprecated** previously the sticker asset hash, now an empty string | -| type | integer | [type of sticker](#DOCS_RESOURCES_STICKER/sticker-object-sticker-types) | -| format_type | integer | [type of sticker format](#DOCS_RESOURCES_STICKER/sticker-object-sticker-format-types) | -| available? | boolean | whether this guild sticker can be used, may be false due to loss of Server Boosts | -| guild_id? | snowflake | id of the guild that owns this sticker | -| user? | [user](#DOCS_RESOURCES_USER/user-object) object | the user that uploaded the guild sticker | -| sort_value? | integer | the standard sticker's sort order within its pack | - -\* A comma separated list of keywords is the format used in this field by standard stickers, but this is just a convention. -Incidentally the client will always use a name generated from an emoji as the value of this field when creating or modifying a guild sticker. - -###### Sticker Types - -| Type | Value | Description | -| -------- | ----- | ----------------------------------------------------------------------------- | -| STANDARD | 1 | an official sticker in a pack, part of Nitro or in a removed purchasable pack | -| GUILD | 2 | a sticker uploaded to a guild for the guild's members | - -###### Sticker Format Types - -| Type | Value | -| ------ | ----- | -| PNG | 1 | -| APNG | 2 | -| LOTTIE | 3 | - -###### Example Sticker - -```json -{ - "id": "749054660769218631", - "name": "Wave", - "tags": "wumpus, hello, sup, hi, oi, heyo, heya, yo, greetings, greet, welcome, wave, :wave, :hello, :hi, :hey, hey, \ud83d\udc4b, \ud83d\udc4b\ud83c\udffb, \ud83d\udc4b\ud83c\udffc, \ud83d\udc4b\ud83c\udffd, \ud83d\udc4b\ud83c\udffe, \ud83d\udc4b\ud83c\udfff, goodbye, bye, see ya, later, laterz, cya", - "type": 1, - "format_type": 3, - "description": "Wumpus waves hello", - "asset": "", - "pack_id": "847199849233514549", - "sort_value": 12 -} -``` - -### Sticker Item Object - -The smallest amount of data required to render a sticker. A partial sticker object. - -###### Sticker Item Structure - -| Field | Type | Description | -| ----------- | --------- | ------------------------------------------------------------------------------------- | -| id | snowflake | id of the sticker | -| name | string | name of the sticker | -| format_type | integer | [type of sticker format](#DOCS_RESOURCES_STICKER/sticker-object-sticker-format-types) | - -### Sticker Pack Object - -Represents a pack of standard stickers. - -###### Sticker Pack Structure - -| Field | Type | Description | -| ---------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------- | -| id | snowflake | id of the sticker pack | -| stickers | array of [sticker](#DOCS_RESOURCES_STICKER/sticker-object) objects | the stickers in the pack | -| name | string | name of the sticker pack | -| sku_id | snowflake | id of the pack's SKU | -| cover_sticker_id? | snowflake | id of a sticker in the pack which is shown as the pack's icon | -| description | string | description of the sticker pack | -| banner_asset_id? | snowflake | id of the sticker pack's [banner image](#DOCS_REFERENCE/image-formatting) | - -###### Example Sticker Pack - -```json -{ - "id": "847199849233514549", - "stickers": [], - "name": "Wumpus Beyond", - "sku_id": "847199849233514547", - "cover_sticker_id": "749053689419006003", - "description": "Say hello to Wumpus!", - "banner_asset_id": "761773777976819732" -} -``` - -## Get Sticker % GET /stickers/{sticker.id#DOCS_RESOURCES_STICKER/sticker-object} - -Returns a [sticker](#DOCS_RESOURCES_STICKER/sticker-object) object for the given sticker ID. - -## List Nitro Sticker Packs % GET /sticker-packs - -Returns the list of sticker packs available to Nitro subscribers. - -###### Response Structure - -| Field | Type | -| ------------- | ---------------------------------------------------------------------------- | -| sticker_packs | array of [sticker pack](#DOCS_RESOURCES_STICKER/sticker-pack-object) objects | - -## List Guild Stickers % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/stickers - -Returns an array of [sticker](#DOCS_RESOURCES_STICKER/sticker-object) objects for the given guild. Includes `user` fields if the bot has the `MANAGE_EMOJIS_AND_STICKERS` permission. - -## Get Guild Sticker % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/stickers/{sticker.id#DOCS_RESOURCES_STICKER/sticker-object} - -Returns a [sticker](#DOCS_RESOURCES_STICKER/sticker-object) object for the given guild and sticker IDs. Includes the `user` field if the bot has the `MANAGE_EMOJIS_AND_STICKERS` permission. - -## Create Guild Sticker % POST /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/stickers - -Create a new sticker for the guild. Send a `multipart/form-data` body. Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. Returns the new [sticker](#DOCS_RESOURCES_STICKER/sticker-object) object on success. Fires a [Guild Stickers Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-stickers-update) Gateway event. - -Every guilds has five free sticker slots by default, and each Boost level will grant access to more slots. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -> warn -> Lottie stickers can only be uploaded on guilds that have either the `VERIFIED` and/or the `PARTNERED` [guild feature](#DOCS_RESOURCES_GUILD/guild-object-guild-features). - -###### Form Params - -| Field | Type | Description | -| ----------- | ------------- | -------------------------------------------------------------------------------------------- | -| name | string | name of the sticker (2-30 characters) | -| description | string | description of the sticker (empty or 2-100 characters) | -| tags | string | autocomplete/suggestion tags for the sticker (max 200 characters) | -| file | file contents | the sticker file to upload, must be a PNG, APNG, or Lottie JSON file, max 500 KB | - -## Modify Guild Sticker % PATCH /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/stickers/{sticker.id#DOCS_RESOURCES_STICKER/sticker-object} - -Modify the given sticker. Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. Returns the updated [sticker](#DOCS_RESOURCES_STICKER/sticker-object) object on success. Fires a [Guild Stickers Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-stickers-update) Gateway event. - -> info -> All parameters to this endpoint are optional. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ----------- | ------- | -------------------------------------------------------------------------------------------- | -| name | string | name of the sticker (2-30 characters) | -| description | ?string | description of the sticker (2-100 characters) | -| tags | string | autocomplete/suggestion tags for the sticker (max 200 characters) | - -## Delete Guild Sticker % DELETE /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/stickers/{sticker.id#DOCS_RESOURCES_STICKER/sticker-object} - -Delete the given sticker. Requires the `MANAGE_EMOJIS_AND_STICKERS` permission. Returns `204 No Content` on success. Fires a [Guild Stickers Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-stickers-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. diff --git a/docs/resources/User.md b/docs/resources/User.md deleted file mode 100644 index c16c0f9aa4..0000000000 --- a/docs/resources/User.md +++ /dev/null @@ -1,229 +0,0 @@ -# Users Resource - -Users in Discord are generally considered the base entity. Users can spawn across the entire platform, be members of -guilds, participate in text and voice chat, and much more. Users are separated by a distinction of "bot" vs "normal." Although they are similar, bot users are automated users that are "owned" by another user. Unlike normal users, bot users do -_not_ have a limitation on the number of Guilds they can be a part of. - -## Usernames and Nicknames - -Discord enforces the following restrictions for usernames and nicknames: - -1. Names can contain most valid unicode characters. We limit some zero-width and non-rendering characters. -2. Usernames must be between 2 and 32 characters long. -3. Nicknames must be between 1 and 32 characters long. -4. Names are sanitized and trimmed of leading, trailing, and excessive internal whitespace. - -The following restrictions are additionally enforced for usernames: - -1. Usernames cannot contain the following substrings: `@`, `#`, `:`, ` ``` `, `discord` -2. Usernames cannot be: `everyone`, `here` - -There are other rules and restrictions not shared here for the sake of spam and abuse mitigation, but the majority of users won't encounter them. It's important to properly handle all error messages returned by Discord when editing or updating names. - -### User Object - -###### User Structure - -| Field | Type | Description | Required OAuth2 Scope | -| ------------- | --------- | ---------------------------------------------------------------------------------------------------- | --------------------- | -| id | snowflake | the user's id | identify | -| username | string | the user's username, not unique across the platform | identify | -| discriminator | string | the user's 4-digit discord-tag | identify | -| avatar | ?string | the user's [avatar hash](#DOCS_REFERENCE/image-formatting) | identify | -| bot? | boolean | whether the user belongs to an OAuth2 application | identify | -| system? | boolean | whether the user is an Official Discord System user (part of the urgent message system) | identify | -| mfa_enabled? | boolean | whether the user has two factor enabled on their account | identify | -| banner? | ?string | the user's [banner hash](#DOCS_REFERENCE/image-formatting) | identify | -| accent_color? | ?integer | the user's banner color encoded as an integer representation of hexadecimal color code | identify | -| locale? | string | the user's chosen [language option](#DOCS_REFERENCE/locales) | identify | -| verified? | boolean | whether the email on this account has been verified | email | -| email? | ?string | the user's email | email | -| flags? | integer | the [flags](#DOCS_RESOURCES_USER/user-object-user-flags) on a user's account | identify | -| premium_type? | integer | the [type of Nitro subscription](#DOCS_RESOURCES_USER/user-object-premium-types) on a user's account | identify | -| public_flags? | integer | the public [flags](#DOCS_RESOURCES_USER/user-object-user-flags) on a user's account | identify | - -###### Example User - -```json -{ - "id": "80351110224678912", - "username": "Nelly", - "discriminator": "1337", - "avatar": "8342729096ea3675442027381ff50dfe", - "verified": true, - "email": "nelly@discord.com", - "flags": 64, - "banner": "06c16474723fe537c283b8efa61a30c8", - "accent_color": 16711680, - "premium_type": 1, - "public_flags": 64 -} -``` - -###### User Flags - -| Value | Name | Description | -| ------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| 1 << 0 | STAFF | Discord Employee | -| 1 << 1 | PARTNER | Partnered Server Owner | -| 1 << 2 | HYPESQUAD | HypeSquad Events Member | -| 1 << 3 | BUG_HUNTER_LEVEL_1 | Bug Hunter Level 1 | -| 1 << 6 | HYPESQUAD_ONLINE_HOUSE_1 | House Bravery Member | -| 1 << 7 | HYPESQUAD_ONLINE_HOUSE_2 | House Brilliance Member | -| 1 << 8 | HYPESQUAD_ONLINE_HOUSE_3 | House Balance Member | -| 1 << 9 | PREMIUM_EARLY_SUPPORTER | Early Nitro Supporter | -| 1 << 10 | TEAM_PSEUDO_USER | User is a [team](#DOCS_TOPICS_TEAMS/) | -| 1 << 14 | BUG_HUNTER_LEVEL_2 | Bug Hunter Level 2 | -| 1 << 16 | VERIFIED_BOT | Verified Bot | -| 1 << 17 | VERIFIED_DEVELOPER | Early Verified Bot Developer | -| 1 << 18 | CERTIFIED_MODERATOR | Discord Certified Moderator | -| 1 << 19 | BOT_HTTP_INTERACTIONS | Bot uses only [HTTP interactions](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/receiving-an-interaction) and is shown in the online member list | - -###### Premium Types - -Premium types denote the level of premium a user has. Visit the [Nitro](https://discord.com/nitro) page to learn more about the premium plans we currently offer. - -| Value | Name | -| ----- | ------------- | -| 0 | None | -| 1 | Nitro Classic | -| 2 | Nitro | - -### Connection Object - -The connection object that the user has attached. - -###### Connection Structure - -| Field | Type | Description | -| ------------- | ------- | ---------------------------------------------------------------------------------------- | -| id | string | id of the connection account | -| name | string | the username of the connection account | -| type | string | the [service](#DOCS_RESOURCES_USER/connection-object-services) of this connection | -| revoked? | boolean | whether the connection is revoked | -| integrations? | array | an array of partial [server integrations](#DOCS_RESOURCES_GUILD/integration-object) | -| verified | boolean | whether the connection is verified | -| friend_sync | boolean | whether friend sync is enabled for this connection | -| show_activity | boolean | whether activities related to this connection will be shown in presence updates | -| two_way_link | boolean | whether this connection has a corresponding third party OAuth2 token | -| visibility | integer | [visibility](#DOCS_RESOURCES_USER/connection-object-visibility-types) of this connection | - -###### Services - -| Value | Name | -| --------------- | ------------------- | -| battlenet | Battle.net | -| ebay | eBay | -| epicgames | Epic Games | -| facebook | Facebook | -| github | GitHub | -| leagueoflegends | League of Legends | -| paypal | PayPal | -| playstation | PlayStation Network | -| reddit | Reddit | -| riotgames | Riot Games | -| spotify | Spotify | -| skype * | Skype | -| steam | Steam | -| twitch | Twitch | -| twitter | Twitter | -| xbox | Xbox | -| youtube | YouTube | - -\* Service can no longer be added by users - -###### Visibility Types - -| Value | Name | Description | -| ----- | -------- | ------------------------------------------------ | -| 0 | None | invisible to everyone except the user themselves | -| 1 | Everyone | visible to everyone | - -## Get Current User % GET /users/@me - -Returns the [user](#DOCS_RESOURCES_USER/user-object) object of the requester's account. For OAuth2, this requires the `identify` scope, which will return the object _without_ an email, and optionally the `email` scope, which returns the object _with_ an email. - -## Get User % GET /users/{user.id#DOCS_RESOURCES_USER/user-object} - -Returns a [user](#DOCS_RESOURCES_USER/user-object) object for a given user ID. - -## Modify Current User % PATCH /users/@me - -Modify the requester's user account settings. Returns a [user](#DOCS_RESOURCES_USER/user-object) object on success. Fires a [User Update](#DOCS_TOPICS_GATEWAY_EVENTS/user-update) Gateway event. - -> info -> All parameters to this endpoint are optional. - -###### JSON Params - -| Field | Type | Description | -| -------- | ----------------------------------------- | -------------------------------------------------------------------------------- | -| username | string | user's username, if changed may cause the user's discriminator to be randomized. | -| avatar | ?[image data](#DOCS_REFERENCE/image-data) | if passed, modifies the user's avatar | - -## Get Current User Guilds % GET /users/@me/guilds - -Returns a list of partial [guild](#DOCS_RESOURCES_GUILD/guild-object) objects the current user is a member of. Requires the `guilds` OAuth2 scope. - -###### Example Partial Guild - -```json -{ - "id": "80351110224678912", - "name": "1337 Krew", - "icon": "8342729096ea3675442027381ff50dfe", - "owner": true, - "permissions": "36953089", - "features": ["COMMUNITY", "NEWS"] -} -``` - -> info -> This endpoint returns 200 guilds by default, which is the maximum number of guilds a non-bot user can join. Therefore, pagination is **not needed** for integrations that need to get a list of the users' guilds. - -###### Query String Params - -| Field | Type | Description | Required | Default | -| ------ | --------- | -------------------------------------- | -------- | ------- | -| before | snowflake | get guilds before this guild ID | false | absent | -| after | snowflake | get guilds after this guild ID | false | absent | -| limit | integer | max number of guilds to return (1-200) | false | 200 | - -## Get Current User Guild Member % GET /users/@me/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/member - -Returns a [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) object for the current user. Requires the `guilds.members.read` OAuth2 scope. - -## Leave Guild % DELETE /users/@me/guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object} - -Leave a guild. Returns a 204 empty response on success. - -## Create DM % POST /users/@me/channels - -Create a new DM channel with a user. Returns a [DM channel](#DOCS_RESOURCES_CHANNEL/channel-object) object. - -> warn -> You should not use this endpoint to DM everyone in a server about something. DMs should generally be initiated by a user action. If you open a significant amount of DMs too quickly, your bot may be rate limited or blocked from opening new ones. - -###### JSON Params - -| Field | Type | Description | -| ------------ | --------- | --------------------------------------- | -| recipient_id | snowflake | the recipient to open a DM channel with | - -## Create Group DM % POST /users/@me/channels - -Create a new group DM channel with multiple users. Returns a [DM channel](#DOCS_RESOURCES_CHANNEL/channel-object) object. This endpoint was intended to be used with the now-deprecated GameBridge SDK. DMs created with this endpoint will not be shown in the Discord client - -> warn -> This endpoint is limited to 10 active group DMs. - -###### JSON Params - -| Field | Type | Description | -| ------------- | ---------------- | ---------------------------------------------------------------------- | -| access_tokens | array of strings | access tokens of users that have granted your app the `gdm.join` scope | -| nicks | dict | a dictionary of user ids to their respective nicknames | - -## Get User Connections % GET /users/@me/connections - -Returns a list of [connection](#DOCS_RESOURCES_USER/connection-object) objects. Requires the `connections` OAuth2 scope. diff --git a/docs/resources/Voice.md b/docs/resources/Voice.md deleted file mode 100644 index b08b699834..0000000000 --- a/docs/resources/Voice.md +++ /dev/null @@ -1,55 +0,0 @@ -# Voice Resource - -### Voice State Object - -Used to represent a user's voice connection status. - -###### Voice State Structure - -| Field | Type | Description | -| -------------------------- | ---------------------------------------------------------------- | ------------------------------------------------- | -| guild_id? | snowflake | the guild id this voice state is for | -| channel_id | ?snowflake | the channel id this user is connected to | -| user_id | snowflake | the user id this voice state is for | -| member? | [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) object | the guild member this voice state is for | -| session_id | string | the session id for this voice state | -| deaf | boolean | whether this user is deafened by the server | -| mute | boolean | whether this user is muted by the server | -| self_deaf | boolean | whether this user is locally deafened | -| self_mute | boolean | whether this user is locally muted | -| self_stream? | boolean | whether this user is streaming using "Go Live" | -| self_video | boolean | whether this user's camera is enabled | -| suppress | boolean | whether this user's permission to speak is denied | -| request_to_speak_timestamp | ?ISO8601 timestamp | the time at which the user requested to speak | - -###### Example Voice State - -```json -{ - "channel_id": "157733188964188161", - "user_id": "80351110224678912", - "session_id": "90326bd25d71d39b9ef95b299e3872ff", - "deaf": false, - "mute": false, - "self_deaf": false, - "self_mute": true, - "suppress": false, - "request_to_speak_timestamp": "2021-03-31T18:45:31.297561+00:00" -} -``` - -### Voice Region Object - -###### Voice Region Structure - -| Field | Type | Description | -| ---------- | ------- | --------------------------------------------------------------------- | -| id | string | unique ID for the region | -| name | string | name of the region | -| optimal | boolean | true for a single server that is closest to the current user's client | -| deprecated | boolean | whether this is a deprecated voice region (avoid switching to these) | -| custom | boolean | whether this is a custom voice region (used for events/etc) | - -## List Voice Regions % GET /voice/regions - -Returns an array of [voice region](#DOCS_RESOURCES_VOICE/voice-region-object) objects that can be used when setting a voice or stage channel's [`rtc_region`](#DOCS_RESOURCES_CHANNEL/channel-object-channel-structure). diff --git a/docs/resources/Webhook.md b/docs/resources/Webhook.md deleted file mode 100644 index f39106e7df..0000000000 --- a/docs/resources/Webhook.md +++ /dev/null @@ -1,287 +0,0 @@ -# Webhook Resource - -Webhooks are a low-effort way to post messages to channels in Discord. They do not require a bot user or authentication to use. - -### Webhook Object - -Used to represent a webhook. - -###### Webhook Structure - -| Field | Type | Description | -| ------------------ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| id | snowflake | the id of the webhook | -| type | integer | the [type](#DOCS_RESOURCES_WEBHOOK/webhook-object-webhook-types) of the webhook | -| guild_id? | ?snowflake | the guild id this webhook is for, if any | -| channel_id | ?snowflake | the channel id this webhook is for, if any | -| user? | [user](#DOCS_RESOURCES_USER/user-object) object | the user this webhook was created by (not returned when getting a webhook with its token) | -| name | ?string | the default name of the webhook | -| avatar | ?string | the default user avatar [hash](#DOCS_REFERENCE/image-formatting) of the webhook | -| token? | string | the secure token of the webhook (returned for Incoming Webhooks) | -| application_id | ?snowflake | the bot/OAuth2 application that created this webhook | -| source_guild? * | partial [guild](#DOCS_RESOURCES_GUILD/guild-object) object | the guild of the channel that this webhook is following (returned for Channel Follower Webhooks) | -| source_channel? * | partial [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object | the channel that this webhook is following (returned for Channel Follower Webhooks) | -| url? | string | the url used for executing the webhook (returned by the [webhooks](#DOCS_TOPICS_OAUTH2/webhooks) OAuth2 flow) | - -\* These fields will be absent if the webhook creator has since lost access to the guild where the followed channel resides - -###### Webhook Types - -| Value | Name | Description | -| ----- | ---------------- | -------------------------------------------------------------------------------------------------------------- | -| 1 | Incoming | Incoming Webhooks can post messages to channels with a generated token | -| 2 | Channel Follower | Channel Follower Webhooks are internal webhooks used with Channel Following to post new messages into channels | -| 3 | Application | Application webhooks are webhooks used with Interactions | - -###### Example Incoming Webhook - -```json -{ - "name": "test webhook", - "type": 1, - "channel_id": "199737254929760256", - "token": "3d89bb7572e0fb30d8128367b3b1b44fecd1726de135cbe28a41f8b2f777c372ba2939e72279b94526ff5d1bd4358d65cf11", - "avatar": null, - "guild_id": "199737254929760256", - "id": "223704706495545344", - "application_id": null, - "user": { - "username": "test", - "discriminator": "7479", - "id": "190320984123768832", - "avatar": "b004ec1740a63ca06ae2e14c5cee11f3", - "public_flags": 131328 - } -} -``` - -###### Example Channel Follower Webhook - -```json -{ - "type": 2, - "id": "752831914402115456", - "name": "Guildy name", - "avatar": "bb71f469c158984e265093a81b3397fb", - "channel_id": "561885260615255432", - "guild_id": "56188498421443265", - "application_id": null, - "source_guild": { - "id": "56188498421476534", - "name": "Guildy name", - "icon": "bb71f469c158984e265093a81b3397fb" - }, - "source_channel": { - "id": "5618852344134324", - "name": "announcements" - }, - "user": { - "username": "test", - "discriminator": "7479", - "id": "190320984123768832", - "avatar": "b004ec1740a63ca06ae2e14c5cee11f3", - "public_flags": 131328 - } -} -``` - -###### Example Application Webhook - -```json -{ - "type": 3, - "id": "658822586720976555", - "name": "Clyde", - "avatar": "689161dc90ac261d00f1608694ac6bfd", - "channel_id": null, - "guild_id": null, - "application_id": "658822586720976555" -} -``` - -## Create Webhook % POST /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/webhooks - -Creates a new webhook and returns a [webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object) object on success. Requires the `MANAGE_WEBHOOKS` permission. Fires a [Webhooks Update](#DOCS_TOPICS_GATEWAY_EVENTS/webhooks-update) Gateway event. - -An error will be returned if a webhook name (`name`) is not valid. A webhook name is valid if: -- It does not contain the substring '**clyde**' (case-insensitive) -- It follows the nickname guidelines in the [Usernames and Nicknames](#DOCS_RESOURCES_USER/usernames-and-nicknames) documentation, with an exception that webhook names can be up to 80 characters - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ------- | ----------------------------------------- | ------------------------------------- | -| name | string | name of the webhook (1-80 characters) | -| avatar? | ?[image data](#DOCS_REFERENCE/image-data) | image for the default webhook avatar | - -## Get Channel Webhooks % GET /channels/{channel.id#DOCS_RESOURCES_CHANNEL/channel-object}/webhooks - -Returns a list of channel [webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object) objects. Requires the `MANAGE_WEBHOOKS` permission. - -## Get Guild Webhooks % GET /guilds/{guild.id#DOCS_RESOURCES_GUILD/guild-object}/webhooks - -Returns a list of guild [webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object) objects. Requires the `MANAGE_WEBHOOKS` permission. - -## Get Webhook % GET /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object} - -Returns the new [webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object) object for the given id. - -## Get Webhook with Token % GET /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object} - -Same as above, except this call does not require authentication and returns no user in the webhook object. - -## Modify Webhook % PATCH /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object} - -Modify a webhook. Requires the `MANAGE_WEBHOOKS` permission. Returns the updated [webhook](#DOCS_RESOURCES_WEBHOOK/webhook-object) object on success. Fires a [Webhooks Update](#DOCS_TOPICS_GATEWAY_EVENTS/webhooks-update) Gateway event. - -> info -> All parameters to this endpoint are optional - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -###### JSON Params - -| Field | Type | Description | -| ---------- | ----------------------------------------- | -------------------------------------------------- | -| name | string | the default name of the webhook | -| avatar | ?[image data](#DOCS_REFERENCE/image-data) | image for the default webhook avatar | -| channel_id | snowflake | the new channel id this webhook should be moved to | - -## Modify Webhook with Token % PATCH /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object} - -Same as above, except this call does not require authentication, does not accept a `channel_id` parameter in the body, and does not return a user in the webhook object. - -## Delete Webhook % DELETE /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object} - -Delete a webhook permanently. Requires the `MANAGE_WEBHOOKS` permission. Returns a `204 No Content` response on success. Fires a [Webhooks Update](#DOCS_TOPICS_GATEWAY_EVENTS/webhooks-update) Gateway event. - -> info -> This endpoint supports the `X-Audit-Log-Reason` header. - -## Delete Webhook with Token % DELETE /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object} - -Same as above, except this call does not require authentication. - -## Execute Webhook % POST /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object} - -Refer to [Uploading Files](#DOCS_REFERENCE/uploading-files) for details on attachments and `multipart/form-data` requests. Returns a message or `204 No Content` depending on the `wait` query parameter. - -> info -> Note that when sending a message, you must provide a value for at **least one of** `content`, `embeds`, `components`, or `file`. - -> info -> If the webhook channel is a forum channel, you must provide either `thread_id` in the query string params, or `thread_name` in the JSON/form params. If `thread_id` is provided, the message will send in that thread. If `thread_name` is provided, a thread with that name will be created in the forum channel. - -###### Query String Params - -| Field | Type | Description | Required | -| ----- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -| wait | boolean | waits for server confirmation of message send before response, and returns the created message body (defaults to `false`; when `false` a message that is not saved does not return an error) | false | -| thread_id | snowflake | Send a message to the specified thread within a webhook's channel. The thread will automatically be unarchived. | false | - -###### JSON/Form Params - -| Field | Type | Description | Required | -| ---------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -| content | string | the message contents (up to 2000 characters) | one of content, file, embeds | -| username | string | override the default username of the webhook | false | -| avatar_url | string | override the default avatar of the webhook | false | -| tts | boolean | true if this is a TTS message | false | -| embeds | array of up to 10 [embed](#DOCS_RESOURCES_CHANNEL/embed-object) objects | embedded `rich` content | one of content, file, embeds | -| allowed_mentions | [allowed mention object](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) | allowed mentions for the message | false | -| components \* | array of [message component](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/component-object) | the components to include with the message | false | -| files[n] \*\* | file contents | the contents of the file being sent | one of content, file, embeds | -| payload_json \*\*| string | JSON encoded body of non-file params | `multipart/form-data` only | -| attachments \*\* | array of partial [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) objects | attachment objects with filename and description | false | -| flags | integer | [message flags](#DOCS_RESOURCES_CHANNEL/message-object-message-flags) combined as a [bitfield](https://en.wikipedia.org/wiki/Bit_field) (only `SUPPRESS_EMBEDS` can be set) | false | -| thread_name | string | name of thread to create (requires the webhook channel to be a forum channel) | false | - -\* Requires an application-owned webhook. - -\*\* See [Uploading Files](#DOCS_REFERENCE/uploading-files) for details. - -> info -> For the webhook embed objects, you can set every field except `type` (it will be `rich` regardless of if you try to set it), `provider`, `video`, and any `height`, `width`, or `proxy_url` values for images. - -## Execute Slack-Compatible Webhook % POST /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object}/slack - -Refer to [Slack's documentation](https://api.slack.com/incoming-webhooks) for more information. We do not support Slack's `channel`, `icon_emoji`, `mrkdwn`, or `mrkdwn_in` properties. - -###### Query String Params - -| Field | Type | Description | Required | -| --------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -| thread_id | snowflake | id of the thread to send the message in | false | -| wait | boolean | waits for server confirmation of message send before response (defaults to `true`; when `false` a message that is not saved does not return an error) | false | - -## Execute GitHub-Compatible Webhook % POST /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object}/github - -Add a new webhook to your GitHub repo (in the repo's settings), and use this endpoint as the "Payload URL." You can choose what events your Discord channel receives by choosing the "Let me select individual events" option and selecting individual events for the new webhook you're configuring. - -###### Query String Params - -| Field | Type | Description | Required | -| --------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -| thread_id | snowflake | id of the thread to send the message in | false | -| wait | boolean | waits for server confirmation of message send before response (defaults to `true`; when `false` a message that is not saved does not return an error) | false | - -## Get Webhook Message % GET /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Returns a previously-sent webhook message from the same token. Returns a [message](#DOCS_RESOURCES_CHANNEL/message-object) object on success. - -###### Query String Params - -| Field | Type | Description | Required | -| --------- | --------- | ---------------------------------- | -------- | -| thread_id | snowflake | id of the thread the message is in | false | - -## Edit Webhook Message % PATCH /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Edits a previously-sent webhook message from the same token. Returns a [message](#DOCS_RESOURCES_CHANNEL/message-object) object on success. - -When the `content` field is edited, the `mentions` array in the message object will be reconstructed from scratch based on the new content. The `allowed_mentions` field of the edit request controls how this happens. If there is no explicit `allowed_mentions` in the edit request, the content will be parsed with _default_ allowances, that is, without regard to whether or not an `allowed_mentions` was present in the request that originally created the message. - -Refer to [Uploading Files](#DOCS_REFERENCE/uploading-files) for details on attachments and `multipart/form-data` requests. -Any provided files will be **appended** to the message. To remove or replace files you will have to supply the `attachments` field which specifies the files to retain on the message after edit. - -> warn -> Starting with API v10, the `attachments` array must contain all attachments that should be present after edit, including **retained and new** attachments provided in the request body. - -> info -> All parameters to this endpoint are optional and nullable. - -###### Query String Params - -| Field | Type | Description | Required | -| --------- | --------- | ---------------------------------- | -------- | -| thread_id | snowflake | id of the thread the message is in | false | - -###### JSON/Form Params - -| Field | Type | Description | -| ---------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------- | -| content | string | the message contents (up to 2000 characters) | -| embeds | array of up to 10 [embed](#DOCS_RESOURCES_CHANNEL/embed-object) objects | embedded `rich` content | -| allowed_mentions | [allowed mention object](#DOCS_RESOURCES_CHANNEL/allowed-mentions-object) | allowed mentions for the message | -| components \* | array of [message component](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS/component-object) | the components to include with the message | -| files[n] \*\* | file contents | the contents of the file being sent/edited | -| payload_json \*\*| string | JSON encoded body of non-file params (multipart/form-data only) | -| attachments \*\* | array of partial [attachment](#DOCS_RESOURCES_CHANNEL/attachment-object) objects | attached files to keep and possible descriptions for new files | - -\* Requires an application-owned webhook. - -\*\* See [Uploading Files](#DOCS_REFERENCE/uploading-files) for details. - -## Delete Webhook Message % DELETE /webhooks/{webhook.id#DOCS_RESOURCES_WEBHOOK/webhook-object}/{webhook.token#DOCS_RESOURCES_WEBHOOK/webhook-object}/messages/{message.id#DOCS_RESOURCES_CHANNEL/message-object} - -Deletes a message that was created by the webhook. Returns a `204 No Content` response on success. - -###### Query String Params - -| Field | Type | Description | Required | -| --------- | --------- | ---------------------------------- | -------- | -| thread_id | snowflake | id of the thread the message is in | false | diff --git a/docs/rich_presence/Best_Practices.md b/docs/rich_presence/Best_Practices.md deleted file mode 100644 index 3d6fe12cea..0000000000 --- a/docs/rich_presence/Best_Practices.md +++ /dev/null @@ -1,88 +0,0 @@ -# Rich Presence Best Practices - -> danger -> The SDK that this documentation references, [Discord-RPC](https://github.com/discord/discord-rpc), has been deprecated in favor of our new [Discord GameSDK](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/). Replacement functionality for the Rich Presence SDK can be found in the [Activity Manager](#DOCS_GAME_SDK_ACTIVITIES/) of that SDK. This documentation can be referenced for education but does not entirely reflect the new SDK. - -Rich Presence is a new feature from Discord that allows you to surface unique, interesting, and actionable data inside a Discord user’s profile when they play your game! This guide is intended to show some best practices on how to make that data the best it can be. It will include images and code samples; for full technical documentation, see our developer documentation. - -If you take away one thing from this guide, let it be this: - -> warn -> Rich Presence data should give others a clear understanding of what someone is doing so they can decide if they want to play together or not. - -## Who should use Rich Presence? - -Rich Presence is a powerful way to integrate your game with Discord. To do it most effectively, you should think about its purpose and how well (or not) it matches with your game and your implementation. Rich Presence is designed for these three things: - -1. Show interesting, unique, actionable data in a user’s profile -2. Allow friends to spectate each other’s games -3. Join a friend’s in-game party or server directly from Discord - -We certainly don’t want to stifle creativity, especially for games that can use Rich Presence in an interesting way. However, keep in mind that this sort of gameplay is what it was designed for, and how players will normally interact with it. - -If you want to do something creative, wacky, funky, or otherwise out-there with Rich Presence for your players and aren’t sure if you can, feel free to drop us a line at [gamedevs@discord.com](mailto:gamedevs@discord.com). We’re always happy to help! - -## How should you think about the data you show? - -The data in your players’ profiles is the first thing that others on Discord will see about your game, both those familiar with it and those who have never played. It should answer two questions: can I play with my friend right now, and if not, when can I? Show data like: - -- What the player is currently doing -- How much time has elapsed or remains (if applicable) -- Their party state -- Your cool artwork! - -For a great real world example, check out [Holodrive](https://store.steampowered.com/app/370770/Holodrive/) for free on Steam! - -## Tips - -### Keep it Short - -- `details` and `state` should be snippets of data, not sentences. -- Make sure your strings stay on one line—especially on the small profile! - -###### Examples - -| Bad | Good | -| :------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------: | -| ![A rich presence string that is too long and does not fit on one line](rp-long-strings.png) | ![Screenshot of a good rich presence string that is concise and easy to read](rp-short-strings.png) | -| The data wraps onto multiple lines. It’s repetitive, slower to read, and messy. | The data all fits on one line per string. Clean! | - -### Make it Actionable! - -- Always keep party size data up to date. -- Keep accurate track of party state: In Queue, In Game, In Menus, etc. -- Include game modes, ranked vs. unranked, etc. so others can clearly see. - -###### Examples - -| Bad | Good | -| :---------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------: | -| ![Screenshot of a rich presence string reading "Rank 9999"](rp-non-actionable.png) | ![Screenshot of a good rich presence string shows a game mode of "Ranked: Control Point" and that the user is in a queue](rp-actionable.png) | -| While Rank 9999 is impressive, it doesn’t present any actionable data for their friends. | This player is in queue for something I want to play. Let's ask to join that open spot! | - -### Use ALL of the fields (where applicable)! - -- Make use of all the fields that are applicable to you. -- Save space by putting map and character names in the tooltips. -- Try not to repeat information. - -###### Examples - -| Bad | Good | -| :---------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------: | -| ![Screenshot of a rich presence string that is hard to read at a glance](rp-not-all-fields.png) | ![Screenshot of a good rich presence that takes advantage of storing less important information in tooltips](rp-all-fields.png) | -| The map name takes up space and makes the player's status harder to read at a glance. | Moving the name of the map to the tooltip makes the data cleaner and frees up space for the score. | - -### Have interesting, expressive art! - -- The large image should be consistent for all players in a party. -- The small image is where you can customize on a per-player basis. -- Use high resolution artwork so your art looks great on fancy, high DPI screens. -- We strongly recommend image sizes of 1024x1024 pixels. - -###### Examples - -| Bad | Good | -| :-----------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------: | -| ![Screenshot of a rich presence icon that is too dark to see clearly](rp-bad-art.png) | ![Screenshot of a rich presence icon that is clear and detailed](rp-good-art.png) | -| The image is dark and unfocused. Highly-detailed images can be hard to see. | This image is bright and matches the details. Let's help! | diff --git a/docs/rich_presence/FAQ.md b/docs/rich_presence/FAQ.md deleted file mode 100644 index febb4a0754..0000000000 --- a/docs/rich_presence/FAQ.md +++ /dev/null @@ -1,45 +0,0 @@ -# Rich Presence FAQ - -> danger -> The SDK that this documentation references, [Discord-RPC](https://github.com/discord/discord-rpc), has been deprecated in favor of our new [Discord GameSDK](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/). Replacement functionality for the Rich Presence SDK can be found in the [Activity Manager](#DOCS_GAME_SDK_ACTIVITIES/) of that SDK. This documentation can be referenced for education but does not entirely reflect the new SDK. - -Below are answers to some common questions about integrating Rich Presence with your game. If you don't see your question answered here, feel free to reach out to [gamedevs@discord.com](mailto:gamedevs@discord.com) for more help. - -#### Q: I see "Playing MyGame", but no Rich Presence data. - -There's a couple things that could be going on: - -- If you're running two instances of the Discord client, check both! -- Double check that your `Discord_Initialize()` function is correct. - -Throughout development, make sure you have your `errored()` and `disconnected()` callbacks hooked up for debugging. You can open up the console in Discord and look for errors pertaining to `SET_ACTIVITY` for more information as well. - -#### Q: I'm not seeing Spectate buttons on my profile. - -Make sure you applied for approval! If you want the Spectate button on your players' profiles, we require your integration to go through an approval process. If you have applied and have been approved and still don't see the buttons, check your Discord console for errors. - -#### Q: What happens if someone has more than one game running that supports Rich Presence? - -Due to recent changes in our infrastructure for support of multi-activities, the behavior of multiple connected Rich Presence apps has changed from what it was before. Previously, whichever application was focused would be the presence that was shown. With the recent changes, the application that connected _first_ is now displayed. - -However, invite functionality across multiple connected applications now works no matter which app is display on a user's profile. For example, if you are hosting a Spotify listening party, playing Game A that allows you to send Join invites, and playing Game B that allows you to send Spectate invites, you'll be able to send invites to all three simultaneously! - -#### Q: What if someone looking at my profile or an invite doesn't own the game? - -Anyone can see your profile data, whether they own the game or not. They'll only be able to interact with chat invites or profile buttons if they own the game and have launched it at least once. Otherwise, the invite/button tooltip will show "Game Not Detected". - -#### Q: Do join invitations allow players to select the number of open slots? - -Currently, the SDK does not support this. Party slot information is determined by the party data you sent in your presence payload. - -#### Q: Can I send images via the payload rather than uploading them to my Developer Dashboard? - -Yes! In addition to uploading an asset and specifying its name, you can also specify an external image URL for us to proxy. For more information, see [Activity Asset Image](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-asset-image). - -#### Q: Can I change something in the SDK for my own purposes? - -Go nuts! The SDK is open source by design. If you need or want to change something for the purposes of your specific integration—like changing our JSON parser, or changing all of the variable names to the names of your pets—go ahead and tinker to your heart's content. - -#### Q: OK—I've got it working! Now, how do I make my integration look _awesome_? - -I'm happy ~~we preempted your question~~ you asked! Check out our [Rich Presence Best Practices](#DOCS_RICH_PRESENCE_BEST_PRACTICES/) guide for a rundown on how to make your integration the best that it can be! diff --git a/docs/rich_presence/How_To.md b/docs/rich_presence/How_To.md deleted file mode 100644 index 9215a80b1e..0000000000 --- a/docs/rich_presence/How_To.md +++ /dev/null @@ -1,302 +0,0 @@ -# Introducing Rich Presence - -> danger -> The SDK that this documentation references, [Discord-RPC](https://github.com/discord/discord-rpc), has been deprecated in favor of our new [Discord GameSDK](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/). Replacement functionality for the Rich Presence SDK can be found in the [Activity Manager](#DOCS_GAME_SDK_ACTIVITIES/) of that SDK. This documentation can be referenced for education but does not entirely reflect the new SDK. - -How easy is it for people to play your game together? With Rich Presence from Discord, it just got so easy, a ~~caveman~~ Junior Dev could do it. - -If you are testing a game integration with Rich Presence, other users will be able to see it. Please create a private test account and do not join any public servers while testing your integration. - -## So, what is it? - -Rich Presence allows you to leverage the totally overhauled "Now Playing" section in a Discord user's profile to help people play your game together. Rich game data—including duration, score, current boss or map, and so much more—lives inside Discord. You can spectate a friend's game directly from their profile popout, or party up via beautiful chat embeds with real-time information about open party slots and the party's in-game status. No more exchanging usernames and friend codes, or wondering if there's room for you to join. Rich Presence is a living invitation to play together, or to watch your friends kick butt. - -## Step 0: Get the SDK - -> warn -> Please use our new Discord GameSDK. Read the documentation and get it [here](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/). - -## So, how does it work? - -We worked hard to make using Discord's Rich Presence system as easy as possible. All you need is our header file—what you will interact with—and our library—where we did all the hard work for you—and you are ready to roll! - -In the header file, you'll find six event-emitting callbacks: - -1. `ready()` -2. `errored()` -3. `disconnected()` -4. `joinGame()` -5. `spectateGame()` -6. `joinRequest()` - -These six callbacks make up the entirety of what you need to implement. Behind the scenes, we do all the heavy lifting for you. - -The header file also contains the `Discord_RunCallbacks()` function. This invokes any pending callbacks from Discord on the calling thread (it's thread safe!). - -## Initialization - -The first step in implementing Rich Presence is [creating an application](#APPLICATIONS). Once you've created your application, note and save your `Client ID`. You will need this to initialize the SDK; this value will be referred to throughout this documentation as both `client_id` and `application_id`. Next, scroll down to the bottom of your application's page and hit the button that says "Enable Rich Presence". This will allow you to upload assets to your dashboard for later use. - -To begin, you'll register your callback functions to the six `DiscordEventHandlers` and then call `Discord_Initialize()` with your `APPLICATION_ID`. If your game is distributed via Steam, you should also pass your application's Steam ID so Discord can launch your game through Steam: - -###### SDK Initialization Example - -```c -void InitDiscord() -{ - DiscordEventHandlers handlers; - memset(&handlers, 0, sizeof(handlers)); - handlers.ready = handleDiscordReady; - handlers.errored = handleDiscordError; - handlers.disconnected = handleDiscordDisconnected; - handlers.joinGame = handleDiscordJoinGame; - handlers.spectateGame = handleDiscordSpectateGame; - handlers.joinRequest = handleDiscordJoinRequest; - - // Discord_Initialize(const char* applicationId, DiscordEventHandlers* handlers, int autoRegister, const char* optionalSteamId) - Discord_Initialize("418562325121990661", &handlers, 1, "1234"); -} -``` - -A quick breakdown on the `Discord_Initialize()` function: - -- `applicationId`: your client_id/application_id -- `handlers`: the callback functions you registered for each Discord event -- `autoRegister`: whether or not to register an application protocol for your game on the player's computer—necessary to launch games from Discord -- `optionalSteamId`: your game's Steam application id, if your game is distributed on Steam - -When you are ready to publish your integration, we recommend digging into the source code of the SDK and copying `discord_register.h`, `discord_register_win.cpp`, `discord_register_osx.m`, and `discord_register_linux.cpp` into your installation and update process. By registering your application protocols on installation and update, your players won't need to run the game before being able to interact with invites, Ask to Join, and spectating in Discord. - -## Shutting Down - -Don't leave so soon! But when you _do_ shut down your application, don't forget to call `Discord_Shutdown()`. This properly terminates the thread and allows your application to shut down cleanly. - -If you don't want to register all your event handlers at initialization, you can do so dynamically with `Discord_UpdateHandlers()`; this will allow you to register a new set of event handlers. Be mindful that this will delete old handlers if they are not explicitly bound to your handlers struct when calling this function. - -## Updating Presence - -> warn -> Deprecated in favor of [Discord GameSDK ActivityManager.UpdateActivity()](#DOCS_GAME_SDK_ACTIVITIES/updateactivity) - -The core of Discord's Rich Presence SDK is the `Discord_UpdatePresence()` function. This is what sends your game data up to Discord to be seen and used by others. You should call `Discord_UpdatePresence()` any time something important in the presence payload changes. - -`Discord_UpdatePresence()` has a rate limit of one update per 15 seconds. Developers do not need to do anything to handle this rate limit. The SDK will queue up any presence updates sent in that window and send the newest one once the client is free to do so. If you are sending presence updates very frequently and wondering why you don't see your Discord presence changing, that's why! - -###### Update Presence Example - -```c -void UpdatePresence() -{ - char buffer[256]; - DiscordRichPresence discordPresence; - memset(&discordPresence, 0, sizeof(discordPresence)); - discordPresence.state = "In a Group"; - sprintf(buffer, "Ranked | Mode: %d", GameEngine.GetMode()); - discordPresence.details = buffer; - discordPresence.endTimestamp = time(0) + 5 * 60; - discordPresence.largeImageKey = "canary-large"; - discordPresence.smallImageKey = "ptb-small"; - discordPresence.partyId = GameEngine.GetPartyId(); - discordPresence.partySize = 1; - discordPresence.partyMax = 6; - discordPresence.matchSecret = "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f"; - discordPresence.spectateSecret = "e7eb30d2ee025ed05c71ea495f770b76454ee4e0"; - discordPresence.instance = 1; - Discord_UpdatePresence(&discordPresence); -} -``` - -###### Update Presence Payload - -```c -typedef struct DiscordRichPresence { - const char* state; /* max 128 bytes */ - const char* details; /* max 128 bytes */ - int64_t startTimestamp; - int64_t endTimestamp; - const char* largeImageKey; /* max 32 bytes */ - const char* largeImageText; /* max 128 bytes */ - const char* smallImageKey; /* max 32 bytes */ - const char* smallImageText; /* max 128 bytes */ - const char* partyId; /* max 128 bytes */ - int partySize; - int partyMax; - const char* matchSecret; /* max 128 bytes */ - const char* joinSecret; /* max 128 bytes */ - const char* spectateSecret; /* max 128 bytes */ - int8_t instance; -} DiscordRichPresence; -``` - -###### Update Presence Payload Fields - -| parameter | type | description | example | -| -------------- | -------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | -| state | char\* | the user's current party status | "Looking to Play", "Playing Solo", "In a Group" | -| details | char\* | what the player is currently doing | "Competitive - Captain's Mode", "In Queue", "Unranked PvP" | -| startTimestamp | int64_t | epoch seconds for game start - including will show time as "elapsed" | 1507665886 | -| endTimestamp | int64_t | epoch seconds for game end - including will show time as "remaining" | 1507665886 | -| largeImageKey | char\* | name of the uploaded image for the large profile artwork | "default" | -| largeImageText | char\* | tooltip for the largeImageKey | "Blade's Edge Arena", "Numbani", "Danger Zone" | -| smallImageKey | char\* | name of the uploaded image for the small profile artwork | "rogue" | -| smallImageText | char\* | tooltip for the smallImageKey | "Rogue - Level 100" | -| partyId | char\* | id of the player's party, lobby, or group | "ae488379-351d-4a4f-ad32-2b9b01c91657" | -| partySize | int | current size of the player's party, lobby, or group | 1 | -| partyMax | int | maximum size of the player's party, lobby, or group | 5 | -| matchSecret | char\* | (for future use) unique hashed string for a player's match | MmhuZToxMjMxMjM6cWl3amR3MWlqZA== | -| spectateSecret | char\* | unique hashed string for Spectate button | MTIzNDV8MTIzNDV8MTMyNDU0 | -| joinSecret | char\* | unique hashed string for chat invitations and Ask to Join | MTI4NzM0OjFpMmhuZToxMjMxMjM= | -| instance | int8_t | (for future use) integer representing a boolean for if the player is in an instance (an in-progress match) | 1 | - -> info -> Sending `endTimestamp` will **always** have the time displayed as "remaining" until the given time. Sending `startTimestamp` will show "elapsed" as long as there is no `endTimestamp` sent. - -Here's a handy image to see how these fields are actually displayed on a profile: - -![Graphical representation of the legend for rich presence details](rp-legend.png) - -| location | field name | notes | -| -------------------------------------- | ---------------------- | --------------------------------------------------------------------------- | -| First row below title | details | | -| Second row below title | state | | -| Second row below title | partySize | In parenthesis next to the `state`, first number in the format `(1 of 10)` | -| Second row below title | partyMax | In parenthesis next to the `state`, second number in the format `(1 of 10)` | -| Third row below title | startTimestamp | Converted to a format such as `01:33 elapsed` | -| First button at the bottom | joinSecret | Button has the text "Ask to join" | -| Second button at the bottom | spectateSecret | Button has the text "Spectate" | -| Large image to the left of any content | largeImageKey | Four rows high, includes the title but not the bottom buttons | -| Small image to the left of any content | smallImageKey | Small icon inset on the bottom right of the `largeImageKey` | - -Note that this layout may be subject to change without warning. This information is only provided to help those with -impaired eyesight to understand the potential layout of this information in a user interface. - -## Joining - -> warn -> Deprecated in favor of [Discord GameSDK ActivityManager.OnActivityJoin](#DOCS_GAME_SDK_ACTIVITIES/onactivityjoin) and [Discord GameSDK ActivityManager.OnActivityJoinRequest](#DOCS_GAME_SDK_ACTIVITIES/onactivityjoinrequest) - -#### Relevant Callbacks: - -`joinGame()` -`joinRequest()` - -#### Relevant Payload Data: - -`partyId` -`partySize` -`partyMax` -`joinSecret` - -When you send the relevant payload data in the `Discord_UpdatePresence()` call, your player can invite a Discord chat channel to play with them. This invite is tied to the player's party information; if their `partyId` changes, the invite will expire. If their `partySize` and `partyMax` changes, the invite will add, remove, and fill up slots dynamically. - -Other Discord users can click "Join" on the invitation. Their game will then launch, and the `joinGame()` callback will fire in their client with the inviting player's `joinSecret`. The client should reverse hash or otherwise unencrypt this secret and match the players together. - -### Ask to Join - -When Player B clicks the Ask to Join button on Player A's profile, the `joinRequest()` callback fires for Player A, sending the following data: - -###### Ask to Join Payload - -```c -typedef struct DiscordJoinRequest { - char userId[32]; - char username[344]; - char discriminator[8]; - char avatar[128]; -} DiscordJoinRequest; -``` - -###### Ask to Join Payload Fields - -| parameter | type | description | -| ------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| userId | char[24] | the userId of the player asking to join | -| username | char[344] | the username of the player asking to join | -| discriminator | char[8] | the discriminator of the player asking to join | -| avatar\* | char[128] | the avatar hash of the player asking to join—see [image formatting](#DOCS_REFERENCE/image-formatting) for how to retrieve the image | - -> warn -> `avatar` can be an empty string if the user has not uploaded an avatar to Discord - -When it fires, your game should surface this data with a Yes or No choice for Player A to accept whether or not they wish to play with Player B. Then, call `Discord_Respond()` with Player B's `userId` and the appropriate response code: - -###### Ask to Join Response Codes - -| code | value | -| -------------------- | ----- | -| DISCORD_REPLY_NO | 0 | -| DISCORD_REPLY_YES | 1 | -| DISCORD_REPLY_IGNORE | 2 | - -The Ask to Join request persists for 30 seconds after the request is received, regardless of whether you have called `Discord_RunCallbacks()` within that window. Therefore, keep these two points in mind: - -- Ensure you call `Discord_RunCallbacks()` as frequently as possible to ensure your game client is up to date with any data from Discord -- If the player is in a state in which they cannot interact with an Ask to Join request—like in the middle of a match—you should not send a `joinSecret` in the presence payload - -## Spectating - -> warn -> Deprecated in favor of [Discord GameSDK ActivityManager.OnActivitySpectate](#DOCS_GAME_SDK_ACTIVITIES/onactivityspectate) - -#### Relevant Callbacks: - -`spectateGame()` - -#### Relevant Payload Data: - -`spectateSecret` - -When you send the relevant payload data in the `Discord_UpdatePresence()` call, your player will gain the ability to invite a Discord chat channel to spectate their game. This invite is tied to the `matchSecret` and will expire when it changes. - -Other Discord users can click "Spectate" on the invitation. Their game will launch, and the `spectateGame()` callback will fire in their client with the original player's `spectateSecret`. The client should reverse hash or otherwise unencrypt this secret and spectate that player's game. - -## Secrets - -Security is of the utmost importance to us here at Discord, and we know it is for you, too. That's why we want to make sure that you properly understand `matchSecret`, `joinSecret`, and `spectateSecret` so that your game data is safe and secure over the wire. - -To keep security on the up and up, Discord requires that you properly hash/encode/encrypt/put-a-padlock-on-and-swallow-the-key-but-wait-then-how-would-you-open-it your secrets. - -Secrets are obfuscated data of your choosing. They could be match ids, player ids, lobby ids, etc. You should send us data that someone else's game client would need to join or spectate their friend. If you can't or don't want to support those actions, you don't need to send us secrets. - -## Rich Presence Field Requirements - -All fields in the `DiscordRichPresence` object are entirely optional. Anything you choose to omit simply won't be displayed; the UI will dynamically adapt from the full Rich Presence object all the way down to the basic presence view. We highly encourage you to include as many fields as you can to make your game as attractive and interactive for other players as possible. However, if you choose to be a bit more selective, here's a handy table for what each piece of Rich Presence requires: - -###### Rich Presence Field Requirements - -| Field | Custom Artwork | Spectating | Joining | Ask to Join | -| :------------: | :------------: | :--------: | :-----: | :---------: | -| state | | | | | -| details | | | | | -| startTimestamp | | | | | -| endTimestamp | | | | | -| largeImageKey | x | | | | -| smallImageKey | x | | | | -| largeImageText | x | | | | -| smallImageText | x | | | | -| partyId | | | x | x | -| partySize | | | x | x | -| partyMax | | | x | x | -| matchSecret | | | | | -| joinSecret | | | x | x | -| spectateSecret | | x | | | -| instance | | | | | - -## Your New Developer Dashboard - -Included with the launch of Rich Presence is an overhaul of Discord's Developer Dashboard. We want to make Rich Presence as easy as possible to use. Our first step is helping you ditch your CDN. You're welcome. - -OK, well, not entirely. But! Discord _will_ host any and all artwork that you need to have the very richest of presences. Upload an image, tag it with a key—preferably one you can remember—and **bam**. It's ready for Rich Presence use. Head over to your [applications page](#APPLICATIONS) to check it out! - -> warn -> **Asset keys are automatically normalized to lowercase**. Be mindful of this when referring to them in your code. - -## A note on testing and Game Detection - -If you don't see Rich Presence data in your profile while testing, make sure you don't have multiple instances of Discord running—if you do, your presence might be changing in one of those! - -If you're testing on your own, we recommend [downloading two separate release channels](https://discord.com/download) of the Discord desktop client. You can log into the stable, public test, and canary builds with separate credentials, making testing easier for a single developer. - -## So, what now? - -Get to coding! We can't wait to see the awesome integration you come up with. If you need some quick questions answered, look no further than our [Rich Presence FAQ](#DOCS_RICH_PRESENCE_FAQ/). If you want to know how to make your integration the best it can be, read our [Best Practices Guide](#DOCS_RICH_PRESENCE_BEST_PRACTICES/). If you're ready to release, consult our [Launch Checklist](#DOCS_RICH_PRESENCE_LAUNCH_CHECKLIST/) and make sure you haven't missed anything! diff --git a/docs/rich_presence/Launch_Checklist.md b/docs/rich_presence/Launch_Checklist.md deleted file mode 100644 index 6c44ad2bff..0000000000 --- a/docs/rich_presence/Launch_Checklist.md +++ /dev/null @@ -1,46 +0,0 @@ -# Rich Presence Launch Checklist - -> danger -> The SDK that this documentation references, [Discord-RPC](https://github.com/discord/discord-rpc), has been deprecated in favor of our new [Discord GameSDK](#DOCS_GAME_SDK_SDK_STARTER_GUIDE/). Replacement functionality for the Rich Presence SDK can be found in the [Activity Manager](#DOCS_GAME_SDK_ACTIVITIES/) of that SDK. This documentation can be referenced for education but does not entirely reflect the new SDK. - -Ready to launch a Rich Presence integration for your game? Did you read our [Best Practices](#DOCS_RICH_PRESENCE_BEST_PRACTICES/) guide? If so, we recommend looking over this checklist one last time to ensure that your integration is as great as it can be! - -> warn -> Our precompiled libraries depend on the [Visual C++ Redistributable for Visual Studio 2015](https://www.microsoft.com/en-us/download/details.aspx?id=48145). If you distribute your game on Steam, make sure to check that box in your common redistributable settings. - -#### Profile Strings - -- Have you made use of all available fields where appropriate? -- Do your strings fit on their own lines without line wrapping? - - Did you check on the smaller profile pop out? -- Do they clearly communicate: - - What the player is currently doing? - - If the player is in a group or playing alone? - - If the player is in a state where they can party up? - -#### Artwork - -- Is your artwork high resolution? -- Are your images at least 1024x1024 pixels? -- Is it clean, interesting, and descriptive without being too highly detailed? -- Do you have artwork for every different state? Don't forget your default state/main menu! -- Did you make use of tooltips and the small image where appropriate? - -#### Joining - -- Have you successfully implemented join invites for your game if applicable? -- Does the state of the invite properly represent the party/group in-game with regards to: - - Size? - - Open slots? - - Discord _and_ non-Discord users in the party? -- Are you able to post invites to Discord without any additional in-game setup or configuration? -- Are you properly removing data from the presence payload when someone can no longer send invites? - - A `joinSecret` should not be sent if the player can't invite anyone! - -#### Spectating - -- Have you successfully implemented spectate invites for your game if applicable? -- Is your game's spectate mode true spectating? - - We do _not_ allow using the Spectate button as a pseudo-Join button. -- Are you properly removing data from the presence payload when someone can no longer spectate? - - A `spectateSecret` should not be sent if the player can't be spectated! diff --git a/docs/topics/Gateway.md b/docs/topics/Gateway.md deleted file mode 100644 index ccd5512f26..0000000000 --- a/docs/topics/Gateway.md +++ /dev/null @@ -1,702 +0,0 @@ -# Gateway - -The Gateway API lets apps open secure WebSocket connections with Discord to receive events about actions that take place in a server/guild, like when a channel is updated or a role is created. There are a few cases where apps will *also* use Gateway connections to update or request resources, like when updating voice state. - -> info -> In *most* cases, performing REST operations on Discord resources can be done using the [HTTP API](#DOCS_REFERENCE/http-api) rather than the Gateway API. - -The Gateway is Discord's form of real-time communication used by clients (including apps), so there are nuances and data passed that simply isn't relevant to apps. Interacting with the Gateway can be tricky, but there are [community-built libraries](#DOCS_TOPICS_COMMUNITY_RESOURCES/libraries) with built-in support that simplify the most complicated bits and pieces. If you're planning on writing a custom implementation, be sure to read the following documentation in its entirety so you understand the sacred secrets of the Gateway (or at least those that matter for apps). - -## Gateway Events - -Gateway events are [payloads](#DOCS_TOPICS_GATEWAY_EVENTS/payload-structure) sent over a [Gateway connection](#DOCS_TOPICS_GATEWAY/connections)—either from an app to Discord, or from Discord to an app. An app typically [*sends* events](#DOCS_TOPICS_GATEWAY/sending-events) when connecting and managing its connection to the Gateway, and [*receives* events](#DOCS_TOPICS_GATEWAY/receiving-events) when listening to actions taking place in a server. - -All Gateway events are encapsulated in a [Gateway event payload](#DOCS_TOPICS_GATEWAY_EVENTS/payload-structure). - -A full list of Gateway events and their details are in the [Gateway events documentation](#DOCS_TOPICS_GATEWAY_EVENTS). - -###### Example Gateway Event - -```json -{ - "op": 0, - "d": {}, - "s": 42, - "t": "GATEWAY_EVENT_NAME" -} -``` - -Details about Gateway event payloads are in the [Gateway events documentation](#DOCS_TOPICS_GATEWAY_EVENTS/payload-structure). - -### Sending Events - -When sending a Gateway event (like when [performing an initial handshake](#DOCS_TOPICS_GATEWAY_EVENTS/identify) or [updating presence](#DOCS_TOPICS_GATEWAY_EVENTS/update-presence)), your app must send an [event payload object](#DOCS_TOPICS_GATEWAY_EVENTS/payload-structure) with a valid opcode (`op`) and inner data object (`d`). - -> info -> Specific rate limits are applied when sending events, which you can read about in the [Rate Limiting](#DOCS_TOPICS_GATEWAY/rate-limiting) section. - -Event payloads sent over a Gateway connection: - -1. Must be serialized in [plain-text JSON or binary ETF](#DOCS_TOPICS_GATEWAY/encoding-and-compression). -2. Must not exceed 4096 bytes. If an event payload *does* exceed 4096 bytes, the connection will be closed with a [`4002` close event code](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes). - -All events that your app can send via a connection are in [Gateway event documentation](#DOCS_TOPICS_GATEWAY_EVENTS/send-events). - -### Receiving Events - -Receiving a Gateway event from Discord (like when [a reaction is added to a message](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-add)) is much more common (and slightly more complex) than sending them. - -While some events are sent to your app automatically, most events require your app to define intents when [Identifying](#DOCS_TOPICS_GATEWAY/identifying). Intents are bitwise values that can be ORed (`|`) to indicate which events (or groups of events) you want Discord to send your app. A list of intents and their corresponding events are listed in the [intents section](#DOCS_TOPICS_GATEWAY/gateway-intents). - -When receiving events, you can also configure *how* events will be sent to your app, like the [encoding and compression](#DOCS_TOPICS_GATEWAY/encoding-and-compression), or whether [sharding should be enabled](#DOCS_TOPICS_GATEWAY/sharding)). - -All events that your app can receive via a connection are in the [Gateway event documentation](#DOCS_TOPICS_GATEWAY_EVENTS/receive-events). - -#### Dispatch Events - -[Dispatch (opcode `0`)](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-opcodes) events are the most common type of event your app will receive. *Most* Gateway events which represent actions taking place in a guild will be sent to your app as Dispatch events. - -When your app is parsing a Dispatch event: -- The `t` field can be used to determine which [Gateway event](#DOCS_TOPICS_GATEWAY_EVENTS/receive-events) the payload represents the data you can expect in the `d` field. -- The `s` field represents the sequence number of the event, which is the relative order in which it occurred. You need to cache the most recent non-null `s` value for heartbeats, and to pass when [Resuming](#DOCS_TOPICS_GATEWAY/resuming) a connection. - -## Connections - -Gateway connections are persistent WebSockets which introduce more complexity than sending HTTP requests or responding to interactions (like Slash Commands). When interacting with the Gateway, your app must know how to open the initial connection, as well as maintain it and handle any disconnects. - -### Connection Lifecycle - -> info -> There are nuances that aren't included in the overview below. More details about each step and event can be found in the individual sections below. - -At a high-level, Gateway connections consist of the following cycle: - -![Flowchart with an overview of Gateway connection lifecycle](gateway-lifecycle.svg) - -1. App establishes a connection with the Gateway after fetching and caching a WSS URL using the [Get Gateway](#DOCS_TOPICS_GATEWAY/get-gateway) or [Get Gateway Bot](#DOCS_TOPICS_GATEWAY/get-gateway-bot) endpoint. -2. Discord sends the app a [Hello (opcode `10`)](#DOCS_TOPICS_GATEWAY/hello-event) event containing a heartbeat interval in milliseconds. **Read the section on [Connecting](#DOCS_TOPICS_GATEWAY/connecting)** -3. Start the Heartbeat interval. App must send a [Heartbeat (opcode `1`)](#DOCS_TOPICS_GATEWAY_EVENTS/heartbeat) event, then continue to send them every heartbeat interval until the connection is closed. **Read the section on [Sending Heartbeats](#DOCS_TOPICS_GATEWAY/sending-heartbeats)** - - Discord will respond to each Heartbeat event with a [Heartbeat ACK (opcode `11`)](#DOCS_TOPICS_GATEWAY/sending-heartbeats) event to confirm it was received. If an app doesn't receive a Heartbeat ACK, it should close the connection and reconnect. - - Discord may send the app a [Heartbeat (opcode `1`)](#DOCS_TOPICS_GATEWAY_EVENTS/heartbeat) event, in which case the app should send a Heartbeat event immediately. -4. App sends an [Identify (opcode `2`)](#DOCS_TOPICS_GATEWAY_EVENTS/identify) event to perform the initial handshake with the Gateway. **Read the section on [Identifying](#DOCS_TOPICS_GATEWAY/identifying)** -5. Discord sends the app a [Ready (opcode `0`)](#DOCS_TOPICS_GATEWAY_EVENTS/ready) event which indicates the handshake was successful and the connection is established. The Ready event contains a `resume_gateway_url` that the app should keep track of to determine the WebSocket URL an app should use to Resume. **Read the section on [the Ready event](#DOCS_TOPICS_GATEWAY/ready-event)** -6. The connection may be dropped for a variety of reasons. Whether the app can [Resume](#DOCS_TOPICS_GATEWAY/resuming) the connection or whether it must re-identify is determined by a variety of factors like the [opcode](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-opcodes) and [close code](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes) that it receives. **Read the section on [Disconnecting](#DOCS_TOPICS_GATEWAY/disconnecting)** -7. If an app **can** resume/reconnect, it should open a new connection using `resume_gateway_url`, then send a [Resume (opcode `6`)](#DOCS_TOPICS_GATEWAY_EVENTS/resume) event. If an app **cannot** resume/reconnect, it should open a new connection using the cached URL from step #1, then repeat the whole Gateway cycle. *Yipee!* **Read the section on [Resuming](#DOCS_TOPICS_GATEWAY/resuming)** - -### Connecting - -Before your app can establish a connection to the Gateway, it should call the [Get Gateway](#DOCS_TOPICS_GATEWAY/get-gateway) or the [Get Gateway Bot](#DOCS_TOPICS_GATEWAY/get-gateway-bot) endpoint. Either endpoint will return a payload with a `url` field whose value is the WSS URL you can use to open a WebSocket connection. In addition to the URL, [Get Gateway Bot](#DOCS_TOPICS_GATEWAY/get-gateway-bot) contains additional information about the recommended number of shards and the session start limits for your app. - -When initially calling either [Get Gateway](#DOCS_TOPICS_GATEWAY/get-gateway) or [Get Gateway Bot](#DOCS_TOPICS_GATEWAY/get-gateway-bot), you should cache the value of the `url` field and use that when re-connecting to the Gateway. - -When connecting to the URL, it's a good idea to explicitly pass the API version and [encoding](#DOCS_TOPICS_GATEWAY/encoding-and-compression) as query parameters. You can also optionally include whether Discord should [compress](#DOCS_TOPICS_GATEWAY/encoding-and-compression) data that it sends your app. - -> info -> `wss://gateway.discord.gg/?v=10&encoding=json` is an example of a WSS URL an app may use to connect to the Gateway. - -###### Gateway URL Query String Params - -| Field | Type | Description | Accepted Values | -| --------- | ------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------- | -| v | integer | [API Version](#DOCS_REFERENCE/api-versioning) to use | [API version](#DOCS_REFERENCE/api-versioning-api-versions) | -| encoding | string | The [encoding](#DOCS_TOPICS_GATEWAY/encoding-and-compression) of received gateway packets | `json` or `etf` | -| compress? | string | The optional [transport compression](#DOCS_TOPICS_GATEWAY/resuming) of gateway packets | `zlib-stream` | - -#### Hello Event - -Once connected to the Gateway, your app will receive a [Hello (opcode `10`)](#DOCS_TOPICS_GATEWAY/hello-event) event that contains your connection's heartbeat interval (`hearbeat_interval`). - -The heartbeat interval indicates a length of time in milliseconds that you should use to determine how often your app needs to send a Heartbeat event in order to maintain the active connection. Heartbeating is detailed in the [Sending Heartbeats](#DOCS_TOPICS_GATEWAY/sending-heartbeats) section. - -###### Example Hello Event - -```json -{ - "op": 10, - "d": { - "heartbeat_interval": 45000 - } -} -``` - -### Sending Heartbeats - -Heartbeats are pings used to let Discord know that your app is still actively using a Gateway connection. After connecting to the Gateway, your app should send heartbeats (as described below) in a background process until the Gateway connection is closed. - -#### Heartbeat Interval - -When your app opens a Gateway connection, it will receive a [Hello (opcode `10`)](#DOCS_TOPICS_GATEWAY/hello-event) event which includes a `heartbeat_interval` field that has a value representing a length of time in milliseconds. - -Upon receiving the Hello event, your app should wait `heartbeat_interval * jitter` where `jitter` is any random value between 0 and 1, then send its first [Heartbeat (opcode `1`)](#DOCS_TOPICS_GATEWAY_EVENTS/heartbeat) event. From that point until the connection is closed, your app must continually send Discord a heartbeat every `heartbeat_interval` milliseconds. If your app fails to send a heartbeat event in time, your connection will be closed and you will be forced to [Resume](#DOCS_TOPICS_GATEWAY/resuming). - -When sending a heartbeat, your app will need to include the last sequence number your app received in the `d` field. The sequence number is sent to your app in the [event payload](#DOCS_TOPICS_GATEWAY_EVENTS/payload-structure) in the `s` field. If your app hasn't received any events yet, you can just pass `null` in the `d` field. - -> info -> In the first heartbeat, `jitter` is an offset value between 0 and `heartbeat_interval` that is meant to prevent too many clients (both desktop and apps) from reconnecting their sessions at the exact same time (which could cause an influx of traffic). - -You *can* send heartbeats before the `heartbeat_interval` elapses, but you should avoid doing so unless necessary. There is already tolerance in the `heartbeat_interval` that will cover network latency, so you don't need to account for it in your implementation. - -When you send a Heartbeat event, Discord will respond with a [Heartbeat ACK (opcode `11`)](#DOCS_TOPICS_GATEWAY/heartbeat-interval-example-heartbeat-ack) event, which is an acknowledgement that the heartbeat was received: - -###### Example Heartbeat ACK - -```json -{ - "op": 11 -} -``` - -> info -> In the event of a service outage where you stay connected to the Gateway, you should continue to send heartbeats and receive heartbeat ACKs. The Gateway will eventually respond and issue a session once it's able to. - -If a client does not receive a heartbeat ACK between its attempts at sending heartbeats, this may be due to a failed or "zombied" connection. The client should immediately terminate the connection with any close code besides `1000` or `1001`, then reconnect and attempt to [Resume](#DOCS_TOPICS_GATEWAY/resuming). - -#### Heartbeat Requests - -In addition to the Heartbeat interval, Discord may request additional heartbeats from your app by sending a [Heartbeat (opcode `1`)](#DOCS_TOPICS_GATEWAY_EVENTS/heartbeat) event. Upon receiving the event, your app should immediately send back another Heartbeat event without waiting the remainder of the current interval. - -Just like with the interval, Discord will respond with an [Heartbeat ACK (opcode `11`)](#DOCS_TOPICS_GATEWAY/heartbeat-interval-example-heartbeat-ack) event. - -### Identifying - -After the connection is open and your app is sending heartbeats, you should send an [Identify (opcode `2`)](#DOCS_TOPICS_GATEWAY_EVENTS/identify) event. The Identify event is an initial handshake with the Gateway that's required before your app can begin sending or receiving most Gateway events. - -Apps are limited by maximum concurrency (`max_concurrency` in the [session start limit object](#DOCS_TOPICS_GATEWAY/session-start-limit-object)) when identifying. If your app exceeds this limit, Discord will respond with a [Invalid Session (opcode `9`)](#DOCS_TOPICS_GATEWAY_EVENTS/invalid-session) event. - -After your app sends a valid Identify payload, Discord will respond with a [Ready](#DOCS_TOPICS_GATEWAY_EVENTS/ready) event which indicates that your app is in a successfully-connected state with the Gateway. The Ready event is sent as a standard [Dispatch (opcode `0`)](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-opcodes). - -> warn -> Clients are limited to 1000 `IDENTIFY` calls to the websocket in a 24-hour period. This limit is global and across all shards, but does not include `RESUME` calls. Upon hitting this limit, all active sessions for the app will be terminated, the bot token will be reset, and the owner will receive an email notification. It's up to the owner to update their application with the new token. - -###### Example Identify Payload - -Below is a minimal `IDENTIFY` payload. `IDENTIFY` supports additional fields for other session properties like payload compression and an initial presence state. - -See the [Identify Structure](#DOCS_TOPICS_GATEWAY_EVENTS/identify-identify-structure) for details about the event. - -```json -{ - "op": 2, - "d": { - "token": "my_token", - "intents": 513, - "properties": { - "os": "linux", - "browser": "my_library", - "device": "my_library" - } - } -} -``` - -#### Ready event - -As mentioned above, the [Ready](#DOCS_TOPICS_GATEWAY_EVENTS/ready) event is sent to your app after it sends a valid Identify payload. The Ready event includes state, like the guilds your app is in, that it needs to start interacting with the rest of the platform. - -The Ready event also includes fields that you'll need to cache in order to eventually [Resume](#DOCS_TOPICS_GATEWAY/resuming) your connection after disconnects. Two fields in particular are important to call out: -- `resume_gateway_url` is a WebSocket URL that your app should use when it Resumes after a disconnect. The `resume_gateway_url` should be used instead of the URL [used when connecting](#DOCS_TOPICS_GATEWAY/connecting). -- `session_id` is the ID for the Gateway session for the new connection. It's required to know which stream of events were associated with your disconnection connection. - -Full details about the Ready event is in the [Gateway events documentation](#DOCS_TOPICS_GATEWAY_EVENTS). - -### Disconnecting - -Gateway disconnects happen for a variety of reasons, and may be initiated by Discord or by your app. - -#### Handling a Disconnect - -Due to Discord's architecture, disconnects are a semi-regular event and should be expected and handled. When your app encounters a disconnect, it will typically be sent a [close code](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes) which can be used to determine whether you can reconnect and [Resume](#DOCS_TOPICS_GATEWAY/resuming) the session, or whether you have to start over and re-Identify. - -After you determine whether or not your app can reconnect, you will do one of the following: - -- If you determine that your app *can* reconnect and resume the previous session, then you should reconnect using the `resume_gateway_url` and `session_id` from the [Ready](#DOCS_TOPICS_GATEWAY_EVENTS/ready) event. Details about when and how to resume can be found in the [Resuming](#DOCS_TOPICS_GATEWAY/resuming) section. -- If you *cannot* reconnect **or the reconnect fails**, you should open a new connection using the URL from the initial call to [Get Gateway](#DOCS_TOPICS_GATEWAY/get-gateway) or [Get Gateway Bot](#DOCS_TOPICS_GATEWAY/get-gateway-bot). In the case you cannot reconnect, you'll have to re-identify after opening a new connection. - -A full list of the close codes can be found in the [Response Codes](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes) documentation. - -#### Initiating a Disconnect - -When you close the connection to the gateway with close code `1000` or `1001`, your session will be invalidated and your bot will appear offline. - -If you simply close the TCP connection or use a different close code, the session will remain active and timeout after a few minutes. This can be useful when you're [Resuming](#DOCS_TOPICS_GATEWAY/resuming) the previous session. - -### Resuming - -When your app is disconnected, Discord has a process for reconnecting and resuming, which allows your app to replay any lost events starting from the last sequence number it received. After Resuming, your app will receive the missed events in the same way it would have had the connection had stayed active. Unlike the initial connection, your app does **not** need to re-Identify when Resuming. - -There are a handful of scenarios when your app should attempt to resume: - -1. It receives a [Reconnect (opcode `7`)](#DOCS_TOPICS_GATEWAY_EVENTS/reconnect) event -2. It's disconnected with a close code that indicates it can reconnect. A list of close codes is in the [Opcodes and Status Codes](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes) documentation. -3. It's disconnected but doesn't receive *any* close code. -4. It receives an [Invalid Session (opcode `9`)](#DOCS_TOPICS_GATEWAY_EVENTS/invalid-session) event with the `d` field set to `true`. This is an unlikely scenario, but it is possible. - -#### Preparing to Resume - -Before your app can send a [Resume (opcode `6`)](#DOCS_TOPICS_GATEWAY_EVENTS/resume) event, it will need three values: the `session_id` and the `resume_gateway_url` from the [Ready](#DOCS_TOPICS_GATEWAY/ready-event) event, and the sequence number (`s`) from the last Dispatch (opcode `0`) event it received before the disconnect. - -After the connection is closed, your app should open a new connection using `resume_gateway_url` rather than the URL used to initially connect. If your app doesn't use the `resume_gateway_url` when reconnecting, it will experience disconnects at a higher rate than normal. - -Once the new connection is opened, your app should send a [Gateway Resume](#DOCS_TOPICS_GATEWAY_EVENTS/resume) event using the `session_id` and sequence number mentioned above. When sending the event, `session_id` will have the same field name, but the last sequence number will be passed as `seq` in the data object (`d`). - -When Resuming, you do not need to send an Identify event after opening the connection. - -If successful, the Gateway will send the missed events in order, finishing with a [Resumed](#DOCS_TOPICS_GATEWAY_EVENTS/resumed) event to signal event replay has finished and that all subsequent events will be new. - -It's possible your app won't reconnect in time to Resume, in which case it will receive an [Invalid Session (opcode `9`)](#DOCS_TOPICS_GATEWAY_EVENTS/invalid-session) event. If the `d` field is set to `false` (which is most of the time), your app should disconnect. After disconnect, your app should create a new connection with your cached URL from the [Get Gateway](#DOCS_TOPICS_GATEWAY/get-gateway) or the [Get Gateway Bot](#DOCS_TOPICS_GATEWAY/get-gateway-bot) endpoint, then send an [Identify (opcode `2`)](#DOCS_TOPICS_GATEWAY_EVENTS/identify) event. - -###### Example Gateway Resume Event - -```json -{ - "op": 6, - "d": { - "token": "my_token", - "session_id": "session_id_i_stored", - "seq": 1337 - } -} -``` - -## Gateway Intents - -Maintaining a stateful application can be difficult when it comes to the amount of data your app is expected to process over a Gateway connection, especially at scale. Gateway intents are a system to help you lower the computational burden. - -Intents are bitwise values passed in the `intents` parameter when [Identifying](#DOCS_TOPICS_GATEWAY/identifying) which correlate to a set of related events. For example, the event sent when a guild is created (`GUILD_CREATE`) and when a channel is updated (`CHANNEL_UPDATE`) both require the same `GUILDS (1 << 0)` intent (as listed in the table below). If you do not specify an intent when identifying, you will not receive *any* of the Gateway events associated with that intent. - -> info -> Intents are optionally supported on the v6 gateway but required as of v8 - -Two types of intents exist: -- **Standard intents** can be passed by default. You don't need any additional permissions or configurations. -- [**Privileged intents**](#DOCS_TOPICS_GATEWAY/privileged-intents) require you to toggle the intent for your app in your app's settings within the Developer Portal before passing said intent. For verified apps (required for apps in 100+ guilds), the intent must also be approved after the verification process to use the intent. More information about privileged intents can be found [in the section below](#DOCS_TOPICS_GATEWAY/privileged-intents). - -The connection with your app will be closed if it passes invalid intents ([`4013` close code](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes)), or a privileged intent that hasn't been configured or approved for your app ([`4014` close code](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes)). - - -### List of Intents - -Below is a list of all intents and the Gateway events associated with them. Any events *not* listed means it's not associated with an intent and will always be sent to your app. - -All events, including those that aren't associated with an intent, are in the [Gateway events](#DOCS_TOPICS_GATEWAY_EVENTS) documentation. - -``` -GUILDS (1 << 0) - - GUILD_CREATE - - GUILD_UPDATE - - GUILD_DELETE - - GUILD_ROLE_CREATE - - GUILD_ROLE_UPDATE - - GUILD_ROLE_DELETE - - CHANNEL_CREATE - - CHANNEL_UPDATE - - CHANNEL_DELETE - - CHANNEL_PINS_UPDATE - - THREAD_CREATE - - THREAD_UPDATE - - THREAD_DELETE - - THREAD_LIST_SYNC - - THREAD_MEMBER_UPDATE - - THREAD_MEMBERS_UPDATE * - - STAGE_INSTANCE_CREATE - - STAGE_INSTANCE_UPDATE - - STAGE_INSTANCE_DELETE - -GUILD_MEMBERS (1 << 1) ** - - GUILD_MEMBER_ADD - - GUILD_MEMBER_UPDATE - - GUILD_MEMBER_REMOVE - - THREAD_MEMBERS_UPDATE * - -GUILD_BANS (1 << 2) - - GUILD_BAN_ADD - - GUILD_BAN_REMOVE - -GUILD_EMOJIS_AND_STICKERS (1 << 3) - - GUILD_EMOJIS_UPDATE - - GUILD_STICKERS_UPDATE - -GUILD_INTEGRATIONS (1 << 4) - - GUILD_INTEGRATIONS_UPDATE - - INTEGRATION_CREATE - - INTEGRATION_UPDATE - - INTEGRATION_DELETE - -GUILD_WEBHOOKS (1 << 5) - - WEBHOOKS_UPDATE - -GUILD_INVITES (1 << 6) - - INVITE_CREATE - - INVITE_DELETE - -GUILD_VOICE_STATES (1 << 7) - - VOICE_STATE_UPDATE - -GUILD_PRESENCES (1 << 8) ** - - PRESENCE_UPDATE - -GUILD_MESSAGES (1 << 9) - - MESSAGE_CREATE - - MESSAGE_UPDATE - - MESSAGE_DELETE - - MESSAGE_DELETE_BULK - -GUILD_MESSAGE_REACTIONS (1 << 10) - - MESSAGE_REACTION_ADD - - MESSAGE_REACTION_REMOVE - - MESSAGE_REACTION_REMOVE_ALL - - MESSAGE_REACTION_REMOVE_EMOJI - -GUILD_MESSAGE_TYPING (1 << 11) - - TYPING_START - -DIRECT_MESSAGES (1 << 12) - - MESSAGE_CREATE - - MESSAGE_UPDATE - - MESSAGE_DELETE - - CHANNEL_PINS_UPDATE - -DIRECT_MESSAGE_REACTIONS (1 << 13) - - MESSAGE_REACTION_ADD - - MESSAGE_REACTION_REMOVE - - MESSAGE_REACTION_REMOVE_ALL - - MESSAGE_REACTION_REMOVE_EMOJI - -DIRECT_MESSAGE_TYPING (1 << 14) - - TYPING_START - -MESSAGE_CONTENT (1 << 15) *** - -GUILD_SCHEDULED_EVENTS (1 << 16) - - GUILD_SCHEDULED_EVENT_CREATE - - GUILD_SCHEDULED_EVENT_UPDATE - - GUILD_SCHEDULED_EVENT_DELETE - - GUILD_SCHEDULED_EVENT_USER_ADD - - GUILD_SCHEDULED_EVENT_USER_REMOVE - -AUTO_MODERATION_CONFIGURATION (1 << 20) - - AUTO_MODERATION_RULE_CREATE - - AUTO_MODERATION_RULE_UPDATE - - AUTO_MODERATION_RULE_DELETE - -AUTO_MODERATION_EXECUTION (1 << 21) - - AUTO_MODERATION_ACTION_EXECUTION -``` - -\* [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) contains different data depending on which intents are used. - -\*\* Events under the `GUILD_PRESENCES` and `GUILD_MEMBERS` intents are turned **off by default on all API versions**. If you are using **API v6**, you will receive those events if you are authorized to receive them and have enabled the intents in the Developer Portal. You do not need to use intents on API v6 to receive these events; you just need to enable the flags. If you are using **API v8** or above, intents are mandatory and must be specified when identifying. - -\*\*\* `MESSAGE_CONTENT` does not represent individual events, but rather affects what data is present for events that could contain message content fields. More information is in the [message content intent](#DOCS_TOPICS_GATEWAY/message-content-intent) section. - -### Caveats - -[Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) is sent for current-user updates regardless of whether the `GUILD_MEMBERS` intent is set. - -[Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) and [Request Guild Members](#DOCS_TOPICS_GATEWAY_EVENTS/request-guild-members) are uniquely affected by intents. See these sections for more information. - -[Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) by default only includes if the current user was added to or removed from a thread. To receive these updates for other users, request the `GUILD_MEMBERS` [Gateway Intent](#DOCS_TOPICS_GATEWAY/gateway-intents). - -### Privileged Intents - -Some intents are defined as "privileged" due to the sensitive nature of the data. Currently, those intents include: - -- `GUILD_PRESENCES` -- `GUILD_MEMBERS` -- [`MESSAGE_CONTENT`](#DOCS_TOPICS_GATEWAY/message-content-intent) - -Apps that qualify for verification **must** be approved for the privileged intent(s) before they can use them. After your app is verified, you can request privileged intents within the app's settings within the Developer Portal. - -Before you specify privileged intents in your `IDENTIFY` payload, you must enable the privileged intents your app requires. [Verified apps](https://support.discord.com/hc/en-us/articles/360040720412-Bot-Verification-and-Data-Whitelisting) can only use privileged intents *after* they've been approved for them. - -> info -> Unverified apps can use privileged intents without approval, but still must enable them in their app's settings. If the app's verification status changes, it will then have to apply for the privileged intent(s). - -In addition to the gateway restrictions described here, Discord's REST API is also affected by Privileged Intents. For example, to use the [List Guild Members](#DOCS_RESOURCES_GUILD/list-guild-members) endpoint, you must have the `GUILD_MEMBERS` intent enabled for your application. This behavior is independent of whether the intent is set during `IDENTIFY`. - -#### Enabling Privileged Intents - -Before using privileged intents, you must enable them in your app's settings. In the [Developer Portal](#APPLICATIONS), you can navigate to your app's settings then toggle the privileged intents on the **Bots** page under the "Privileged Gateway Intents" section. You should only toggle privileged intents that your bot *requires to function*. - -If your app qualifies for [verification](https://dis.gd/bot-verification), you must first [verify your app](https://support.discord.com/hc/en-us/articles/360040720412-Bot-Verification-and-Data-Whitelisting) and request access to these intents during the verification process. If your app is already verified and you need to request additional privileged intents, you can [contact support](https://dis.gd/support). - -#### Gateway Restrictions - -Privileged intents affect which Gateway events your app is permitted to receive. When using **API v8** and above, all intents (privileged and not) must be specified in the `intents` parameter when Identifying. If you pass a privileged intent in the `intents` parameter without configuring it in your app's settings, or being approved for it during verification, your Gateway connection will be closed with a ([`4014` close code](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes)). - -> info -> For **API v6**, you will receive events associated with the privileged intents your app has configured and is authorized to receive *without* passing those intents into the `intents` parameter when Identifying. - -Events associated with the `GUILD_PRESENCES` and `GUILD_MEMBERS` intents are turned off by default regardless of the API version. - -#### HTTP Restrictions - -In addition to Gateway restrictions, privileged intents also affect the [HTTP API](#DOCS_REFERENCE/http-api) endpoints your app is permitted to call, and the data it can receive. For example, to use the [List Guild Members](#DOCS_RESOURCES_GUILD/list-guild-members) endpoint, your app must configure the `GUILD_MEMBERS` intent (and be approved for it if eligible for verified). - -HTTP API restrictions are independent of Gateway restrictions, and are unaffected by which intents your app passes in the `intents` parameter when Identifying. - -#### Message Content Intent - -`MESSAGE_CONTENT (1 << 15)` is a unique privileged intent that isn't directly associated with any Gateway events. Instead, access to `MESSAGE_CONTENT` permits your app to receive message content data across the APIs. - -Any fields affected by the message content intent are noted in the relevant documentation. For example, the `content`, `embeds`, `attachments`, and `components` fields in [message objects](#DOCS_RESOURCES_CHANNEL/message-object) all contain message content and therefore require the intent. - -> info -> Like other privileged intents, `MESSAGE_CONTENT` must be approved for your app. After your app is verified, you can apply for the intent from your app's settings within the Developer Portal. You can read more about the message content intent review policy [in the Help Center](https://support-dev.discord.com/hc/en-us/articles/5324827539479). - -Apps **without** the intent will receive empty values in fields that contain user-inputted content with a few exceptions: -- Content in messages that an app sends -- Content in DMs with the app -- Content in which the app is [mentioned](#DOCS_REFERENCE/message-formatting-formats) - -## Rate Limiting - -> info -> This section refers to Gateway rate limits, not [HTTP API rate limits](#DOCS_TOPICS_RATE_LIMITS) - -Apps can send 120 [gateway events](#DOCS_TOPICS_GATEWAY_EVENTS) every 60 seconds, meaning an average of 2 commands per second. Apps that surpass the limit are immediately disconnected from the Gateway. Similar to other rate limits, repeat offenders will have their API access revoked. - -Apps also have a limit for [concurrent](#DOCS_TOPICS_GATEWAY/session-start-limit-object) [Identify](#DOCS_TOPICS_GATEWAY/identifying) requests allowed per 5 seconds. If you hit this limit, the Gateway will respond with an [Invalid Session (opcode `9`)](#DOCS_TOPICS_GATEWAY_EVENTS/invalid-session). - -## Encoding and Compression - -When [establishing a connection](#DOCS_TOPICS_GATEWAY/connecting) to the Gateway, apps can use the `encoding` parameter to choose whether to communicate with Discord using a plain-text JSON or binary [ETF](https://erlang.org/doc/apps/erts/erl_ext_dist.html) encoding. You can pick whichever encoding type you're more comfortable with, but both have their own quirks. If you aren't sure which encoding to use, JSON is generally recommended. - -Apps can also optionally enable compression to receive zlib-compressed packets. [Payload compression](#DOCS_TOPICS_GATEWAY/payload-compression) can only be enabled when using a JSON encoding, but [transport compression](#DOCS_TOPICS_GATEWAY/transport-compression) can be used regardless of encoding type. - -### Using JSON Encoding - -When using the plain-text JSON encoding, apps have the option to enable [payload compression](#DOCS_TOPICS_GATEWAY/payload-compression). - -#### Payload Compression - -> warn -> If an app is using payload compression, it cannot use [transport compression](#DOCS_TOPICS_GATEWAY/transport-compression). - -Payload compression enables optional per-packet compression for *some* events when Discord is sending events over the connection. - -Payload compression uses the zlib format (see [RFC1950 2.2](https://tools.ietf.org/html/rfc1950#section-2.2)) when sending payloads. To enable payload compression, your app can set `compress` to `true` when sending an [Identify (opcode `2`)](#DOCS_TOPICS_GATEWAY_EVENTS/identify) event. Note that even when payload compression is enabled, not all payloads will be compressed. - -When payload compression is enabled, your app (or library) _must_ detect and decompress these payloads to plain-text JSON before attempting to parse them. If you are using payload compression, the gateway does not implement a shared compression context between events sent. - -Payload compression will be disabled if you use [transport compression](#DOCS_TOPICS_GATEWAY/transport-compression). - -### Using ETF Encoding - -When using ETF (External Term Format) encoding, there are some specific behaviors you should know: - -- Snowflake IDs are transmitted as 64-bit integers or strings. -- Your app can't send compressed messages to the guild. -- When sending payloads, you must use string keys. Using atom keys will result in a [`4002`](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-close-event-codes) decode error. - -See [erlpack](https://github.com/discord/erlpack) for an ETF implementation example. - -### Transport Compression - -Transport compression enables optional compression for all packets when Discord is sending events over the connection. The only currently-available transport compression option is `zlib-stream`. - -When transport compression is enabled, your app needs to process received data through a single Gateway connection using a shared zlib context. However, each Gateway connection should use its own unique zlib context. - -When processing transport-compressed data, you should push received data to a buffer until you receive the 4-byte `Z_SYNC_FLUSH` suffix (`00 00 ff ff`). After you receive the `Z_SYNC_FLUSH` suffix, you can then decompress the buffer. - -###### Transport Compression Example - -```python -# Z_SYNC_FLUSH suffix -ZLIB_SUFFIX = b'\x00\x00\xff\xff' -# initialize a buffer to store chunks -buffer = bytearray() -# create a shared zlib inflation context to run chunks through -inflator = zlib.decompressobj() - -# ... -def on_websocket_message(msg): - # always push the message data to your cache - buffer.extend(msg) - - # check if the last four bytes are equal to ZLIB_SUFFIX - if len(msg) < 4 or msg[-4:] != ZLIB_SUFFIX: - return - - # if the message *does* end with ZLIB_SUFFIX, - # get the full message by decompressing the buffers - # NOTE: the message is utf-8 encoded. - msg = inflator.decompress(buffer) - buffer = bytearray() - - # here you can treat `msg` as either JSON or ETF encoded, - # depending on your `encoding` param -``` - -## Tracking State - -Most of a client's state is provided during the initial [Ready](#DOCS_TOPICS_GATEWAY/ready-event) event and in the [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) events that follow. - -As resources continue to be created, updated, and deleted, Gateway events are sent to notify the app of these changes and to provide associated data. To avoid excessive API calls, apps should cache as many relevant resource states as possible, and update them as new events are received. - -> info -> For larger apps, client state can grow to be very large. Therefore, we recommend only storing data in memory that are *needed* for the app to operate. In some cases, there isn't a need to cache member information (like roles or permissions) since some events like [MESSAGE_CREATE](#DOCS_TOPICS_GATEWAY_EVENTS/message-create) have the full member object included. - -An example of state tracking can be considered in the case of an app that wants to track member status: when initially connecting to the Gateway, the app will receive information about the online status of guild members (whether they're online, idle, dnd, or offline). To keep the state updated, the app will track and parse [Presence Update](#DOCS_TOPICS_GATEWAY_EVENTS/presence-update) events as they're received, then update the cached member objects accordingly. - -## Guild Availability - -When connecting to the gateway as a bot user, guilds that the bot is a part of will start out as unavailable. Don't fret! The gateway will automatically attempt to reconnect on your behalf. As guilds become available to you, you will receive [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) events. - -## Sharding - -As apps grow and are added to an increasing number of guilds, some developers may find it necessary to divide portions of their app's operations across multiple processes. As such, the Gateway implements a method of user-controlled guild sharding which allows apps to split events across a number of Gateway connections. Guild sharding is entirely controlled by an app, and requires no state-sharing between separate connections to operate. While all apps *can* enable sharding, it's not necessary for apps in a smaller number of guilds. - -> warn -> Each shard can only support a maximum of 2500 guilds, and apps that are in 2500+ guilds *must* enable sharding. - -To enable sharding on a connection, the app should send the `shard` array in the [Identify](#DOCS_TOPICS_GATEWAY_EVENTS/identify) payload. The first item in this array should be the zero-based integer value of the current shard, while the second represents the total number of shards. DMs will only be sent to shard 0. - -> info -> The [Get Gateway Bot](#DOCS_TOPICS_GATEWAY/get-gateway-bot) endpoint provides a recommended number of shards for your app in the `shards` field - -To calculate which events will be sent to which shard, the following formula can be used: - -###### Sharding Formula - -```python -shard_id = (guild_id >> 22) % num_shards -``` - -As an example, if you wanted to split the connection between three shards, you'd use the following values for `shard` for each connection: `[0, 3]`, `[1, 3]`, and `[2, 3]`. Note that only the first shard (`[0, 3]`) would receive DMs. - -Note that `num_shards` does not relate to (or limit) the total number of potential sessions. It is only used for *routing* traffic. As such, sessions do not have to be identified in an evenly-distributed manner when sharding. You can establish multiple sessions with the same `[shard_id, num_shards]`, or sessions with different `num_shards` values. This allows you to create sessions that will handle more or less traffic for more fine-tuned load balancing, or to orchestrate "zero-downtime" scaling/updating by handing off traffic to a new deployment of sessions with a higher or lower `num_shards` count that are prepared in parallel. - -###### Max Concurrency - -If you have multiple shards, you may start them concurrently based on the [`max_concurrency`](#DOCS_TOPICS_GATEWAY/session-start-limit-object) value returned to you on session start. Which shards you can start concurrently are assigned based on a key for each shard. The rate limit key for a given shard can be computed with - -``` -rate_limit_key = shard_id % max_concurrency -``` - -This puts your shards into "buckets" of `max_concurrency` size. When you start your bot, you may start up to `max_concurrency` shards at a time, and you must start them by "bucket" **in order**. To explain another way, let's say you have 16 shards, and your `max_concurrency` is 16: - -``` -shard_id: 0, rate limit key (0 % 16): 0 -shard_id: 1, rate limit key (1 % 16): 1 -shard_id: 2, rate limit key (2 % 16): 2 -shard_id: 3, rate limit key (3 % 16): 3 -shard_id: 4, rate limit key (4 % 16): 4 -shard_id: 5, rate limit key (5 % 16): 5 -shard_id: 6, rate limit key (6 % 16): 6 -shard_id: 7, rate limit key (7 % 16): 7 -shard_id: 8, rate limit key (8 % 16): 8 -shard_id: 9, rate limit key (9 % 16): 9 -shard_id: 10, rate limit key (10 % 16): 10 -shard_id: 11, rate limit key (11 % 16): 11 -shard_id: 12, rate limit key (12 % 16): 12 -shard_id: 13, rate limit key (13 % 16): 13 -shard_id: 14, rate limit key (14 % 16): 14 -shard_id: 15, rate limit key (15 % 16): 15 -``` - -You may start all 16 of your shards at once, because each has a `rate_limit_key` which fills the bucket of 16 shards. However, let's say you had 32 shards: - -``` -shard_id: 0, rate limit key (0 % 16): 0 -shard_id: 1, rate limit key (1 % 16): 1 -shard_id: 2, rate limit key (2 % 16): 2 -shard_id: 3, rate limit key (3 % 16): 3 -shard_id: 4, rate limit key (4 % 16): 4 -shard_id: 5, rate limit key (5 % 16): 5 -shard_id: 6, rate limit key (6 % 16): 6 -shard_id: 7, rate limit key (7 % 16): 7 -shard_id: 8, rate limit key (8 % 16): 8 -shard_id: 9, rate limit key (9 % 16): 9 -shard_id: 10, rate limit key (10 % 16): 10 -shard_id: 11, rate limit key (11 % 16): 11 -shard_id: 12, rate limit key (12 % 16): 12 -shard_id: 13, rate limit key (13 % 16): 13 -shard_id: 14, rate limit key (14 % 16): 14 -shard_id: 15, rate limit key (15 % 16): 15 -shard_id: 16, rate limit key (16 % 16): 0 -shard_id: 17, rate limit key (17 % 16): 1 -shard_id: 18, rate limit key (18 % 16): 2 -shard_id: 19, rate limit key (19 % 16): 3 -shard_id: 20, rate limit key (20 % 16): 4 -shard_id: 21, rate limit key (21 % 16): 5 -shard_id: 22, rate limit key (22 % 16): 6 -shard_id: 23, rate limit key (23 % 16): 7 -shard_id: 24, rate limit key (24 % 16): 8 -shard_id: 25, rate limit key (25 % 16): 9 -shard_id: 26, rate limit key (26 % 16): 10 -shard_id: 27, rate limit key (27 % 16): 11 -shard_id: 28, rate limit key (28 % 16): 12 -shard_id: 29, rate limit key (29 % 16): 13 -shard_id: 30, rate limit key (30 % 16): 14 -shard_id: 31, rate limit key (31 % 16): 15 -``` - -In this case, you must start the shard buckets **in "order"**. That means that you can start shard 0 -> shard 15 concurrently, and then you can start shard 16 -> shard 31. - -### Sharding for Large Bots - -If your bot is in more than 150,000 guilds, there are some additional considerations you must take around sharding. Discord will migrate your bot to large bot sharding when it starts to get near the large bot sharding threshold. The bot owner(s) will receive a system DM and email confirming this move has completed as well as what shard number has been assigned. - -The number of shards you run must be a multiple of the shard number provided when reaching out to you. If you attempt to start your bot with an invalid number of shards, your Gateway connection will close with a `4010` Invalid Shard close code. - -The [Get Gateway Bot endpoint](#DOCS_TOPICS_GATEWAY/get-gateway-bot) will always return the correct amount of shards, so if you're already using this endpoint to determine your number of shards, you shouldn't require any changes. - -The session start limit for these bots will also be increased from 1000 to `max(2000, (guild_count / 1000) * 3)` per day. You also receive an increased `max_concurrency`, the number of [shards you can concurrently start](#DOCS_TOPICS_GATEWAY/session-start-limit-object). - -## Get Gateway % GET /gateway - -> info -> This endpoint does not require authentication. - -Returns an object with a valid WSS URL which the app can use when [Connecting](#DOCS_TOPICS_GATEWAY/connecting) to the Gateway. Apps should cache this value and only call this endpoint to retrieve a new URL when they are unable to properly establish a connection using the cached one. - -###### Example Response - -```json -{ - "url": "wss://gateway.discord.gg/" -} -``` - -## Get Gateway Bot % GET /gateway/bot - -> warn -> This endpoint requires authentication using a valid bot token. - -Returns an object based on the information in [Get Gateway](#DOCS_TOPICS_GATEWAY/get-gateway), plus additional metadata that can help during the operation of large or [sharded](#DOCS_TOPICS_GATEWAY/sharding) bots. Unlike the [Get Gateway](#DOCS_TOPICS_GATEWAY/get-gateway), this route should not be cached for extended periods of time as the value is not guaranteed to be the same per-call, and changes as the bot joins/leaves guilds. - -###### JSON Response - -| Field | Type | Description | -| ------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| url | string | WSS URL that can be used for connecting to the Gateway | -| shards | integer | Recommended number of [shards](#DOCS_TOPICS_GATEWAY/sharding) to use when connecting | -| session_start_limit | [session_start_limit](#DOCS_TOPICS_GATEWAY/session-start-limit-object) object | Information on the current session start limit | - -###### Example Response - -```json -{ - "url": "wss://gateway.discord.gg/", - "shards": 9, - "session_start_limit": { - "total": 1000, - "remaining": 999, - "reset_after": 14400000, - "max_concurrency": 1 - } -} -``` - -## Session Start Limit Object - -###### Session Start Limit Structure - -| Field | Type | Description | -| --------------- | ------- | -------------------------------------------------------------- | -| total | integer | Total number of session starts the current user is allowed | -| remaining | integer | Remaining number of session starts the current user is allowed | -| reset_after | integer | Number of milliseconds after which the limit resets | -| max_concurrency | integer | Number of identify requests allowed per 5 seconds | diff --git a/docs/topics/Gateway_Events.md b/docs/topics/Gateway_Events.md deleted file mode 100644 index 14ab44b9c0..0000000000 --- a/docs/topics/Gateway_Events.md +++ /dev/null @@ -1,1219 +0,0 @@ -# Gateway Events - -Gateway connections are WebSockets, meaning they're bidirectional and either side of the WebSocket can send events to the other. The following events are split up into two types: - -- **Send events** are Gateway events sent by an app to Discord (like when identifying with the Gateway) -- **Receive events** are Gateway events that are sent by Discord to an app. These events typically represent something happening inside of a server where an app is installed, like a channel being updated. - -All Gateway events are encapsulated in a [Gateway event payload](#DOCS_TOPICS_GATEWAY_EVENTS/payload-structure). - -For more information about interacting with the Gateway, you can reference the [Gateway documentation](#DOCS_TOPICS_GATEWAY). - -> warn -> Not all Gateway event fields are documented. You should assume that undocumented fields are not supported for apps, and their format and data may change at any time. - -### Event Names - -In practice, event names are UPPER-CASED with under_scores joining each word in the name. For instance, [Channel Create](#DOCS_TOPICS_GATEWAY_EVENTS/channel-create) would be `CHANNEL_CREATE` and [Voice State Update](#DOCS_TOPICS_GATEWAY_EVENTS/voice-state-update) would be `VOICE_STATE_UPDATE`. - -For readability, event names in the following documentation are typically left in Title Case. - -### Payload Structure - -Gateway event payloads have a common structure, but the contents of the associated data (`d`) varies between the different events. - -| Field | Type | Description | -| ----- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| op | integer | [Gateway opcode](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-opcodes), which indicates the payload type | -| d | ?mixed (any JSON value) | Event data | -| s | ?integer \* | Sequence number of event used for [resuming sessions](#DOCS_TOPICS_GATEWAY/resuming) and [heartbeating](#DOCS_TOPICS_GATEWAY/sending-heartbeats) | -| t | ?string \* | Event name | - -\* `s` and `t` are `null` when `op` is not `0` ([Gateway Dispatch opcode](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway-gateway-opcodes)). - -###### Example Gateway Event Payload - -```json -{ - "op": 0, - "d": {}, - "s": 42, - "t": "GATEWAY_EVENT_NAME" -} -``` - -## Send Events - -Send events are Gateway events encapsulated in an [event payload](#DOCS_TOPICS_GATEWAY_EVENTS/payload-structure), and are sent by an app to Discord through a Gateway connection. - -> info -> Previously, Gateway send events were labeled as commands - -| Name | Description | -| -------------------------------------------------------------------------- | --------------------------------------------------------- | -| [Identify](#DOCS_TOPICS_GATEWAY_EVENTS/identify) | Triggers the initial handshake with the gateway | -| [Resume](#DOCS_TOPICS_GATEWAY_EVENTS/resume) | Resumes a dropped gateway connection | -| [Heartbeat](#DOCS_TOPICS_GATEWAY_EVENTS/heartbeat) | Maintains an active gateway connection | -| [Request Guild Members](#DOCS_TOPICS_GATEWAY_EVENTS/request-guild-members) | Requests members for a guild | -| [Update Voice State](#DOCS_TOPICS_GATEWAY_EVENTS/update-voice-state) | Joins, moves, or disconnects the app from a voice channel | -| [Update Presence](#DOCS_TOPICS_GATEWAY_EVENTS/update-presence) | Updates an app's presence | - -#### Identify - -Used to trigger the initial handshake with the gateway. - -Details about identifying is in the [Gateway documentation](#DOCS_TOPICS_GATEWAY/identifying). - -###### Identify Structure - -| Field | Type | Description | Default | -| ---------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------- | -| token | string | Authentication token | - | -| properties | object | [Connection properties](#DOCS_TOPICS_GATEWAY_EVENTS/identify-identify-connection-properties) | - | -| compress? | boolean | Whether this connection supports compression of packets | false | -| large_threshold? | integer | Value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list | 50 | -| shard? | array of two integers (shard_id, num_shards) | Used for [Guild Sharding](#DOCS_TOPICS_GATEWAY/sharding) | - | -| presence? | [update presence](#DOCS_TOPICS_GATEWAY_EVENTS/update-presence) object | Presence structure for initial presence information | - | -| intents | integer | [Gateway Intents](#DOCS_TOPICS_GATEWAY/gateway-intents) you wish to receive | - | - -###### Identify Connection Properties - -| Field | Type | Description | -| ------- | ------ | --------------------- | -| os | string | Your operating system | -| browser | string | Your library name | -| device | string | Your library name | - -> warn -> These fields originally were $ prefixed (i.e: `$browser`) but [this syntax is deprecated](#DOCS_CHANGE_LOG/updated-connection-property-field-names). While they currently still work, it is recommended to move to non-prefixed fields. - -###### Example Identify - -```json -{ - "op": 2, - "d": { - "token": "my_token", - "properties": { - "os": "linux", - "browser": "disco", - "device": "disco" - }, - "compress": true, - "large_threshold": 250, - "shard": [0, 1], - "presence": { - "activities": [{ - "name": "Cards Against Humanity", - "type": 0 - }], - "status": "dnd", - "since": 91879201, - "afk": false - }, - // This intent represents 1 << 0 for GUILDS, 1 << 1 for GUILD_MEMBERS, and 1 << 2 for GUILD_BANS - // This connection will only receive the events defined in those three intents - "intents": 7 - } -} -``` - -#### Resume - -Used to replay missed events when a disconnected client resumes. - -Details about resuming are in the [Gateway documentation](#DOCS_TOPICS_GATEWAY/resuming). - -###### Resume Structure - -| Field | Type | Description | -| ---------- | ------- | ----------------------------- | -| token | string | Session token | -| session_id | string | Session ID | -| seq | integer | Last sequence number received | - -###### Example Resume - -```json -{ - "op": 6, - "d": { - "token": "randomstring", - "session_id": "evenmorerandomstring", - "seq": 1337 - } -} -``` - -#### Heartbeat - -Used to maintain an active gateway connection. Must be sent every `heartbeat_interval` milliseconds after the [Opcode 10 Hello](#DOCS_TOPICS_GATEWAY_EVENTS/hello) payload is received. The inner `d` key is the last sequence number—`s`—received by the client. If you have not yet received one, send `null`. - -Details about heartbeats are in the [Gateway documentation](#DOCS_TOPICS_GATEWAY/sending-heartbeats). - -###### Example Heartbeat - -``` -{ - "op": 1, - "d": 251 -} -``` - -#### Request Guild Members - -Used to request all members for a guild or a list of guilds. When initially connecting, if you don't have the `GUILD_PRESENCES` [Gateway Intent](#DOCS_TOPICS_GATEWAY/gateway-intents), or if the guild is over 75k members, it will only send members who are in voice, plus the member for you (the connecting user). Otherwise, if a guild has over `large_threshold` members (value in the [Gateway Identify](#DOCS_TOPICS_GATEWAY_EVENTS/identify)), it will only send members who are online, have a role, have a nickname, or are in a voice channel, and if it has under `large_threshold` members, it will send all members. If a client wishes to receive additional members, they need to explicitly request them via this operation. The server will send [Guild Members Chunk](#DOCS_TOPICS_GATEWAY_EVENTS/guild-members-chunk) events in response with up to 1000 members per chunk until all members that match the request have been sent. - -Due to our privacy and infrastructural concerns with this feature, there are some limitations that apply: - -- `GUILD_PRESENCES` intent is required to set `presences = true`. Otherwise, it will always be false -- `GUILD_MEMBERS` intent is required to request the entire member list—`(query=‘’, limit=0<=n)` -- You will be limited to requesting 1 `guild_id` per request -- Requesting a prefix (`query` parameter) will return a maximum of 100 members -- Requesting `user_ids` will continue to be limited to returning 100 members - -###### Guild Request Members Structure - -| Field | Type | Description | Required | -| ---------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | -| guild_id | snowflake | ID of the guild to get members for | true | -| query? | string | string that username starts with, or an empty string to return all members | one of query or user_ids | -| limit | integer | maximum number of members to send matching the `query`; a limit of `0` can be used with an empty string `query` to return all members | true when specifying query | -| presences? | boolean | used to specify if we want the presences of the matched members | false | -| user_ids? | snowflake or array of snowflakes | used to specify which users you wish to fetch | one of query or user_ids | -| nonce? | string | nonce to identify the [Guild Members Chunk](#DOCS_TOPICS_GATEWAY_EVENTS/guild-members-chunk) response | false | - -> info -> Nonce can only be up to 32 bytes. If you send an invalid nonce it will be ignored and the reply member_chunk(s) will not have a nonce set. - -###### Guild Request Members - -```json -{ - "op": 8, - "d": { - "guild_id": "41771983444115456", - "query": "", - "limit": 0 - } -} -``` - -#### Update Voice State - -Sent when a client wants to join, move, or disconnect from a voice channel. - -###### Gateway Voice State Update Structure - -| Field | Type | Description | -| ---------- | ---------- | -------------------------------------------------------------------- | -| guild_id | snowflake | ID of the guild | -| channel_id | ?snowflake | ID of the voice channel client wants to join (null if disconnecting) | -| self_mute | boolean | Whether the client is muted | -| self_deaf | boolean | Whether the client deafened | - -###### Example Gateway Voice State Update - -```json -{ - "op": 4, - "d": { - "guild_id": "41771983423143937", - "channel_id": "127121515262115840", - "self_mute": false, - "self_deaf": false - } -} -``` - -#### Update Presence - -Sent by the client to indicate a presence or status update. - -###### Gateway Presence Update Structure - -| Field | Type | Description | -| ---------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | -| since | ?integer | Unix time (in milliseconds) of when the client went idle, or null if the client is not idle | -| activities | array of [activity](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object) objects | User's activities | -| status | string | User's new [status](#DOCS_TOPICS_GATEWAY_EVENTS/update-presence-status-types) | -| afk | boolean | Whether or not the client is afk | - -###### Status Types - -| Status | Description | -| --------- | ------------------------------ | -| online | Online | -| dnd | Do Not Disturb | -| idle | AFK | -| invisible | Invisible and shown as offline | -| offline | Offline | - -###### Example Gateway Presence Update - -```json -{ - "op": 3, - "d": { - "since": 91879201, - "activities": [{ - "name": "Save the Oxford Comma", - "type": 0 - }], - "status": "online", - "afk": false - } -} -``` - -## Receive Events - -Receive events are Gateway events encapsulated in an [event payload](#DOCS_TOPICS_GATEWAY_EVENTS/payload-structure), and are sent by Discord to an app through a Gateway connection. Receive events correspond to events that happen in a Discord server where the app is installed. - -| Name | Description | -| ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| [Hello](#DOCS_TOPICS_GATEWAY_EVENTS/hello) | Defines the heartbeat interval | -| [Ready](#DOCS_TOPICS_GATEWAY_EVENTS/ready) | Contains the initial state information | -| [Resumed](#DOCS_TOPICS_GATEWAY_EVENTS/resumed) | Response to [Resume](#DOCS_TOPICS_GATEWAY_EVENTS/resume) | -| [Reconnect](#DOCS_TOPICS_GATEWAY_EVENTS/reconnect) | Server is going away, client should reconnect to gateway and resume | -| [Invalid Session](#DOCS_TOPICS_GATEWAY_EVENTS/invalid-session) | Failure response to [Identify](#DOCS_TOPICS_GATEWAY_EVENTS/identify) or [Resume](#DOCS_TOPICS_GATEWAY_EVENTS/resume) or invalid active session | -| [Application Command Permissions Update](#DOCS_TOPICS_GATEWAY_EVENTS/application-command-permissions-update) | Application command permission was updated | -| [Auto Moderation Rule Create](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-create) | Auto Moderation rule was created | -| [Auto Moderation Rule Update](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-update) | Auto Moderation rule was updated | -| [Auto Moderation Rule Delete](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-rule-delete) | Auto Moderation rule was deleted | -| [Auto Moderation Action Execution](#DOCS_TOPICS_GATEWAY_EVENTS/auto-moderation-action-execution) | Auto Moderation rule was triggered and an action was executed (e.g. a message was blocked) | -| [Channel Create](#DOCS_TOPICS_GATEWAY_EVENTS/channel-create) | New guild channel created | -| [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update) | Channel was updated | -| [Channel Delete](#DOCS_TOPICS_GATEWAY_EVENTS/channel-delete) | Channel was deleted | -| [Channel Pins Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-pins-update) | Message was pinned or unpinned | -| [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) | Thread created, also sent when being added to a private thread | -| [Thread Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-update) | Thread was updated | -| [Thread Delete](#DOCS_TOPICS_GATEWAY_EVENTS/thread-delete) | Thread was deleted | -| [Thread List Sync](#DOCS_TOPICS_GATEWAY_EVENTS/thread-list-sync) | Sent when gaining access to a channel, contains all active threads in that channel | -| [Thread Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-member-update) | [Thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) for the current user was updated | -| [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) | Some user(s) were added to or removed from a thread | -| [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) | Lazy-load for unavailable guild, guild became available, or user joined a new guild | -| [Guild Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-update) | Guild was updated | -| [Guild Delete](#DOCS_TOPICS_GATEWAY_EVENTS/guild-delete) | Guild became unavailable, or user left/was removed from a guild | -| [Guild Ban Add](#DOCS_TOPICS_GATEWAY_EVENTS/guild-ban-add) | User was banned from a guild | -| [Guild Ban Remove](#DOCS_TOPICS_GATEWAY_EVENTS/guild-ban-remove) | User was unbanned from a guild | -| [Guild Emojis Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-emojis-update) | Guild emojis were updated | -| [Guild Stickers Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-stickers-update) | Guild stickers were updated | -| [Guild Integrations Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-integrations-update) | Guild integration was updated | -| [Guild Member Add](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-add) | New user joined a guild | -| [Guild Member Remove](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-remove) | User was removed from a guild | -| [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) | Guild member was updated | -| [Guild Members Chunk](#DOCS_TOPICS_GATEWAY_EVENTS/guild-members-chunk) | Response to [Request Guild Members](#DOCS_TOPICS_GATEWAY_EVENTS/request-guild-members) | -| [Guild Role Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-role-create) | Guild role was created | -| [Guild Role Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-role-update) | Guild role was updated | -| [Guild Role Delete](#DOCS_TOPICS_GATEWAY_EVENTS/guild-role-delete) | Guild role was deleted | -| [Guild Scheduled Event Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-scheduled-event-create) | Guild scheduled event was created | -| [Guild Scheduled Event Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-scheduled-event-update) | Guild scheduled event was updated | -| [Guild Scheduled Event Delete](#DOCS_TOPICS_GATEWAY_EVENTS/guild-scheduled-event-delete) | Guild scheduled event was deleted | -| [Guild Scheduled Event User Add](#DOCS_TOPICS_GATEWAY_EVENTS/guild-scheduled-event-user-add) | User subscribed to a guild scheduled event | -| [Guild Scheduled Event User Remove](#DOCS_TOPICS_GATEWAY_EVENTS/guild-scheduled-event-user-remove) | User unsubscribed from a guild scheduled event | -| [Integration Create](#DOCS_TOPICS_GATEWAY_EVENTS/integration-create) | Guild integration was created | -| [Integration Update](#DOCS_TOPICS_GATEWAY_EVENTS/integration-update) | Guild integration was updated | -| [Integration Delete](#DOCS_TOPICS_GATEWAY_EVENTS/integration-delete) | Guild integration was deleted | -| [Interaction Create](#DOCS_TOPICS_GATEWAY_EVENTS/interaction-create) | User used an interaction, such as an [Application Command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/) | -| [Invite Create](#DOCS_TOPICS_GATEWAY_EVENTS/invite-create) | Invite to a channel was created | -| [Invite Delete](#DOCS_TOPICS_GATEWAY_EVENTS/invite-delete) | Invite to a channel was deleted | -| [Message Create](#DOCS_TOPICS_GATEWAY_EVENTS/message-create) | Message was created | -| [Message Update](#DOCS_TOPICS_GATEWAY_EVENTS/message-update) | Message was edited | -| [Message Delete](#DOCS_TOPICS_GATEWAY_EVENTS/message-delete) | Message was deleted | -| [Message Delete Bulk](#DOCS_TOPICS_GATEWAY_EVENTS/message-delete-bulk) | Multiple messages were deleted at once | -| [Message Reaction Add](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-add) | User reacted to a message | -| [Message Reaction Remove](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-remove) | User removed a reaction from a message | -| [Message Reaction Remove All](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-remove-all) | All reactions were explicitly removed from a message | -| [Message Reaction Remove Emoji](#DOCS_TOPICS_GATEWAY_EVENTS/message-reaction-remove-emoji) | All reactions for a given emoji were explicitly removed from a message | -| [Presence Update](#DOCS_TOPICS_GATEWAY_EVENTS/presence-update) | User was updated | -| [Stage Instance Create](#DOCS_TOPICS_GATEWAY_EVENTS/stage-instance-create) | Stage instance was created | -| [Stage Instance Update](#DOCS_TOPICS_GATEWAY_EVENTS/stage-instance-update) | Stage instance was updated | -| [Stage Instance Delete](#DOCS_TOPICS_GATEWAY_EVENTS/stage-instance-delete) | Stage instance was deleted or closed | -| [Typing Start](#DOCS_TOPICS_GATEWAY_EVENTS/typing-start) | User started typing in a channel | -| [User Update](#DOCS_TOPICS_GATEWAY_EVENTS/user-update) | Properties about the user changed | -| [Voice State Update](#DOCS_TOPICS_GATEWAY_EVENTS/voice-state-update) | Someone joined, left, or moved a voice channel | -| [Voice Server Update](#DOCS_TOPICS_GATEWAY_EVENTS/voice-server-update) | Guild's voice server was updated | -| [Webhooks Update](#DOCS_TOPICS_GATEWAY_EVENTS/webhooks-update) | Guild channel webhook was created, update, or deleted | - -#### Hello - -Sent on connection to the websocket. Defines the heartbeat interval that an app should heartbeat to. - -###### Hello Structure - -| Field | Type | Description | -| ------------------ | ------- | ------------------------------------------------------- | -| heartbeat_interval | integer | Interval (in milliseconds) an app should heartbeat with | - -###### Example Hello - -```json -{ - "op": 10, - "d": { - "heartbeat_interval": 45000 - } -} -``` - -#### Ready - -The ready event is dispatched when a client has completed the initial handshake with the gateway (for new sessions). The ready event can be the largest and most complex event the gateway will send, as it contains all the state required for a client to begin interacting with the rest of the platform. - -`guilds` are the guilds of which your bot is a member. They start out as unavailable when you connect to the gateway. As they become available, your bot will be notified via [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) events. - -###### Ready Event Fields - -| Field | Type | Description | -| ------------------ | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | -| v | integer | [API version](#DOCS_REFERENCE/api-versioning-api-versions) | -| user | [user](#DOCS_RESOURCES_USER/user-object) object | Information about the user including email | -| guilds | array of [Unavailable Guild](#DOCS_RESOURCES_GUILD/unavailable-guild-object) objects | Guilds the user is in | -| session_id | string | Used for resuming connections | -| resume_gateway_url | string | Gateway URL for resuming connections | -| shard? | array of two integers (shard_id, num_shards) | [Shard information](#DOCS_TOPICS_GATEWAY/sharding) associated with this session, if sent when identifying | -| application | partial [application object](#DOCS_RESOURCES_APPLICATION/application-object) | Contains `id` and `flags` | - -#### Resumed - -The resumed event is dispatched when a client has sent a [resume payload](#DOCS_TOPICS_GATEWAY_EVENTS/resume) to the gateway (for resuming existing sessions). - -#### Reconnect - -The reconnect event is dispatched when a client should reconnect to the gateway (and resume their existing session, if they have one). This event usually occurs during deploys to migrate sessions gracefully off old hosts. - -###### Example Gateway Reconnect - -```json -{ - "op": 7, - "d": null -} -``` - -#### Invalid Session - -Sent to indicate one of at least three different situations: - -- the gateway could not initialize a session after receiving an [Opcode 2 Identify](#DOCS_TOPICS_GATEWAY_EVENTS/identify) -- the gateway could not resume a previous session after receiving an [Opcode 6 Resume](#DOCS_TOPICS_GATEWAY_EVENTS/resume) -- the gateway has invalidated an active session and is requesting client action - -The inner `d` key is a boolean that indicates whether the session may be resumable. See [Connecting](#DOCS_TOPICS_GATEWAY/connecting) and [Resuming](#DOCS_TOPICS_GATEWAY/resuming) for more information. - -###### Example Gateway Invalid Session - -```json -{ - "op": 9, - "d": false -} -``` - -### Application Commands - -#### Application Command Permissions Update - -`APPLICATION_COMMAND_PERMISSIONS_UPDATE` event, sent when an application command's permissions are updated. The inner payload is an [application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/application-command-permissions-object-guild-application-command-permissions-structure) object. - -### Auto Moderation - -All auto moderation related events are currently only sent to bot users which have the `MANAGE_GUILD` permission. - -#### Auto Moderation Rule Create - -Sent when a rule is created. The inner payload is an [auto moderation rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) object. - -#### Auto Moderation Rule Update - -Sent when a rule is updated. The inner payload is an [auto moderation rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) object. - -#### Auto Moderation Rule Delete - -Sent when a rule is deleted. The inner payload is an [auto moderation rule](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object) object. - -#### Auto Moderation Action Execution - -Sent when a rule is triggered and an action is executed (e.g. when a message is blocked). - -###### Auto Moderation Action Execution Event Fields - -| Field | Type | Description | -| ------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -| guild_id | snowflake | ID of the guild in which action was executed | -| action | [auto moderation action](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-action-object) object | Action which was executed | -| rule_id | snowflake | ID of the rule which action belongs to | -| rule_trigger_type | [trigger_type](#DOCS_RESOURCES_AUTO_MODERATION/auto-moderation-rule-object-trigger-types) | Trigger type of rule which was triggered | -| user_id | snowflake | ID of the user which generated the content which triggered the rule | -| channel_id? | snowflake | ID of the channel in which user content was posted | -| message_id? | snowflake | ID of any user message which content belongs to * | -| alert_system_message_id? | snowflake | ID of any system auto moderation messages posted as a result of this action ** | -| content *** | string | User-generated text content | -| matched_keyword | ?string | Word or phrase configured in the rule that triggered the rule | -| matched_content *** | ?string | Substring in content that triggered the rule | - - -\* `message_id` will not exist if message was blocked by automod or content was not part of any message - -\*\* `alert_system_message_id` will not exist if this event does not correspond to an action with type `SEND_ALERT_MESSAGE` - -\*\*\* `MESSAGE_CONTENT` (`1 << 15`) [gateway intent](#DOCS_TOPICS_GATEWAY/gateway-intents) is required to receive the `content` and `matched_content` fields - -### Channels - -#### Channel Create - -Sent when a new guild channel is created, relevant to the current user. The inner payload is a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object. - -#### Channel Update - -Sent when a channel is updated. The inner payload is a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object. This is not sent when the field `last_message_id` is altered. To keep track of the last_message_id changes, you must listen for [Message Create](#DOCS_TOPICS_GATEWAY_EVENTS/message-create) events (or [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) events for `GUILD_FORUM` channels). - -#### Channel Delete - -Sent when a channel relevant to the current user is deleted. The inner payload is a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object. - -#### Thread Create - -Sent when a thread is created, relevant to the current user, or when the current user is added to a thread. The inner payload is a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object. -- When a thread is created, includes an additional `newly_created` boolean field. -- When being added to an existing private thread, includes a [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object. - -#### Thread Update - -Sent when a thread is updated. The inner payload is a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object. This is not sent when the field `last_message_id` is altered. To keep track of the last_message_id changes, you must listen for [Message Create](#DOCS_TOPICS_GATEWAY_EVENTS/message-create) events. - -#### Thread Delete - -Sent when a thread relevant to the current user is deleted. The inner payload is a subset of the [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object, containing just the `id`, `guild_id`, `parent_id`, and `type` fields. - -#### Thread List Sync - -Sent when the current user _gains_ access to a channel. - -###### Thread List Sync Event Fields - -| Field | Type | Description | -| ------------ | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| guild_id | snowflake | ID of the guild | -| channel_ids? | array of snowflakes | Parent channel IDs whose threads are being synced. If omitted, then threads were synced for the entire guild. This array may contain channel_ids that have no active threads as well, so you know to clear that data. | -| threads | array of [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | All active threads in the given channels that the current user can access | -| members | array of [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects | All thread member objects from the synced threads for the current user, indicating which threads the current user has been added to | - -#### Thread Member Update - -Sent when the [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object for the current user is updated. The inner payload is a [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object with an extra `guild_id` field. This event is documented for completeness, but unlikely to be used by most bots. For bots, this event largely is just a signal that you are a member of the thread. See the [threads docs](#DOCS_TOPICS_THREADS) for more details. - -###### Thread Member Update Event Extra Fields - -| Field | Type | Description | -| -------- | --------- | --------------- | -| guild_id | snowflake | ID of the guild | - - -#### Thread Members Update - -Sent when anyone is added to or removed from a thread. If the current user does not have the `GUILD_MEMBERS` [Gateway Intent](#DOCS_TOPICS_GATEWAY/gateway-intents), then this event will only be sent if the current user was added to or removed from the thread. - -###### Thread Members Update Event Fields - -| Field | Type | Description | -| ------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------- | -| id | snowflake | ID of the thread | -| guild_id | snowflake | ID of the guild | -| member_count | integer | Approximate number of members in the thread, capped at 50 | -| added_members?\* | array of [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects | Users who were added to the thread | -| removed_member_ids? | array of snowflakes | ID of the users who were removed from the thread | - -\* In this gateway event, the thread member objects will also include the [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) and nullable [presence](#DOCS_TOPICS_GATEWAY_EVENTS/presence) objects for each added thread member. - -#### Channel Pins Update - -Sent when a message is pinned or unpinned in a text channel. This is not sent when a pinned message is deleted. - -###### Channel Pins Update Event Fields - -| Field | Type | Description | -| ------------------- | ------------------ | ------------------------------------------------------- | -| guild_id? | snowflake | ID of the guild | -| channel_id | snowflake | ID of the channel | -| last_pin_timestamp? | ?ISO8601 timestamp | Time at which the most recent pinned message was pinned | - -### Guilds - -#### Guild Create - -This event can be sent in three different scenarios: - -1. When a user is initially connecting, to lazily load and backfill information for all unavailable guilds sent in the [Ready](#DOCS_TOPICS_GATEWAY_EVENTS/ready) event. Guilds that are unavailable due to an outage will send a [Guild Delete](#DOCS_TOPICS_GATEWAY_EVENTS/guild-delete) event. -2. When a Guild becomes available again to the client. -3. When the current user joins a new Guild. - -> note -> During an outage, the guild object in scenarios 1 and 3 may be marked as unavailable. - -The inner payload can be: -- An available Guild: a [guild](#DOCS_RESOURCES_GUILD/guild-object) object with extra fields, as noted below. -- An unavailable Guild: an [unavailable guild](#DOCS_RESOURCES_GUILD/unavailable-guild-object) object. - -###### Guild Create Extra Fields - -| Field | Type | Description | -| ---------------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -| joined_at | ISO8601 timestamp | When this guild was joined at | -| large | boolean | `true` if this is considered a large guild | -| unavailable? | boolean | `true` if this guild is unavailable due to an outage | -| member_count | integer | Total number of members in this guild | -| voice_states | array of partial [voice state](#DOCS_RESOURCES_VOICE/voice-state-object) objects | States of members currently in voice channels; lacks the `guild_id` key | -| members | array of [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) objects | Users in the guild | -| channels | array of [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | Channels in the guild | -| threads | array of [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects | All active threads in the guild that current user has permission to view | -| presences | array of partial [presence update](#DOCS_TOPICS_GATEWAY_EVENTS/presence-update) objects | Presences of the members in the guild, will only include non-offline members if the size is greater than `large threshold` | -| stage_instances | array of [stage instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object) objects | Stage instances in the guild | -| guild_scheduled_events | array of [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) objects | Scheduled events in the guild | - -> warn -> If your bot does not have the `GUILD_PRESENCES` [Gateway Intent](#DOCS_TOPICS_GATEWAY/gateway-intents), or if the guild has over 75k members, members and presences returned in this event will only contain your bot and users in voice channels. - -#### Guild Update - -Sent when a guild is updated. The inner payload is a [guild](#DOCS_RESOURCES_GUILD/guild-object) object. - -#### Guild Delete - -Sent when a guild becomes or was already unavailable due to an outage, or when the user leaves or is removed from a guild. The inner payload is an [unavailable guild](#DOCS_RESOURCES_GUILD/unavailable-guild-object) object. If the `unavailable` field is not set, the user was removed from the guild. - -#### Guild Ban Add - -Sent when a user is banned from a guild. - -###### Guild Ban Add Event Fields - -| Field | Type | Description | -| -------- | ------------------------------------------------- | ------------------- | -| guild_id | snowflake | ID of the guild | -| user | a [user](#DOCS_RESOURCES_USER/user-object) object | User who was banned | - -#### Guild Ban Remove - -Sent when a user is unbanned from a guild. - -###### Guild Ban Remove Event Fields - -| Field | Type | Description | -| -------- | ------------------------------------------------- | --------------------- | -| guild_id | snowflake | ID of the guild | -| user | a [user](#DOCS_RESOURCES_USER/user-object) object | User who was unbanned | - -#### Guild Emojis Update - -Sent when a guild's emojis have been updated. - -###### Guild Emojis Update Event Fields - -| Field | Type | Description | -| -------- | --------- | ----------------------------------------------------- | -| guild_id | snowflake | ID of the guild | -| emojis | array | Array of [emojis](#DOCS_RESOURCES_EMOJI/emoji-object) | - -#### Guild Stickers Update - -Sent when a guild's stickers have been updated. - -###### Guild Stickers Update Event Fields - -| Field | Type | Description | -| -------- | --------- | ----------------------------------------------------------- | -| guild_id | snowflake | ID of the guild | -| stickers | array | Array of [stickers](#DOCS_RESOURCES_STICKER/sticker-object) | - -#### Guild Integrations Update - -Sent when a guild integration is updated. - -###### Guild Integrations Update Event Fields - -| Field | Type | Description | -| -------- | --------- | ----------------------------------------------- | -| guild_id | snowflake | ID of the guild whose integrations were updated | - -#### Guild Member Add - -> warn -> If using [Gateway Intents](#DOCS_TOPICS_GATEWAY/gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event. - -Sent when a new user joins a guild. The inner payload is a [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) object with an extra `guild_id` key: - -###### Guild Member Add Extra Fields - -| Field | Type | Description | -| -------- | --------- | --------------- | -| guild_id | snowflake | ID of the guild | - -#### Guild Member Remove - -> warn -> If using [Gateway Intents](#DOCS_TOPICS_GATEWAY/gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event. - -Sent when a user is removed from a guild (leave/kick/ban). - -###### Guild Member Remove Event Fields - -| Field | Type | Description | -| -------- | ------------------------------------------------- | -------------------- | -| guild_id | snowflake | ID of the guild | -| user | a [user](#DOCS_RESOURCES_USER/user-object) object | User who was removed | - -#### Guild Member Update - -> warn -> If using [Gateway Intents](#DOCS_TOPICS_GATEWAY/gateway-intents), the `GUILD_MEMBERS` intent will be required to receive this event. - -Sent when a guild member is updated. This will also fire when the user object of a guild member changes. - -###### Guild Member Update Event Fields - -| Field | Type | Description | -| ----------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| guild_id | snowflake | ID of the guild | -| roles | array of snowflakes | User role ids | -| user | a [user](#DOCS_RESOURCES_USER/user-object) object | User | -| nick? | ?string | Nickname of the user in the guild | -| avatar | ?string | Member's [guild avatar hash](#DOCS_REFERENCE/image-formatting) | -| joined_at | ?ISO8601 timestamp | When the user joined the guild | -| premium_since? | ?ISO8601 timestamp | When the user starting [boosting](https://support.discord.com/hc/en-us/articles/360028038352-Server-Boosting-) the guild | -| deaf? | boolean | Whether the user is deafened in voice channels | -| mute? | boolean | Whether the user is muted in voice channels | -| pending? | boolean | Whether the user has not yet passed the guild's [Membership Screening](#DOCS_RESOURCES_GUILD/membership-screening-object) requirements | -| communication_disabled_until? | ?ISO8601 timestamp | When the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out | - -#### Guild Members Chunk - -Sent in response to [Guild Request Members](#DOCS_TOPICS_GATEWAY_EVENTS/request-guild-members). -You can use the `chunk_index` and `chunk_count` to calculate how many chunks are left for your request. - -###### Guild Members Chunk Event Fields - -| Field | Type | Description | -| ----------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| guild_id | snowflake | ID of the guild | -| members | array of [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) objects | Set of guild members | -| chunk_index | integer | Chunk index in the expected chunks for this response (0 <= chunk\_index < chunk\_count) | -| chunk_count | integer | Total number of expected chunks for this response | -| not_found? | array | When passing an invalid ID to `REQUEST_GUILD_MEMBERS`, it will be returned here | -| presences? | array of [presence](#DOCS_TOPICS_GATEWAY_EVENTS/presence) objects | When passing `true` to `REQUEST_GUILD_MEMBERS`, presences of the returned members will be here | -| nonce? | string | Nonce used in the [Guild Members Request](#DOCS_TOPICS_GATEWAY_EVENTS/request-guild-members) | - -#### Guild Role Create - -Sent when a guild role is created. - -###### Guild Role Create Event Fields - -| Field | Type | Description | -| -------- | ----------------------------------------------------- | --------------------- | -| guild_id | snowflake | ID of the guild | -| role | a [role](#DOCS_TOPICS_PERMISSIONS/role-object) object | Role that was created | - -#### Guild Role Update - -Sent when a guild role is updated. - -###### Guild Role Update Event Fields - -| Field | Type | Description | -| -------- | ----------------------------------------------------- | --------------------- | -| guild_id | snowflake | ID of the guild | -| role | a [role](#DOCS_TOPICS_PERMISSIONS/role-object) object | Role that was updated | - -#### Guild Role Delete - -Sent when a guild role is deleted. - -###### Guild Role Delete Event Fields - -| Field | Type | Description | -| -------- | --------- | --------------- | -| guild_id | snowflake | ID of the guild | -| role_id | snowflake | ID of the role | - -### Guild Scheduled Event Create - -Sent when a guild scheduled event is created. The inner payload is a [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) object. - -### Guild Scheduled Event Update - -Sent when a guild scheduled event is updated. The inner payload is a [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) object. - -### Guild Scheduled Event Delete - -Sent when a guild scheduled event is deleted. The inner payload is a [guild scheduled event](#DOCS_RESOURCES_GUILD_SCHEDULED_EVENT/guild-scheduled-event-object) object. - -### Guild Scheduled Event User Add - -Sent when a user has subscribed to a guild scheduled event. - -###### Guild Scheduled Event User Add Event Fields - -| Field | Type | Description | -| ------------------------ | --------- | ------------------------------- | -| guild_scheduled_event_id | snowflake | ID of the guild scheduled event | -| user_id | snowflake | ID of the user | -| guild_id | snowflake | ID of the guild | - -### Guild Scheduled Event User Remove - -Sent when a user has unsubscribed from a guild scheduled event. - -###### Guild Scheduled Event User Remove Event Fields - -| Field | Type | Description | -| ------------------------ | --------- | ------------------------------- | -| guild_scheduled_event_id | snowflake | ID of the guild scheduled event | -| user_id | snowflake | ID of the user | -| guild_id | snowflake | ID of the guild | - -### Integrations - -#### Integration Create - -Sent when an integration is created. The inner payload is an [integration](#DOCS_RESOURCES_GUILD/integration-object) object with an additional `guild_id` key: - -###### Integration Create Event Additional Fields - -| Field | Type | Description | -| -------- | --------- | --------------- | -| guild_id | snowflake | ID of the guild | - -#### Integration Update - -Sent when an integration is updated. The inner payload is an [integration](#DOCS_RESOURCES_GUILD/integration-object) object with an additional `guild_id` key: - -###### Integration Update Event Additional Fields - -| Field | Type | Description | -| -------- | --------- | --------------- | -| guild_id | snowflake | ID of the guild | - -#### Integration Delete - -Sent when an integration is deleted. - -###### Integration Delete Event Fields - -| Field | Type | Description | -| --------------- | --------- | ------------------------------------------------------------- | -| id | snowflake | Integration ID | -| guild_id | snowflake | ID of the guild | -| application_id? | snowflake | ID of the bot/OAuth2 application for this discord integration | - -### Invites - -#### Invite Create - -Sent when a new invite to a channel is created. - -###### Invite Create Event Fields - -| Field | Type | Description | -| ------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| channel_id | snowflake | Channel the invite is for | -| code | string | Unique invite [code](#DOCS_RESOURCES_INVITE/invite-object) | -| created_at | ISO8601 timestamp | Time at which the invite was created | -| guild_id? | snowflake | Guild of the invite | -| inviter? | [user](#DOCS_RESOURCES_USER/user-object) object | User that created the invite | -| max_age | integer | How long the invite is valid for (in seconds) | -| max_uses | integer | Maximum number of times the invite can be used | -| target_type? | integer | [Type of target](#DOCS_RESOURCES_INVITE/invite-object-invite-target-types) for this voice channel invite | -| target_user? | [user](#DOCS_RESOURCES_USER/user-object) object | User whose stream to display for this voice channel stream invite | -| target_application? | partial [application](#DOCS_RESOURCES_APPLICATION/application-object) object | Embedded application to open for this voice channel embedded application invite | -| temporary | boolean | Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) | -| uses | integer | How many times the invite has been used (always will be 0) | - -#### Invite Delete - -Sent when an invite is deleted. - -###### Invite Delete Event Fields - -| Field | Type | Description | -| ---------- | --------- | ---------------------------------------------------------- | -| channel_id | snowflake | Channel of the invite | -| guild_id? | snowflake | Guild of the invite | -| code | string | Unique invite [code](#DOCS_RESOURCES_INVITE/invite-object) | - -### Messages - -> warn -> Unlike persistent messages, ephemeral messages are sent directly to the user and the bot who sent the message rather than through the guild channel. Because of this, ephemeral messages are tied to the [`DIRECT_MESSAGES` intent](#DOCS_TOPICS_GATEWAY/list-of-intents), and the message object won't include `guild_id` or `member`. - -#### Message Create - -Sent when a message is created. The inner payload is a [message](#DOCS_RESOURCES_CHANNEL/message-object) object with the following extra fields: - -###### Message Create Extra Fields - -| Field | Type | Description | -| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| guild_id? | snowflake | ID of the guild the message was sent in - unless it is an ephemeral message | -| member? | partial [guild member](#DOCS_RESOURCES_GUILD/guild-member-object) object | Member properties for this message's author. Missing for ephemeral messages and messages from webhooks | -| mentions | array of [user](#DOCS_RESOURCES_USER/user-object) objects, with an additional partial [member](#DOCS_RESOURCES_GUILD/guild-member-object) field | Users specifically mentioned in the message | - -#### Message Update - -Sent when a message is updated. The inner payload is a [message](#DOCS_RESOURCES_CHANNEL/message-object) object with the same extra fields as [MESSAGE_CREATE](#DOCS_TOPICS_GATEWAY_EVENTS/message-create). - -> warn -> Unlike creates, message updates may contain only a subset of the full message object payload (but will always contain an ID and channel_id). - -#### Message Delete - -Sent when a message is deleted. - -###### Message Delete Event Fields - -| Field | Type | Description | -| ---------- | --------- | ----------------- | -| id | snowflake | ID of the message | -| channel_id | snowflake | ID of the channel | -| guild_id? | snowflake | ID of the guild | - -#### Message Delete Bulk - -Sent when multiple messages are deleted at once. - -###### Message Delete Bulk Event Fields - -| Field | Type | Description | -| ---------- | ------------------- | ------------------- | -| ids | array of snowflakes | IDs of the messages | -| channel_id | snowflake | ID of the channel | -| guild_id? | snowflake | ID of the guild | - -#### Message Reaction Add - -Sent when a user adds a reaction to a message. - -###### Message Reaction Add Event Fields - -| Field | Type | Description | -| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | -| user_id | snowflake | ID of the user | -| channel_id | snowflake | ID of the channel | -| message_id | snowflake | ID of the message | -| guild_id? | snowflake | ID of the guild | -| member? | [member](#DOCS_RESOURCES_GUILD/guild-member-object) object | Member who reacted if this happened in a guild | -| emoji | a partial [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) object | Emoji used to react - [example](#DOCS_RESOURCES_EMOJI/emoji-object-standard-emoji-example) | - -#### Message Reaction Remove - -Sent when a user removes a reaction from a message. - -###### Message Reaction Remove Event Fields - -| Field | Type | Description | -| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | -| user_id | snowflake | ID of the user | -| channel_id | snowflake | ID of the channel | -| message_id | snowflake | ID of the message | -| guild_id? | snowflake | ID of the guild | -| emoji | a partial [emoji](#DOCS_RESOURCES_EMOJI/emoji-object) object | Emoji used to react - [example](#DOCS_RESOURCES_EMOJI/emoji-object-standard-emoji-example) | - -#### Message Reaction Remove All - -Sent when a user explicitly removes all reactions from a message. - -###### Message Reaction Remove All Event Fields - -| Field | Type | Description | -| ---------- | --------- | ----------------- | -| channel_id | snowflake | ID of the channel | -| message_id | snowflake | ID of the message | -| guild_id? | snowflake | ID of the guild | - -#### Message Reaction Remove Emoji - -Sent when a bot removes all instances of a given emoji from the reactions of a message. - -###### Message Reaction Remove Emoji Event Fields - -| Field | Type | Description | -| ---------- | ---------------------------------------------------------- | ---------------------- | -| channel_id | snowflake | ID of the channel | -| guild_id? | snowflake | ID of the guild | -| message_id | snowflake | ID of the message | -| emoji | [partial emoji object](#DOCS_RESOURCES_EMOJI/emoji-object) | Emoji that was removed | - -### Presence - -#### Presence Update - -> warn -> If you are using [Gateway Intents](#DOCS_TOPICS_GATEWAY/gateway-intents), you _must_ specify the `GUILD_PRESENCES` intent in order to receive Presence Update events - -A user's presence is their current state on a guild. This event is sent when a user's presence or info, such as name or avatar, is updated. - -> warn -> The user object within this event can be partial, the only field which must be sent is the `id` field, everything else is optional. Along with this limitation, no fields are required, and the types of the fields are **not** validated. Your client should expect any combination of fields and types within this event. - -###### Presence Update Event Fields - -| Field | Type | Description | -| ------------- | ------------------------------------------------------------------------ | -------------------------------------------- | -| user | [user](#DOCS_RESOURCES_USER/user-object) object | User whose presence is being updated | -| guild_id | snowflake | ID of the guild | -| status | string | Either "idle", "dnd", "online", or "offline" | -| activities | array of [activity](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object) objects | User's current activities | -| client_status | [client_status](#DOCS_TOPICS_GATEWAY_EVENTS/client-status-object) object | User's platform-dependent status | - -#### Client Status Object - -Active sessions are indicated with an "online", "idle", or "dnd" string per platform. If a user is offline or invisible, the corresponding field is not present. - -| Field | Type | Description | -| -------- | ------ | --------------------------------------------------------------------------------- | -| desktop? | string | User's status set for an active desktop (Windows, Linux, Mac) application session | -| mobile? | string | User's status set for an active mobile (iOS, Android) application session | -| web? | string | User's status set for an active web (browser, bot account) application session | - -#### Activity Object - -###### Activity Structure - -| Field | Type | Description | -| --------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | -| name | string | Activity's name | -| type | integer | [Activity type](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-types) | -| url? | ?string | Stream URL, is validated when type is 1 | -| created_at | integer | Unix timestamp (in milliseconds) of when the activity was added to the user's session | -| timestamps? | [timestamps](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-timestamps) object | Unix timestamps for start and/or end of the game | -| application_id? | snowflake | Application ID for the game | -| details? | ?string | What the player is currently doing | -| state? | ?string | User's current party status | -| emoji? | ?[emoji](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-emoji) object | Emoji used for a custom status | -| party? | [party](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-party) object | Information for the current party of the player | -| assets? | [assets](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-assets) object | Images for the presence and their hover texts | -| secrets? | [secrets](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-secrets) object | Secrets for Rich Presence joining and spectating | -| instance? | boolean | Whether or not the activity is an instanced game session | -| flags? | integer | [Activity flags](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-flags) `OR`d together, describes what the payload includes | -| buttons? | array of [buttons](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-buttons) | Custom buttons shown in the Rich Presence (max 2) | - -> info -> Bots are only able to send `name`, `type`, and optionally `url`. - -###### Activity Types - -| ID | Name | Format | Example | -| --- | --------- | ------------------- | ------------------------------------ | -| 0 | Game | Playing {name} | "Playing Rocket League" | -| 1 | Streaming | Streaming {details} | "Streaming Rocket League" | -| 2 | Listening | Listening to {name} | "Listening to Spotify" | -| 3 | Watching | Watching {name} | "Watching YouTube Together" | -| 4 | Custom | {emoji} {name} | ":smiley: I am cool" | -| 5 | Competing | Competing in {name} | "Competing in Arena World Champions" | - -> info -> The streaming type currently only supports Twitch and YouTube. Only `https://twitch.tv/` and `https://youtube.com/` urls will work. - -###### Activity Timestamps - -| Field | Type | Description | -| ------ | ------- | -------------------------------------------------------- | -| start? | integer | Unix time (in milliseconds) of when the activity started | -| end? | integer | Unix time (in milliseconds) of when the activity ends | - -###### Activity Emoji - -| Field | Type | Description | -| --------- | --------- | ----------------------------- | -| name | string | Name of the emoji | -| id? | snowflake | ID of the emoji | -| animated? | boolean | Whether the emoji is animated | - -###### Activity Party - -| Field | Type | Description | -| ----- | ---------------------------------------------- | ------------------------------------------------- | -| id? | string | ID of the party | -| size? | array of two integers (current_size, max_size) | Used to show the party's current and maximum size | - -###### Activity Assets - -| Field | Type | Description | -| ------------ | ------ | -------------------------------------------------------------------------------------------- | -| large_image? | string | See [Activity Asset Image](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-asset-image) | -| large_text? | string | Text displayed when hovering over the large image of the activity | -| small_image? | string | See [Activity Asset Image](#DOCS_TOPICS_GATEWAY_EVENTS/activity-object-activity-asset-image) | -| small_text? | string | Text displayed when hovering over the small image of the activity | - -###### Activity Asset Image - -Activity asset images are arbitrary strings which usually contain snowflake IDs or prefixed image IDs. Treat data within this field carefully, as it is user-specifiable and not sanitized. - -To use an external image via media proxy, specify the URL as the field's value when sending. You will only receive the `mp:` prefix via the gateway. - -| Type | Format | Image URL | -| ----------------- | ------------------------ | -------------------------------------------------------------------------- | -| Application Asset | `{application_asset_id}` | See [Application Asset Image Formatting](#DOCS_REFERENCE/image-formatting) | -| Media Proxy Image | `mp:{image_id}` | `https://media.discordapp.net/{image_id}` | - -###### Activity Secrets - -| Field | Type | Description | -| --------- | ------ | ------------------------------------- | -| join? | string | Secret for joining a party | -| spectate? | string | Secret for spectating a game | -| match? | string | Secret for a specific instanced match | - -###### Activity Flags - -| Name | Value | -| --------------------------- | ------ | -| INSTANCE | 1 << 0 | -| JOIN | 1 << 1 | -| SPECTATE | 1 << 2 | -| JOIN_REQUEST | 1 << 3 | -| SYNC | 1 << 4 | -| PLAY | 1 << 5 | -| PARTY_PRIVACY_FRIENDS | 1 << 6 | -| PARTY_PRIVACY_VOICE_CHANNEL | 1 << 7 | -| EMBEDDED | 1 << 8 | - -###### Activity Buttons - -When received over the gateway, the `buttons` field is an array of strings, which are the button labels. Bots cannot access a user's activity button URLs. When sending, the `buttons` field must be an array of the below object: - -| Field | Type | Description | -| ----- | ------ | ------------------------------------------------------ | -| label | string | Text shown on the button (1-32 characters) | -| url | string | URL opened when clicking the button (1-512 characters) | - -###### Example Activity - -```json -{ - "details": "24H RL Stream for Charity", - "state": "Rocket League", - "name": "Twitch", - "type": 1, - "url": "https://www.twitch.tv/discord" -} -``` - -###### Example Activity with Rich Presence - -```json -{ - "name": "Rocket League", - "type": 0, - "application_id": "379286085710381999", - "state": "In a Match", - "details": "Ranked Duos: 2-1", - "timestamps": { - "start": 15112000660000 - }, - "party": { - "id": "9dd6594e-81b3-49f6-a6b5-a679e6a060d3", - "size": [2, 2] - }, - "assets": { - "large_image": "351371005538729000", - "large_text": "DFH Stadium", - "small_image": "351371005538729111", - "small_text": "Silver III" - }, - "secrets": { - "join": "025ed05c71f639de8bfaa0d679d7c94b2fdce12f", - "spectate": "e7eb30d2ee025ed05c71ea495f770b76454ee4e0", - "match": "4b2fdce12f639de8bfa7e3591b71a0d679d7c93f" - } -} -``` - -> warn -> Clients may only update their game status 5 times per 20 seconds. - -#### Typing Start - -Sent when a user starts typing in a channel. - -###### Typing Start Event Fields - -| Field | Type | Description | -| ---------- | ---------------------------------------------------------- | ------------------------------------------------------ | -| channel_id | snowflake | ID of the channel | -| guild_id? | snowflake | ID of the guild | -| user_id | snowflake | ID of the user | -| timestamp | integer | Unix time (in seconds) of when the user started typing | -| member? | [member](#DOCS_RESOURCES_GUILD/guild-member-object) object | Member who started typing if this happened in a guild | - -#### User Update - -Sent when properties about the user change. Inner payload is a [user](#DOCS_RESOURCES_USER/user-object) object. - -### Voice - -#### Voice State Update - -Sent when someone joins/leaves/moves voice channels. Inner payload is a [voice state](#DOCS_RESOURCES_VOICE/voice-state-object) object. - -#### Voice Server Update - -Sent when a guild's voice server is updated. This is sent when initially connecting to voice, and when the current voice instance fails over to a new server. - -> warn -> A null endpoint means that the voice server allocated has gone away and is trying to be reallocated. You should attempt to disconnect from the currently connected voice server, and not attempt to reconnect until a new voice server is allocated. - -###### Voice Server Update Event Fields - -| Field | Type | Description | -| -------- | --------- | ------------------------------------- | -| token | string | Voice connection token | -| guild_id | snowflake | Guild this voice server update is for | -| endpoint | ?string | Voice server host | - -###### Example Voice Server Update Payload - -```json -{ - "token": "my_token", - "guild_id": "41771983423143937", - "endpoint": "smart.loyal.discord.gg" -} -``` - -### Webhooks - -#### Webhooks Update - -Sent when a guild channel's webhook is created, updated, or deleted. - -###### Webhooks Update Event Fields - -| Field | Type | Description | -| ---------- | --------- | ----------------- | -| guild_id | snowflake | ID of the guild | -| channel_id | snowflake | ID of the channel | - -### Interactions - -#### Interaction Create - -Sent when a user uses an [Application Command](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/) or [Message Component](#DOCS_INTERACTIONS_MESSAGE_COMPONENTS). Inner payload is an [Interaction](#DOCS_INTERACTIONS_RECEIVING_AND_RESPONDING/interaction-object-interaction-structure). - -### Stage Instances - -#### Stage Instance Create - -Sent when a [Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE) is created (i.e. the Stage is now "live"). Inner payload is a [Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object) - -#### Stage Instance Update - -Sent when a [Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE) has been updated. Inner payload is a [Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object) - -#### Stage Instance Delete - -Sent when a [Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE) has been deleted (i.e. the Stage has been closed). Inner payload is a [Stage instance](#DOCS_RESOURCES_STAGE_INSTANCE/stage-instance-object) - - diff --git a/docs/topics/Permissions.md b/docs/topics/Permissions.md deleted file mode 100644 index ddc6ef3729..0000000000 --- a/docs/topics/Permissions.md +++ /dev/null @@ -1,233 +0,0 @@ -# Permissions - -Permissions are a way to limit and grant certain abilities to users in Discord. A set of base permissions can be configured at the guild level for different roles. When these roles are attached to users, they grant or revoke specific privileges within the guild. Along with the guild-level permissions, Discord also supports permission overwrites that can be assigned to individual roles or members on a per-channel basis. - -> info -> [Application command permissions](#DOCS_INTERACTIONS_APPLICATION_COMMANDS/permissions) allow you to enable or disable specific commands for entire channels in addition to individual roles or users. - -Permissions are stored in a variable-length integer serialized into a string, and are calculated using bitwise operations. For example, the permission value `123` will be serialized as `"123"`. For long-term stability, it's recommended to deserialize the permissions using your preferred languages' Big Integer libraries. The total permissions integer can be determined by OR-ing (`|`) together each individual value, and flags can be checked using AND (`&`) operations. - -In API v8 and above, all permissions are serialized as strings, including the `allow` and `deny` fields in overwrites. Any new permissions are rolled back into the base field. - -> info -> In [API v6 (now deprecated)](#DOCS_REFERENCE), the `permissions`, `allow`, and `deny` fields in roles and overwrites are still serialized as a number; however, these numbers shall not grow beyond 31 bits. During the remaining lifetime of API v6, all new permission bits will only be introduced in `permissions_new`, `allow_new`, and `deny_new`. These `_new` fields are just for response serialization; requests with these fields should continue to use the original `permissions`, `allow`, and `deny` fields, which accept both string or number values. - -```python -# Permissions value that can Send Messages (0x800) and Add Reactions (0x40): -permissions = 0x40 | 0x800 # 2112 - -# Checking for flags that are set: -(permissions & 0x40) == 0x40 # True -(permissions & 0x800) == 0x800 # True - -# Kick Members (0x2) was not set: -(permissions & 0x2) == 0x2 # False -``` - -Additional logic is required when permission overwrites are involved; this is further explained below. For more information about bitwise operations and flags, see [this page](https://en.wikipedia.org/wiki/Bit_field). - -Below is a table of all current permissions, their integer values in hexadecimal, brief descriptions of the privileges that they grant, and the channel type they apply to, if applicable. - -###### Bitwise Permission Flags - -| Permission | Value | Description | Channel Type | -| ----------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | -| CREATE_INSTANT_INVITE | `0x0000000000000001` `(1 << 0)` | Allows creation of instant invites | T, V, S | -| KICK_MEMBERS \* | `0x0000000000000002` `(1 << 1)` | Allows kicking members | | -| BAN_MEMBERS \* | `0x0000000000000004` `(1 << 2)` | Allows banning members | | -| ADMINISTRATOR \* | `0x0000000000000008` `(1 << 3)` | Allows all permissions and bypasses channel permission overwrites | | -| MANAGE_CHANNELS \* | `0x0000000000000010` `(1 << 4)` | Allows management and editing of channels | T, V, S | -| MANAGE_GUILD \* | `0x0000000000000020` `(1 << 5)` | Allows management and editing of the guild | | -| ADD_REACTIONS | `0x0000000000000040` `(1 << 6)` | Allows for the addition of reactions to messages | T, V | -| VIEW_AUDIT_LOG | `0x0000000000000080` `(1 << 7)` | Allows for viewing of audit logs | | -| PRIORITY_SPEAKER | `0x0000000000000100` `(1 << 8)` | Allows for using priority speaker in a voice channel | V | -| STREAM | `0x0000000000000200` `(1 << 9)` | Allows the user to go live | V | -| VIEW_CHANNEL | `0x0000000000000400` `(1 << 10)` | Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels | T, V, S | -| SEND_MESSAGES | `0x0000000000000800` `(1 << 11)` | Allows for sending messages in a channel and creating threads in a forum (does not allow sending messages in threads) | T, V | -| SEND_TTS_MESSAGES | `0x0000000000001000` `(1 << 12)` | Allows for sending of `/tts` messages | T, V | -| MANAGE_MESSAGES \* | `0x0000000000002000` `(1 << 13)` | Allows for deletion of other users messages | T, V | -| EMBED_LINKS | `0x0000000000004000` `(1 << 14)` | Links sent by users with this permission will be auto-embedded | T, V | -| ATTACH_FILES | `0x0000000000008000` `(1 << 15)` | Allows for uploading images and files | T, V | -| READ_MESSAGE_HISTORY | `0x0000000000010000` `(1 << 16)` | Allows for reading of message history | T, V | -| MENTION_EVERYONE | `0x0000000000020000` `(1 << 17)` | Allows for using the `@everyone` tag to notify all users in a channel, and the `@here` tag to notify all online users in a channel | T, V, S | -| USE_EXTERNAL_EMOJIS | `0x0000000000040000` `(1 << 18)` | Allows the usage of custom emojis from other servers | T, V | -| VIEW_GUILD_INSIGHTS | `0x0000000000080000` `(1 << 19)` | Allows for viewing guild insights | | -| CONNECT | `0x0000000000100000` `(1 << 20)` | Allows for joining of a voice channel | V, S | -| SPEAK | `0x0000000000200000` `(1 << 21)` | Allows for speaking in a voice channel | V | -| MUTE_MEMBERS | `0x0000000000400000` `(1 << 22)` | Allows for muting members in a voice channel | V, S | -| DEAFEN_MEMBERS | `0x0000000000800000` `(1 << 23)` | Allows for deafening of members in a voice channel | V, S | -| MOVE_MEMBERS | `0x0000000001000000` `(1 << 24)` | Allows for moving of members between voice channels | V, S | -| USE_VAD | `0x0000000002000000` `(1 << 25)` | Allows for using voice-activity-detection in a voice channel | V | -| CHANGE_NICKNAME | `0x0000000004000000` `(1 << 26)` | Allows for modification of own nickname | | -| MANAGE_NICKNAMES | `0x0000000008000000` `(1 << 27)` | Allows for modification of other users nicknames | | -| MANAGE_ROLES \* | `0x0000000010000000` `(1 << 28)` | Allows management and editing of roles | T, V, S | -| MANAGE_WEBHOOKS \* | `0x0000000020000000` `(1 << 29)` | Allows management and editing of webhooks | T, V | -| MANAGE_EMOJIS_AND_STICKERS \* | `0x0000000040000000` `(1 << 30)` | Allows management and editing of emojis and stickers | | -| USE_APPLICATION_COMMANDS | `0x0000000080000000` `(1 << 31)` | Allows members to use application commands, including slash commands and context menu commands. | T, V | -| REQUEST_TO_SPEAK | `0x0000000100000000` `(1 << 32)` | Allows for requesting to speak in stage channels. (_This permission is under active development and may be changed or removed._) | S | -| MANAGE_EVENTS | `0x0000000200000000` `(1 << 33)` | Allows for creating, editing, and deleting scheduled events | V, S | -| MANAGE_THREADS \* | `0x0000000400000000` `(1 << 34)` | Allows for deleting and archiving threads, and viewing all private threads | T | -| CREATE_PUBLIC_THREADS | `0x0000000800000000` `(1 << 35)` | Allows for creating public and announcement threads | T | -| CREATE_PRIVATE_THREADS | `0x0000001000000000` `(1 << 36)` | Allows for creating private threads | T | -| USE_EXTERNAL_STICKERS | `0x0000002000000000` `(1 << 37)` | Allows the usage of custom stickers from other servers | T, V | -| SEND_MESSAGES_IN_THREADS | `0x0000004000000000` `(1 << 38)` | Allows for sending messages in threads | T | -| USE_EMBEDDED_ACTIVITIES | `0x0000008000000000` `(1 << 39)` | Allows for using Activities (applications with the `EMBEDDED` flag) in a voice channel | V | -| MODERATE_MEMBERS \*\* | `0x0000010000000000` `(1 << 40)` | Allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels | | - -**\* These permissions require the owner account to use [two-factor authentication](#DOCS_TOPICS_OAUTH2/twofactor-authentication-requirement) when used on a guild that has server-wide 2FA enabled.** - -**\*\* See [Permissions for Timed Out Members](#DOCS_TOPICS_PERMISSIONS/permissions-for-timed-out-members) to understand how permissions are temporarily modified for timed out users.** - -Note that permission names may be referred to differently in the Discord client. For example, "Manage Permissions" refers to MANAGE_ROLES, "Use Voice Activity" refers to USE_VAD, and "Timeout Members" refers to MODERATE_MEMBERS. - -## Permission Hierarchy - -How permissions apply may at first seem intuitive, but there are some hidden restrictions that prevent bots from performing certain inappropriate actions based on a bot's highest role compared to its target's highest role. A bot's or user's highest role is its role that has the greatest position value in the guild, with the default @everyone role starting at 0. Permissions follow a hierarchy with the following rules: - -- A bot can grant roles to other users that are of a lower position than its own highest role. -- A bot can edit roles of a lower position than its highest role, but it can only grant permissions it has to those roles. -- A bot can only sort roles lower than its highest role. -- A bot can only kick, ban, and edit nicknames for users whose highest role is lower than the bot's highest role. - -Otherwise, permissions do not obey the role hierarchy. For example, a user has two roles: A and B. A denies the `VIEW_CHANNEL` permission on a #coolstuff channel. B allows the `VIEW_CHANNEL` permission on the same #coolstuff channel. The user would ultimately be able to view the #coolstuff channel, regardless of the role positions. - -## Permission Overwrites - -Overwrites can be used to apply certain permissions to roles or members on a channel-level. Applicable permissions are indicated by a **T** for text channels, **V** for voice channels, or **S** for stage channels in the table above. - -When using overwrites, there are cases where permission collisions could occur for a user; that is to say, the user may have certain overwrites with permissions that contradict each other or their guild-level role permissions. With this in mind, permissions are applied to users in the following hierarchy: - -1. Base permissions given to @everyone are applied at a guild level -2. Permissions allowed to a user by their roles are applied at a guild level -3. Overwrites that deny permissions for @everyone are applied at a channel level -4. Overwrites that allow permissions for @everyone are applied at a channel level -5. Overwrites that deny permissions for specific roles are applied at a channel level -6. Overwrites that allow permissions for specific roles are applied at a channel level -7. Member-specific overwrites that deny permissions are applied at a channel level -8. Member-specific overwrites that allow permissions are applied at a channel level - -The following pseudocode demonstrates this process programmatically: - -```python -def compute_base_permissions(member, guild): - if guild.is_owner(member): - return ALL - - role_everyone = guild.get_role(guild.id) # get @everyone role - permissions = role_everyone.permissions - - for role in member.roles: - permissions |= role.permissions - - if permissions & ADMINISTRATOR == ADMINISTRATOR: - return ALL - - return permissions - -def compute_overwrites(base_permissions, member, channel): - # ADMINISTRATOR overrides any potential permission overwrites, so there is nothing to do here. - if base_permissions & ADMINISTRATOR == ADMINISTRATOR: - return ALL - - permissions = base_permissions - overwrite_everyone = overwrites.get(channel.guild_id) # Find (@everyone) role overwrite and apply it. - if overwrite_everyone: - permissions &= ~overwrite_everyone.deny - permissions |= overwrite_everyone.allow - - # Apply role specific overwrites. - overwrites = channel.permission_overwrites - allow = NONE - deny = NONE - for role_id in member.roles: - overwrite_role = overwrites.get(role_id) - if overwrite_role: - allow |= overwrite_role.allow - deny |= overwrite_role.deny - - permissions &= ~deny - permissions |= allow - - # Apply member specific overwrite if it exist. - overwrite_member = overwrites.get(member.user_id) - if overwrite_member: - permissions &= ~overwrite_member.deny - permissions |= overwrite_member.allow - - return permissions - -def compute_permissions(member, channel): - base_permissions = compute_base_permissions(member, channel.guild) - return compute_overwrites(base_permissions, member, channel) -``` - -## Implicit Permissions - -Permissions in Discord are sometimes implicitly denied or allowed based on logical use. The two main cases are `VIEW_CHANNEL` and `SEND_MESSAGES` for text channels. Denying a user or a role `VIEW_CHANNEL` on a channel implicitly denies other permissions on the channel. Though permissions like `SEND_MESSAGES` are not explicitly denied for the user, they are ignored because the user cannot read messages in the channel. - -Denying `SEND_MESSAGES` implicitly denies `MENTION_EVERYONE`, `SEND_TTS_MESSAGES`, `ATTACH_FILES`, and `EMBED_LINKS`. Again, they are not explicitly denied when doing permissions calculations, but they are ignored because the user cannot do the base action of sending messages. - -For voice and stage channels, denying the `CONNECT` permission also implicitly denies other permissions such as `MANAGE_CHANNEL`. - -There may be other cases in which certain permissions implicitly deny or allow other permissions. In all cases, it is based on logical conclusions about how a user with certain permissions should or should not interact with Discord. - -## Inherited Permissions (Threads) - -Threads inherit permissions from the parent channel (the channel they were created in), with one exception: The `SEND_MESSAGES` permission is not inherited; users must have `SEND_MESSAGES_IN_THREADS` to send a message in a thread, which allows for users to participate in threads in places like announcement channels. - -Users must have the `VIEW_CHANNEL` permission to view _any_ threads in the channel, even if they are directly mentioned or added to the thread. - -## Permission Syncing - -Permissions with regards to categories and channels within categories are a bit tricky. Rather than inheritance, permissions are calculated by means of what we call Permission Syncing. If a child channel has the same permissions and overwrites (or lack thereof) as its parent category, the channel is considered "synced" to the category. Any further changes to a **parent category** will be reflected in its synced child channels. Any further changes to a **child channel** will cause it to become de-synced from its parent category, and its permissions will no longer change with changes to its parent category. - -### Role Object - -Roles represent a set of permissions attached to a group of users. Roles have names, colors, and can be "pinned" to the side bar, causing their members to be listed separately. Roles can have separate permission profiles for the global context (guild) and channel context. The `@everyone` role has the same ID as the guild it belongs to. - -###### Role Structure - -| Field | Type | Description | -| -------------- | ---------------------------------------------------------------------------- | ------------------------------------------------- | -| id | snowflake | role id | -| name | string | role name | -| color | integer | integer representation of hexadecimal color code | -| hoist | boolean | if this role is pinned in the user listing | -| icon? | ?string | role [icon hash](#DOCS_REFERENCE/image-formatting)| -| unicode_emoji? | ?string | role unicode emoji | -| position | integer | position of this role | -| permissions | string | permission bit set | -| managed | boolean | whether this role is managed by an integration | -| mentionable | boolean | whether this role is mentionable | -| tags? | [role tags](#DOCS_TOPICS_PERMISSIONS/role-object-role-tags-structure) object | the tags this role has | - -Roles without colors (`color == 0`) do not count towards the final computed color in the user list. - -###### Role Tags Structure - -| Field | Type | Description | -| ------------------- | --------- | --------------------------------------------------- | -| bot_id? | snowflake | the id of the bot this role belongs to | -| integration_id? | snowflake | the id of the integration this role belongs to | -| premium_subscriber? | null | whether this is the guild's premium subscriber role | - -###### Example Role - -```json -{ - "id": "41771983423143936", - "name": "WE DEM BOYZZ!!!!!!", - "color": 3447003, - "hoist": true, - "icon": "cf3ced8600b777c9486c6d8d84fb4327", - "unicode_emoji": null, - "position": 1, - "permissions": "66321471", - "managed": false, - "mentionable": false -} -``` - -## Permissions For Timed Out Members - -Timed out members will temporarily lose all permissions except `VIEW_CHANNEL` and `READ_MESSAGE_HISTORY`. Owners and admin users with `ADMINISTRATOR` permissions are exempt. diff --git a/docs/topics/Teams.md b/docs/topics/Teams.md deleted file mode 100644 index 0671f36026..0000000000 --- a/docs/topics/Teams.md +++ /dev/null @@ -1,80 +0,0 @@ -# Teams - -Teams are groups of developers on Discord who want to collaborate on apps. On other platforms, these may be referred to as "organizations", "companies", or "teams". We went with the name Teams because it best encompassed all the awesome conglomerates of devs that work together to make awesome things on Discord. Also, we never got picked for kickball in gym class, so now we get to be on a team. - -## What Do They Do - -Teams allow you and other Discord users to share access to apps. No more sharing login credentials in order to reset the token on a bot that your friend owns but you work on, or other such cases. - -For game developers, this means that you can get your engineers access to your app for credentials they may need, your marketing folks access to store page management, and your finance people access to sales and performance metrics. - -> danger -> For the initial release, Teams only support one kind of user: Admin. Admins have full access to all parts of an app _except_ for deleting the app and adding/removing users. That can only be done by the owner of the Team. - -## How Do I Make One - -Making a Team is easy! Head on over to our [Team creation](https://discord.com/developers/teams) page and make your own. - -![Screenshot of the initial landing page for viewing Teams that you are a part of](team-page.png) - -Note that to use Discord Teams, you need to have 2FA enabled on your account. Security is of the utmost importance, especially when it comes to shared resources. If you're developing on your own and don't want to use Teams, you do not need 2FA. But, in order to keep other Team members safe, you'll need to add it to use Teams. - -![Screenshot of the 2FA requirement modal](team-2fa.png) - -Once your team is made, you can start inviting other Discord users to join. - -> info -> For the initial release, only the Team owner can invite or remove additional users. - -## Apps on Teams - -Now that you've got your Team set up, you can start creating apps under it. Teams can own a maximum of 25 apps. To create a new app under a Team, select the Team in the app creation modal. If you want to keep the app under your own ownership, choose `Personal`: - -![Screenshot of the Team Application creation modal](team-make-app.png) - -If you have an existing app that you want to transfer to a Team, you can do that, too! Just go into the app that you want to transfer, hit `Transfer App to Team`, and send the app to its new home. - -![Screenshot of where to find the button to transfer an Application to a Team](transfer-app-to-team.png) - -> danger -> Once an app has been transferred to a team, it _cannot_ be transferred back. - -## What Next - -What next? Go make awesome stuff! Whether you're a Game Developer, Mad Bot Scientist, or OAuth2 Enthusiast, you can now work together with other like-minded Discordians to bring your creations to life. - -We've got a lot of awesome features planned for teams in the future, so stay tuned for things like: - -- Roles and Permissions -- Audit Logs -- More cat pictures - -Go team! - -## Data Models - -###### Team Object - -| field | type | description | -| ------------- | --------------------------------------------------------------------------------- | -------------------------------------- | -| icon | ?string | a hash of the image of the team's icon | -| id | snowflake | the unique id of the team | -| members | array of [team member](#DOCS_TOPICS_TEAMS/data-models-team-member-object) objects | the members of the team | -| name | string | the name of the team | -| owner_user_id | snowflake | the user id of the current team owner | - -###### Team Member Object - -| field | type | description | -| ---------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| membership_state | integer | the user's [membership state](#DOCS_TOPICS_TEAMS/data-models-membership-state-enum) on the team | -| permissions | array of strings | will always be `["*"]` | -| team_id | snowflake | the id of the parent team of which they are a member | -| user | partial [user](#DOCS_RESOURCES_USER/user-object) object | the avatar, discriminator, id, and username of the user | - -###### Membership State Enum - -| name | value | -| -------- | ----- | -| INVITED | 1 | -| ACCEPTED | 2 | diff --git a/docs/topics/Threads.md b/docs/topics/Threads.md deleted file mode 100644 index 85b9c0f503..0000000000 --- a/docs/topics/Threads.md +++ /dev/null @@ -1,186 +0,0 @@ -# Threads - -[Threads](#DOCS_RESOURCES_CHANNEL/channel-object) can be thought of as temporary sub-channels inside an existing channel, to help better organize conversation in a busy channel. - -Threads have been designed to be very similar to [channel](#DOCS_RESOURCES_CHANNEL/channel-object) objects, and this topic aggregates all of the information about threads, which should all help to make migrating very straightforward. - -## Backwards Compatibility - -Threads are only available in API v9 and above. Bots that do not update to API v9 or above will not receive most gateway events for threads, or things that happen in threads (such as [Message Create](#DOCS_TOPICS_GATEWAY_EVENTS/message-create)). Bots on API v8 will still receive gateway events for Interactions. - -The list of gateway events that may be dropped includes, but is not limited to: - -- MESSAGE_CREATE -- MESSAGE_DELETE -- MESSAGE_DELETE_BULK -- MESSAGE_REACTION_ADD -- MESSAGE_REACTION_REMOVE -- MESSAGE_REACTION_REMOVE_ALL -- MESSAGE_REACTION_REMOVE_EMOJI -- MESSAGE_UPDATE -- THREAD_CREATE -- THREAD_UPDATE -- THREAD_DELETE -- THREAD_MEMBER_UPDATE -- THREAD_MEMBERS_UPDATE - -## New Thread Fields - -Since threads are a new [type of channel](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types), they share and re-purpose a number of the existing fields on a [channel](#DOCS_RESOURCES_CHANNEL/channel-object) object: - -- `id`, `guild_id`, `type`, `name`, `last_message_id`, `last_pin_timestamp`, `rate_limit_per_user` are being re-used -- `owner_id` has been repurposed to store the id of the user that started the thread -- `parent_id` has been repurposed to store the id of the `GUILD_TEXT` or `GUILD_ANNOUNCEMENT` channel the thread was created in - -Additionally, there are a few new fields that are only available on threads: - -- `member_count` stores an approximate member count, but it stops counting at 50 (this is only used in our UI, so it is not valuable to bots) -- `message_count` and `total_message_sent` store the number of messages in a thread. The difference is that when a message is deleted, `message_count` is decremented, but `total_message_sent` will not be. (threads created before July 1, 2022 stop counting both values at 50). -- `thread_metadata` contains a few thread specific fields, `archived`, `archive_timestamp`, `auto_archive_duration`, `locked`. `archive_timestamp` is changed when creating, archiving, or unarchiving a thread, and when changing the `auto_archive_duration` field. - -## Public & Private Threads - -Public threads are viewable by everyone who can view the parent channel of the thread. Public threads must be created from an existing message, but can be "orphaned" if that message is deleted. The created thread and the message it was started from will share the same id. The [type](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) of thread created matches the [type](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) of the parent channel. `GUILD_TEXT` channels [create](#DOCS_RESOURCES_CHANNEL/start-thread-from-message) `PUBLIC_THREAD` and `GUILD_ANNOUNCEMENT` channels [create](#DOCS_RESOURCES_CHANNEL/start-thread-from-message) `ANNOUNCEMENT_THREAD`. - -Private threads behave similar to Group DMs, but in a Guild. Private threads are always [created](#DOCS_RESOURCES_CHANNEL/start-thread-without-message) with the `GUILD_PRIVATE_THREAD` [type](#DOCS_RESOURCES_CHANNEL/channel-object-channel-types) and can only be created in `GUILD_TEXT` channels. - -## Active & Archived Threads - -Every thread can be either active or archived. Changing a thread from archived -> active is referred to as unarchiving the thread. Threads that have `locked` set to true can only be unarchived by a user with the `MANAGE_THREADS` permission. - -Besides helping to de-clutter the UI for users, archiving exists to limit the working set of threads that need to be kept around. Since the number of archived threads can be quite large, keeping all of them in memory may be quite prohibitive. Therefore guilds are capped at a certain number of active threads, and only active threads can be manipulated. Users cannot edit messages, add reactions, use application commands, or join archived threads. The only operation that should happen within an archived thread is messages being deleted. Sending a message will automatically unarchive the thread, unless the thread has been locked by a moderator. - -Because of this constraint, the gateway protocol is designed to ensure that bots are able to have an accurate view of the full set of active threads, but archived threads are not synced up-front via the gateway. - -Threads do not count against the max-channels limit in a guild, but there will be a new limit on the maximum number of active threads in a guild. - -Threads automatically archive after 7 days of inactivity (as a server approaches the max thread limit this timer will automatically lower, but never below the `auto_archive_duration`). "Activity" is defined as sending a message, unarchiving a thread, or changing the auto-archive time. The `auto_archive_duration` field previously controlled how long a thread could stay active, but is now repurposed to control how long the thread stays in the channel list. Channels can also set `default_auto_archive_duration`, which is used by our clients to pre-select a different `auto_archive_duration` value when a user creates a thread. - -## Permissions - -Threads generally inherit permissions from the parent channel (e.g. if you can add reactions in the parent channel, you can do that in a thread as well). - -Three new permission bits have been added, `CREATE_PUBLIC_THREADS`, `CREATE_PRIVATE_THREADS`, and `SEND_MESSAGES_IN_THREADS`. Note: `SEND_MESSAGES` has no effect in threads; users must have `SEND_MESSAGES_IN_THREADS` to talk in a thread. - -Private threads are similar to Group DMs, but in a guild: You must be invited to the thread to be able to view or participate in it, or be a moderator (`MANAGE_THREADS` permission). - -Finally, threads are treated slightly differently from channels in the gateway protocol. Clients will not be informed of a thread through the gateway if they do not have permission to view that thread. - -## Gateway Events - -- [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) contains a new field, `threads`, which is an array of channel objects. This represents all active threads in the guild, that the current user is able to view. -- When a thread is created, updated, or deleted, a [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create), [Thread Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-update), or [Thread Delete](#DOCS_TOPICS_GATEWAY_EVENTS/thread-delete) event is sent. Like their channel counterparts, these just contain a thread. -- Since the gateway only syncs active threads that the user can see, if a user _gains_ access to a channel, then the gateway may need to sync the active threads in that channel to the user. It will send a [Thread List Sync](#DOCS_TOPICS_GATEWAY_EVENTS/thread-list-sync) event for this. - -## Thread Membership - -Each thread tracks explicit membership. There are two primary use cases for this data: - -- Clients use _their own_ [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) to calculate read states and notification settings. This is largely irrelevant for bots though, but is the reason for some of the syncing complexity detailed here. -- Knowing everyone that is in a thread. - -Membership is tracked in an array of [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects. These have four fields, `id` (the thread id), `user_id`, `join_timestamp`, and `flags`. Currently the only `flags` are for notification settings, but others may be added in future updates. - -### Syncing for the current user - -- A [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) Gateway Event is always sent when the current user is added to or removed from a thread. -- A [Thread Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-member-update) Gateway Event is sent whenever the current user's [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object is updated. -- Certain API calls, such as listing archived threads and search will return an array of [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects for any returned threads the current user is a member of. Other API calls, such as getting a channel will return the [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object for the current user as a property on the channel, if the current user is a member of the thread. -- The [Guild Create](#DOCS_TOPICS_GATEWAY_EVENTS/guild-create) Gateway Event will contain a [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object as a property on any returned threads the current is a member of. -- The [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) Gateway Event will contain a [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) object as a property of the thread if the current user is a member of, and the user has recently gained access to view the parent channel. -- The [Thread List Sync](#DOCS_TOPICS_GATEWAY_EVENTS/thread-list-sync) Gateway Event will contain an array of [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects for any returned threads the current user is a member of. - -### Syncing for other users - -> info -> These require the `GUILD_MEMBERS` [Gateway Intent](#DOCS_TOPICS_GATEWAY/gateway-intents) - -- An API `GET` call to [`/channels//thread-members`](#DOCS_RESOURCES_CHANNEL/list-thread-members) which returns an array of [thread member](#DOCS_RESOURCES_CHANNEL/thread-member-object) objects. -- The [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) Gateway Event which will include all users who were added to or removed from a thread by an action. - -## Editing & Deleting Threads - -Threads can be edited and deleted with the existing `PATCH` and `DELETE` endpoints to edit a channel. - -- Deleting a thread requires the `MANAGE_THREADS` permission. -- Editing a thread to set `archived` to `false` only requires the current user has already been added to the thread. If `locked` is true, then the user must have `MANAGE_THREADS` -- Editing a thread to change the `name`, `archived`, `auto_archive_duration` fields requires `MANAGE_THREADS` or that the current user is the thread creator -- Editing a thread to change `rate_limit_per_user` or `locked` requires `MANAGE_THREADS` - -## NSFW Threads - -Threads do not explicitly set the `nsfw` field. All threads in a channel marked as `nsfw` inherit that setting though. - -## New Message Types - -Threads introduce a few new [message types](#DOCS_RESOURCES_CHANNEL/message-object-message-types), and re-purpose some others: - -- `RECIPIENT_ADD` and `RECIPIENT_REMOVE` have been repurposed to also send when a user is added to or removed from a thread by someone else -- `CHANNEL_NAME_CHANGE` has been repurposed and is sent when the thread's name is changed -- `THREAD_CREATED` is a new message sent to the parent `GUILD_TEXT` channel, used to inform users that a thread has been created. It is currently only sent in one case: when a `PUBLIC_THREAD` is created from an older message (older is still TBD, but is currently set to a very small value). The message contains a [message reference](#DOCS_RESOURCES_CHANNEL/message-reference-object-message-reference-structure) with the `guild_id` and `channel_id` of the thread. The `content` of the message is the `name` of the thread. -- `THREAD_STARTER_MESSAGE` is a new message sent as the first message in threads that are started from an existing message in the parent channel. It _only_ contains a [message reference](#DOCS_RESOURCES_CHANNEL/message-reference-object-message-reference-structure) field that points to the message from which the thread was started. - -## Enumerating threads - -There are four new `GET` routes for enumerating threads in a specific channel: - -- [`/guilds//threads/active`](#DOCS_RESOURCES_GUILD/list-active-guild-threads) returns all active threads in a guild that the current user can access, includes public & private threads -- [`/channels//users/@me/threads/archived/private`](#DOCS_RESOURCES_CHANNEL/list-joined-private-archived-threads) returns all archived, private threads in a channel, that the current user has is a member of, sorted by thread id descending -- [`/channels//threads/archived/public`](#DOCS_RESOURCES_CHANNEL/list-public-archived-threads) returns all archived, public threads in a channel, sorted by archive timestamp descending -- [`/channels//threads/archived/private`](#DOCS_RESOURCES_CHANNEL/list-private-archived-threads) returns all archived, private threads in a channel, sorted by archive timestamp descending - -## Webhooks - -Webhooks can send messages to threads by using the `thread_id` query parameter. See the [execute webhook](#DOCS_RESOURCES_WEBHOOK/execute-webhook) docs for more details. - -## Additional context on the the THREAD_LIST_SYNC and THREAD_CREATE dispatches - -While threads are mostly similar to channels in terms of structure and how they are synced, there are two important product requirements that lead to differences in how threads and channels are synced. This section helps explain the behavior behind the [Thread List Sync](#DOCS_TOPICS_GATEWAY_EVENTS/thread-list-sync) and [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) dispatches by going over those problems and how they are solved. - -The two product requirements are: The gateway will only sync threads to a client that the client has permission to view, and it will only sync those threads once the client has "subscribed" to the guild. For context, in Discord's official clients, a subscription happens when the user visits a channel in the guild. - -As mentioned, these lead to a couple of edge cases that are worth going into: - -### Gaining access to a private thread - -When a client is added to a private thread, they likely don't have that private thread in memory yet because of the product requirement that we only sync threads you have permission to view. Private threads are only synced to you if you are a member or a moderator. To solve this, whenever a user is added to a private thread, the gateway also sends a [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) dispatch. This ensures the client always has a non-null value for that thread. Note: This is also sent even if the user is a moderator, and thus would already have the channel in memory, mainly for simplicity purposes. - -### Gaining access to a public thread - -When a client is added to a public thread, but has not yet subscribed to threads, they might not have that public thread in memory yet. This is actually only a problem for Discord's official clients, and not for bots. The gateway will auto-subscribe bots to all thread dispatches and active threads on connect. But Discord's clients only receive threads that are active and they have also joined on connect in order to reduce the amount of data needed on initial connect. But this means when a user with the official client is added to a thread, that thread now becomes an "active-joined" thread and needs to be synced to the client. To solve this, whenever a user is added to _any_ thread, the gateway also sends a [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create) dispatch. - -### Gaining access to a channel - -When a client gains access to a channel (example: they _gain_ the moderator role, and thus now they have more channels they can view), they won't have any of the threads in memory for that channel (since the gateway only syncs threads that the client has permission to view). To solve this, we send the [Thread List Sync](#DOCS_TOPICS_GATEWAY_EVENTS/thread-list-sync) dispatch to a client when they gain access to a channel! This dispatch includes a `channel_ids` array, which is the id of all the channels whose threads are being synced. This field can be used to first clear out any active threads whose `parent_id` is in the `channel_ids` array, and then ingest any threads that were in the dispatch. - -### Losing access to a channel - -When a client loses access to a channel, the gateway does not send them a [Thread Delete](#DOCS_TOPICS_GATEWAY_EVENTS/thread-delete) event or any equivalent. They will still receive _an_ event when this happens, it just will not be a thread-specific event. Usually the event will be [Guild Role Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-role-update), [Guild Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/guild-member-update) or [Channel Update](#DOCS_TOPICS_GATEWAY_EVENTS/channel-update). It will be some event that caused the permissions on a channel to change. So _if_ a bot wanted to simulate a "lost access to thread" event, it is entirely possible, albeit quite complicated to handle all cases correctly. Under the hood, Discord's clients actually don't worry about this detail. Instead, when performing an action, the client checks permissions first (which implicitly checks if the client has access to the parent channel too, since threads inherit permissions), that way _even if_ the client has some stale data, it does not end up acting on it. - -Additionally, when a client loses access to a channel they are not removed from the thread. Users may want to temporarily shut down access to a server or channel. Removing someone from all threads when that happens would not be a good experience, so we've chosen not to go that route. Users will still be reported as members of a thread, even if they no longer have access to the parent channel. They will **not** receive new gateway events for those threads though, with one exception: If a client is removed from a thread _after_ losing access to the parent channel, they will still receive a [Thread Members Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-members-update) dispatch. - -### Unarchiving a thread - -Discord's clients only load active threads into memory on start. So when a thread is unarchived, there is no guarantee that the client has either the thread or whether they are a member, in memory. As such, the Gateway sends a [Thread Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-update) first, which contains the full channel object. And then sends a [Thread Member Update](#DOCS_TOPICS_GATEWAY_EVENTS/thread-member-update) to each member of the thread, so those clients know they are a member, and what their notification setting is. This event is not that valuable for bots right now, but is going to be received by bots, so is documented here none the less. - -# Forums - -A `GUILD_FORUM` channel is very similar (from an API perspective) to a `GUILD_TEXT` channel, except only threads can be created in that channel; messages cannot be sent directly in that channel. - -> info -> More information about forum channels and how they appear in Discord can be found in the [Forum Channels FAQ](https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ#h_01G69FJQWTWN88HFEHK7Z6X79N) - -- Forums are a new channel type `15`. -- Forums do not allow messages to be sent in them. Endpoints like /channels/channel_id/messages will not work on a forum channel. -- Threads can be created in a forum channel. All threads in a forum are of type `PUBLIC_THREAD`. These threads and messages within the thread have the same gateway events as threads in a normal text channel. -- The APIs for loading active & archived threads, joining & leaving a thread, and loading who is in a thread are unchanged and work the same for threads in a forum. -- The API to create a thread in a forum will create _both_ a thread and message in the same call, and as such requires passing in parameters for both the thread and message. The name and behavior of parameters is the same as they are for the existing create thread/message endpoints to simplify integrating with it. -- The message created by that API call will have the same id as the thread. -- The `last_message_id` field on the forum channel object tracks the id of the most recently created thread. It has the same behavior and requirements as it does for messages, namely that you will not receive a `CHANNEL_UPDATE` when it is changed. Instead clients should update the value when receiving [Thread Create](#DOCS_TOPICS_GATEWAY_EVENTS/thread-create). -- The `message_count` and `total_message_sent` fields on threads created in a forum will increment on `MESSAGE_CREATE` and will decrement on `MESSAGE_DELETE`/`MESSAGE_DELETE_BULK`. There will be no `CHANNEL_UPDATE` event through gateway notifying those fields' changes (similar to `last_message_id` changes). Clients should update those values when receiving corresponding events. -- The `topic` field on a forum channel is what is shown in the "Guidelines" section visually -- The `rate_limit_per_user` field currently behaves the same as in a text channel, limiting how frequently threads can be created. There is a new `default_thread_rate_limit_per_user` field that can be set on the forum as well, which will limit how often messages can be sent _in a thread_. This field is copied into `rate_limit_per_user` on the thread at creation time. -- Threads in a forum have the same permissions behavior as threads in a text channel, inheriting all permissions from the parent channel, with just one exception: Creating a thread in a forum channel only requires the permission that is currently named `SEND_MESSAGES`. -- The first message in a forum thread can contain additional markdown for bulleted list and headings. -- A thread can be pinned within a forum. A thread that is pinned will have the `(1 << 1)` flag set. Archiving a pinned thread will unset the flag. A pinned thread will not auto archive. -- Forums can specify `available_tags` that can be set on individual threads via the `applied_tags` field. diff --git a/docs/topics/Voice_Connections.md b/docs/topics/Voice_Connections.md deleted file mode 100644 index 46600d0d55..0000000000 --- a/docs/topics/Voice_Connections.md +++ /dev/null @@ -1,282 +0,0 @@ -# Voice - -Voice connections operate in a similar fashion to the [Gateway](#DOCS_TOPICS_GATEWAY) connection. However, they use a different set of payloads and a separate UDP-based connection for voice data transmission. Because UDP is used for both receiving and transmitting voice data, your client _must_ be able to receive UDP packets, even through a firewall or NAT (see [UDP Hole Punching](https://en.wikipedia.org/wiki/UDP_hole_punching) for more information). The Discord Voice servers implement functionality (see [IP Discovery](#DOCS_TOPICS_VOICE_CONNECTIONS/ip-discovery)) for discovering the local machines remote UDP IP/Port, which can assist in some network configurations. - -## Voice Gateway Versioning - -To ensure that you have the most up-to-date information, please use [version 4](#DOCS_TOPICS_VOICE_CONNECTIONS/voice-gateway-versioning-gateway-versions). Otherwise, we cannot guarantee that the [Opcodes](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) documented here will reflect what you receive over the socket. - -###### Gateway Versions - -| Version | Status | WebSocket URL Append | -| ------- | ----------- | -------------------- | -| 4 | recommended | ?v=4 | -| 3 | available | ?v=3 | -| 2 | available | ?v=2 | -| 1 | default | ?v=1 or omit | - -## Connecting to Voice - -### Retrieving Voice Server Information - -The first step in connecting to a voice server (and in turn, a guild's voice channel) is formulating a request that can be sent to the [Gateway](#DOCS_TOPICS_GATEWAY), which will return information about the voice server we will connect to. Because Discord's voice platform is widely distributed, users **should never** cache or save the results of this call. To inform the gateway of our intent to establish voice connectivity, we first send an [Opcode 4 Gateway Voice State Update](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/gateway): - -###### Gateway Voice State Update Example - -```json -{ - "op": 4, - "d": { - "guild_id": "41771983423143937", - "channel_id": "127121515262115840", - "self_mute": false, - "self_deaf": false - } -} -``` - -If our request succeeded, the gateway will respond with _two_ events—a [Voice State Update](#DOCS_TOPICS_GATEWAY_EVENTS/voice-state-update) event and a [Voice Server Update](#DOCS_TOPICS_GATEWAY_EVENTS/voice-server-update) event—meaning your library must properly wait for both events before continuing. The first will contain a new key, `session_id`, and the second will provide voice server information we can use to establish a new voice connection: - -###### Example Voice Server Update Payload - -```json -{ - "t": "VOICE_SERVER_UPDATE", - "s": 2, - "op": 0, - "d": { - "token": "my_token", - "guild_id": "41771983423143937", - "endpoint": "smart.loyal.discord.gg" - } -} -``` - -With this information, we can move on to establishing a voice WebSocket connection. - -> info -> Bot users respect the voice channel's user limit, if set. When the voice channel is full, you will not receive -> the Voice State Update or Voice Server Update events in response to your own Voice State Update. Having `MANAGE_CHANNELS` -> permission bypasses this limit and allows you to join regardless of the channel being full or not. - -## Establishing a Voice Websocket Connection - -Once we retrieve a session_id, token, and endpoint information, we can connect and handshake with the voice server over another secure WebSocket. Unlike the gateway endpoint we receive in an HTTP [Get Gateway](#DOCS_TOPICS_GATEWAY/get-gateway) request, the endpoint received from our [Voice Server Update](#DOCS_TOPICS_GATEWAY_EVENTS/voice-server-update) payload does not contain a URL protocol, so some libraries may require manually prepending it with "wss://" before connecting. Once connected to the voice WebSocket endpoint, we can send an [Opcode 0 Identify](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) payload with our server_id, user_id, session_id, and token: - -###### Example Voice Identify Payload - -```json -{ - "op": 0, - "d": { - "server_id": "41771983423143937", - "user_id": "104694319306248192", - "session_id": "my_session_id", - "token": "my_token" - } -} -``` - -The voice server should respond with an [Opcode 2 Ready](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) payload, which informs us of the `SSRC`, UDP IP/port, and supported encryption modes the voice server expects: - -###### Example Voice Ready Payload - -```json -{ - "op": 2, - "d": { - "ssrc": 1, - "ip": "127.0.0.1", - "port": 1234, - "modes": ["xsalsa20_poly1305", "xsalsa20_poly1305_suffix", "xsalsa20_poly1305_lite"], - "heartbeat_interval": 1 - } -} -``` - -> danger -> `heartbeat_interval` here is an erroneous field and should be ignored. The correct `heartbeat_interval` value comes from the Hello payload. - -## Heartbeating - -In order to maintain your WebSocket connection, you need to continuously send heartbeats at the interval determined in [Opcode 8 Hello](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice): - -###### Example Hello Payload below V3 - -```json -{ - "heartbeat_interval": 41250 -} -``` - -###### Example Hello Payload since V3 - -```json -{ - "op": 8, - "d": { - "heartbeat_interval": 41250 - } -} -``` - -This is sent at the start of the connection. Be warned that the [Opcode 8 Hello](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) structure differs by gateway version as shown in the above examples. Versions below v3 do not have an opcode or a data field denoted by `d`. V3 and above was updated to be structured like other payloads. Be sure to expect this different format based on your version. - -After receiving [Opcode 8 Hello](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice), you should send [Opcode 3 Heartbeat](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice)—which contains an integer nonce—every elapsed interval: - -###### Example Heartbeat Payload - -```json -{ - "op": 3, - "d": 1501184119561 -} -``` - -In return, you will be sent back an [Opcode 6 Heartbeat ACK](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) that contains the previously sent nonce: - -###### Example Heartbeat ACK Payload - -```json -{ - "op": 6, - "d": 1501184119561 -} -``` - -## Establishing a Voice UDP Connection - -Once we receive the properties of a UDP voice server from our [Opcode 2 Ready](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) payload, we can proceed to the final step of voice connections, which entails establishing and handshaking a UDP connection for voice data. First, we open a UDP connection to the IP and port provided in the Ready payload. If required, we can now perform an [IP Discovery](#DOCS_TOPICS_VOICE_CONNECTIONS/ip-discovery) using this connection. Once we've fully discovered our external IP and UDP port, we can then tell the voice WebSocket what it is, and start receiving/sending data. We do this using [Opcode 1 Select Protocol](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice): - -###### Example Select Protocol Payload - -```json -{ - "op": 1, - "d": { - "protocol": "udp", - "data": { - "address": "127.0.0.1", - "port": 1337, - "mode": "xsalsa20_poly1305_lite" - } - } -} -``` - -###### Encryption Modes - -| Mode | Key | Nonce Bytes | Generating Nonce | -|--------|--------------------------|------------------------------------------------------------------------|---------------------------------------| -| Normal | xsalsa20_poly1305 | The nonce bytes are the RTP header | Copy the RTP header | -| Suffix | xsalsa20_poly1305_suffix | The nonce bytes are 24 bytes appended to the payload of the RTP packet | Generate 24 random bytes | -| Lite | xsalsa20_poly1305_lite | The nonce bytes are 4 bytes appended to the payload of the RTP packet | Incremental 4 bytes (32bit) int value | - ->warn ->The nonce has to be stripped from the payload before encrypting and before decrypting the audio data - -Finally, the voice server will respond with a [Opcode 4 Session Description](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) that includes the `mode` and `secret_key`, a 32 byte array used for [encrypting and sending](#DOCS_TOPICS_VOICE_CONNECTIONS/encrypting-and-sending-voice) voice data: - -###### Example Session Description Payload - -```json -{ - "op": 4, - "d": { - "mode": "xsalsa20_poly1305_lite", - "secret_key": [ ...251, 100, 11...] - } -} -``` - -We can now start encrypting and sending voice data over the previously established UDP connection. - -## Encrypting and Sending Voice - -Voice data sent to discord should be encoded with [Opus](https://www.opus-codec.org/), using two channels (stereo) and a sample rate of 48kHz. Voice Data is sent using a [RTP Header](https://www.rfcreader.com/#rfc3550_line548), followed by encrypted Opus audio data. Voice encryption uses the key passed in [Opcode 4 Session Description](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) and the nonce formed with the 12 byte header appended with 12 null bytes to achieve the 24 required by xsalsa20_poly1305. Discord encrypts with the [libsodium](https://download.libsodium.org/doc/) encryption library. - -###### Voice Packet Structure - -| Field | Type | Size | -| --------------- | ----------------------------- | ------- | -| Version + Flags | Single byte value of `0x80` | 1 byte | -| Payload Type | Single byte value of `0x78` | 1 byte | -| Sequence | Unsigned short (big endian) | 2 bytes | -| Timestamp | Unsigned integer (big endian) | 4 bytes | -| SSRC | Unsigned integer (big endian) | 4 bytes | -| Encrypted audio | Binary data | n bytes | - -## Speaking - -To notify clients that you are speaking or have stopped speaking, send an [Opcode 5 Speaking](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) payload: - -The following flags can be used as a bitwise mask. For example `5` would be priority and voice. - -| Flag | Meaning | Value | -| ---------- | -------------------------------------------------------------- | ------ | -| Microphone | Normal transmission of voice audio | 1 << 0 | -| Soundshare | Transmission of context audio for video, no speaking indicator | 1 << 1 | -| Priority | Priority speaker, lowering audio of other speakers | 1 << 2 | - -###### Example Speaking Payload - -```json -{ - "op": 5, - "d": { - "speaking": 5, - "delay": 0, - "ssrc": 1 - } -} -``` - -> warn -> You must send at least one [Opcode 5 Speaking](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) payload before sending voice data, or you will be disconnected with an invalid SSRC error. - -### Voice Data Interpolation - -When there's a break in the sent data, the packet transmission shouldn't simply stop. Instead, send five frames of silence (`0xF8, 0xFF, 0xFE`) before stopping to avoid unintended Opus interpolation with subsequent transmissions. - -## Resuming Voice Connection - -When your client detects that its connection has been severed, it should open a new WebSocket connection. Once the new connection has been opened, your client should send an [Opcode 7 Resume](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) payload: - -###### Example Resume Connection Payload - -```json -{ - "op": 7, - "d": { - "server_id": "41771983423143937", - "session_id": "my_session_id", - "token": "my_token" - } -} -``` - -If successful, the Voice server will respond with an [Opcode 9 Resumed](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice) to signal that your client is now resumed: - -###### Example Resumed Payload - -```json -{ - "op": 9, - "d": null -} -``` - -If the resume is unsuccessful—for example, due to an invalid session—the WebSocket connection will close with the appropriate [close event code](#DOCS_TOPICS_OPCODES_AND_STATUS_CODES/voice-voice-close-event-codes). You should then follow the [Connecting](#DOCS_TOPICS_VOICE_CONNECTIONS/connecting-to-voice) flow to reconnect. - -#### IP Discovery - -Generally routers on the Internet mask or obfuscate UDP ports through a process called NAT. Most users who implement voice will want to utilize IP discovery to find their external IP and port which will then be used for receiving voice communications. To retrieve your external IP and port, send the following UDP packet to your voice port (all numeric are big endian): - -| Field | Description | Size | -| --------------- | -------------------------------------------------------------- | -------- | -| Type | Values 0x1 and 0x2 indicate request and response, respectively | 2 bytes | -| Length | Message length excluding Type and Length fields (value 70) | 2 bytes | -| SSRC | Unsigned integer | 4 bytes | -| Address | Null-terminated string in response | 64 bytes | -| Port | Unsigned short | 2 bytes | - diff --git a/images/API_center.gif b/images/API_center.gif deleted file mode 100644 index c22e319897..0000000000 Binary files a/images/API_center.gif and /dev/null differ diff --git a/images/app-add-bot.png b/images/app-add-bot.png deleted file mode 100644 index 6e5fc0bd34..0000000000 Binary files a/images/app-add-bot.png and /dev/null differ diff --git a/images/app-create-modal.png b/images/app-create-modal.png deleted file mode 100644 index 081015c09b..0000000000 Binary files a/images/app-create-modal.png and /dev/null differ diff --git a/images/ask-to-join.gif b/images/ask-to-join.gif deleted file mode 100644 index 1b717a197b..0000000000 Binary files a/images/ask-to-join.gif and /dev/null differ diff --git a/images/available-published.png b/images/available-published.png deleted file mode 100644 index 659fe6791c..0000000000 Binary files a/images/available-published.png and /dev/null differ diff --git a/images/button-styles.png b/images/button-styles.png deleted file mode 100644 index 7480e1da90..0000000000 Binary files a/images/button-styles.png and /dev/null differ diff --git a/images/command-types.png b/images/command-types.png deleted file mode 100644 index f9e005b348..0000000000 Binary files a/images/command-types.png and /dev/null differ diff --git a/images/create-store-channel.png b/images/create-store-channel.png deleted file mode 100644 index d52fe6d56f..0000000000 Binary files a/images/create-store-channel.png and /dev/null differ diff --git a/images/deferred-example.png b/images/deferred-example.png deleted file mode 100644 index 99daf305b4..0000000000 Binary files a/images/deferred-example.png and /dev/null differ diff --git a/images/desktop-select.png b/images/desktop-select.png deleted file mode 100644 index f18dc06fdb..0000000000 Binary files a/images/desktop-select.png and /dev/null differ diff --git a/images/examples-message-reactive.png b/images/examples-message-reactive.png deleted file mode 100644 index ebedccf797..0000000000 Binary files a/images/examples-message-reactive.png and /dev/null differ diff --git a/images/game-overlay-sdk-invite.gif b/images/game-overlay-sdk-invite.gif deleted file mode 100644 index 96d75b3b55..0000000000 Binary files a/images/game-overlay-sdk-invite.gif and /dev/null differ diff --git a/images/game-overlay-sdk-voice-settings.png b/images/game-overlay-sdk-voice-settings.png deleted file mode 100644 index 169f732beb..0000000000 Binary files a/images/game-overlay-sdk-voice-settings.png and /dev/null differ diff --git a/images/game-overlay-sdk-voice-widget.png b/images/game-overlay-sdk-voice-widget.png deleted file mode 100644 index 53c4d4a27d..0000000000 Binary files a/images/game-overlay-sdk-voice-widget.png and /dev/null differ diff --git a/images/gateway-lifecycle.svg b/images/gateway-lifecycle.svg deleted file mode 100644 index 53ce68b97c..0000000000 --- a/images/gateway-lifecycle.svg +++ /dev/null @@ -1,462 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/gift-code-creation.png b/images/gift-code-creation.png deleted file mode 100644 index 72cace895a..0000000000 Binary files a/images/gift-code-creation.png and /dev/null differ diff --git a/images/glitch-project-share.png b/images/glitch-project-share.png deleted file mode 100644 index a9a5e16adc..0000000000 Binary files a/images/glitch-project-share.png and /dev/null differ diff --git a/images/glitch-project.png b/images/glitch-project.png deleted file mode 100644 index 6fe56ba1a6..0000000000 Binary files a/images/glitch-project.png and /dev/null differ diff --git a/images/heroku-baking.gif b/images/heroku-baking.gif deleted file mode 100644 index 73c7852e78..0000000000 Binary files a/images/heroku-baking.gif and /dev/null differ diff --git a/images/heroku-buildpack.png b/images/heroku-buildpack.png deleted file mode 100644 index 8e0b322924..0000000000 Binary files a/images/heroku-buildpack.png and /dev/null differ diff --git a/images/heroku-configVars.png b/images/heroku-configVars.png deleted file mode 100644 index ac265add0b..0000000000 Binary files a/images/heroku-configVars.png and /dev/null differ diff --git a/images/heroku-connectGH.png b/images/heroku-connectGH.png deleted file mode 100644 index e623b04e31..0000000000 Binary files a/images/heroku-connectGH.png and /dev/null differ diff --git a/images/heroku-deploy.png b/images/heroku-deploy.png deleted file mode 100644 index 04a210f2b9..0000000000 Binary files a/images/heroku-deploy.png and /dev/null differ diff --git a/images/heroku-dynos.png b/images/heroku-dynos.png deleted file mode 100644 index c94918170a..0000000000 Binary files a/images/heroku-dynos.png and /dev/null differ diff --git a/images/heroku-login.png b/images/heroku-login.png deleted file mode 100644 index 30e41f381c..0000000000 Binary files a/images/heroku-login.png and /dev/null differ diff --git a/images/heroku-logs.png b/images/heroku-logs.png deleted file mode 100644 index a73f32e3ee..0000000000 Binary files a/images/heroku-logs.png and /dev/null differ diff --git a/images/heroku-procfile.png b/images/heroku-procfile.png deleted file mode 100644 index 19c28e09c0..0000000000 Binary files a/images/heroku-procfile.png and /dev/null differ diff --git a/images/heroku-token.png b/images/heroku-token.png deleted file mode 100644 index c768547b08..0000000000 Binary files a/images/heroku-token.png and /dev/null differ diff --git a/images/interactions-url.png b/images/interactions-url.png deleted file mode 100644 index aa3e7154d2..0000000000 Binary files a/images/interactions-url.png and /dev/null differ diff --git a/images/mobile-select.png b/images/mobile-select.png deleted file mode 100644 index 860e211c04..0000000000 Binary files a/images/mobile-select.png and /dev/null differ diff --git a/images/previous-new-server-background.png b/images/previous-new-server-background.png deleted file mode 100644 index f35ebe93da..0000000000 Binary files a/images/previous-new-server-background.png and /dev/null differ diff --git a/images/rp-actionable.png b/images/rp-actionable.png deleted file mode 100644 index 7dc4a48708..0000000000 Binary files a/images/rp-actionable.png and /dev/null differ diff --git a/images/rp-all-fields.png b/images/rp-all-fields.png deleted file mode 100644 index c5e9953c04..0000000000 Binary files a/images/rp-all-fields.png and /dev/null differ diff --git a/images/rp-bad-art.png b/images/rp-bad-art.png deleted file mode 100644 index 39f98d023c..0000000000 Binary files a/images/rp-bad-art.png and /dev/null differ diff --git a/images/rp-good-art.png b/images/rp-good-art.png deleted file mode 100644 index 346de029dc..0000000000 Binary files a/images/rp-good-art.png and /dev/null differ diff --git a/images/rp-legend.png b/images/rp-legend.png deleted file mode 100644 index 9ba7a850f2..0000000000 Binary files a/images/rp-legend.png and /dev/null differ diff --git a/images/rp-long-strings.png b/images/rp-long-strings.png deleted file mode 100644 index 5330b35d4a..0000000000 Binary files a/images/rp-long-strings.png and /dev/null differ diff --git a/images/rp-non-actionable.png b/images/rp-non-actionable.png deleted file mode 100644 index 493c63bdd4..0000000000 Binary files a/images/rp-non-actionable.png and /dev/null differ diff --git a/images/rp-not-all-fields.png b/images/rp-not-all-fields.png deleted file mode 100644 index ec576d26a3..0000000000 Binary files a/images/rp-not-all-fields.png and /dev/null differ diff --git a/images/rp-profile-example-1.png b/images/rp-profile-example-1.png deleted file mode 100644 index a91fc4ed51..0000000000 Binary files a/images/rp-profile-example-1.png and /dev/null differ diff --git a/images/rp-profile-example-2.png b/images/rp-profile-example-2.png deleted file mode 100644 index 8e4948ebb8..0000000000 Binary files a/images/rp-profile-example-2.png and /dev/null differ diff --git a/images/rp-short-strings.png b/images/rp-short-strings.png deleted file mode 100644 index 116a2e8c6b..0000000000 Binary files a/images/rp-short-strings.png and /dev/null differ diff --git a/images/server-banner-example.png b/images/server-banner-example.png deleted file mode 100644 index b8b10a458a..0000000000 Binary files a/images/server-banner-example.png and /dev/null differ diff --git a/images/server-banner-margin-top.png b/images/server-banner-margin-top.png deleted file mode 100644 index 1ab9fca92e..0000000000 Binary files a/images/server-banner-margin-top.png and /dev/null differ diff --git a/images/sku-management.png b/images/sku-management.png deleted file mode 100644 index 6aa92fc53e..0000000000 Binary files a/images/sku-management.png and /dev/null differ diff --git a/images/snowflake.png b/images/snowflake.png deleted file mode 100644 index 3b23756f9c..0000000000 Binary files a/images/snowflake.png and /dev/null differ diff --git a/images/snowflake_original_size.png b/images/snowflake_original_size.png deleted file mode 100644 index fa39ff6156..0000000000 Binary files a/images/snowflake_original_size.png and /dev/null differ diff --git a/images/spectate.gif b/images/spectate.gif deleted file mode 100644 index 2fad544025..0000000000 Binary files a/images/spectate.gif and /dev/null differ diff --git a/images/team-2fa.png b/images/team-2fa.png deleted file mode 100644 index 81b821a4d6..0000000000 Binary files a/images/team-2fa.png and /dev/null differ diff --git a/images/team-make-app.png b/images/team-make-app.png deleted file mode 100644 index 78a297ef61..0000000000 Binary files a/images/team-make-app.png and /dev/null differ diff --git a/images/team-page.png b/images/team-page.png deleted file mode 100644 index a90baa8bda..0000000000 Binary files a/images/team-page.png and /dev/null differ diff --git a/images/transfer-app-to-team.png b/images/transfer-app-to-team.png deleted file mode 100644 index 6672d1d772..0000000000 Binary files a/images/transfer-app-to-team.png and /dev/null differ diff --git a/images/url-generator.png b/images/url-generator.png deleted file mode 100644 index 0d20698f16..0000000000 Binary files a/images/url-generator.png and /dev/null differ diff --git a/index.js b/index.js deleted file mode 100644 index f053ebf797..0000000000 --- a/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 89a928af28..0000000000 --- a/package-lock.json +++ /dev/null @@ -1,3203 +0,0 @@ -{ - "name": "discord-api-docs", - "version": "1.1.1", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "discord-api-docs", - "version": "1.1.1", - "license": "MIT", - "devDependencies": { - "@actions/core": "^1.9.1", - "@types/node": "^16.10.3", - "@typescript-eslint/eslint-plugin": "^4.33.0", - "@typescript-eslint/parser": "^4.33.0", - "chalk": "^4.1.2", - "eslint": "^7.32.0", - "eslint-config-marine": "^9.0.6", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^4.0.0", - "prettier": "^2.4.1", - "typescript": "^4.4.3" - } - }, - "node_modules/@actions/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", - "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", - "dev": true, - "dependencies": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "node_modules/@actions/http-client": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", - "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", - "dev": true, - "dependencies": { - "tunnel": "^0.0.6" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", - "dev": true - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "16.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", - "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", - "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", - "dev": true, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-aqua": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-aqua/-/eslint-config-aqua-9.0.2.tgz", - "integrity": "sha512-WqV4EYX3pXoL0TkUBH9VVHoWjR3eJ0ud1p5OmSfbRoyjDbUJURs78fpeU4H8t88pO8mFB4SHTZM95DJEwKG/MQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-config-marine": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/eslint-config-marine/-/eslint-config-marine-9.0.6.tgz", - "integrity": "sha512-PPx84UDPQrLM5kyGG4kaDCQ2EAfzh3bcFjS9Sqg322z2vzERg8di3D4YSjUHWgXNqgooOUor8qjzM2NnPL0AKw==", - "dev": true, - "dependencies": { - "eslint-config-aqua": "^9.0.2" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/table": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.2.tgz", - "integrity": "sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.6.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", - "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true, - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - }, - "dependencies": { - "@actions/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", - "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", - "dev": true, - "requires": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "@actions/http-client": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", - "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", - "dev": true, - "requires": { - "tunnel": "^0.0.6" - } - }, - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", - "dev": true - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/node": { - "version": "16.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", - "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/experimental-utils": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", - "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" - } - }, - "@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" - } - }, - "@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "eslint-config-aqua": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-aqua/-/eslint-config-aqua-9.0.2.tgz", - "integrity": "sha512-WqV4EYX3pXoL0TkUBH9VVHoWjR3eJ0ud1p5OmSfbRoyjDbUJURs78fpeU4H8t88pO8mFB4SHTZM95DJEwKG/MQ==", - "dev": true - }, - "eslint-config-marine": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/eslint-config-marine/-/eslint-config-marine-9.0.6.tgz", - "integrity": "sha512-PPx84UDPQrLM5kyGG4kaDCQ2EAfzh3bcFjS9Sqg322z2vzERg8di3D4YSjUHWgXNqgooOUor8qjzM2NnPL0AKw==", - "dev": true, - "requires": { - "eslint-config-aqua": "^9.0.2" - } - }, - "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", - "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", - "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "table": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.2.tgz", - "integrity": "sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.6.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", - "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index a6cfcb85ea..0000000000 --- a/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "discord-api-docs", - "version": "1.1.1", - "description": "Documentation for using Discord's API", - "main": "index.js", - "directories": { - "doc": "docs" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/discord/discord-api-docs.git" - }, - "author": "Discord Krew", - "license": "MIT", - "bugs": { - "url": "https://github.com/discord/discord-api-docs/issues" - }, - "homepage": "https://github.com/discord/discord-api-docs#readme", - "scripts": { - "build": "tsc --build", - "lint": "eslint ci", - "lint:fix": "eslint ci --fix", - "test:links": "node dist/ci/checkLinks.js" - }, - "devDependencies": { - "@actions/core": "^1.9.1", - "@types/node": "^16.10.3", - "@typescript-eslint/eslint-plugin": "^4.33.0", - "@typescript-eslint/parser": "^4.33.0", - "chalk": "^4.1.2", - "eslint": "^7.32.0", - "eslint-config-marine": "^9.0.6", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^4.0.0", - "prettier": "^2.4.1", - "typescript": "^4.4.3" - } -} diff --git a/resources/discord-social-sdk/linked-channel-icon.svg b/resources/discord-social-sdk/linked-channel-icon.svg new file mode 100644 index 0000000000..a448872a45 --- /dev/null +++ b/resources/discord-social-sdk/linked-channel-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tools/.prettierrc.json b/tools/.prettierrc.json new file mode 100644 index 0000000000..ebd6551687 --- /dev/null +++ b/tools/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "printWidth": 120 +} diff --git a/tools/checkBuild.ts b/tools/checkBuild.ts new file mode 100644 index 0000000000..f4c5aded44 --- /dev/null +++ b/tools/checkBuild.ts @@ -0,0 +1,37 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import { compile } from "@mdx-js/mdx"; + +const args = process.argv.slice(2); +const bail = args.includes("--bail"); + +const ROOT_DIR = path.join(import.meta.dirname, ".."); +const DOCS_DIR = path.join(ROOT_DIR, "discord", "developers", "docs"); + +const extensions = [".mdx", ".md"]; +let hasErrors = false; + +for (const docsRelativePath of await fs.readdir(DOCS_DIR, { recursive: true })) { + const filePath = path.join(DOCS_DIR, docsRelativePath); + const rootRelPath = path.relative(ROOT_DIR, filePath); + + if (extensions.includes(path.extname(filePath))) { + try { + console.error(`Compiling ${rootRelPath}`); + await compile(await fs.readFile(filePath), { + format: path.extname(filePath) === ".mdx" ? "mdx" : "md", + }); + } catch (error) { + console.error(`Error compiling ${rootRelPath}:`); + console.error(error); + hasErrors = true; + if (bail) { + process.exit(1); + } + } + } +} + +if (hasErrors) { + process.exit(1); +} diff --git a/tools/doxygen/decorate-social-sdk-references.js b/tools/doxygen/decorate-social-sdk-references.js new file mode 100644 index 0000000000..4214acc9f7 --- /dev/null +++ b/tools/doxygen/decorate-social-sdk-references.js @@ -0,0 +1,185 @@ +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Configure paths +const REPO_ROOT = path.resolve(__dirname, "..", ".."); +const MARKDOWN_DIR = path.join(REPO_ROOT, "/discord/developers/docs"); +const MAPPING_FILE = path.join(__dirname, "social-sdk-mappings.json"); + +// Helper function to ensure paths are resolved correctly +const resolvePath = (relativePath) => { + // If it's an absolute path, try it directly + if (path.isAbsolute(relativePath) && fs.existsSync(relativePath)) { + return relativePath; + } + + // First try relative to current directory + const currentPath = path.resolve(relativePath); + if (fs.existsSync(currentPath)) { + return currentPath; + } + + // Then try relative to repo root + const repoPath = path.join(REPO_ROOT, relativePath); + if (fs.existsSync(repoPath)) { + return repoPath; + } + + throw new Error(`Could not resolve path: ${relativePath} (tried ${currentPath} and ${repoPath})`); +}; + +// Load the function/class mapping +const loadMapping = () => { + const mappingPath = resolvePath(MAPPING_FILE); + if (!fs.existsSync(mappingPath)) { + // Mapping file ${mappingPath} not found! + return {}; + } + return JSON.parse(fs.readFileSync(mappingPath, "utf-8")); +}; + +// Function to find Markdown files recursively +const findMarkdownFiles = (dir) => { + const resolvedDir = resolvePath(dir); + + let results = []; + const files = fs.readdirSync(resolvedDir); + + for (const file of files) { + const filePath = path.join(resolvedDir, file); + const stat = fs.statSync(filePath); + + if (stat.isDirectory()) { + results = results.concat(findMarkdownFiles(filePath)); + } else if (file.endsWith(".md") || file.endsWith(".mdx")) { + results.push(filePath); + } + } + return results; +}; + +// Generate reference markdown links while ignoring code blocks +const processMarkdownFile = (filePath, mapping) => { + let content = fs.readFileSync(filePath, "utf-8"); + let lines = content.split("\n"); + + // Find and remove existing auto-generated section + const isMarkdown = filePath.endsWith(".md"); + const autoGenIndex = lines.findIndex((line) => + isMarkdown + ? line.includes("") + : line.includes("{/* Autogenerated Reference Links */}"), + ); + + if (autoGenIndex !== -1) { + lines = lines.slice(0, autoGenIndex); + } + + let insideCodeBlock = false; + // Use Map instead of Set to ensure uniqueness by short symbol + let referencesFound = new Map(); + + lines.forEach((line) => { + if (line.trim().startsWith("```")) { + insideCodeBlock = !insideCodeBlock; + return; + } + + if (!insideCodeBlock) { + const regex = /\[`([A-Za-z0-9]+(?:::[A-Za-z0-9]+)?)`\](?!\()/g; + let match; + + while ((match = regex.exec(line)) !== null) { + const shortSymbol = match[1]; + const fullSymbol = `discordpp::${shortSymbol}`; + + if (mapping[fullSymbol] && !referencesFound.has(shortSymbol)) { + referencesFound.set(shortSymbol, { + short: shortSymbol, + full: fullSymbol, + url: mapping[fullSymbol], + }); + } else if (!shortSymbol.includes("::")) { + const constructorSymbol = `discordpp::${shortSymbol}::${shortSymbol}`; + if (mapping[constructorSymbol] && !referencesFound.has(shortSymbol)) { + referencesFound.set(shortSymbol, { + short: shortSymbol, + full: constructorSymbol, + url: mapping[constructorSymbol], + }); + } + } + } + } + }); + + if (referencesFound.size > 0) { + if (lines[lines.length - 1] !== "") { + lines.push(""); + } + + lines.push(isMarkdown ? "" : "{/* Autogenerated Reference Links */}"); + + // Convert Map values to array and sort for consistent output + const sortedRefs = Array.from(referencesFound.values()).sort((a, b) => a.short.localeCompare(b.short)); + + for (const ref of sortedRefs) { + lines.push(`[\`${ref.short}\`]: ${ref.url}`); + } + } + + const newContent = lines.join("\n"); + const hasChanged = newContent !== content; + + if (hasChanged) { + fs.writeFileSync(filePath, newContent, "utf-8"); + + console.log(`Updated ${filePath} with ${referencesFound.size} references`); + } + + return { + updated: hasChanged, + referenceCount: referencesFound.size, + }; +}; + +// Process all markdown files +const processMarkdownFiles = async () => { + const mapping = loadMapping(); + if (!Object.keys(mapping).length) { + console.error("Mapping is empty! Make sure the JSON is generated correctly."); + return; + } + + const markdownFiles = findMarkdownFiles(MARKDOWN_DIR); + let filesUpdated = 0; + let totalReferences = 0; + + for (const filePath of markdownFiles) { + const content = fs.readFileSync(filePath, "utf-8"); + const potentialMatches = content.match(/\[`([A-Za-z0-9]+(?:::[A-Za-z0-9]+)?)`\](?!\()/g); + + if (potentialMatches) { + const { updated, referenceCount } = processMarkdownFile(filePath, mapping); + if (updated) { + filesUpdated++; + totalReferences += referenceCount; + } + } + } + + if (filesUpdated > 0) { + console.log( + `\nSDK reference links processed. Updated ${filesUpdated} files with ${totalReferences} total references.`, + ); + } else { + console.log("SDK reference links processed. No changes were made to any files."); + } +}; + +// Run the script +processMarkdownFiles(); diff --git a/tools/doxygen/generate-social-sdk-mapping.js b/tools/doxygen/generate-social-sdk-mapping.js new file mode 100644 index 0000000000..df055dd42d --- /dev/null +++ b/tools/doxygen/generate-social-sdk-mapping.js @@ -0,0 +1,100 @@ +import fs from "fs"; +import path from "path"; +import { parseStringPromise } from "xml2js"; +import { fileURLToPath } from "url"; + +// Resolve __dirname in ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Configure paths +const REPO_ROOT = path.resolve(__dirname, "..", ".."); +const XML_DIR = path.join(__dirname, "xml_output"); +const OUTPUT_JSON = path.join(__dirname, "social-sdk-mappings.json"); +const BASE_URL = "https://discord.com/developers/docs/social-sdk"; + +// Helper function to ensure paths are resolved correctly +const resolvePath = (relativePath, isOutput = false) => { + // For output files, just return the path relative to current directory + if (isOutput) { + return path.resolve(relativePath); + } + + // For input files, try both locations + if (fs.existsSync(relativePath)) { + return relativePath; + } + const repoPath = path.join(REPO_ROOT, relativePath); + if (fs.existsSync(repoPath)) { + return repoPath; + } + throw new Error(`Could not resolve input path: ${relativePath}`); +}; + +// Function to read and parse XML files +const parseXml = async (filePath) => { + const data = await fs.promises.readFile(filePath, "utf-8"); + return parseStringPromise(data); +}; + +const generateMapping = async () => { + // Check if XML output directory exists + if (!fs.existsSync(XML_DIR)) { + // XML output directory not found! Run Doxygen first to generate XML files. + return; + } + + const indexPath = resolvePath(path.join(XML_DIR, "index.xml")); + if (!fs.existsSync(indexPath)) { + // index.xml not found! Ensure Doxygen XML output exists. + return; + } + + const indexData = await parseXml(indexPath); + const compounds = indexData.doxygenindex.compound; + const mapping = {}; + + for (const compound of compounds) { + const refid = compound.$.refid; + const name = compound.name[0]; + const detailedXmlPath = resolvePath(path.join(XML_DIR, `${refid}.xml`)); + + if (!fs.existsSync(detailedXmlPath)) { + continue; + } + + const detailedData = await parseXml(detailedXmlPath); + const members = detailedData.doxygen.compounddef[0].sectiondef || []; + + for (const section of members) { + if (!section.memberdef) continue; + + for (const member of section.memberdef) { + const memberKind = member.$.kind; + const memberName = member.name[0]; + let memberId = member.$.id; + + if (["function", "enum", "class", "struct"].includes(memberKind)) { + memberId = memberId.replace(`${refid}_1`, ""); + const url = `${BASE_URL}/${refid}.html#${memberId}`; + // Keep the discordpp:: prefix in the symbol name + const symbol = `${name}::${memberName}`; + + if (!(symbol in mapping)) { + mapping[symbol] = url; + } + } + } + } + } + + // Save JSON mapping + const outputJsonPath = resolvePath(OUTPUT_JSON, true); + await fs.promises.writeFile(outputJsonPath, JSON.stringify(mapping, null, 2)); + + console.log(`JSON mapping saved to ${outputJsonPath}`); +}; + +// Run the script + +generateMapping().catch(console.error); diff --git a/tools/doxygen/social-sdk-mappings.json b/tools/doxygen/social-sdk-mappings.json new file mode 100644 index 0000000000..364b7db3a9 --- /dev/null +++ b/tools/doxygen/social-sdk-mappings.json @@ -0,0 +1,552 @@ +{ + "discordpp::Activity::Activity": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ae793d9adbe16fef402b859ba02bee682", + "discordpp::Activity::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a35cec32f9bf9a2625a6f6b175403d3cd", + "discordpp::Activity::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a646a72b3db59369215eaa65f14ac90ce", + "discordpp::Activity::AddButton": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#aab07650fcff18565eb78a1e2df46627e", + "discordpp::Activity::Equals": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a46127e59d0e582c49310d60758fed691", + "discordpp::Activity::GetButtons": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a03ae78c821e134a53b4526018657c530", + "discordpp::Activity::Name": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a13d323c3454fe32f5d33c94fac5bc11c", + "discordpp::Activity::SetName": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a59f9a63a8b105946d0c9838f3e643ae2", + "discordpp::Activity::Type": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#afbab0968c2cbef94691b3996fe3d6ea4", + "discordpp::Activity::SetType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a3c686b85c6567f5b88d9d9a2aa3934ab", + "discordpp::Activity::StatusDisplayType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a837741f6a574a23536b208c8f44630c7", + "discordpp::Activity::SetStatusDisplayType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a9e8051f8dec35e2af025e5ed3a4dc6bf", + "discordpp::Activity::State": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#af7c8802a7df283dbe5ba06ba1c00ca5a", + "discordpp::Activity::SetState": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#abfd658c93df153a53acd1ad0270c1e02", + "discordpp::Activity::StateUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#af3a6411c8ec4ee24845c6114b21aab3d", + "discordpp::Activity::SetStateUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a1cadcb36d533ad0e73dc0b03beed66e5", + "discordpp::Activity::Details": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#abe3446f06b53f4358e0efdc2878fa3d0", + "discordpp::Activity::SetDetails": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a28d98bbf86841526a0b2c033f3a70b28", + "discordpp::Activity::DetailsUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#ab5cc7690e9fd84b56443ec3c6e358af0", + "discordpp::Activity::SetDetailsUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a08dec0a9b7bffcff09be2bc2a92d7666", + "discordpp::Activity::ApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a1203f29b82e9716cdce9151c7ea3b2db", + "discordpp::Activity::SetApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a48b156412b91a9a09c268bf2c3f7f9ec", + "discordpp::Activity::ParentApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a091058291278bf1ff0250b28bf311786", + "discordpp::Activity::SetParentApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a37969990d16e5f58e6180ff49038311c", + "discordpp::Activity::Assets": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a3fb85cc97867d2bf01e7e1a9a6414d95", + "discordpp::Activity::SetAssets": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a7f0b6d7a92925a005444cb3f4055e2c3", + "discordpp::Activity::Timestamps": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a912e6e9f685f9e7003731222016ed1a2", + "discordpp::Activity::SetTimestamps": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a437bd31e05be54bbb2f259ef8b9c68ad", + "discordpp::Activity::Party": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#aa1a0a3f5d20579c2dbc2502065c8a09f", + "discordpp::Activity::SetParty": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a93c9c542eeeed49c4f7ee5bc0a12da90", + "discordpp::Activity::Secrets": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#af4ec01eeb75f394f0019ae839145a0a2", + "discordpp::Activity::SetSecrets": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#a6c61c1231b00562606bbb7d18fb697b7", + "discordpp::Activity::SupportedPlatforms": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#af19a7afb979550d9323be2146bd372e0", + "discordpp::Activity::SetSupportedPlatforms": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Activity.html#abe00e28f4705ec904b635655c336a487", + "discordpp::ActivityAssets::ActivityAssets": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a4569c410d197e153971cab35f5d84f80", + "discordpp::ActivityAssets::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#ad6d52c5ded92e5644f603b40f73718d1", + "discordpp::ActivityAssets::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#acf0fe7ef1451c7514eac6a6b8e8898a2", + "discordpp::ActivityAssets::LargeImage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a90ec2ac7102c8f99d233e95858603962", + "discordpp::ActivityAssets::SetLargeImage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a09e51cf262fe2b58522f0516a1d8f1d3", + "discordpp::ActivityAssets::LargeText": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a7e0e5680e47954c3131702cd8dfacd18", + "discordpp::ActivityAssets::SetLargeText": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a351a903fd45643cf8a084a72e30207ce", + "discordpp::ActivityAssets::LargeUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#aaa7725c0ebefcda564fdfbddf7df628a", + "discordpp::ActivityAssets::SetLargeUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a65294ee7dd810226642c0b5356bf1d80", + "discordpp::ActivityAssets::SmallImage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a4a76a48496ed5c9661e4a0551b324de8", + "discordpp::ActivityAssets::SetSmallImage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a7f406e1e7722470e753d9acb9a0ee690", + "discordpp::ActivityAssets::SmallText": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a27f61aebf3ef5c2357d2d99cb4dcfeac", + "discordpp::ActivityAssets::SetSmallText": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#aae2b92760fb365439e8ad38444acdebb", + "discordpp::ActivityAssets::SmallUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a862e2fe4b209d18f7633c3436af2860c", + "discordpp::ActivityAssets::SetSmallUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#afd3ed179bf1924a49631eebeaa1f6493", + "discordpp::ActivityAssets::InviteCoverImage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#a0910e12ce7708db7aee9296cc56d947c", + "discordpp::ActivityAssets::SetInviteCoverImage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityAssets.html#ad0106c55fa323b8520efd77cbd0b0d4f", + "discordpp::ActivityButton::ActivityButton": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityButton.html#a04bc93c6984c61a23dbbcf27ad4819d4", + "discordpp::ActivityButton::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityButton.html#a523e2457c808c8f5b2e89d5b8efaed50", + "discordpp::ActivityButton::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityButton.html#aae08573498def90bc33dca67fda690bb", + "discordpp::ActivityButton::Label": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityButton.html#afb79ff9903a3aa609548f90e942e166e", + "discordpp::ActivityButton::SetLabel": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityButton.html#a9b8d70b0f65fe094b571ef221b11e259", + "discordpp::ActivityButton::Url": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityButton.html#a3e4af76071bff97a4954b57fec59a628", + "discordpp::ActivityButton::SetUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityButton.html#abbeb2fc8eefae9e956d91fb42b9d48a4", + "discordpp::ActivityInvite::ActivityInvite": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#af980f140c1459e1cd8f6ef3f3c07547c", + "discordpp::ActivityInvite::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#ac0df3818ad0ce1f94168ffeea6fcee22", + "discordpp::ActivityInvite::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a330011886f9fc05e31ff8d637efdff64", + "discordpp::ActivityInvite::SenderId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a58153cc37c7bcb733a28f143dc1ed5a8", + "discordpp::ActivityInvite::SetSenderId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a813f12036f91d4ca27db4cdafe597132", + "discordpp::ActivityInvite::ChannelId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#aa74d7a03e46c5ae5f0ee720b70836f0e", + "discordpp::ActivityInvite::SetChannelId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a954e3292e3a03392297c214b1d1c11bb", + "discordpp::ActivityInvite::MessageId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a4c1747cc8655bc583d00e8d13d4888c2", + "discordpp::ActivityInvite::SetMessageId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#ab4114f4cd277683d22e65581d7472cd3", + "discordpp::ActivityInvite::Type": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#af5ecd0fa6a4ce8435929c086894471e2", + "discordpp::ActivityInvite::SetType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#acc8d2278bbd08a5c26b8cd6e4fa62e9a", + "discordpp::ActivityInvite::ApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a38ff19dd954a406c8a21434a081f1c33", + "discordpp::ActivityInvite::SetApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a660bc6a697802fae9a3bdc3f11c8ed6b", + "discordpp::ActivityInvite::ParentApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#acffc092f6a3d069c24cd4f36bdd40318", + "discordpp::ActivityInvite::SetParentApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a91cbd5bad30b80b608f2c88f047860f9", + "discordpp::ActivityInvite::PartyId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#ab4d0b0ccc568941375458518fb571176", + "discordpp::ActivityInvite::SetPartyId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#ad538b78267ff9114f43244e3eb28f92e", + "discordpp::ActivityInvite::SessionId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a0c947f356db2b20a68a9ad536bbd4f5c", + "discordpp::ActivityInvite::SetSessionId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a2a5326a8dbca7f97509dc7c1fc21a25a", + "discordpp::ActivityInvite::IsValid": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a92e24e683870b13e8f464d6b93312433", + "discordpp::ActivityInvite::SetIsValid": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityInvite.html#a204f3077312a695ec2fea5f3f1430c2d", + "discordpp::ActivityParty::ActivityParty": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#a09930740b177c52b2c5f38d3990818e4", + "discordpp::ActivityParty::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#a940f3fedb51eac80cc81de2ac0aa1172", + "discordpp::ActivityParty::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#a582ffecf157afbb93025dba71ebbd822", + "discordpp::ActivityParty::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#ad5b269834e93f9d2b794a48cc7fb95f3", + "discordpp::ActivityParty::SetId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#a2c7f7600a26d1715171f3f3d5d3bd146", + "discordpp::ActivityParty::CurrentSize": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#a532fdee79a3adcc8f9a01fd2cf957595", + "discordpp::ActivityParty::SetCurrentSize": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#a2a3c1f8dee45eecc5b7879c3bfc34e38", + "discordpp::ActivityParty::MaxSize": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#a60d20e2272f1d66156c191152f7e4ced", + "discordpp::ActivityParty::SetMaxSize": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#a08e938b892bae2e928d723db5e8505c6", + "discordpp::ActivityParty::Privacy": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#ac57169df0239478d2e79c7d5cee5df34", + "discordpp::ActivityParty::SetPrivacy": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityParty.html#abe7d225e6bada72fe471969979ce647b", + "discordpp::ActivitySecrets::ActivitySecrets": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivitySecrets.html#a6f7c63cbab32bdcd87c15c238ecc0d41", + "discordpp::ActivitySecrets::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivitySecrets.html#a09706f33f63afd3542c37996d7fc553d", + "discordpp::ActivitySecrets::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivitySecrets.html#acea06c727ea4070700a761b56eaa0191", + "discordpp::ActivitySecrets::Join": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivitySecrets.html#a46e512adb1fa1238280b6d009d7767df", + "discordpp::ActivitySecrets::SetJoin": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivitySecrets.html#aaa0c717654d3b11c15b186b245b899e7", + "discordpp::ActivityTimestamps::ActivityTimestamps": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#a0ff108aac69639c18f1669994e459ee2", + "discordpp::ActivityTimestamps::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#a76fcfff02d0607cbff0eb9b93e51e1a4", + "discordpp::ActivityTimestamps::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#abec39a6229e67f3ab89a620af271d546", + "discordpp::ActivityTimestamps::Start": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#a4806e31609893206e81c1963857ef829", + "discordpp::ActivityTimestamps::SetStart": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#a5bb6a6bc243fedb954ae82d6c4ef3542", + "discordpp::ActivityTimestamps::End": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#aba1847d3e0223ff1bb7695ab58c5d097", + "discordpp::ActivityTimestamps::SetEnd": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ActivityTimestamps.html#ab6478ec46860feb64174da3eb1063aaa", + "discordpp::AdditionalContent::AdditionalContent": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#aaae725626946a5bda7448c160c3bfc8c", + "discordpp::AdditionalContent::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a21ccadad0bf9ff22a7812764e42fc4c3", + "discordpp::AdditionalContent::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a32740cbe47696a36eba98ade81f8d214", + "discordpp::AdditionalContent::Equals": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a74b36937fd27724ab5454709cf4545bb", + "discordpp::AdditionalContent::Type": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a5f71d7a58bfcf125e32cff555f6dd032", + "discordpp::AdditionalContent::SetType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a4c18621756a4d587660d8a849a5bc8b2", + "discordpp::AdditionalContent::Title": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a5162c8a105790c4a654ace2a0fba7b6c", + "discordpp::AdditionalContent::SetTitle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#acb289b99006d9ffcb829eeb006f7ebdd", + "discordpp::AdditionalContent::Count": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a67f810f29c69d51eff30eb424e17428c", + "discordpp::AdditionalContent::SetCount": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a335b1c5766a6f0461c657e22ea2737d8", + "discordpp::AdditionalContent::TypeToString": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AdditionalContent.html#a104d763a401c1a53688c11f60bf9db81", + "discordpp::AudioDevice::AudioDevice": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#a7d083c7bad8ebde4fc16b3151d5761f6", + "discordpp::AudioDevice::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#a807a5b01a3b53a3e3f23b778c5aeb323", + "discordpp::AudioDevice::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#a398573781b3f9ade6cfa015c5e92ca42", + "discordpp::AudioDevice::Equals": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#a09b99233147f4a70ecdc5c3e667e56ca", + "discordpp::AudioDevice::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#a3f85e5323b6a5d6ce8b6b2abb05490b6", + "discordpp::AudioDevice::SetId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#aafde2cb9f47e19fd505438a354d95091", + "discordpp::AudioDevice::Name": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#a1f021287a4bf98992d3c9b27589c5f59", + "discordpp::AudioDevice::SetName": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#ad09b74c8c38a9371ccff147c38300a86", + "discordpp::AudioDevice::IsDefault": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#a39f19c6eb7774ffd939811a3484e0475", + "discordpp::AudioDevice::SetIsDefault": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AudioDevice.html#abddad2867cd2c8ffb87f6262d532c5ca", + "discordpp::AuthorizationArgs::AuthorizationArgs": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#adb47ac55258db29d4cb8a2c506093eed", + "discordpp::AuthorizationArgs::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#a101a83b677b5bf631f51896f38257a8d", + "discordpp::AuthorizationArgs::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#a657c054debaaa75469b021aacc683190", + "discordpp::AuthorizationArgs::ClientId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#a7579dadf3052c4ea4379131ac9e2e30b", + "discordpp::AuthorizationArgs::SetClientId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#aade2fe309692a82176ef9f196d7f563e", + "discordpp::AuthorizationArgs::Scopes": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#ae5989180022eccc3bfdf65dda4877771", + "discordpp::AuthorizationArgs::SetScopes": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#aa3714d11a196e0d71c8c1cf38c506d92", + "discordpp::AuthorizationArgs::State": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#a0c85866ae01ac14ad0f9038fc2559a05", + "discordpp::AuthorizationArgs::SetState": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#aff148d94b797a4cba89cf119b8b212e9", + "discordpp::AuthorizationArgs::Nonce": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#a1dff6b488e573fc4eaa63f4ddb6262aa", + "discordpp::AuthorizationArgs::SetNonce": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#aeaf87c0b26a074ed7b23f2bd4647b6a6", + "discordpp::AuthorizationArgs::CodeChallenge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#a59930370c7ab835cf09fb6d8f893dd96", + "discordpp::AuthorizationArgs::SetCodeChallenge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#ad9c0f8479812266dbe3eb0eae6e7ea36", + "discordpp::AuthorizationArgs::IntegrationType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#ac1ab5d5840789b48896d427cd78102d3", + "discordpp::AuthorizationArgs::SetIntegrationType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#a01eedddbf32654ba7b17a0f14a9431ca", + "discordpp::AuthorizationArgs::CustomSchemeParam": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#abfe84accfc44740ffb36a408ac17277f", + "discordpp::AuthorizationArgs::SetCustomSchemeParam": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationArgs.html#a12c13c64dfc180657971c438ada66560", + "discordpp::AuthorizationCodeChallenge::AuthorizationCodeChallenge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeChallenge.html#a8b5d198d3508b0129b58f5175e49bc0a", + "discordpp::AuthorizationCodeChallenge::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeChallenge.html#a36cc806bc61f2d9eb21a355ea0c91b9e", + "discordpp::AuthorizationCodeChallenge::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeChallenge.html#a5a9c68e52c8f21d57d93240bc332cdc4", + "discordpp::AuthorizationCodeChallenge::Method": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeChallenge.html#a044c58aee91ba4c992418c2326837290", + "discordpp::AuthorizationCodeChallenge::SetMethod": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeChallenge.html#a68ab9d9bfbc374913ad5a657dcb8788a", + "discordpp::AuthorizationCodeChallenge::Challenge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeChallenge.html#a0faf6b1bd98ea1c7b34534a2a7586f88", + "discordpp::AuthorizationCodeChallenge::SetChallenge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeChallenge.html#aa4d6f75f5858ca5d5e0f9613ea03eb4a", + "discordpp::AuthorizationCodeVerifier::AuthorizationCodeVerifier": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeVerifier.html#a336c5f858c6ff157029753542e72be66", + "discordpp::AuthorizationCodeVerifier::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeVerifier.html#af50583032cf69f14d75fa9e41118cd8a", + "discordpp::AuthorizationCodeVerifier::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeVerifier.html#a085b01ce52ead116182f5bcf3a245d3d", + "discordpp::AuthorizationCodeVerifier::Challenge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeVerifier.html#ae439edf58719b8da93054e39014d106a", + "discordpp::AuthorizationCodeVerifier::SetChallenge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeVerifier.html#a53ee1f66e655d18ad8311eec6d97f5d2", + "discordpp::AuthorizationCodeVerifier::Verifier": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeVerifier.html#a4b43a10fb1297c3400f67198d00c73d1", + "discordpp::AuthorizationCodeVerifier::SetVerifier": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1AuthorizationCodeVerifier.html#a22f59e504520a15a115417309788dccf", + "discordpp::Call::Error": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a92b477828e777e1cab576821a170dff0", + "discordpp::Call::Status": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a9a8311653be2263ad61243749ff44d4e", + "discordpp::Call::Call": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a1cc8a7f73c15a960bc409d734b5edbd1", + "discordpp::Call::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a32ac0c29a7c9789c091c40080edf1280", + "discordpp::Call::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#aee1d9b4cd38da708c6b28ca3d4dfde54", + "discordpp::Call::GetAudioMode": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a3e2bfcf4d0b7e8a6a4dcb21943eb3d1d", + "discordpp::Call::GetChannelId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#afcd15d87a8e88873c3fb6697bb1e69fb", + "discordpp::Call::GetGuildId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#abe22991ec5980670ceb15fbd4cbeae24", + "discordpp::Call::GetLocalMute": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ae71f2a124c13e20f55bf1e6dee5017c3", + "discordpp::Call::GetParticipants": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#acbbbfad47511535755cb55631a8c9922", + "discordpp::Call::GetParticipantVolume": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#aeff7fa60c6029ffde272160bf1f16748", + "discordpp::Call::GetPTTActive": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a48e2d2809e8787178bec44c946b31328", + "discordpp::Call::GetPTTReleaseDelay": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ab8dc6b1527728fecb17f266d5b3e9e6e", + "discordpp::Call::GetSelfDeaf": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a7ea04973139a803d198661874eb13786", + "discordpp::Call::GetSelfMute": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ad2305065d5e633678c9242ed886e65e2", + "discordpp::Call::GetStatus": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ad614dfd6578370a0d9b73b40db1e780f", + "discordpp::Call::GetVADThreshold": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a55fbfb0e3333f227bf16801b787f0a63", + "discordpp::Call::GetVoiceStateHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a5b162505a2a076a14ff977ba0377f7a2", + "discordpp::Call::SetAudioMode": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ae6613253763877950408d29f4d68d13f", + "discordpp::Call::SetLocalMute": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#aaf8e7728b15da5d1be8d8b4258225171", + "discordpp::Call::SetOnVoiceStateChangedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a257a02bdfd79a6443eb810cb687e098a", + "discordpp::Call::SetParticipantChangedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#acb20d338a04abec2369217f41c22c0e5", + "discordpp::Call::SetParticipantVolume": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ad974fadbe89c453e4d8a3f9824e21ceb", + "discordpp::Call::SetPTTActive": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ac442b1d69b9256abbb188583c0c81c41", + "discordpp::Call::SetPTTReleaseDelay": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ad3d36056f2b19a98d5efb99897753b66", + "discordpp::Call::SetSelfDeaf": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a07d67c210f2a4655c6f1d2899c6d32d6", + "discordpp::Call::SetSelfMute": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#afa35a5d6a4564df97452df58bb74f617", + "discordpp::Call::SetSpeakingStatusChangedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#ab29b68345ffa1d6af3c8321bdc39e9f0", + "discordpp::Call::SetStatusChangedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#adffbbbd726f05fa574c96635e76c7860", + "discordpp::Call::SetVADThreshold": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a7c3fd83c5dfe37d796e30c5e28c93b6e", + "discordpp::Call::ErrorToString": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a05e758b1fa053e9f694b93bcd9439a3c", + "discordpp::Call::StatusToString": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Call.html#a69b0ea36ae6859a0ead2cb046bcdbbae", + "discordpp::CallInfoHandle::CallInfoHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1CallInfoHandle.html#ade345c06ac2938c443f2a6884bf9f0df", + "discordpp::CallInfoHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1CallInfoHandle.html#a646eb5db3aa75bbe432e9301441316d6", + "discordpp::CallInfoHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1CallInfoHandle.html#ab51398814fb1574a60a7e20958fb0491", + "discordpp::CallInfoHandle::ChannelId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1CallInfoHandle.html#ae4a234adc67578891c21c4dac03ee391", + "discordpp::CallInfoHandle::GetParticipants": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1CallInfoHandle.html#a1fc60183a120701835e7609cc017ec59", + "discordpp::CallInfoHandle::GetVoiceStateHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1CallInfoHandle.html#a06d06b854f2095f74865b5d5c20b3031", + "discordpp::CallInfoHandle::GuildId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1CallInfoHandle.html#ad5511f5400b464fd05fcf6df69f82e33", + "discordpp::ChannelHandle::ChannelHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#ac32096b2ef15c5c220e9b7b92253cc46", + "discordpp::ChannelHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#aa5a430dd368cb56e41fa194db97a38e9", + "discordpp::ChannelHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#ab8e1d63035236dee17493032078749b6", + "discordpp::ChannelHandle::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#a8f72c0a96fe980c79749ef7e51f0026b", + "discordpp::ChannelHandle::Name": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#afde4eb29870827f488c99fd1b547e464", + "discordpp::ChannelHandle::Recipients": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#af97e15368d188837ff02958397822bd0", + "discordpp::ChannelHandle::Type": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ChannelHandle.html#aff3b40ab2690c966532fc4d8709a9e85", + "discordpp::Client::EndCall": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae69be6fa03b2543c639209acd67b6f25", + "discordpp::Client::EndCalls": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a0dc346ff844840166c1cf651070e15b6", + "discordpp::Client::GetCall": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a0bd43f93d0ca78d76d967317edfd0fb4", + "discordpp::Client::GetCalls": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a6a69988948b239e11644699e31ef68c4", + "discordpp::Client::GetCurrentInputDevice": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8062b4e90fd424b61a1c96ea9b78c661", + "discordpp::Client::GetCurrentOutputDevice": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae59bc42c0608dc9b1b8d7dab7eb3e455", + "discordpp::Client::GetInputDevices": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a1ee5c3d6e4efbb49a31177914c789b2b", + "discordpp::Client::GetInputVolume": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8ce90b14a03fb7cb5915c566ea3d27ab", + "discordpp::Client::GetOutputDevices": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a58f864ff0a0c49be34701f8d0f7bcf72", + "discordpp::Client::GetOutputVolume": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7a6696f8d4a101f45bfd41e55d221333", + "discordpp::Client::GetSelfDeafAll": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac1e26e0b769557da89a7f1d7c09f1487", + "discordpp::Client::GetSelfMuteAll": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adf4c02ceb03ab34f2c1c621bfc407cda", + "discordpp::Client::SetAecDump": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3a05b2cafaa546d915a5249c63f4059f", + "discordpp::Client::SetAutomaticGainControl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a818ae7f46b5bd3873dcd51dd3d9fa64d", + "discordpp::Client::SetDeviceChangeCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a31f683bd8df9f6fdcc384e4678301ef0", + "discordpp::Client::SetEchoCancellation": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a1def244b7ecd388902ba5256ce506ca3", + "discordpp::Client::SetEngineManagedAudioSession": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ade08897214152b9acfa79c263e77e366", + "discordpp::Client::SetInputDevice": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad1e898d1486f9dfece15c6913261b66f", + "discordpp::Client::SetInputVolume": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad4358f5baffd9a5f2a6fa74d62459313", + "discordpp::Client::SetNoAudioInputCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a479e60724bf6b0b39b555c1ff8489b9e", + "discordpp::Client::SetNoAudioInputThreshold": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab33f5d70461ee7590b6f3cfccaeb6df4", + "discordpp::Client::SetNoiseSuppression": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae3f6e33b956964525adfa4536bd1fe73", + "discordpp::Client::SetOpusHardwareCoding": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a50f3315b3c13ad6e543b60981976fe33", + "discordpp::Client::SetOutputDevice": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa06fdf131c2105cd06fb79592624678e", + "discordpp::Client::SetOutputVolume": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a61a9321a79479c8b1be1559e2bbdd934", + "discordpp::Client::SetSelfDeafAll": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a59be56ae5752e9f2f0f299bc552282b2", + "discordpp::Client::SetSelfMuteAll": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9c6ef96590533d103a866cb8a99d2669", + "discordpp::Client::SetSpeakerMode": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac269ab57407e3b83e2bb2d30895e666d", + "discordpp::Client::SetThreadPriority": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7cfa5a1c9e9ed708ce2674d885c47d7b", + "discordpp::Client::SetVoiceParticipantChangedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2d9fe7f8d8c2e7a8cfc3442a6259ae1a", + "discordpp::Client::ShowAudioRoutePicker": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a55650612a709132b4801e00002e4e2d5", + "discordpp::Client::StartCall": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aef4f25d761fe198fbe9bc721fc24d83f", + "discordpp::Client::StartCallWithAudioCallbacks": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#abcaa891769f9e912bfa0e06ff7221b05", + "discordpp::Client::AbortAuthorize": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac5d9273ed96e958e322bed0ae19c4c39", + "discordpp::Client::AbortGetTokenFromDevice": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a219c7bca16f34dbdf2bc273cdba0c80b", + "discordpp::Client::Authorize": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace94a58e27545a933d79db32b387a468", + "discordpp::Client::CloseAuthorizeDeviceScreen": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a4fcdd697702d086a8170a2d60a69acb8", + "discordpp::Client::CreateAuthorizationCodeVerifier": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aba6adc1f978e7bf4c5433c560e1ad704", + "discordpp::Client::ExchangeChildToken": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a59f5d9d14f79eb318bf4d57f4e87a5c1", + "discordpp::Client::FetchCurrentUser": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a45b7144dcb203ed420e6bcb20386da39", + "discordpp::Client::GetProvisionalToken": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8003130b6c46e54ac68442483bf0480c", + "discordpp::Client::GetToken": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aaee636f91fb1ea3465157c20313b702c", + "discordpp::Client::GetTokenFromDevice": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a4a9c666b2d30bae0a16f5afd7ccee60d", + "discordpp::Client::GetTokenFromDeviceProvisionalMerge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afd2207590ae7d6f60ee7bbb4fc7c21c8", + "discordpp::Client::GetTokenFromProvisionalMerge": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a41062b7dafa331ddd2320daf1b4b273b", + "discordpp::Client::IsAuthenticated": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afd970e3e340c8c907d4b0481240e12fc", + "discordpp::Client::OpenAuthorizeDeviceScreen": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af29a13acc992a75fc0870051ff68575b", + "discordpp::Client::ProvisionalUserMergeCompleted": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af836d40617bb6d74648309c768234184", + "discordpp::Client::RefreshToken": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a187af0f99f94b3b9a4ad4302f6a443e7", + "discordpp::Client::RegisterAuthorizeRequestCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5f34b873e127a446c9ab549e4588ccd7", + "discordpp::Client::RemoveAuthorizeRequestCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab7e48864b0cedf3e8572a228ca401f2a", + "discordpp::Client::RevokeToken": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a30ccea6366efaf0b884efcdcc28a6f2d", + "discordpp::Client::SetAuthorizeDeviceScreenClosedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a27d1ac5d868a93a141254c59023a13bb", + "discordpp::Client::SetGameWindowPid": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aae200241796a779e4b9e8cb2d22138c2", + "discordpp::Client::SetTokenExpirationCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aab5bfc76809ea22e79f2f7a067ac4519", + "discordpp::Client::UnmergeIntoProvisionalAccount": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2da21ae8a3015e0e5e42c1a7226b256f", + "discordpp::Client::UpdateProvisionalAccountDisplayName": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7485979ab2d4c533b75f8efd5e50bc60", + "discordpp::Client::UpdateToken": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a606b32cef7796f7fb91c2497bc31afc4", + "discordpp::Client::CanOpenMessageInDiscord": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae2aac143a691091691c5cc75aa07dace", + "discordpp::Client::DeleteUserMessage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2d5710f9d38ec0cf077d700365833cb3", + "discordpp::Client::EditUserMessage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a822a3a4a60b73211297e5f43ed92ba23", + "discordpp::Client::GetChannelHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a6ab6f4217d04d84935d012af88ce5752", + "discordpp::Client::GetLobbyMessagesWithLimit": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a0586192e85caf548b8b321f1cb21301f", + "discordpp::Client::GetMessageHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7825220b28952a2156bd0e46db40ea5c", + "discordpp::Client::GetUserMessageSummaries": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a32dafc20ff1f99b019e40bdc81f46dde", + "discordpp::Client::GetUserMessagesWithLimit": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a054a758a76c5873b38a4d79651a5f26c", + "discordpp::Client::OpenMessageInDiscord": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a66b8f85b14403a5d5ea125f39aa6e1b1", + "discordpp::Client::SendLobbyMessage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a779e0483f51dc99f0db3dd761d22ab6f", + "discordpp::Client::SendLobbyMessageWithMetadata": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a1b94019d64e9788d842b28ff8982eec9", + "discordpp::Client::SendUserMessage": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3cf9d2b1b5a4a61dcad995dfc1009703", + "discordpp::Client::SendUserMessageWithMetadata": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa4604284f76686f96e1d9d6ecb8e374c", + "discordpp::Client::SetMessageCreatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28325a8e8c688a84ac851da4bc86e148", + "discordpp::Client::SetMessageDeletedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a2b6079eded10bea29abbb9695702637b", + "discordpp::Client::SetMessageUpdatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa01cf3c15403f29780dabfcfaf3b1dcd", + "discordpp::Client::SetShowingChat": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#acdf400ecb926392d1a110da73152b594", + "discordpp::Client::AddLogCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af78996cff24a40f5dc7066beed16692c", + "discordpp::Client::AddVoiceLogCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac4f9b459f853ef7270d1bfc12509ede5", + "discordpp::Client::Connect": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a873a844c7c4c72e9e693419bb3e290aa", + "discordpp::Client::Disconnect": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a115b0be168a18da1119e522abaa92106", + "discordpp::Client::GetStatus": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aff09cced22a1b08cbd85b3ef25aa5f22", + "discordpp::Client::OpenConnectedGamesSettingsInDiscord": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a24f268f5eebe9919a3f774354eb577e0", + "discordpp::Client::SetApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad452335c06b28be0406dab824acccc49", + "discordpp::Client::SetLogDir": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a23bd5802dfa3072201ea864ee839c001", + "discordpp::Client::SetStatusChangedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9d0e8824570b93149d5d1c65bb3fb97f", + "discordpp::Client::SetVoiceLogDir": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a48c6b7e8bbc2b632a935acafc6a5f7a7", + "discordpp::Client::CreateOrJoinLobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b4e195555ecaa89ccdfc0acd28d3512", + "discordpp::Client::CreateOrJoinLobbyWithMetadata": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a5c84fa76c73cf3c0bfd68794ca5595c1", + "discordpp::Client::GetGuildChannels": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adba1e5a83c219a9c4f6dab1657778017", + "discordpp::Client::GetLobbyHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aee1d8d43efe5caa2d97b64ab699e5854", + "discordpp::Client::GetLobbyIds": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a33c341b651e3a11b1213ebafa7e4c0ae", + "discordpp::Client::GetUserGuilds": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aac1ec02df6074ed9213ce230e6a42fe1", + "discordpp::Client::JoinLinkedLobbyGuild": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a54ec764e72e168de419ac14e24e8fc60", + "discordpp::Client::LeaveLobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8c78f797240b35d721383461a2e62926", + "discordpp::Client::LinkChannelToLobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3114d58d50d4d2cb5752d95e121315d4", + "discordpp::Client::SetLobbyCreatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace81d150a0217d2436af8263860a01f3", + "discordpp::Client::SetLobbyDeletedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a89f6b85edcbab677f7f68c0a82605901", + "discordpp::Client::SetLobbyMemberAddedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae5388407cfc02f25919cb9fbe14a8cb8", + "discordpp::Client::SetLobbyMemberRemovedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a90be7a74cdcbcc4daa0c07f7a2a568c5", + "discordpp::Client::SetLobbyMemberUpdatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ade147c60761a6bb31361f878e812e6b4", + "discordpp::Client::SetLobbyUpdatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ada626c060ba77db627152a8c03222843", + "discordpp::Client::UnlinkChannelFromLobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a28f78a6fe46eb11eb54ee9b53fa94ffe", + "discordpp::Client::AcceptActivityInvite": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad12cf35065e4d2b303ee470af7c6ef37", + "discordpp::Client::ClearRichPresence": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9a7901ecde99d17a357e4113115b5e6b", + "discordpp::Client::RegisterLaunchCommand": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a024d7222931fdcb7d09c2b107642ecab", + "discordpp::Client::RegisterLaunchSteamApplication": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a45b2c791c5b06f77d457dacb53dfba40", + "discordpp::Client::SendActivityInvite": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afc14e98fc070399895739da6d53efa60", + "discordpp::Client::SendActivityJoinRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a6c16b1cd68b4a3ce198575a7efb3a87b", + "discordpp::Client::SendActivityJoinRequestReply": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adb88f1aa7976f4e9bafa8db4e533df07", + "discordpp::Client::SetActivityInviteCreatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3b4e37a222a8662506d763514774bedc", + "discordpp::Client::SetActivityInviteUpdatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab212b92fff59a4e439ebf8256d4dd047", + "discordpp::Client::SetActivityJoinCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a587d1c6d0352eba397c888987aa58418", + "discordpp::Client::SetActivityJoinWithApplicationCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ade2feb833e0e06a51dbec8f12eba6396", + "discordpp::Client::SetOnlineStatus": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a99626f047f37f3caa4b88cf8675b3412", + "discordpp::Client::UpdateRichPresence": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af0a85e30f2b3d8a0b502fd23744ee58e", + "discordpp::Client::AcceptDiscordFriendRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af9bec645e18a2d4db4aa2951157e6993", + "discordpp::Client::AcceptGameFriendRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae6f5544e7890b7f6af2cc5780dab4426", + "discordpp::Client::BlockUser": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#add4a917c8382e411d5a55737c9edc8ad", + "discordpp::Client::CancelDiscordFriendRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9702a2da9a5f3054ccb45fd3a1ce2eeb", + "discordpp::Client::CancelGameFriendRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a210d3fef24a5c2d3847a38e2c42d4b58", + "discordpp::Client::GetRelationshipHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab8dd47239817ffbe268db4c596de1346", + "discordpp::Client::GetRelationships": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad481849835cd570f0e03adafcf90125d", + "discordpp::Client::GetRelationshipsByGroup": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a9f7898d3f3d1ec92b06c662df70746d5", + "discordpp::Client::RejectDiscordFriendRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a6d9a984cb0c481508e0049724dcd9d0f", + "discordpp::Client::RejectGameFriendRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad3711b8ec4f70e4ad28592f04fbdab30", + "discordpp::Client::RemoveDiscordAndGameFriend": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#aa6d393a3d98ec5d06faef49a57d1a89b", + "discordpp::Client::RemoveGameFriend": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a692ac69d3d33f04f3a06f2ed53e28765", + "discordpp::Client::SearchFriendsByUsername": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a486180a3b8ec7ab74998cc13e3956d82", + "discordpp::Client::SendDiscordFriendRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a12c3ecc8fb47d84859796cce7adfeb13", + "discordpp::Client::SendDiscordFriendRequestById": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afa9bc8870b4a4aef6f247782f494eeca", + "discordpp::Client::SendGameFriendRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#afbce38bed626ec20cb05ea16fad79d57", + "discordpp::Client::SendGameFriendRequestById": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a66686efa9a445af4696ef083fe95e885", + "discordpp::Client::SetRelationshipCreatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a04ef0cd95c7d88e97fc236603544c7ff", + "discordpp::Client::SetRelationshipDeletedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a733fedb02d2b857194646949618f58c9", + "discordpp::Client::UnblockUser": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#acd8dc8d7e5c202256a219a65cfedae47", + "discordpp::Client::GetCurrentUser": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7edea2a3dfe9ae560d5fa5ba8663b5cc", + "discordpp::Client::GetCurrentUserV2": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae52259570ba657252d91f5580636fe5d", + "discordpp::Client::GetDiscordClientConnectedUser": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8af6f7ed750a87f36c68774ca76851f7", + "discordpp::Client::GetUser": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab6f98c5e36bc4070b3ee0e45fae12f2b", + "discordpp::Client::SetRelationshipGroupsUpdatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#af12441ef091298f968075b7190851098", + "discordpp::Client::SetUserUpdatedCallback": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3559f375165acedc6d6677ef599b3a4a", + "discordpp::Client::Error": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac8d8140b9e1251e59e59a267f8a6295f", + "discordpp::Client::Status": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a6f714c5d6aebefa91c1ff8ba97bcce22", + "discordpp::Client::Thread": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adadf74b4796b37cf738bdaa61993f0ba", + "discordpp::Client::Client": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91716140c699d8ef0bdf6bfd7ee0ae13", + "discordpp::Client::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a3fc6d6655e04c98a39ed5e79cfdb0cba", + "discordpp::Client::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ad35d2a626e64f4313883ce22cd45efb7", + "discordpp::Client::GetApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#abe66dd4bf3cbc27961d41e8e11015ff5", + "discordpp::Client::SetHttpRequestTimeout": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ae5fe2518b0b1b05ee1745ab0a79096b9", + "discordpp::Client::ErrorToString": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ab40c4c45450eb5d5f14346612b57ddae", + "discordpp::Client::GetDefaultAudioDeviceId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a91c6f22803d437153ad192f3395ac47c", + "discordpp::Client::GetDefaultCommunicationScopes": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a71499da752fbdc2d4326ae0fd36c0dd1", + "discordpp::Client::GetDefaultPresenceScopes": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a7648bd1d2f7d9a86ebd0edb8bef12b5c", + "discordpp::Client::GetVersionHash": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a4231cecf92efab1e46185cd119455632", + "discordpp::Client::GetVersionMajor": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ace35d89ad23584649788d16de3aab579", + "discordpp::Client::GetVersionMinor": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#ac314e5038e06b565672c5ca58fbb9211", + "discordpp::Client::GetVersionPatch": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a8b3242e835241feb7a4ec70257abb5fc", + "discordpp::Client::StatusToString": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#a0e56ea9f1f59c1fb36f521e1f02d146a", + "discordpp::Client::ThreadToString": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1Client.html#adca1c23392db46c00de6c921ca61e9ae", + "discordpp::ClientCreateOptions::ClientCreateOptions": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#ae655ad66ba64f443496c158307cc77b4", + "discordpp::ClientCreateOptions::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#afde9899a61abe4c173bdf7beb2a6e18d", + "discordpp::ClientCreateOptions::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#aa73bfe976c33cb916002445ae5507961", + "discordpp::ClientCreateOptions::WebBase": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#aae3dfb794e5cb64a5b7c4185fc21be4a", + "discordpp::ClientCreateOptions::SetWebBase": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#aef30d2e0aaae55cf363ce22606b6b857", + "discordpp::ClientCreateOptions::ApiBase": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#ab75bc5cb2fba2679be638eaa77d33b53", + "discordpp::ClientCreateOptions::SetApiBase": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#ab8ea2ea26aafb92ebd54b443ae839595", + "discordpp::ClientCreateOptions::ExperimentalAudioSystem": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#a4a5859333e09347aef91f9bbda4ca08b", + "discordpp::ClientCreateOptions::SetExperimentalAudioSystem": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#adfd12e55eff0ae99f54b336f8d256770", + "discordpp::ClientCreateOptions::ExperimentalAndroidPreventCommsForBluetooth": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#aa9a720bd4f7f3ff12d0c1a3054d00a85", + "discordpp::ClientCreateOptions::SetExperimentalAndroidPreventCommsForBluetooth": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientCreateOptions.html#af979cbdbf18096496189143d34101d7c", + "discordpp::ClientResult::ClientResult": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a685015ca8d29a50d47fd1ed5d469ac2e", + "discordpp::ClientResult::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#ab04a08a811079846f4c963d733e806de", + "discordpp::ClientResult::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#ad719dc037d1c61b9b33499f85af9e884", + "discordpp::ClientResult::ToString": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a7a5491b45a35dfdbd4228e1cb557a879", + "discordpp::ClientResult::Type": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a4fb34d5bb67e2e742e1878c6d07a05ac", + "discordpp::ClientResult::SetType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#af1e9e03e1aaaababc5d1ef2967506c3b", + "discordpp::ClientResult::Error": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a3175ab59cd9268a3b2c6484129c89e04", + "discordpp::ClientResult::SetError": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#ae093e56f212aaaf6a8cb84fe6b9d68ee", + "discordpp::ClientResult::ErrorCode": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#ab361cb3d3381401f0d73736d5f49b3f5", + "discordpp::ClientResult::SetErrorCode": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a20891b4a11298b10ac5abe6d915daeed", + "discordpp::ClientResult::Status": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#adbd9aeece922b420d020987e8c4afbcd", + "discordpp::ClientResult::SetStatus": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a2b1a904eb2796a8b3ddc4dde9c3f440f", + "discordpp::ClientResult::ResponseBody": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#acc890e84f91a54bfa4e6257442fba9ed", + "discordpp::ClientResult::SetResponseBody": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a5bafd06d48db6b17aaebda160e1e83da", + "discordpp::ClientResult::Successful": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#aef3b1aca3cd156daf488ca13ae87313b", + "discordpp::ClientResult::SetSuccessful": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a7842714bc39a9145b9a38d5d480f4165", + "discordpp::ClientResult::Retryable": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a0d220638f4a36c0b8b731f601e0ac02d", + "discordpp::ClientResult::SetRetryable": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#a066337d0ef9aa69ae813b07c7139182f", + "discordpp::ClientResult::RetryAfter": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#ac739adca52b90d6b4cbed5753c30fd65", + "discordpp::ClientResult::SetRetryAfter": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1ClientResult.html#abb747da57e424ff9c084d48711aa476b", + "discordpp::DeviceAuthorizationArgs::DeviceAuthorizationArgs": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1DeviceAuthorizationArgs.html#a2b45ff23c20c17bc7ad82cb5753b6031", + "discordpp::DeviceAuthorizationArgs::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1DeviceAuthorizationArgs.html#ad3c66e7bf84bc9544614d3879c439797", + "discordpp::DeviceAuthorizationArgs::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1DeviceAuthorizationArgs.html#a7557758d3e23ae2ca694f8e7573f4078", + "discordpp::DeviceAuthorizationArgs::ClientId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1DeviceAuthorizationArgs.html#a5c8d15b74d6e5fdcfaed01e6e58a9377", + "discordpp::DeviceAuthorizationArgs::SetClientId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1DeviceAuthorizationArgs.html#a81950ebb58f086de565f128c07874bd6", + "discordpp::DeviceAuthorizationArgs::Scopes": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1DeviceAuthorizationArgs.html#ab68f041359323131d7bb33228ddd2f15", + "discordpp::DeviceAuthorizationArgs::SetScopes": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1DeviceAuthorizationArgs.html#a9968629e4bfeb8e35958cdee2346c410", + "discordpp::GuildChannel::GuildChannel": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#ac83eedfa1d9434dfa245ae39bb3a0915", + "discordpp::GuildChannel::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#ab829759413e87e41dcbaa948984a2700", + "discordpp::GuildChannel::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a600757164689b2da65291a9973ea14be", + "discordpp::GuildChannel::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a2fe6d0dbf525d4ff07ee5b3b11919c9b", + "discordpp::GuildChannel::SetId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a54d49b0f134e4b88bf30f2ad4bc5af53", + "discordpp::GuildChannel::Name": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a60ea639d62e0a24b2a59ff7afce35a97", + "discordpp::GuildChannel::SetName": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a32e619dcb109fe8af27de24c0196a0f5", + "discordpp::GuildChannel::Type": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a9cf329c0ae46c5509666543839cbfa33", + "discordpp::GuildChannel::SetType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a6fbbd299823f1e1342f424daf0d27c1b", + "discordpp::GuildChannel::Position": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#ae0749779d6a9b708af2bfa1c06bc40ba", + "discordpp::GuildChannel::SetPosition": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a81853ebe5e40edb347ce10e289f8e781", + "discordpp::GuildChannel::ParentId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a56a251fe1df193a95de654df96e0b8bf", + "discordpp::GuildChannel::SetParentId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#af789881615443c828d3c4b73ee34ba48", + "discordpp::GuildChannel::IsLinkable": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#ad69573b499384939e41cc45e6cbc151c", + "discordpp::GuildChannel::SetIsLinkable": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a07c70db9e7d0093cc2e68aa8c9cf2d4f", + "discordpp::GuildChannel::IsViewableAndWriteableByAllMembers": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a0a5655eda07dea3fc4b8643334fdee28", + "discordpp::GuildChannel::SetIsViewableAndWriteableByAllMembers": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#af341737fe3de7b6eb4be6798c032007d", + "discordpp::GuildChannel::LinkedLobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#a24a02a757b1c34f057776f2e633ab624", + "discordpp::GuildChannel::SetLinkedLobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildChannel.html#ae7c87ec3ca9f274249b19ad19c7c1282", + "discordpp::GuildMinimal::GuildMinimal": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildMinimal.html#a89360851ff87ef134f330ad5f774df28", + "discordpp::GuildMinimal::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildMinimal.html#a443d687cb668f4bbbc384d4b77c3ce5b", + "discordpp::GuildMinimal::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildMinimal.html#a96a88e4a9e3d5b7d0a7ddb2d80bd8a41", + "discordpp::GuildMinimal::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildMinimal.html#ae31b9a80af6e404325dda37007056263", + "discordpp::GuildMinimal::SetId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildMinimal.html#a31b7a83ae5ffe84eb103e249e6a0e0dc", + "discordpp::GuildMinimal::Name": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildMinimal.html#a27c6a10f72b07d46dc14292115f9ab72", + "discordpp::GuildMinimal::SetName": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1GuildMinimal.html#a20d39f5596a1287a825e2154098ea4b7", + "discordpp::LinkedChannel::LinkedChannel": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#ae8e8139c85beb64be681007a0f2979b0", + "discordpp::LinkedChannel::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#a4871deb8204544bc92fabcec8580ab4f", + "discordpp::LinkedChannel::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#a0b0201e9d199892751e2af3c004d1551", + "discordpp::LinkedChannel::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#ac7ea820618d014feb7b210b0b4209752", + "discordpp::LinkedChannel::SetId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#abd15185be304d01a031579952f1a99f9", + "discordpp::LinkedChannel::Name": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#a73c69f821057e52e8afa3748de5014d2", + "discordpp::LinkedChannel::SetName": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#a46b989be332394b185506a3939f2ab87", + "discordpp::LinkedChannel::GuildId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#a2c08e7248f5a9de42d6105aef7cfed86", + "discordpp::LinkedChannel::SetGuildId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedChannel.html#a7a0af9c2e029482316217ee037e9ab7e", + "discordpp::LinkedLobby::LinkedLobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedLobby.html#abfc16fc4cf32f78c0df01e3d7a9acfcf", + "discordpp::LinkedLobby::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedLobby.html#aa74929f2d09347cd6a0a7fecc99db543", + "discordpp::LinkedLobby::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedLobby.html#a52bd21ea3c75de085912419b5184c892", + "discordpp::LinkedLobby::ApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedLobby.html#a54f7d406aa6290b623dd201b367d2672", + "discordpp::LinkedLobby::SetApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedLobby.html#a8382002e2a364d578e2175169876ec06", + "discordpp::LinkedLobby::LobbyId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedLobby.html#aac0fd786fa7fa9fab342887a2988cdef", + "discordpp::LinkedLobby::SetLobbyId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LinkedLobby.html#a64345c37d6431a3295127ff9c6e19a7d", + "discordpp::LobbyHandle::LobbyHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#a04cebab69ab0e7fb930346a14a87e843", + "discordpp::LobbyHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#aeb2f52f8bacd7f1ea702e12cb3223cde", + "discordpp::LobbyHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#ada444725f51458471d8cecaba40571b1", + "discordpp::LobbyHandle::GetCallInfoHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#a5a5838f7382f5564024a28d3bc6aeb7a", + "discordpp::LobbyHandle::GetLobbyMemberHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#a9e64cab5c6cfbf7477c6d868a3b47c0d", + "discordpp::LobbyHandle::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#acb6ea038ce7341422ae9217b30b5560a", + "discordpp::LobbyHandle::LinkedChannel": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#a0b6d46073d591c62697b39df215bf297", + "discordpp::LobbyHandle::LobbyMemberIds": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#acf329ee87e0f2f9f14b61adbd931b9c7", + "discordpp::LobbyHandle::LobbyMembers": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#aae5593b064ff4a67157f462ce306342f", + "discordpp::LobbyHandle::Metadata": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyHandle.html#a5e512d30cbea86ccd41fe9e6ca8b485d", + "discordpp::LobbyMemberHandle::LobbyMemberHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyMemberHandle.html#ac21fbbd2626f79180dbbf8708940bc5d", + "discordpp::LobbyMemberHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyMemberHandle.html#a4aad374e7a21c052778fb3e0776efbec", + "discordpp::LobbyMemberHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyMemberHandle.html#ac27f9977b230f4fc4e8bdd47272dac45", + "discordpp::LobbyMemberHandle::CanLinkLobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyMemberHandle.html#aa4e1ce775f007d90762fd9795fb25a45", + "discordpp::LobbyMemberHandle::Connected": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyMemberHandle.html#a44bb06829376a179284e864d6232ab5c", + "discordpp::LobbyMemberHandle::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyMemberHandle.html#a3799195d808effd0be3c4d2c01b47b78", + "discordpp::LobbyMemberHandle::Metadata": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyMemberHandle.html#ade3709e5e5f44f53428b386924416f6b", + "discordpp::LobbyMemberHandle::User": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1LobbyMemberHandle.html#a9e09c3cc6ea4ee0442e367683a999118", + "discordpp::MessageHandle::MessageHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae25595b43bc74b0c4c92c5165d16382f", + "discordpp::MessageHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a62b6440741052c8593db05c66b9fb9c7", + "discordpp::MessageHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a118a3c041afcc84879451374c29f7cef", + "discordpp::MessageHandle::AdditionalContent": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#af4497491a95fda65402b6acf7a8b42e5", + "discordpp::MessageHandle::ApplicationId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a37865567c5dba319fd6baf5cff6d5590", + "discordpp::MessageHandle::Author": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a5e5b89806a8f03f2cf80ad9e1c10665f", + "discordpp::MessageHandle::AuthorId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a0f85af33c2322a9afec892c2bcdce7c6", + "discordpp::MessageHandle::Channel": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a1e32d54fe034821cc9bc9fd626d7f069", + "discordpp::MessageHandle::ChannelId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ad8b8ccfab8c8e06dc9fc3201284db1ce", + "discordpp::MessageHandle::Content": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a7f80e02307db527ab2a55c426ce13d8b", + "discordpp::MessageHandle::DisclosureType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#abefb9be7836951a6acf78a4bb1676638", + "discordpp::MessageHandle::EditedTimestamp": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a781f821c3aa2802681d6f6f33c2443b4", + "discordpp::MessageHandle::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a59f6a4468a3926ffcdf8d0ffff1e4910", + "discordpp::MessageHandle::Lobby": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#aa9ab0eb82985680399dcc1d5c0ea5605", + "discordpp::MessageHandle::Metadata": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a881df6c0ee36e8009a7b516da4a065d5", + "discordpp::MessageHandle::RawContent": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#aa63a42124cd944ca2c67bc24a22e7421", + "discordpp::MessageHandle::Recipient": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#a369267d15083885fbcf5edfc21ecf1e7", + "discordpp::MessageHandle::RecipientId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ab1a22f5da27cfe4cce9ddcefeed33a6d", + "discordpp::MessageHandle::SentFromGame": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#ae3f3678ea0c19c655b85f5670e0fa88a", + "discordpp::MessageHandle::SentTimestamp": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1MessageHandle.html#aba9db7eb0500371f4bb8666c912eeca9", + "discordpp::RelationshipHandle::RelationshipHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a7da36b15ad0b7d38ba658a622e9ded77", + "discordpp::RelationshipHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#af1079f5dcd7cc03bd51b01ab1a4e7ea8", + "discordpp::RelationshipHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#aa7781c9bd3e7a06a9437ddde999ecc65", + "discordpp::RelationshipHandle::DiscordRelationshipType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a5fecfb79a4a2b6f3dc5f73b09d0c3881", + "discordpp::RelationshipHandle::GameRelationshipType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#aa60146eb72ede07e3e615565f61f97eb", + "discordpp::RelationshipHandle::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#a3a14b6cfa9a9fffc61e500d8329be587", + "discordpp::RelationshipHandle::IsSpamRequest": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#acf4d21fb62cf80ac10afa9d10308777a", + "discordpp::RelationshipHandle::User": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1RelationshipHandle.html#ad87172b7df999ab56991c1be598e995e", + "discordpp::UserApplicationProfileHandle::UserApplicationProfileHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#a479b07c5bae65ca04771ca0f05c6ebdd", + "discordpp::UserApplicationProfileHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#a93ab72bd482c41ae7c931776da25dfa7", + "discordpp::UserApplicationProfileHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#a3557027837de41303aa5a43dff06e84d", + "discordpp::UserApplicationProfileHandle::AvatarHash": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#aa71e13c943d4b296aa22891a88a87a82", + "discordpp::UserApplicationProfileHandle::Metadata": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#a3a2482dcae30353630ceab2e2508673e", + "discordpp::UserApplicationProfileHandle::ProviderId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#acc5774b55ad2a83a7cd985bdc4a0cd07", + "discordpp::UserApplicationProfileHandle::ProviderIssuedUserId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#a90a1a48c9f290f9d89c7da626623dcbb", + "discordpp::UserApplicationProfileHandle::ProviderType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#af42fdb09666b62865c9ff2ed58df78be", + "discordpp::UserApplicationProfileHandle::Username": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserApplicationProfileHandle.html#a2d7c710f95968dc9c5391bb2207bed3b", + "discordpp::UserHandle::AvatarType": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a9cbf5933bc6181eb1900574f6eda9b18", + "discordpp::UserHandle::UserHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a587bcc838e42dc5c56f840a350070707", + "discordpp::UserHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a08cacd9bb2ced65159c10e144e115e07", + "discordpp::UserHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#ada58eea04b10ebdacd50bbf4dfdf55c6", + "discordpp::UserHandle::Avatar": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a3b331158dc7086b62f4352ae80f2a2eb", + "discordpp::UserHandle::AvatarUrl": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a9cb876d37f8975521e0a7b3bca96aaa0", + "discordpp::UserHandle::DisplayName": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#af6447fa2011bfa4fcd7e55bc56847f5c", + "discordpp::UserHandle::GameActivity": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#ae263140d5170206864aa443a3c47935c", + "discordpp::UserHandle::GlobalName": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#aaf8a9f1f396d0a9a83ce0856d92a5a68", + "discordpp::UserHandle::Id": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a038747eb762742288fcc1bde2cf5a76d", + "discordpp::UserHandle::IsProvisional": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#aa2999e91f84540d9bd957d9d0d294c95", + "discordpp::UserHandle::Relationship": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#affa211bcf6a19a17f188f142168c2288", + "discordpp::UserHandle::Status": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a3ec233e43f67892c3ff22eb4199e6590", + "discordpp::UserHandle::UserApplicationProfiles": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a64b7fee0afff58b87b63ac385ef13866", + "discordpp::UserHandle::Username": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#a0eda41fe18b50bce373fb5a1b88cb411", + "discordpp::UserHandle::AvatarTypeToString": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserHandle.html#ae8c9b5d2edc5fc9a53baf74060ba7185", + "discordpp::UserMessageSummary::UserMessageSummary": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserMessageSummary.html#a7284a4debea92bfffc4db7781874449a", + "discordpp::UserMessageSummary::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserMessageSummary.html#af40e3a499b10d7a7844d743fc22f41a6", + "discordpp::UserMessageSummary::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserMessageSummary.html#a0853d32ccc92a97be7221492e4ffe93f", + "discordpp::UserMessageSummary::LastMessageId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserMessageSummary.html#a99e5028531ba53003eac068947b116c7", + "discordpp::UserMessageSummary::UserId": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1UserMessageSummary.html#ad271915d85bac0e6227018aacd7bacca", + "discordpp::VADThresholdSettings::VADThresholdSettings": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VADThresholdSettings.html#a0d1ec1c699d7b0d2fa55a69d26552277", + "discordpp::VADThresholdSettings::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VADThresholdSettings.html#a067f3518e79b80447cb9e7bd0a1bbf63", + "discordpp::VADThresholdSettings::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VADThresholdSettings.html#acd1182b2c6703a999ba1fe337036d0f3", + "discordpp::VADThresholdSettings::VadThreshold": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VADThresholdSettings.html#a92ddf7ac4143d02c5fb895b11474c15c", + "discordpp::VADThresholdSettings::SetVadThreshold": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VADThresholdSettings.html#ad95294ad835cf0282fe11595f7d94cfa", + "discordpp::VADThresholdSettings::Automatic": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VADThresholdSettings.html#a7f28f03efbb001bc0468762a7f3a5b5c", + "discordpp::VADThresholdSettings::SetAutomatic": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VADThresholdSettings.html#ab5f0db1ed7a73a3f7601d187bc958e86", + "discordpp::VoiceStateHandle::VoiceStateHandle": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#aad2d4454b6677d82721128b0cd98a2d8", + "discordpp::VoiceStateHandle::operator=": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#a50e7dd192dd4a3cb7dd2485802ec6b18", + "discordpp::VoiceStateHandle::operator bool": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#a724fc1220c513d694a6cd9a4542030a1", + "discordpp::VoiceStateHandle::SelfDeaf": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#a9fd4ac5fb813b926d1336fc65b440f42", + "discordpp::VoiceStateHandle::SelfMute": "https://discord.com/developers/docs/social-sdk/classdiscordpp_1_1VoiceStateHandle.html#a5476a6e8d5e9092a153b4646371a9f3f", + "discordpp::DiscordObjectState": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#aea9100375a03b1208c37cc43c4982242", + "discordpp::ActivityActionTypes": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#afff4c537aa07d869a4576c87a28f36b8", + "discordpp::ActivityPartyPrivacy": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a05688bf2282548dba7cca3fd26f8dbd4", + "discordpp::ActivityTypes": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a6c76a8cbbc9270f025fd6854d5558660", + "discordpp::StatusDisplayTypes": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a9a99c608f6825577fbcd89ded0378e25", + "discordpp::ActivityGamePlatforms": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a591ad54a3029d15c9c58d6064770fe87", + "discordpp::ErrorType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ae19a5aed498bc40359e4d89d14ffb446", + "discordpp::HttpStatusCode": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a12b04d48d8ea98ec007270a10e0c88ba", + "discordpp::AuthenticationCodeChallengeMethod": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a1c123930bb42c6f9f77f18b5593ad876", + "discordpp::IntegrationType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a5307c5c42ea96ec19f79e679a47c84be", + "discordpp::ChannelType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a1b68b260e8659da9a7792b2b2046fe58", + "discordpp::AdditionalContentType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#aee4b786af6ac9cd4ade73eb2f873177b", + "discordpp::AudioSystem": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a566c99cfc3b8ee94ca322d35bc008633", + "discordpp::AudioModeType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ad12f4e0df799c97f417320ef861fcffb", + "discordpp::RelationshipType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a28fc5199b9211c24124c06f30c1d0cbb", + "discordpp::ExternalIdentityProviderType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#aa206a1e72b76e940f53bb3ff0dbca6ca", + "discordpp::StatusType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a7e874dde2b41420ed5d26d4bc3ea5b43", + "discordpp::DisclosureTypes": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a0d3a4099faa1d5870eb8a49828e05d34", + "discordpp::AuthorizationTokenType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a1968a8b2eabcbe41a254cc88ab330a89", + "discordpp::AuthenticationExternalAuthType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a4783ed8cd71c53439960e035527988ad", + "discordpp::LoggingSeverity": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a4b50332ce64ddd00391c442b60c99a35", + "discordpp::RelationshipGroupType": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a503ed2f7b0bfbd435321a0e8b1dfba35", + "discordpp::RunCallbacks": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#ab5dd8cf274f581ee1885de5816be3c29", + "discordpp::EnumToString": "https://discord.com/developers/docs/social-sdk/namespacediscordpp.html#a0fd967a23d2d106ced3d6669b9a810ad" +} \ No newline at end of file diff --git a/tools/eslint.config.js b/tools/eslint.config.js new file mode 100644 index 0000000000..b3584aa6ac --- /dev/null +++ b/tools/eslint.config.js @@ -0,0 +1,20 @@ +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; +import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"; + +export default tseslint.config( + eslint.configs.recommended, + eslintPluginPrettierRecommended, + ...tseslint.configs.recommended, + { + rules: { + "prettier/prettier": "error", + }, + languageOptions: { + globals: { + console: true, + process: true, + }, + }, + }, +); diff --git a/tools/find-unused-images.js b/tools/find-unused-images.js new file mode 100644 index 0000000000..6b01fe8a17 --- /dev/null +++ b/tools/find-unused-images.js @@ -0,0 +1,323 @@ +#!/usr/bin/env node + +import fs from "fs"; +import path from "path"; + +// Configuration +const CONFIG = { + imagesDir: "static/images", + docsDir: "docs", + rootFiles: ["."], // Check root markdown files too + imageExtensions: [".gif", ".png", ".jpg", ".jpeg", ".webp", ".svg"], + markupExtensions: [".md", ".mdx", ".js", ".ts", ".tsx", ".jsx"], // Include JS/TS for potential imports + verbose: false, +}; + +class UnusedImageFinder { + constructor(config) { + this.config = config; + this.stats = { + totalImages: 0, + referencedImages: 0, + unusedImages: 0, + totalMarkupFiles: 0, + }; + this.imageReferences = new Set(); // Track which images are referenced + } + + log(message, level = "info") { + if (!this.config.verbose && level === "debug") return; + const prefix = level === "error" ? "❌" : level === "warn" ? "⚠️" : level === "debug" ? "🔍" : ""; + if (prefix) { + console.error(`${prefix} ${message}`); + } else { + console.error(message); // Use stderr for logging, stdout for results + } + } + + // Recursively find all files with given extensions + findFiles(dir, extensions) { + const files = []; + + const walk = (currentDir) => { + try { + const items = fs.readdirSync(currentDir); + for (const item of items) { + const fullPath = path.join(currentDir, item); + let stat; + try { + stat = fs.statSync(fullPath); + } catch { + continue; // Skip inaccessible files + } + + if (stat.isDirectory()) { + // Skip node_modules and other common ignore patterns + if (!item.startsWith(".") && item !== "node_modules") { + walk(fullPath); + } + } else if (extensions.some((ext) => item.toLowerCase().endsWith(ext.toLowerCase()))) { + files.push(fullPath); + } + } + } catch (error) { + this.log(`Cannot read directory ${currentDir}: ${error.message}`, "warn"); + } + }; + + walk(dir); + return files; + } + + // Get all images in the repository + getAllImages() { + const imageFiles = this.findFiles(this.config.imagesDir, this.config.imageExtensions); + this.stats.totalImages = imageFiles.length; + this.log(`Found ${imageFiles.length} images`, "debug"); + return imageFiles; + } + + // Get all markup files that could reference images + getAllMarkupFiles() { + const markupFiles = []; + + // Check docs directory + if (fs.existsSync(this.config.docsDir)) { + markupFiles.push(...this.findFiles(this.config.docsDir, this.config.markupExtensions)); + } + + // Check root files + const rootMarkupFiles = this.findFiles(".", this.config.markupExtensions).filter( + (file) => !file.includes("node_modules") && !file.includes(this.config.docsDir), + ); + markupFiles.push(...rootMarkupFiles); + + this.stats.totalMarkupFiles = markupFiles.length; + this.log(`Found ${markupFiles.length} markup files`, "debug"); + return markupFiles; + } + + // Extract image references from a file's content + extractImageReferences(filePath, content) { + const references = new Set(); + + // Pattern 1: Markdown image syntax ![alt](path) + const markdownImagePattern = /!\[[^\]]*\]\(([^)]+)\)/g; + let match; + while ((match = markdownImagePattern.exec(content)) !== null) { + references.add(this.normalizeImagePath(match[1], filePath)); + } + + // Pattern 2: HTML img src + const htmlImagePattern = /]+src=["']([^"']+)["'][^>]*>/gi; + while ((match = htmlImagePattern.exec(content)) !== null) { + references.add(this.normalizeImagePath(match[1], filePath)); + } + + // Pattern 3: Import statements (for JS/TS files) + const importPattern = /import\s+[^'"]*['"]([^'"]*\.(?:png|jpg|jpeg|gif|webp|svg))['"];?/gi; + while ((match = importPattern.exec(content)) !== null) { + references.add(this.normalizeImagePath(match[1], filePath)); + } + + // Pattern 4: require() calls for images + const requirePattern = /require\s*\(\s*['"]([^'"]*\.(?:png|jpg|jpeg|gif|webp|svg))['"]?\s*\)/gi; + while ((match = requirePattern.exec(content)) !== null) { + references.add(this.normalizeImagePath(match[1], filePath)); + } + + // Pattern 5: URL() in CSS-like content + const urlPattern = /url\s*\(\s*['"]?([^'"]*\.(?:png|jpg|jpeg|gif|webp|svg))['"]?\s*\)/gi; + while ((match = urlPattern.exec(content)) !== null) { + references.add(this.normalizeImagePath(match[1], filePath)); + } + + return Array.from(references); + } + + // Normalize image paths to match actual file paths + normalizeImagePath(imagePath, referencingFile) { + // Remove query parameters and fragments + imagePath = imagePath.split("?")[0].split("#")[0]; + + // Skip external URLs + if (imagePath.startsWith("http://") || imagePath.startsWith("https://") || imagePath.startsWith("//")) { + return null; + } + + let normalizedPath; + + if (imagePath.startsWith("static/images/")) { + // Absolute reference from project root + normalizedPath = imagePath; + } else if (imagePath.startsWith("images/")) { + // Relative to static/ directory + normalizedPath = `static/${imagePath}`; + } else if (imagePath.startsWith("./images/") || imagePath.startsWith("../")) { + // Relative path - need to resolve based on referencing file location + const referencingDir = path.dirname(referencingFile); + const resolved = path.resolve(referencingDir, imagePath); + const relative = path.relative(".", resolved); + normalizedPath = relative.replace(/\\/g, "/"); // Normalize path separators + } else if (imagePath.startsWith("/")) { + // Absolute path from web root - assume it's in static/ + normalizedPath = `static${imagePath}`; + } else { + // Relative path without explicit prefix + if (imagePath.includes("/")) { + // Has directory structure, likely relative to static/images + normalizedPath = `static/images/${imagePath}`; + } else { + // Just a filename, could be in various locations + normalizedPath = imagePath; + } + } + + return normalizedPath; + } + + // Scan all markup files for image references + scanForImageReferences() { + const markupFiles = this.getAllMarkupFiles(); + + for (const filePath of markupFiles) { + try { + const content = fs.readFileSync(filePath, "utf8"); + const references = this.extractImageReferences(filePath, content); + + for (const ref of references) { + if (ref) { + this.imageReferences.add(ref); + this.log(`${filePath} references: ${ref}`, "debug"); + } + } + } catch (error) { + this.log(`Could not read ${filePath}: ${error.message}`, "warn"); + } + } + + this.log(`Found ${this.imageReferences.size} unique image references`, "debug"); + } + + // Check if an image file is referenced + isImageReferenced(imagePath) { + // Try exact match first + if (this.imageReferences.has(imagePath)) { + return true; + } + + // Try various normalizations + const relativePath = path.relative(".", imagePath).replace(/\\/g, "/"); + if (this.imageReferences.has(relativePath)) { + return true; + } + + // Check if any reference ends with this file's path + const fileName = path.basename(imagePath); + for (const ref of this.imageReferences) { + if (ref.endsWith(imagePath) || ref.endsWith(relativePath) || ref.endsWith(fileName)) { + return true; + } + } + + // Check path variations + const variations = [ + imagePath.replace("static/images/", "images/"), + imagePath.replace("static/", ""), + `/${imagePath}`, + `/${relativePath}`, + ]; + + for (const variation of variations) { + if (this.imageReferences.has(variation)) { + return true; + } + } + + return false; + } + + // Main function to find unused images + findUnusedImages() { + this.log("🔍 Finding unused images in Discord API docs..."); + + // Get all images and references + const allImages = this.getAllImages(); + this.scanForImageReferences(); + + // Find unused images + const unusedImages = []; + + for (const imagePath of allImages) { + if (!this.isImageReferenced(imagePath)) { + unusedImages.push(imagePath); + this.stats.unusedImages++; + } else { + this.stats.referencedImages++; + } + } + + // Output results + for (const unusedImage of unusedImages) { + console.log(unusedImage); // Output to stdout for piping + } + + // Log summary to stderr + this.log(`\n📊 Summary:`); + this.log(` Total images: ${this.stats.totalImages}`); + this.log(` Referenced images: ${this.stats.referencedImages}`); + this.log(` Unused images: ${this.stats.unusedImages}`); + this.log(` Markup files scanned: ${this.stats.totalMarkupFiles}`); + + if (this.stats.unusedImages > 0) { + this.log(`\n💡 To delete unused images: node find-unused-images.js | xargs rm`); + this.log(` Or to see sizes: node find-unused-images.js | xargs ls -lh`); + } + + return unusedImages; + } +} + +// CLI handling +if (import.meta.url === `file://${process.argv[1]}`) { + const args = process.argv.slice(2); + const verbose = args.includes("--verbose") || args.includes("-v"); + + if (args.includes("--help") || args.includes("-h")) { + console.log(` +Discord API Docs - Find Unused Images + +Usage: node find-unused-images.js [options] + +Options: + -v, --verbose Show detailed logging + -h, --help Show this help message + +Output: + Prints unused image paths to stdout (one per line) + Logs summary and progress to stderr + +Examples: + node find-unused-images.js # Find unused images + node find-unused-images.js | wc -l # Count unused images + node find-unused-images.js | xargs ls -lh # Show sizes of unused images + node find-unused-images.js | xargs rm # Delete unused images (careful!) + `); + process.exit(0); + } + + const config = { ...CONFIG, verbose }; + const finder = new UnusedImageFinder(config); + + try { + finder.findUnusedImages(); + } catch (error) { + console.error("❌ Failed to find unused images:", error.message); + if (verbose) { + console.error(error.stack); + } + process.exit(1); + } +} + +export { UnusedImageFinder }; diff --git a/tools/optimize-images.js b/tools/optimize-images.js new file mode 100644 index 0000000000..4aa337a24a --- /dev/null +++ b/tools/optimize-images.js @@ -0,0 +1,733 @@ +#!/usr/bin/env node + +import fs from "fs"; +import path from "path"; +import { exec } from "child_process"; + +// Configuration +const CONFIG = { + imagesDir: "static/images", + docsDir: "docs", + tempDir: "/tmp/image-optimization-backup", + reportsDir: "reports", + dryRun: false, // Set to true to preview changes without executing + verbose: true, +}; + +class ImageOptimizer { + constructor(config) { + this.config = config; + this.stats = { + conversions: { gif: 0, png: 0, jpg: 0, svg: 0 }, + references: { updated: 0, files: 0 }, + sizeBefore: 0, + sizeAfter: 0, + errors: [], + fileDetails: [], + }; + + // Setup report directory + this.reportDir = null; + this.reportHtmlPath = null; + this.setupReportDirectory(); + } + + log(message, level = "info") { + if (!this.config.verbose && level === "debug") return; + const prefix = level === "error" ? "❌" : level === "warn" ? "⚠️" : "✅"; + console.log(`${prefix} ${message}`); + } + + // Setup report directory structure + setupReportDirectory() { + if (this.config.dryRun) return; + + const timestamp = new Date().toISOString().slice(0, 19).replace(/[:-]/g, "").replace("T", "-"); + this.reportDir = path.join(this.config.reportsDir, `optimization-${timestamp}`); + + try { + fs.mkdirSync(this.reportDir, { recursive: true }); + fs.mkdirSync(path.join(this.reportDir, "before"), { recursive: true }); + fs.mkdirSync(path.join(this.reportDir, "after"), { recursive: true }); + + this.reportHtmlPath = path.join(this.reportDir, "comparison.html"); + this.initializeHtmlReport(); + this.log(`Report directory created: ${this.reportDir}`, "debug"); + } catch (error) { + this.log(`Failed to create report directory: ${error.message}`, "warn"); + } + } + + // Initialize HTML report with header + initializeHtmlReport() { + if (!this.reportHtmlPath || this.config.dryRun) return; + + const htmlHeader = ` + + + + + Discord API Docs - Image Optimization Report + + + +
+

📊 Discord API Docs - Image Optimization Report

+

Generated on ${new Date().toLocaleString()}

+
Loading summary...
+
+
+`; + + try { + fs.writeFileSync(this.reportHtmlPath, htmlHeader); + } catch (e) { + this.log(`Failed to initialize HTML report: ${e.message}`, "warn"); + } + } + + // Copy original file to report directory before optimization + copyOriginalToReport(originalPath) { + if (!this.reportDir || this.config.dryRun) return null; + + const filename = path.basename(originalPath); + const beforePath = path.join(this.reportDir, "before", filename); + + try { + fs.copyFileSync(originalPath, beforePath); + this.log(`Copied original ${filename} to report`, "debug"); + return beforePath; + } catch (error) { + this.log(`Failed to copy original ${filename}: ${error.message}`, "debug"); + return null; + } + } + + // Add comparison to HTML report (collect data, don't write HTML yet) + addComparisonToReport(originalPath, convertedPath, originalSize, convertedSize, beforeReportPath) { + if (!this.reportDir || this.config.dryRun || !beforeReportPath) return; + + const filename = path.basename(originalPath); + const afterPath = path.join(this.reportDir, "after", path.basename(convertedPath)); + + try { + // Copy converted file + fs.copyFileSync(convertedPath, afterPath); + + // Calculate savings + const savedBytes = originalSize - convertedSize; + const savingsPercent = ((savedBytes / originalSize) * 100).toFixed(1); + + // Track details for summary and later HTML generation + this.stats.fileDetails.push({ + filename, + originalSize, + convertedSize, + savedBytes, + savingsPercent: parseFloat(savingsPercent), + originalPath, + convertedPath: path.basename(convertedPath), + }); + + this.log(`Added ${filename} to report`, "debug"); + } catch (error) { + this.log(`Failed to add ${filename} to report: ${error.message}`, "debug"); + } + } + + // Recursively find all files with given extensions + findFiles(dir, extensions) { + const files = []; + + const walk = (currentDir) => { + const items = fs.readdirSync(currentDir); + for (const item of items) { + const fullPath = path.join(currentDir, item); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + walk(fullPath); + } else if (extensions.some((ext) => item.toLowerCase().endsWith(ext))) { + files.push(fullPath); + } + } + }; + + walk(dir); + return files; + } + + // Get file size in bytes + getFileSize(filePath) { + try { + return fs.statSync(filePath).size; + } catch { + return 0; + } + } + + // Convert image using ImageMagick + async convertImage(inputPath, outputPath, quality = 85) { + return new Promise((resolve, reject) => { + const command = `magick "${inputPath}" -quality ${quality} "${outputPath}"`; + + if (this.config.dryRun) { + this.log(`[DRY RUN] Would convert: ${inputPath} → ${outputPath}`, "debug"); + resolve(); + return; + } + + exec(command, (error) => { + if (error) { + this.log(`Failed to convert ${inputPath}: ${error.message}`, "error"); + this.stats.errors.push(`Convert failed: ${inputPath} - ${error.message}`); + reject(error); + } else { + this.log(`Converted: ${path.basename(inputPath)} → ${path.basename(outputPath)}`, "debug"); + resolve(); + } + }); + }); + } + + // Optimize SVG using SVGO + async optimizeSvg(filePath) { + return new Promise((resolve, reject) => { + if (this.config.dryRun) { + this.log(`[DRY RUN] Would optimize SVG: ${filePath}`, "debug"); + resolve(); + return; + } + + // Get original size before optimization + const originalSize = this.getFileSize(filePath); + const backupPath = `${filePath}.backup`; + + try { + // Create backup + fs.copyFileSync(filePath, backupPath); + + const command = `pnpm dlx svgo "${filePath}" --output "${filePath}"`; + + exec(command, (error) => { + if (error) { + // Restore backup on error + try { + fs.copyFileSync(backupPath, filePath); + fs.unlinkSync(backupPath); + } catch (restoreError) { + this.log(`Failed to restore SVG backup: ${restoreError.message}`, "warn"); + } + this.log(`Failed to optimize SVG ${filePath}: ${error.message}`, "error"); + this.stats.errors.push(`SVG optimization failed: ${filePath} - ${error.message}`); + reject(error); + return; + } + + // Check if optimization reduced file size + const optimizedSize = this.getFileSize(filePath); + + if (optimizedSize >= originalSize) { + // Optimization made file larger or same size - revert + try { + fs.copyFileSync(backupPath, filePath); + this.log( + `Reverted ${path.basename(filePath)} - SVG optimization increased size (${originalSize} → ${optimizedSize} bytes)`, + "warn", + ); + } catch (revertError) { + this.log(`Failed to revert SVG optimization: ${revertError.message}`, "warn"); + } + } else { + this.log(`Optimized SVG: ${path.basename(filePath)} (${originalSize} → ${optimizedSize} bytes)`, "debug"); + } + + // Clean up backup + try { + fs.unlinkSync(backupPath); + } catch (cleanupError) { + this.log(`Failed to cleanup SVG backup: ${cleanupError.message}`, "debug"); + } + + resolve(); + }); + } catch (backupError) { + this.log(`Failed to create SVG backup: ${backupError.message}`, "warn"); + reject(backupError); + } + }); + } + + // Update image references in markdown files + updateMarkdownReferences(filePath, replacements) { + let content = fs.readFileSync(filePath, "utf8"); + let updated = false; + let changeCount = 0; + + for (const [oldRef, newRef] of replacements) { + const regex = new RegExp(oldRef.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"); + const newContent = content.replace(regex, newRef); + if (newContent !== content) { + content = newContent; + updated = true; + changeCount++; + this.log(` Updated reference: ${oldRef} → ${newRef}`, "debug"); + } + } + + if (updated && !this.config.dryRun) { + fs.writeFileSync(filePath, content); + this.stats.references.updated += changeCount; + } + + return updated; + } + + // Find all markdown references to images + findImageReferences() { + const mdFiles = this.findFiles(this.config.docsDir, [".md", ".mdx"]); + mdFiles.push(...this.findFiles(".", [".md"]).filter((f) => !f.includes("node_modules"))); + + const references = new Map(); + + for (const file of mdFiles) { + const content = fs.readFileSync(file, "utf8"); + + // Pattern 1: static/images/path/file.ext + const staticPattern = /!\[([^\]]*)\]\(static\/images\/([^)]+)\.(gif|png|jpg|jpeg)\)/g; + + // Pattern 2: images/path/file.ext + const relativePattern = /!\[([^\]]*)\]\(images\/([^)]+)\.(gif|png|jpg|jpeg)\)/g; + + let match; + + // Find static/images references + while ((match = staticPattern.exec(content)) !== null) { + const [fullMatch, altText, imagePath, ext] = match; + const key = `static/images/${imagePath}.${ext}`; + if (!references.has(key)) references.set(key, []); + references.get(key).push({ + file, + oldRef: fullMatch, + newRef: `![${altText}](static/images/${imagePath}.webp)`, + }); + } + + // Reset regex + relativePattern.lastIndex = 0; + + // Find images/ references + while ((match = relativePattern.exec(content)) !== null) { + const [fullMatch, altText, imagePath, ext] = match; + const key = `static/images/${imagePath}.${ext}`; + if (!references.has(key)) references.set(key, []); + references.get(key).push({ + file, + oldRef: fullMatch, + newRef: `![${altText}](images/${imagePath}.webp)`, + }); + } + } + + return references; + } + + // Main optimization workflow + async optimize() { + this.log("🚀 Starting Discord API Docs Image Optimization"); + this.log(`Mode: ${this.config.dryRun ? "DRY RUN" : "LIVE"}`, "warn"); + + // Step 1: Analyze current images + this.log("\n📊 Analyzing current images..."); + const imageFiles = this.findFiles(this.config.imagesDir, [".gif", ".png", ".jpg", ".jpeg"]); + const svgFiles = this.findFiles(this.config.imagesDir, [".svg"]); + + // Calculate initial size + for (const file of [...imageFiles, ...svgFiles]) { + this.stats.sizeBefore += this.getFileSize(file); + } + + this.log(`Found ${imageFiles.length} raster images and ${svgFiles.length} SVGs`); + this.log(`Total size before: ${(this.stats.sizeBefore / 1024 / 1024).toFixed(1)}MB`); + + // Step 2: Convert images to WebP + this.log("\n🔄 Converting images to WebP..."); + const conversions = []; + + for (const imagePath of imageFiles) { + const ext = path.extname(imagePath).toLowerCase(); + const baseName = path.basename(imagePath, ext); + const dir = path.dirname(imagePath); + const webpPath = path.join(dir, `${baseName}.webp`); + + try { + // Copy original to report directory before conversion + const originalSize = this.getFileSize(imagePath); + const beforeReportPath = this.copyOriginalToReport(imagePath); + + // Convert to WebP + await this.convertImage(imagePath, webpPath); + + // Check if optimization actually reduced file size + const convertedSize = this.getFileSize(webpPath); + + if (convertedSize >= originalSize) { + // Optimization made file larger or same size - revert + try { + fs.unlinkSync(webpPath); + this.log( + `Reverted ${path.basename(imagePath)} - optimization increased size (${originalSize} → ${convertedSize} bytes)`, + "warn", + ); + continue; + } catch (unlinkError) { + this.log(`Failed to remove larger optimized file ${webpPath}: ${unlinkError.message}`, "warn"); + } + } + + // Add to report after successful optimization + this.addComparisonToReport(imagePath, webpPath, originalSize, convertedSize, beforeReportPath); + + conversions.push({ original: imagePath, converted: webpPath, ext }); + + if (ext === ".gif") this.stats.conversions.gif++; + else if (ext === ".png") this.stats.conversions.png++; + else if (ext.match(/\.jpe?g/)) this.stats.conversions.jpg++; + } catch { + this.log(`Skipping ${imagePath} due to conversion error`, "warn"); + } + } + + // Step 3: Optimize SVGs + this.log("\n🎨 Optimizing SVG files..."); + for (const svgPath of svgFiles) { + try { + await this.optimizeSvg(svgPath); + this.stats.conversions.svg++; + } catch { + this.log(`Skipping ${svgPath} due to optimization error`, "warn"); + } + } + + // Step 4: Find all image references + this.log("\n🔍 Finding image references in markdown files..."); + const references = this.findImageReferences(); + this.log(`Found references to ${references.size} unique images`); + + // Step 5: Update markdown references + this.log("\n📝 Updating markdown references..."); + const fileUpdates = new Map(); + + for (const [imagePath, refs] of references) { + // Check if we converted this image + const webpPath = imagePath.replace(/\.(gif|png|jpg|jpeg)$/i, ".webp"); + const webpExists = fs.existsSync(webpPath) || this.config.dryRun; + + if (webpExists) { + for (const ref of refs) { + if (!fileUpdates.has(ref.file)) fileUpdates.set(ref.file, []); + fileUpdates.get(ref.file).push([ref.oldRef, ref.newRef]); + } + } else { + this.log(`Warning: WebP not found for ${imagePath}, skipping reference updates`, "warn"); + } + } + + // Apply updates to files + for (const [file, replacements] of fileUpdates) { + if (this.updateMarkdownReferences(file, replacements)) { + this.stats.references.files++; + this.log(`Updated references in ${path.relative(".", file)}`, "debug"); + } + } + + // Step 6: Calculate final sizes and cleanup + if (!this.config.dryRun) { + for (const { converted } of conversions) { + this.stats.sizeAfter += this.getFileSize(converted); + } + + for (const svgPath of svgFiles) { + this.stats.sizeAfter += this.getFileSize(svgPath); + } + } + + // Step 7: Remove original files (only if everything succeeded) + if (!this.config.dryRun && this.stats.errors.length === 0) { + this.log("\n🗑️ Removing original files..."); + for (const { original } of conversions) { + try { + fs.unlinkSync(original); + this.log(`Removed: ${path.basename(original)}`, "debug"); + } catch (error) { + this.log(`Failed to remove ${original}: ${error.message}`, "warn"); + } + } + } + + // Finalize HTML report + this.finalizeHtmlReport(); + + // Final report + this.printSummary(); + } + + // Finalize HTML report with summary and footer + finalizeHtmlReport() { + if (!this.reportHtmlPath || this.config.dryRun) return; + + try { + // Calculate summary stats + const totalOriginalSize = this.stats.fileDetails.reduce((sum, file) => sum + file.originalSize, 0); + const totalConvertedSize = this.stats.fileDetails.reduce((sum, file) => sum + file.convertedSize, 0); + const totalSaved = totalOriginalSize - totalConvertedSize; + const totalSavingsPercent = totalOriginalSize > 0 ? ((totalSaved / totalOriginalSize) * 100).toFixed(1) : "0"; + + // Format file sizes + const formatBytes = (bytes) => { + if (bytes === 0) return "0 B"; + const k = 1024; + const sizes = ["B", "KB", "MB", "GB"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i]; + }; + + // Top 10 biggest savers + const topSavers = this.stats.fileDetails.sort((a, b) => b.savedBytes - a.savedBytes).slice(0, 10); + + // Generate summary content + const summaryHtml = ` +
+
+
${this.stats.fileDetails.length}
+
Files Optimized
+
+
+
${formatBytes(totalOriginalSize)}
+
Original Size
+
+
+
${formatBytes(totalConvertedSize)}
+
Optimized Size
+
+
+
${formatBytes(totalSaved)}
+
Total Saved (${totalSavingsPercent}%)
+
+
+ +
+ 🏆 Top 10 Space Savers +
+ ${topSavers + .map( + (file, i) => + `
+ ${i + 1}. ${file.filename} - Saved ${formatBytes(file.savedBytes)} (${file.savingsPercent}%) +
`, + ) + .join("")} +
+
+ `; + + // Sort files by converted size (descending) - largest optimized files first + const sortedFiles = this.stats.fileDetails.sort((a, b) => b.convertedSize - a.convertedSize); + + // Generate HTML comparisons for all files (sorted) + const comparisonsHtml = sortedFiles + .map((file) => { + const savingsClass = file.savedBytes > 0 ? "savings-positive" : "savings-negative"; + + return ` +
+
${file.filename}
+
+ Original: ${formatBytes(file.originalSize)} → Optimized: ${formatBytes(file.convertedSize)} + | Saved: ${formatBytes(Math.abs(file.savedBytes))} (${Math.abs(file.savingsPercent)}%) +
+
+
+

Before

+ Original ${file.filename} +
+
+

After (WebP)

+ Optimized ${file.filename} +
+
+
`; + }) + .join("\n"); + + // Update summary section and add all comparisons + const currentContent = fs.readFileSync(this.reportHtmlPath, "utf8"); + const updatedContent = currentContent.replace( + '
Loading summary...
', + `
${summaryHtml}
`, + ); + + // Add footer + const htmlFooter = ` +${comparisonsHtml} +
+
+

🎉 Optimization Complete! Generated ${this.stats.fileDetails.length} comparisons.

+

Files are sorted by optimized file size (largest first)

+

Report generated by Discord API Docs Image Optimizer

+
+ +`; + + fs.writeFileSync(this.reportHtmlPath, updatedContent + htmlFooter); + + this.log(`\n📊 Visual comparison report generated: ${this.reportHtmlPath}`); + this.log(` View in browser: file://${path.resolve(this.reportHtmlPath)}`); + } catch (error) { + this.log(`Failed to finalize HTML report: ${error.message}`, "warn"); + } + } + + printSummary() { + console.log("\n" + "=".repeat(60)); + console.log("📈 OPTIMIZATION SUMMARY"); + console.log("=".repeat(60)); + + console.log(`\n🔄 Conversions:`); + console.log(` GIFs → WebP: ${this.stats.conversions.gif}`); + console.log(` PNGs → WebP: ${this.stats.conversions.png}`); + console.log(` JPGs → WebP: ${this.stats.conversions.jpg}`); + console.log(` SVG optimized: ${this.stats.conversions.svg}`); + + console.log(`\n📝 Reference Updates:`); + console.log(` Files updated: ${this.stats.references.files}`); + console.log(` References updated: ${this.stats.references.updated}`); + + if (!this.config.dryRun) { + const beforeMB = (this.stats.sizeBefore / 1024 / 1024).toFixed(1); + const afterMB = (this.stats.sizeAfter / 1024 / 1024).toFixed(1); + const savedMB = (beforeMB - afterMB).toFixed(1); + const percentage = ((savedMB / beforeMB) * 100).toFixed(1); + + console.log(`\n💾 Size Reduction:`); + console.log(` Before: ${beforeMB}MB`); + console.log(` After: ${afterMB}MB`); + console.log(` Saved: ${savedMB}MB (${percentage}% reduction)`); + } + + if (this.stats.errors.length > 0) { + console.log(`\n❌ Errors (${this.stats.errors.length}):`); + this.stats.errors.forEach((error) => console.log(` ${error}`)); + } + + console.log("\n✨ Optimization complete!"); + } +} + +// CLI handling +if (import.meta.url === `file://${process.argv[1]}`) { + const args = process.argv.slice(2); + const dryRun = args.includes("--dry-run"); + const verbose = !args.includes("--quiet"); + + if (args.includes("--help")) { + console.log(` +Discord API Docs Image Optimizer + +Usage: node optimize-images.js [options] + +Options: + --dry-run Preview changes without executing + --quiet Suppress verbose output + --help Show this help message + +Examples: + node optimize-images.js --dry-run # Preview what would be changed + node optimize-images.js # Run full optimization + `); + process.exit(0); + } + + const config = { ...CONFIG, dryRun, verbose }; + const optimizer = new ImageOptimizer(config); + + optimizer.optimize().catch((error) => { + console.error("❌ Optimization failed:", error); + process.exit(1); + }); +} + +export { ImageOptimizer }; diff --git a/tools/package-lock.json b/tools/package-lock.json new file mode 100644 index 0000000000..ded68f55f6 --- /dev/null +++ b/tools/package-lock.json @@ -0,0 +1,4594 @@ +{ + "name": "discord-api-docs", + "version": "1.1.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "discord-api-docs", + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "xml2js": "^0.6.2" + }, + "devDependencies": { + "@actions/core": "^1.10.1", + "@eslint/js": "^9.2.0", + "@mdx-js/mdx": "^3.0.1", + "@mdx-js/react": "^3.0.1", + "@types/js-yaml": "^4.0.9", + "@types/node": "^24.0.8", + "chalk": "^5.3.0", + "eslint": "^9.2.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-prettier": "^5.1.3", + "execa": "^9.5.2", + "js-yaml": "^4.1.0", + "markdown-table-formatter": "^1.6.0", + "mdast-util-from-markdown": "^2.0.1", + "mdast-util-to-markdown": "^2.1.0", + "prettier": "^3.2.5", + "remark": "^15.0.1", + "tsx": "^4.20.3", + "typescript": "^5.4.5", + "typescript-eslint": "^8.0.0-alpha.12" + }, + "engines": { + "node": ">= 20.11.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@actions/core": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", + "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", + "dev": true, + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/http-client": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", + "dev": true, + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.0.2.tgz", + "integrity": "sha512-wV19ZEGEMAC1eHgrS7UQPqsdEiCIbTKTasEfcXAigzoXICcqZSjBZEHlZwNVvKg6UBCjSlos84XiLqsRJnIcIg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.2.0.tgz", + "integrity": "sha512-ESiIudvhoYni+MdsI8oD7skpprZ89qKocwRM2KEvhhBJ9nl5MRh7BXU5GTod7Mdygq+AUl+QzId6iWJKR/wABA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.2.3.tgz", + "integrity": "sha512-X38nUbachlb01YMlvPFojKoiXq+LzZvuSce70KPMPdeM1Rj03k4dR7lDslhbqXn3Ang4EU3+EAmwEAsbrjHW3g==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.0.1.tgz", + "integrity": "sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-to-js": "^2.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-estree": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "periscopic": "^3.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", + "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", + "dev": true, + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.8.tgz", + "integrity": "sha512-r7/zWe+f9x+zjXqGxf821qz++ld8tp6Z4jUS6qmPZUXH6tfh4riXOhAqb12tWGWAevCFtMt1goLWkQMqIJKpsA==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, + "node_modules/@types/node": { + "version": "24.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.8.tgz", + "integrity": "sha512-WytNrFSgWO/esSH9NbpWUfTMGQwCGIKfCmNlmFDNiI5gGhgMmEA+V1AEvKLeBNvvtBnailJtkrEa2OIISwrVAA==", + "dev": true, + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "dev": true, + "peer": true + }, + "node_modules/@types/react": { + "version": "18.3.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz", + "integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==", + "dev": true, + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.12.tgz", + "integrity": "sha512-/UMqX/DFKWnLgAFikO9FkpwL9VZoql0092mnP+xqbuznt6wkcLHwlCi1l/pQ4jMPkpAUDNubUkzxDx1l2CCaMw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.0.0-alpha.12", + "@typescript-eslint/type-utils": "8.0.0-alpha.12", + "@typescript-eslint/utils": "8.0.0-alpha.12", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.12", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.12.tgz", + "integrity": "sha512-zzSnj8CuHEL7E8Vfd9AdhsKQzB8z2ckUzIMD/41CS1ZRRUtw8xlpK0gz+Vitma7D0mEMqmLMz7jYKqiJfSY8sQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.0.0-alpha.12", + "@typescript-eslint/types": "8.0.0-alpha.12", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.12", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.12", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.12.tgz", + "integrity": "sha512-SDgvb7NQAqPmlZyZw+ABhGdOvQ4Kv46ch4bK05wWEqmdxVpwUTsT6eSIHW262mdd3O3I+opojPHiBTczsjBPqQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.12", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.12" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.12.tgz", + "integrity": "sha512-MW1XWx3Gnx/TtgZ3eY/EZhlcO1tKsXdtd7eE3u11/fDJs8Qa07h9MCkyErYU8JBMenf5U0RhWBQFOJDwLB+XwA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.0.0-alpha.12", + "@typescript-eslint/utils": "8.0.0-alpha.12", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.12.tgz", + "integrity": "sha512-jFJE3TbHlXQMXu2i5Dfsecr0Lc8t3NA6jXhhR1HGYBdz3F7H+VNcHka247exq6RD0eZDjHWdwPoEP30OUSissQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.12.tgz", + "integrity": "sha512-tcEcXQY6+qU+r+ySTDcb5Jdurv/jRHgT8y2O7cut4auX+oxk0wrGNwTU7g1YZHBpNKKEx0sloauonMLpgBmd5w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.12", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.12", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.12.tgz", + "integrity": "sha512-LVYBJwsGHpPWyUfSGUp5FbQ3JtXCFssxwORqWgGaipBWVZUOYMYmmYMTa/0wPKyBOlvrp2BdIR1GLBi3RblkCw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "8.0.0-alpha.12", + "@typescript-eslint/types": "8.0.0-alpha.12", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.12", + "semver": "^7.6.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.12.tgz", + "integrity": "sha512-E6WgenClMnVgwcFXPh7obHEOqWT49DQgzAcjZ4aaKg0tpf5fnTNL6SnA7ePVatWDmZvP4P2qM2rVNZiTuN2D1g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.0.0-alpha.12", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", + "dev": true, + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "peer": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.2.0.tgz", + "integrity": "sha512-0n/I88vZpCOzO+PQpt0lbsqmn9AsnsJAQseIqhZFI8ibQT0U1AkEKRxA3EVMos0BoHSXDQvCXY25TUjB5tr8Og==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^3.0.2", + "@eslint/js": "9.2.0", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.2.3", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", + "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", + "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-package-json": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/find-package-json/-/find-package-json-1.2.0.tgz", + "integrity": "sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==", + "dev": true + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.0.tgz", + "integrity": "sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz", + "integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==", + "dev": true + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz", + "integrity": "sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==", + "dev": true, + "dependencies": { + "inline-style-parser": "0.2.3" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/human-signals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", + "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "dev": true + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table-formatter": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/markdown-table-formatter/-/markdown-table-formatter-1.6.0.tgz", + "integrity": "sha512-TiqVZJB09VXGB7cInio5QDViAmY+lOzG+pDAib5klBmzbg0kRy3MEqn/dBtMsgk8RRNLtZbW1Vlm/UWgY1KyMg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "find-package-json": "^1.2.0", + "fs-extra": "^11.1.1", + "glob": "^10.3.10", + "markdown-table-prettify": "^3.6.0", + "optionator": "^0.9.3" + }, + "bin": { + "markdown-table-formatter": "lib/index.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/markdown-table-formatter/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/markdown-table-formatter/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdown-table-formatter/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdown-table-prettify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/markdown-table-prettify/-/markdown-table-prettify-3.6.0.tgz", + "integrity": "sha512-xZg+sL5yWyPz75GwNHtCOLe85CPnssoTLqpGc19xSr6CirGu4xRW2f8wj1f7c8Kx1IItXo3hUIqlUX4qAOwAdg==", + "dev": true, + "bin": { + "markdown-table-prettify": "cli/index.js" + }, + "engines": { + "vscode": "^1.59.0" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", + "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "dev": true, + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", + "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.2.tgz", + "integrity": "sha512-eKMQDeywY2wlHc97k5eD8VC+9ASMjN8ItEZQNGwJ6E0XWKiW/Z0V5/H8pvoXUf+y+Mj0VIgeRRbujBmFn4FTyA==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^5.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", + "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz", + "integrity": "sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz", + "integrity": "sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==", + "dev": true, + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "dev": true, + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "dev": true, + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz", + "integrity": "sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz", + "integrity": "sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", + "dev": true + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dev": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remark": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz", + "integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.0.1.tgz", + "integrity": "sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA==", + "dev": true, + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", + "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "dev": true, + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/tsx": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.12.tgz", + "integrity": "sha512-Rb//r8CVSWVZqCyYTuNRhIWLqscSxIBANAgaHMaR8eoE9zf1bDL5qlPDzWhEEE0nUcJpjZj/By32pQAXTDyS7w==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.12", + "@typescript-eslint/parser": "8.0.0-alpha.12", + "@typescript-eslint/utils": "8.0.0-alpha.12" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unified": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", + "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/tools/package.json b/tools/package.json new file mode 100644 index 0000000000..2e4aa1ae8d --- /dev/null +++ b/tools/package.json @@ -0,0 +1,57 @@ +{ + "name": "discord-api-docs", + "version": "1.1.1", + "description": "Documentation for using Discord's Developer Platform", + "main": "index.js", + "type": "module", + "directories": { + "doc": "docs" + }, + "engines": { + "node": ">= 20.11.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/discord/discord-api-docs.git" + }, + "author": "Discord for Developers", + "license": "MIT", + "bugs": { + "url": "https://github.com/discord/discord-api-docs/issues" + }, + "homepage": "https://github.com/discord/discord-api-docs#readme", + "scripts": { + "build": "tsc", + "lint": "eslint", + "lint:fix": "eslint --fix", + "test:build": "tsx checkBuild.ts", + "test:tables": "npx markdown-table-formatter \"../discord/developers/docs/**/*.{md,mdx}\" --check", + "fix:tables": "npx markdown-table-formatter \"../discord/developers/docs/**/*.{md,mdx}\"", + "decorate:docs": "node ./doxygen/generate-social-sdk-mapping.js && node ./doxygen/decorate-social-sdk-references.js" + }, + "devDependencies": { + "@actions/core": "^1.10.1", + "@eslint/js": "^9.2.0", + "@mdx-js/mdx": "^3.0.1", + "@mdx-js/react": "^3.0.1", + "@types/js-yaml": "^4.0.9", + "@types/node": "^24.0.8", + "chalk": "^5.3.0", + "eslint": "^9.2.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-prettier": "^5.1.3", + "execa": "^9.5.2", + "js-yaml": "^4.1.0", + "markdown-table-formatter": "^1.6.0", + "mdast-util-from-markdown": "^2.0.1", + "mdast-util-to-markdown": "^2.1.0", + "prettier": "^3.2.5", + "remark": "^15.0.1", + "tsx": "^4.20.3", + "typescript": "^5.4.5", + "typescript-eslint": "^8.0.0-alpha.12" + }, + "dependencies": { + "xml2js": "^0.6.2" + } +} diff --git a/tools/parse-changelog.js b/tools/parse-changelog.js new file mode 100644 index 0000000000..b21e047416 --- /dev/null +++ b/tools/parse-changelog.js @@ -0,0 +1,71 @@ +/** + * This script will parse the changelog and split it into individual files. + * It was designed for one time use, but will be helpful if we ever need to + * go back and do batch processing with ASTs over our markdown. + */ + +import fs from "node:fs/promises"; +import { fromMarkdown } from "mdast-util-from-markdown"; +import { toMarkdown } from "mdast-util-to-markdown"; + +const doc = await fs.readFile("./old_changelog.md", "utf-8"); +const tree = fromMarkdown(doc); + +// write this for debugging purposes while parsig +await fs.writeFile("changelog-ast.json", JSON.stringify(tree, null, 2)); + +const files = []; +let currentFile; +let postElementIndex = 0; + +for (const element of tree.children) { + // each changelog entry starts with an h2 + if (element.type === "heading" && element.depth === 2) { + postElementIndex = 0; + currentFile = { + title: element.children[0].value, + content: [], + }; + files.push(currentFile); + } else if (postElementIndex === 1 && element.type === "heading" && element.depth === 4) { + const maybeDate = Date.parse(element.children[0].value); + if (!isNaN(maybeDate)) { + currentFile.date = new Date(maybeDate); + } + } else if ( + postElementIndex === 2 && + element.type === "blockquote" && + element.children?.length > 0 && + element.children[0].type === "paragraph" && + element.children[0].children?.length > 0 && + element.children[0].children[0].type === "text" && + element.children[0].children[0].value === "danger\nThis entry includes breaking changes" + ) { + currentFile.breaking = true; + } else { + currentFile.content.push(element); + } + postElementIndex++; +} + +for (const file of files) { + const frontmatter = `--- +title: "${file.title}" +date: "${file.date.toISOString()}" +breaking: ${file.breaking ? "true" : "false"} +--- + +`; + const content = frontmatter + toMarkdown({ type: "root", children: file.content }); + const slug = + file.date.toISOString().split("T")[0] + + "-" + + file.title + .toLowerCase() + .replace(/[ _:&,/*]/g, "-") + .replace(/---/g, "-") + .replace(/--/g, "-"); + + console.log(slug); + await fs.writeFile(`./docs/change_log/${slug}.md`, content); +} diff --git a/tools/rename-files.js b/tools/rename-files.js new file mode 100644 index 0000000000..836144517e --- /dev/null +++ b/tools/rename-files.js @@ -0,0 +1,24 @@ +import { $ } from "execa"; +import fs from "fs/promises"; +import path from "path"; + +async function renameFilesInDirectory(directory) { + const files = await fs.readdir(directory, { withFileTypes: true }); + + for (const file of files) { + const fullPath = path.join(directory, file.name); + + if (file.isDirectory()) { + await renameFilesInDirectory(fullPath); + } else { + const newFileName = file.name.toLowerCase().replace(/_/g, "-"); + + if (newFileName !== file.name) { + const newFullPath = path.join(directory, newFileName); + await $`git mv ${fullPath} ${newFullPath}`; + } + } + } +} + +await renameFilesInDirectory("docs"); diff --git a/tools/tsconfig.json b/tools/tsconfig.json new file mode 100644 index 0000000000..e304cc9d28 --- /dev/null +++ b/tools/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "noEmit": true, + "target": "ES2022", + "strict": true, + "esModuleInterop": true, + "rootDir": "./", + "module": "NodeNext", + "outDir": "./dist" + }, + "include": ["**/*.ts"], +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json deleted file mode 100644 index b13fa84b33..0000000000 --- a/tsconfig.eslint.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["ci"] -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 1b89d9de2a..0000000000 --- a/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2021", - "lib": ["ESNext"], - "skipLibCheck": true, - "strict": true, - "alwaysStrict": true, - "forceConsistentCasingInFileNames": true, - "esModuleInterop": true, - "module": "commonjs", - "moduleResolution": "node", - "importsNotUsedAsValues": "error", - "isolatedModules": true, - "rootDir": "./", - "outDir": "./dist" - }, - "include": ["**/*.ts"], - "exclude": ["node_modules"] -}