├── .editorconfig ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── config.yaml │ ├── page_addition.yaml │ └── typo_grammar.yaml ├── pull_request_template.md └── workflows │ ├── lint-build-deploy.yml │ └── status_embed.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .vscode ├── admonitions.code-snippets ├── components.code-snippets └── discord-messages.code-snippets ├── README.md ├── code-samples ├── getting-started │ ├── creating-commands │ │ ├── main.py │ │ └── secrets.env │ └── initial-files │ │ ├── main.py │ │ └── secrets.env ├── interactions │ └── modals │ │ ├── create-tag-low.py │ │ └── create-tag.py └── popular-topics │ └── embeds │ ├── dict-embed.py │ └── full-embed.py ├── guide ├── babel.config.js ├── docs │ ├── contributing.mdx │ ├── faq │ │ ├── administrative.mdx │ │ ├── coroutines.mdx │ │ ├── extensions.mdx │ │ ├── general.mdx │ │ ├── good-practices.mdx │ │ └── intro.mdx │ ├── getting-started │ │ ├── creating-commands.mdx │ │ ├── initial-files.mdx │ │ └── using-cogs.mdx │ ├── interactions │ │ ├── buttons.mdx │ │ ├── context-menus.mdx │ │ ├── images │ │ │ ├── context-menus-intro.png │ │ │ ├── message-components-intro.png │ │ │ ├── modals-preview.png │ │ │ ├── slash-commands-intro.png │ │ │ └── string-select.png │ │ ├── intro.mdx │ │ ├── modals.mdx │ │ ├── select-menus.mdx │ │ └── slash-commands.mdx │ ├── intro.mdx │ ├── popular-topics │ │ ├── embeds.mdx │ │ ├── errors.mdx │ │ ├── images │ │ │ ├── embeds-intro.png │ │ │ ├── intents-bot-tab.png │ │ │ ├── intents-intro.png │ │ │ ├── intents-privileged.png │ │ │ ├── monetization-intro.png │ │ │ ├── monetization-response.png │ │ │ ├── permissions-intro.png │ │ │ ├── reactions-intro.png │ │ │ ├── threads-intro.png │ │ │ └── webhooks-intro.png │ │ ├── intents.mdx │ │ ├── intro.mdx │ │ ├── monetization.mdx │ │ ├── permissions.mdx │ │ ├── reactions.mdx │ │ ├── threads.mdx │ │ └── webhooks.mdx │ └── prerequisites │ │ ├── creating-your-application.mdx │ │ ├── images │ │ ├── application-name.png │ │ ├── copy-token.png │ │ ├── new-application.png │ │ ├── new-bot.png │ │ ├── oauth-application-commands.png │ │ ├── oauth-page.png │ │ ├── oauth-permissions.png │ │ ├── oauth-scopes.png │ │ ├── system-dm.png │ │ └── system-message.png │ │ ├── installing-disnake.mdx │ │ └── migrating-from-dpy.mdx ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── components │ │ ├── DocsLink.jsx │ │ ├── ResultingCode.jsx │ │ └── WorkInProgress.jsx │ ├── hooks │ │ └── isDarkTheme.js │ ├── styles │ │ ├── _layout.scss │ │ ├── _navbar.scss │ │ ├── _search.scss │ │ ├── _variables.scss │ │ └── index.scss │ ├── theme │ │ └── MDXComponents │ │ │ ├── A.js │ │ │ ├── Code.js │ │ │ ├── Details.js │ │ │ ├── Head.js │ │ │ ├── Heading.js │ │ │ ├── Pre.js │ │ │ └── index.js │ └── utils │ │ ├── prismDark.js │ │ └── prismLight.js └── static │ ├── .nojekyll │ ├── CNAME │ ├── fonts │ └── uni-sans.heavy-caps.otf │ └── public │ ├── disnake-banner.png │ ├── disnake-logo.png │ ├── disnake-meta-image.png │ ├── disnake-readme-banner.png │ ├── disnake-thin-banner.png │ ├── favicon.ico │ └── message-author.png └── package-lock.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*.{css,scss,js,jsx,ts,tsx,mdx,py}] 6 | end_of_line = lf 7 | indent_size = 4 8 | indent_style = tab 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.mdx] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize line endings to LF 2 | * text=auto 3 | 4 | .git* text 5 | *.json text 6 | *.env text linguist-language=python 7 | *.yml text 8 | *.yaml text 9 | *.css text 10 | *.scss text 11 | *.js text 12 | *.jsx text 13 | *.ts text 14 | *.tsx text 15 | *.py text 16 | *.mdx text linguist-detectable 17 | 18 | *.png binary 19 | *.{jpeg,jpg} binary 20 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First off, thanks for taking the time to contribute. :+1: 4 | 5 | This guide uses [`Docusaurus 2`](https://docusaurus.io/) for building the guide itself, and [`pre-commit`](https://pre-commit.com/) for various formatting/cleanup tasks - running content through prettier, formatting Python codeblocks and samples, etc. 6 | 7 | To get started with contributing to this guide, follow these steps: 8 | 9 | 1. `cd guide` as this is our docusaurus directory. 10 | 2. Install dependencies: `npm install`. 11 | - Note: Node version 16.14 or higher is required for Docusaurus 2. 12 | 3. Build using `Docusaurus`. 13 | - To build the documentation once, use `npm run build` and open `build/index.html` in your browser 14 | - Alternatively, if you want to see your changes to the content in real-time, use `npm run start` to start an autoreloading server on localhost. 15 | 4. Run `pre-commit`. 16 | - If you want to install the pre-commit hooks to automatically run before every commit, use `pre-commit install` 17 | - Otherwise, use `pre-commit run --all-files` to run all checks once. 18 | 19 | > [!NOTE] 20 | > Before contributing new content, please consider checking if there is already an issue about the topic. If not, it would be advisable to create one first, in case you start working on something that may not end up being accepted. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Discord server 4 | about: Use our official Discord server to ask for help. 5 | url: https://discord.gg/disnake 6 | - name: disnake issues 7 | about: Issue tracker for the disnake library. 8 | url: https://github.com/DisnakeDev/disnake/issues/new/choose 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/page_addition.yaml: -------------------------------------------------------------------------------- 1 | name: Page/section addition 2 | description: Suggestion for a new page/section that doesn't already exist. 3 | labels: ["t: request"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: > 8 | Thanks for taking the time to open an issue! We advise you to make the report descriptive of the issue, with links and images wherever required. 9 | Also make sure to go through the [Contributing Guidelines](https://github.com/DisnakeDev/guide/blob/main/CONTRIBUTING.md). 10 | - type: input 11 | attributes: 12 | label: Expands on 13 | description: If the page/section expands on an existing article, mention the same here. (With links/repo filenames) 14 | placeholder: guide.disnake.dev/intents 15 | - type: input 16 | attributes: 17 | label: "Title" 18 | description: Short, one-line summary of the addition. 19 | placeholder: Using gateway intents 20 | validations: 21 | required: true 22 | - type: textarea 23 | attributes: 24 | label: "Description" 25 | description: A short description of what this would showcase. If you have screenshots, examples, or links, add them here. 26 | validations: 27 | required: true 28 | - type: textarea 29 | attributes: 30 | label: Additional context 31 | description: If any extra information is required, mention the same here. 32 | - type: dropdown 33 | attributes: 34 | label: Would you like to implement this yourself? 35 | multiple: false 36 | options: 37 | - Yes, I will be contributing this addition. 38 | - No, I am only suggesting this addition. 39 | validations: 40 | required: true 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/typo_grammar.yaml: -------------------------------------------------------------------------------- 1 | name: Typo/grammar report 2 | description: Issues regarding incorrect spelling/grammar, missing information, etc. 3 | labels: ["t: spelling/grammar"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: > 8 | Thanks for taking the time to open an issue! We advise you to make the report descriptive of the issue, with links and images wherever required. 9 | Also make sure to go through the [Contributing Guidelines](https://github.com/DisnakeDev/guide/blob/main/CONTRIBUTING.md). 10 | - type: textarea 11 | attributes: 12 | label: Affected pages 13 | description: "Which articles/pages on the guide are affected? (Use links, or repo filenames)" 14 | placeholder: | 15 | - guide.disnake.dev/example 16 | - guide.disnake.dev/another-example 17 | - 100-directory/101-example.md 18 | validations: 19 | required: true 20 | - type: textarea 21 | attributes: 22 | label: Current text 23 | description: > 24 | Current text at fault. (Include the line/paragraph number, if applicable.) 25 | placeholder: > 26 | - guide.disnake.dev/example 27 | * P2 L40 - "disnek" 28 | validations: 29 | required: true 30 | - type: textarea 31 | attributes: 32 | label: Recommended text 33 | description: > 34 | Replace the text at fault. (If possible, include sources that verify your suggestions.) 35 | placeholder: > 36 | - guide.disnake.dev/example 37 | * P2 L40 - "disnek" -> "disnake" (www.example.com/this-is-valid) 38 | validations: 39 | required: true 40 | - type: textarea 41 | attributes: 42 | label: Additional context 43 | description: If any extra information is required, mention the same here. 44 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 6 | 7 | ## Relevant Issues 8 | 9 | 16 | -------------------------------------------------------------------------------- /.github/workflows/lint-build-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Lint, Build, Deploy 2 | 3 | on: 4 | push: 5 | paths: 6 | - .github/** 7 | - guide/** 8 | pull_request: 9 | branches: 10 | - main 11 | - dev 12 | workflow_dispatch: 13 | 14 | jobs: 15 | lint: 16 | name: Lint with pre-commit 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Run pre-commit 21 | uses: pre-commit/action@v3.0.0 22 | 23 | build-deploy: 24 | name: Deploy to GitHub Pages 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | - uses: actions/setup-node@v4 29 | - name: Cache npm 30 | uses: actions/cache@v4 31 | with: 32 | path: ~/.npm 33 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 34 | restore-keys: | 35 | ${{ runner.os }}-node- 36 | 37 | - name: Build website 38 | working-directory: guide 39 | run: | 40 | npm ci 41 | npm run build 42 | 43 | - name: Deploy to GitHub Pages 44 | if: github.ref == 'refs/heads/main' 45 | uses: peaceiris/actions-gh-pages@v3 46 | with: 47 | github_token: ${{ secrets.GITHUB_TOKEN }} 48 | publish_dir: ./guide/build 49 | user_name: 'github-actions[bot]' 50 | user_email: 'github-actions[bot]@users.noreply.github.com' 51 | force_orphan: true 52 | 53 | artifact: 54 | name: Generate Artifact 55 | if: always() 56 | needs: [lint, build-deploy] 57 | runs-on: ubuntu-latest 58 | steps: 59 | # Prepare the Pull Request Payload artifact. If this fails, we 60 | # we fail silently using the `continue-on-error` option. It's 61 | # nice if this succeeds, but if it fails for any reason, it 62 | # does not mean that our lint-test checks failed. 63 | - name: Prepare Pull Request Payload artifact 64 | id: prepare-artifact 65 | if: always() && github.event_name == 'pull_request' 66 | continue-on-error: true 67 | run: cat $GITHUB_EVENT_PATH | jq '.pull_request' > pull_request_payload.json 68 | 69 | # This only makes sense if the previous step succeeded. To 70 | # get the original outcome of the previous step before the 71 | # `continue-on-error` conclusion is applied, we use the 72 | # `.outcome` value. This step also fails silently. 73 | - name: Upload a Build Artifact 74 | if: always() && steps.prepare-artifact.outcome == 'success' 75 | continue-on-error: true 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: pull-request-payload 79 | path: pull_request_payload.json 80 | -------------------------------------------------------------------------------- /.github/workflows/status_embed.yml: -------------------------------------------------------------------------------- 1 | # Sends a status embed to a discord webhook 2 | 3 | name: Status Embed 4 | 5 | on: 6 | workflow_run: 7 | workflows: 8 | - Lint, Build, Deploy 9 | types: 10 | - completed 11 | 12 | jobs: 13 | status_embed: 14 | name: Send Status Embed to Discord 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | # Process the artifact uploaded in the `pull_request`-triggered workflow: 19 | - name: Get Pull Request Information 20 | id: pr_info 21 | if: github.event.workflow_run.event == 'pull_request' 22 | run: | 23 | curl -s -H "Authorization: token $GITHUB_TOKEN" ${{ github.event.workflow_run.artifacts_url }} > artifacts.json 24 | DOWNLOAD_URL=$(cat artifacts.json | jq -r '.artifacts[] | select(.name == "pull-request-payload") | .archive_download_url') 25 | [ -z "$DOWNLOAD_URL" ] && exit 1 26 | wget --quiet --header="Authorization: token $GITHUB_TOKEN" -O pull_request_payload.zip $DOWNLOAD_URL || exit 2 27 | unzip -p pull_request_payload.zip > pull_request_payload.json 28 | [ -s pull_request_payload.json ] || exit 3 29 | echo "pr_author_login=$(jq -r '.user.login // empty' pull_request_payload.json)" >> $GITHUB_OUTPUT 30 | echo "pr_number=$(jq -r '.number // empty' pull_request_payload.json)" >> $GITHUB_OUTPUT 31 | echo "pr_title=$(jq -r '.title // empty' pull_request_payload.json)" >> $GITHUB_OUTPUT 32 | echo "pr_source=$(jq -r '.head.label // empty' pull_request_payload.json)" >> $GITHUB_OUTPUT 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | # Send an informational status embed to Discord instead of the 37 | # standard embeds that Discord sends. This embed will contain 38 | # more information and we can fine tune when we actually want 39 | # to send an embed. 40 | - name: GitHub Actions Status Embed for Discord 41 | uses: SebastiaanZ/github-status-embed-for-discord@v0.3.0 42 | with: 43 | # Webhook token 44 | webhook_id: ${{ secrets.discord_webhook_id }} 45 | webhook_token: ${{ secrets.discord_webhook_token }} 46 | 47 | # We need to provide the information of the workflow that 48 | # triggered this workflow instead of this workflow. 49 | workflow_name: ${{ github.event.workflow_run.name }} 50 | run_id: ${{ github.event.workflow_run.id }} 51 | run_number: ${{ github.event.workflow_run.run_number }} 52 | status: ${{ github.event.workflow_run.conclusion }} 53 | actor: ${{ github.actor }} 54 | repository: ${{ github.repository }} 55 | ref: ${{ github.ref }} 56 | sha: ${{ github.event.workflow_run.head_sha }} 57 | 58 | # Now we can use the information extracted in the previous step: 59 | pr_author_login: ${{ steps.pr_info.outputs.pr_author_login }} 60 | pr_number: ${{ steps.pr_info.outputs.pr_number }} 61 | pr_title: ${{ steps.pr_info.outputs.pr_title }} 62 | pr_source: ${{ steps.pr_info.outputs.pr_source }} 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (https://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directories 28 | node_modules/ 29 | jspm_packages/ 30 | 31 | # Optional npm cache directory 32 | .npm 33 | 34 | # Optional eslint cache 35 | .eslintcache 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | # Output of 'npm pack' 41 | *.tgz 42 | 43 | # Yarn Integrity file 44 | .yarn-integrity 45 | 46 | # dotenv environment variables file 47 | .env 48 | .env.test 49 | 50 | # parcel-bundler cache (https://parceljs.org/) 51 | .cache 52 | 53 | # Serverless directories 54 | .serverless/ 55 | 56 | # Generated files 57 | .docusaurus 58 | .cache-loader 59 | 60 | # Misc 61 | .DS_Store 62 | .env.local 63 | .env.development.local 64 | .env.test.local 65 | .env.production.local 66 | 67 | npm-debug.log* 68 | yarn-debug.log* 69 | yarn-error.log* 70 | 71 | guide/build 72 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.2.0 4 | hooks: 5 | - id: check-case-conflict 6 | - id: end-of-file-fixer 7 | - id: trailing-whitespace 8 | args: ['--markdown-linebreak-ext=md,mdx'] 9 | # for code in .md files 10 | # (patched with support for codeblock options and `py` shortcodes) 11 | - repo: https://github.com/shiftinv/blacken-docs 12 | rev: 9b32b93b0a44216eacada45c4574a65bd1df3641 13 | hooks: 14 | - id: blacken-docs 15 | additional_dependencies: [black==22.3.0] 16 | files: '\.(rst|md|markdown|mdx|py|tex)$' 17 | args: [--line-length=100] 18 | # for code samples 19 | - repo: https://github.com/psf/black 20 | rev: 22.3.0 21 | hooks: 22 | - id: black 23 | args: [--line-length=100] 24 | # for markdown formatting 25 | - repo: https://github.com/pre-commit/mirrors-prettier 26 | rev: v2.6.2 27 | hooks: 28 | - id: prettier 29 | types_or: [html, css, javascript, markdown, mdx] 30 | args: [--print-width=120, --tab-width=4, --single-quote] 31 | -------------------------------------------------------------------------------- /.vscode/admonitions.code-snippets: -------------------------------------------------------------------------------- 1 | // https://code.visualstudio.com/docs/editor/userdefinedsnippets 2 | // https://docusaurus.io/docs/markdown-features/admonitions 3 | 4 | { 5 | "note": { 6 | "scope": "markdown", 7 | "prefix": ":note", 8 | "description": "Insert a note admonition", 9 | "body": [ 10 | ":::note ${1:Admonition title}", 11 | "", 12 | "${2:Admonition text}", 13 | "", 14 | ":::", 15 | "", 16 | "$0" 17 | ], 18 | }, 19 | "tip": { 20 | "scope": "markdown", 21 | "prefix": ":tip", 22 | "description": "Insert a tip admonition", 23 | "body": [ 24 | ":::tip ${1:Admonition title}", 25 | "", 26 | "${2:Admonition text}", 27 | "", 28 | ":::", 29 | "", 30 | "$0" 31 | ], 32 | }, 33 | "info": { 34 | "scope": "markdown", 35 | "prefix": ":info", 36 | "description": "Insert an info admonition", 37 | "body": [ 38 | ":::info ${1:Admonition title}", 39 | "", 40 | "${2:Admonition text}", 41 | "", 42 | ":::", 43 | "", 44 | "$0" 45 | ], 46 | }, 47 | "caution": { 48 | "scope": "markdown", 49 | "prefix": [":caution", ":warning", ":warn"], 50 | "description": "Insert a caution admonition", 51 | "body": [ 52 | ":::caution ${1:Admonition title}", 53 | "", 54 | "${2:Admonition text}", 55 | "", 56 | ":::", 57 | "", 58 | "$0" 59 | ], 60 | }, 61 | "danger": { 62 | "scope": "markdown", 63 | "prefix": ":danger", 64 | "description": "Insert a danger admonition", 65 | "body": [ 66 | ":::danger ${1:Admonition title}", 67 | "", 68 | "${2:Admonition text}", 69 | "", 70 | ":::", 71 | "", 72 | "$0" 73 | ], 74 | }, 75 | } 76 | -------------------------------------------------------------------------------- /.vscode/components.code-snippets: -------------------------------------------------------------------------------- 1 | // https://code.visualstudio.com/docs/editor/userdefinedsnippets 2 | 3 | { 4 | "image": { 5 | "scope": "markdown", 6 | "prefix": [":image", ":img"], 7 | "description": "Insert an image", 8 | "body": [ 9 | "
", 10 | "

", 11 | "\t\"${2:Image", 12 | "

", 13 | "
", 14 | ], 15 | }, 16 | "codeblock": { 17 | "scope": "markdown", 18 | "prefix": [":code", ":codeblock"], 19 | "description": "Insert a code block", 20 | "body": [ 21 | "```${1:python} title=\"${2:Title}\" {${3:Highlights}}", 22 | "$0", 23 | "```", 24 | ], 25 | }, 26 | "resulting-code": { 27 | "scope": "markdown", 28 | "prefix": ":result", 29 | "description": "Insert resulting code of section", 30 | "body": [ 31 | "", 32 | ] 33 | }, 34 | "docs-link": { 35 | "scope": "markdown", 36 | "prefix": [":docs", ":docs-link"], 37 | "description": "Insert a link to documentation", 38 | "body": [ 39 | "" 40 | ] 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /.vscode/discord-messages.code-snippets: -------------------------------------------------------------------------------- 1 | // https://code.visualstudio.com/docs/editor/userdefinedsnippets 2 | // https://github.com/Danktuary/discord-message-components 3 | 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Disnake Banner](https://raw.githubusercontent.com/DisnakeDev/disnake/master/assets/banner.png)](https://disnake.dev/) 2 | 3 | # Disnake Guide (WIP) 4 | 5 |

6 | Discord server invite 7 | Commit activity 8 | GitHub checks 9 |

10 | 11 | Welcome! The purpose of this guide is to make your journey with `disnake` easier, whether you're an experienced 12 | developer just getting into coding Discord bots, or an advanced bot developer who has decided to proceed with `disnake` 13 | as their library of choice. 14 | 15 | The concept we will be going over, include: 16 | 17 | - How to get started on working with bots; 18 | - How to create and organize commands, using cogs/extensions; 19 | - Working with databases (such as [`sqlite`][sqlite-docs] and [`mongodb (motor)`][motor-docs]); 20 | - Using the [`AutoShardedClient`](https://disnake.readthedocs.io/en/stable/api.html#disnake.AutoShardedClient) class 21 | to shard your bot; 22 | - A plethora of examples with popular topics along with in-depth explanation, and much more! 23 | 24 | [sqlite-docs]: https://docs.python.org/3/library/sqlite3.html 25 | [motor-docs]: https://motor.readthedocs.io/en/stable/ 26 | 27 | This guide will showcase the various features and events that our library has, while giving you an idea of how these 28 | functions work together as well as how the syntax looks in production. 29 | 30 | ## Development 31 | 32 | For more info on development/contributions, see [CONTRIBUTING.md](./.github/CONTRIBUTING.md). 33 | 34 |
35 |

36 | Documentation 37 | ⁕ 38 | Guide 39 | ⁕ 40 | Discord Server 41 | ⁕ 42 | Discord Developers 43 |

44 |
45 | -------------------------------------------------------------------------------- /code-samples/getting-started/creating-commands/main.py: -------------------------------------------------------------------------------- 1 | import disnake 2 | from disnake.ext import commands 3 | 4 | import os 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | 9 | bot = commands.Bot(test_guilds=[1234, 5678]) 10 | 11 | 12 | @bot.event 13 | async def on_ready(): 14 | print("The bot is ready!") 15 | 16 | 17 | @bot.slash_command() 18 | async def ping(inter): 19 | await inter.response.send_message("Pong!") 20 | 21 | 22 | @bot.slash_command() 23 | async def server(inter): 24 | await inter.response.send_message( 25 | f"Server name: {inter.guild.name}\nTotal members: {inter.guild.member_count}" 26 | ) 27 | 28 | 29 | @bot.slash_command() 30 | async def user(inter): 31 | await inter.response.send_message(f"Your tag: {inter.author}\nYour ID: {inter.author.id}") 32 | 33 | 34 | YOUR_BOT_TOKEN = os.environ["YOUR_BOT_TOKEN"] 35 | bot.run("YOUR_BOT_TOKEN") 36 | -------------------------------------------------------------------------------- /code-samples/getting-started/creating-commands/secrets.env: -------------------------------------------------------------------------------- 1 | # The token used here has been mentioned as an example. 2 | YOUR_BOT_TOKEN=OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg 3 | -------------------------------------------------------------------------------- /code-samples/getting-started/initial-files/main.py: -------------------------------------------------------------------------------- 1 | import disnake 2 | from disnake.ext import commands 3 | 4 | import os 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | 9 | bot = commands.Bot() 10 | 11 | 12 | @bot.event 13 | async def on_ready(): 14 | print("The bot is ready!") 15 | 16 | 17 | YOUR_BOT_TOKEN = os.environ["YOUR_BOT_TOKEN"] 18 | bot.run("YOUR_BOT_TOKEN") 19 | -------------------------------------------------------------------------------- /code-samples/getting-started/initial-files/secrets.env: -------------------------------------------------------------------------------- 1 | # The token used here has been mentioned as an example. 2 | YOUR_BOT_TOKEN=OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg 3 | -------------------------------------------------------------------------------- /code-samples/interactions/modals/create-tag-low.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | import disnake 4 | from disnake import TextInputStyle 5 | from disnake.ext import commands 6 | 7 | bot = commands.Bot() 8 | 9 | 10 | @bot.slash_command() 11 | async def tags_low(inter: disnake.AppCmdInter): 12 | """Sends a Modal to create a tag but with a low-level implementation.""" 13 | await inter.response.send_modal( 14 | title="Create Tag", 15 | custom_id="create_tag_low", 16 | components=[ 17 | disnake.ui.TextInput( 18 | label="Name", 19 | placeholder="Bar Tag", 20 | custom_id="name", 21 | style=TextInputStyle.short, 22 | max_length=50, 23 | ), 24 | disnake.ui.TextInput( 25 | label="Description", 26 | placeholder="Lorem ipsum dolor sit amet.", 27 | custom_id="description", 28 | style=TextInputStyle.paragraph, 29 | ), 30 | ], 31 | ) 32 | try: 33 | modal_inter: disnake.ModalInteraction = await bot.wait_for( 34 | "modal_submit", 35 | check=lambda i: i.custom_id == "create_tag_low" and i.author.id == inter.author.id, 36 | timeout=300, 37 | ) 38 | except asyncio.TimeoutError: 39 | # user didn't submit the modal, so a timeout error is raised 40 | # we don't have any action to take, so just return early 41 | return 42 | 43 | embed = disnake.Embed(title="Tag Creation") 44 | for key, value in modal_inter.text_values.items(): 45 | embed.add_field(name=key.capitalize(), value=value[:1024], inline=False) 46 | await modal_inter.response.send_message(embed=embed) 47 | 48 | 49 | bot.run("YOUR_BOT_TOKEN") 50 | -------------------------------------------------------------------------------- /code-samples/interactions/modals/create-tag.py: -------------------------------------------------------------------------------- 1 | import disnake 2 | from disnake.ext import commands 3 | from disnake import TextInputStyle 4 | 5 | 6 | bot = commands.Bot(command_prefix="!") 7 | 8 | 9 | class MyModal(disnake.ui.Modal): 10 | def __init__(self): 11 | # The details of the modal, and its components 12 | components = [ 13 | disnake.ui.TextInput( 14 | label="Name", 15 | placeholder="Foo Tag", 16 | custom_id="name", 17 | style=TextInputStyle.short, 18 | max_length=50, 19 | ), 20 | disnake.ui.TextInput( 21 | label="Description", 22 | placeholder="Lorem ipsum dolor sit amet.", 23 | custom_id="description", 24 | style=TextInputStyle.paragraph, 25 | ), 26 | ] 27 | super().__init__(title="Create Tag", components=components) 28 | 29 | # The callback received when the user input is completed. 30 | async def callback(self, inter: disnake.ModalInteraction): 31 | embed = disnake.Embed(title="Tag Creation") 32 | for key, value in inter.text_values.items(): 33 | embed.add_field( 34 | name=key.capitalize(), 35 | value=value[:1024], 36 | inline=False, 37 | ) 38 | await inter.response.send_message(embed=embed) 39 | 40 | 41 | @bot.slash_command() 42 | async def tags(inter: disnake.AppCmdInter): 43 | """Sends a Modal to create a tag.""" 44 | await inter.response.send_modal(modal=MyModal()) 45 | 46 | 47 | bot.run("YOUR_BOT_TOKEN") 48 | -------------------------------------------------------------------------------- /code-samples/popular-topics/embeds/dict-embed.py: -------------------------------------------------------------------------------- 1 | import disnake 2 | from disnake.ext import commands 3 | 4 | import datetime 5 | 6 | bot = commands.Bot(command_prefix="!", test_guilds=[123456789]) 7 | 8 | 9 | @bot.event 10 | async def on_ready(): 11 | print("The bot is ready!") 12 | 13 | 14 | @bot.command() 15 | async def dictembed(ctx): 16 | 17 | embed_dict = { 18 | "title": "Embed Title", 19 | "description": "Embed Description", 20 | "color": 0xFEE75C, 21 | "timestamp": datetime.datetime.now().isoformat(), 22 | "author": { 23 | "name": "Embed Author", 24 | "url": "https://disnake.dev/", 25 | "icon_url": "https://disnake.dev/assets/disnake-logo.png", 26 | }, 27 | "thumbnail": {"url": "https://disnake.dev/assets/disnake-logo.png"}, 28 | "fields": [ 29 | {"name": "Regular Title", "value": "Regular Value", "inline": "false"}, 30 | {"name": "Inline Title", "value": "Inline Value", "inline": "true"}, 31 | {"name": "Inline Title", "value": "Inline Value", "inline": "true"}, 32 | {"name": "Inline Title", "value": "Inline Value", "inline": "true"}, 33 | ], 34 | "image": {"url": "https://disnake.dev/assets/disnake-banner-thin.png"}, 35 | "footer": { 36 | "text": "Embed Footer", 37 | "icon_url": "https://disnake.dev/assets/disnake-logo.png", 38 | }, 39 | } 40 | 41 | await ctx.send(embed=disnake.Embed.from_dict(embed_dict)) 42 | 43 | 44 | bot.run("YOUR_BOT_TOKEN") 45 | -------------------------------------------------------------------------------- /code-samples/popular-topics/embeds/full-embed.py: -------------------------------------------------------------------------------- 1 | import disnake 2 | from disnake.ext import commands 3 | 4 | import datetime 5 | 6 | bot = commands.Bot(command_prefix="!", test_guilds=[123456789]) 7 | 8 | 9 | @bot.event 10 | async def on_ready(): 11 | print("The bot is ready!") 12 | 13 | 14 | @bot.command() 15 | async def fullembed(ctx): 16 | 17 | embed = disnake.Embed( 18 | title="Embed Title", 19 | description="Embed Description", 20 | color=disnake.Colour.yellow(), 21 | url="https://disnake.dev/", 22 | timestamp=datetime.datetime.now(), 23 | ) 24 | 25 | embed.set_author( 26 | name="Embed Author", 27 | url="https://disnake.dev/", 28 | icon_url="https://disnake.dev/assets/disnake-logo.png", 29 | ) 30 | 31 | embed.set_thumbnail(url="https://disnake.dev/assets/disnake-logo.png") 32 | 33 | embed.set_image(url="https://disnake.dev/assets/disnake-banner-thin.png") 34 | 35 | embed.add_field(name="Regular Title", value="Regular Value", inline=False) 36 | 37 | embed.add_field(name="Inline Title", value="Inline Value", inline=True) 38 | embed.add_field(name="Inline Title", value="Inline Value", inline=True) 39 | embed.add_field(name="Inline Title", value="Inline Value", inline=True) 40 | 41 | embed.set_footer( 42 | text="Embed Footer", 43 | icon_url="https://disnake.dev/assets/disnake-logo.png", 44 | ) 45 | 46 | await ctx.send(embed=embed) 47 | 48 | 49 | bot.run("YOUR_BOT_TOKEN") 50 | -------------------------------------------------------------------------------- /guide/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /guide/docs/contributing.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | hide_sidebar: true 3 | --- 4 | 5 | # Contributing 6 | 7 | First off, thanks for taking the time to contribute. This guide uses [Docusaurus](https://docusaurus.io/) for building the guide itself, and [`pre-commit`](https://pre-commit.com/) for various formatting/cleanup tasks, like formatting Python codeblocks. Make sure to install their respective CLIs, as they're utilized by our `npm` scripts. 8 | 9 | ## Prerequisites 10 | 11 | - node v16 or newer 12 | - pre-commit 13 | 14 | ## Local development 15 | 16 | Clone the repo to your desired folder, and `cd` into it. 17 | 18 | ``` 19 | git clone https://github.com/DisnakeDev/guide.git 20 | cd guide/guide 21 | ``` 22 | 23 | Further, install the dependencies using `npm install`. 24 | 25 | ### Starting the server 26 | 27 | To build and serve a preview of your site locally, run the following commands while in root: 28 | 29 | ``` 30 | cd guide 31 | npm run start 32 | ``` 33 | 34 | This will serve the site on `http://localhost:3000/`. To specify the port of the devserver, you can use the `--port` option. 35 | 36 | ``` 37 | npm run start -- --port=8000 38 | ``` 39 | 40 | ## Linting 41 | 42 | Linting your edits/additions is critical to ensure the consistency of the material with the rest of the guide. 43 | 44 | ## Using components 45 | 46 | ### `` 47 | 48 | ### `` 49 | 50 | ### `` 51 | 52 | ## Commit convention 53 | -------------------------------------------------------------------------------- /guide/docs/faq/administrative.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | hide_table_of_contents: true 3 | --- 4 | 5 | # Administrative 6 | 7 | 8 | -------------------------------------------------------------------------------- /guide/docs/faq/coroutines.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: A list of FAQs regarding how coroutines and async functions work. 3 | keywords: [disnake, bot, guide, tutorial, python, coroutines] 4 | --- 5 | 6 | # Coroutines 7 | 8 | Questions regarding coroutines and asyncio belong here. 9 | 10 | :::note 11 | 12 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/faq.html), and inherited from `discord.py`. It will most likely be rewritten in the future. 13 | 14 | ::: 15 | 16 | ### What is a coroutine? 17 | 18 | A [coroutine](https://docs.python.org/3/library/asyncio-task.html#coroutine) is a function that must be invoked with `await` or `yield from`. When Python encounters an `await` it stops the function's execution at that point and works on other things until it comes back to that point and finishes off its work. This allows for your program to be doing multiple things at the same time without using threads or complicated multiprocessing. 19 | 20 | **If you forget to await a coroutine then the coroutine will not run. Never forget to await a coroutine.** 21 | 22 | ### Where can I use `await`? 23 | 24 | You can only use `await` inside `async def` functions and nowhere else. 25 | 26 | ### What does "blocking" mean? 27 | 28 | In asynchronous programming a blocking call is essentially all the parts of the function that are not `await`. Do not despair however, because not all forms of blocking are bad! Using blocking calls is inevitable, but you must work to make sure that you don't excessively block functions. Remember, if you block for too long then your bot will freeze since it has not stopped the function's execution at that point to do other things. 29 | 30 | If logging is enabled, this library will attempt to warn you that blocking is occurring with the message: `Heartbeat blocked for more than N seconds.` See [Setting Up Logging](https://docs.disnake.dev/en/stable/logging.html#logging-setup) for details on enabling logging. 31 | 32 | A common source of blocking for too long is something like [`time.sleep`](https://docs.python.org/3/library/time.html#time.sleep). Don't do that. Use [`asyncio.sleep`](https://docs.python.org/3/library/asyncio-task.html#asyncio.sleep) 33 | instead. Similar to this example: 34 | 35 | ```python 36 | # Bad 37 | time.sleep(10) 38 | 39 | # Good 40 | await asyncio.sleep(10) 41 | ``` 42 | 43 | Another common source of blocking for too long is using HTTP requests with the famous module [Requests: HTTP for Humans™](https://docs.python-requests.org/en/latest/). While [Requests: HTTP for Humans™](https://docs.python-requests.org/en/latest/) is an amazing module for non-asynchronous programming, it is not a good choice for [`asyncio`](https://docs.python.org/3/library/asyncio.html#module-asyncio) because certain requests can block the event loop too long. Instead, use the [`aiohttp`](https://docs.aiohttp.org/en/stable/index.html) library which is installed on the side with this library. 44 | 45 | Consider the following example: 46 | 47 | ```python 48 | # Bad 49 | r = requests.get("http://aws.random.cat/meow") 50 | if r.status_code == 200: 51 | json = r.json() 52 | await channel.send(json["file"]) 53 | 54 | # Good 55 | async with aiohttp.ClientSession() as session: 56 | async with session.get("http://aws.random.cat/meow") as r: 57 | if r.status == 200: 58 | json = await r.json() 59 | await channel.send(json["file"]) 60 | ``` 61 | -------------------------------------------------------------------------------- /guide/docs/faq/extensions.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: A list of FAQs regarding the 'commands', 'tasks' and 'ui' extensions. 3 | keywords: [disnake, bot, guide, tutorial, python, extensions] 4 | --- 5 | 6 | # Extensions 7 | 8 | Questions regarding `disnake.ext.commands`, `disnake.ext.tasks` and `disnake.ui` belong here. 9 | 10 | :::note 11 | 12 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/faq.html), and inherited from `discord.py`. It will most likely be rewritten in the future. 13 | 14 | ::: 15 | 16 | ### Why does `on_message` make my commands stop working? 17 | 18 | Overriding the default provided `on_message` forbids any extra commands from running. To fix this, add a 19 | `bot.process_commands(message)` line at the end of your `on_message`. For example: 20 | 21 | ```python 22 | @bot.event 23 | async def on_message(message): 24 | # Do some extra stuff here 25 | 26 | await bot.process_commands(message) 27 | ``` 28 | 29 | Alternatively, you can place your `on_message` logic into a **listener**. In this setup, you should not 30 | manually call `bot.process_commands()`. This also allows you to do multiple things asynchronously in response 31 | to a message. Example: 32 | 33 | ```python 34 | @bot.listen("on_message") 35 | async def whatever_you_want_to_call_it(message): 36 | await do_stuff_here() 37 | # Do not process commands here 38 | ``` 39 | 40 | ### Why do my arguments require quotes? 41 | 42 | In a simple command defined as: 43 | 44 | ```python 45 | @bot.command() 46 | async def echo(ctx, message: str): 47 | await ctx.send(message) 48 | ``` 49 | 50 | Calling it via `?echo a b c` will only fetch the first argument and disregard the rest. To fix this you should either call 51 | it via `?echo "a b c"` or change the signature to have "consume rest" behaviour. Example: 52 | 53 | ```python 54 | @bot.command() 55 | async def echo(ctx, *, message: str): 56 | await ctx.send(message) 57 | ``` 58 | 59 | This will allow you to use `?echo a b c` without needing the quotes. 60 | 61 | ### How do I get the original `message`? 62 | 63 | The contains an attribute, message to get the original 64 | message. 65 | 66 | Example: 67 | 68 | ```python 69 | @bot.command() 70 | async def length(ctx): 71 | await ctx.send(f"Your message is {len(ctx.message.content)} characters long.") 72 | ``` 73 | 74 | ### How do I make a subcommand? 75 | 76 | Use the group() decorator. This will transform the callback into a which will allow you to add commands into 77 | the group operating as "subcommands". These groups can be arbitrarily nested as well. 78 | 79 | Example: 80 | 81 | ```python 82 | @bot.group() 83 | async def git(ctx): 84 | if ctx.invoked_subcommand is None: 85 | await ctx.send("Invalid git command passed...") 86 | 87 | 88 | @git.command() 89 | async def push(ctx, remote: str, branch: str): 90 | await ctx.send(f"Pushing to {remote} {branch}.") 91 | ``` 92 | 93 | This could then be used as `?git push origin master`. 94 | 95 | ### Which components can I use with modals? 96 | 97 | The only component that can currently be used with modals is TextInput. The API does not have integration for any other inputs as of now. 98 | 99 | ### Do modals support autocomplete? 100 | 101 | Modals (or text inputs) do not support autocomplete. This is a Discord limitation. 102 | -------------------------------------------------------------------------------- /guide/docs/faq/general.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: A list of general FAQs regarding library usage. 3 | keywords: [disnake, bot, guide, tutorial, python, general] 4 | --- 5 | 6 | # General 7 | 8 | General questions regarding library usage belong here. 9 | 10 | :::note 11 | 12 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/faq.html), and inherited from `discord.py`. It will most likely be rewritten in the future. 13 | 14 | ::: 15 | 16 | ### Where can I find usage examples? 17 | 18 | Example code can be found in the [examples folder](https://github.com/DisnakeDev/disnake/tree/master/examples) in the repository. 19 | 20 | ### How do I set the "Playing" status? 21 | 22 | The `activity` keyword argument may be passed in the commands.Bot constructor or Bot.change_presence, given an object. 23 | 24 | The constructor may be used for static activities, while Bot.change_presence may be used to update the activity at runtime. 25 | 26 | :::warning 27 | 28 | It is highly discouraged to use Bot.change_presence or API calls in as this event may be called many times while running, not just once. There is a high chance of disconnecting if presences are changed right after connecting. 29 | 30 | ::: 31 | 32 | The status type (playing, listening, streaming, watching) can be set using the enum. 33 | For memory optimisation purposes, some activities are offered in slimmed-down versions: 34 | 35 | - 36 | - 37 | 38 | Putting both of these pieces of info together, you get the following: 39 | 40 | ```python 41 | bot = commands.Bot(..., activity=disnake.Game(name="My game")) 42 | 43 | # Or, for watching: 44 | activity = disnake.Activity( 45 | name="My activity", 46 | type=disnake.ActivityType.watching, 47 | ) 48 | bot = commands.Bot(..., activity=activity) 49 | ``` 50 | 51 | ### How do I send a message to a specific channel? 52 | 53 | You must get or fetch the channel directly and then call the appropriate method. Example: 54 | 55 | ```python 56 | # If Bot.get_channel returns None because the channel is not in the Bot's cache, 57 | # make an API call to fetch the channel 58 | channel = bot.get_channel(12324234183172) or await bot.fetch_channel(12324234183172) 59 | 60 | await channel.send("hello") 61 | ``` 62 | 63 | ### How do I send a DM? 64 | 65 | Get the or object and call . For example: 66 | 67 | ```python 68 | user = bot.get_user(381870129706958858) 69 | await user.send("👀") 70 | ``` 71 | 72 | If you are responding to an event, such as , you already have the object via : 73 | 74 | ```python 75 | await message.author.send("👋") 76 | ``` 77 | 78 | ### How do I get the ID of a sent message? 79 | 80 | The method returns the that was sent. 81 | The ID of a message can be accessed via : 82 | 83 | ```python 84 | message = await channel.send("hmm…") 85 | message_id = message.id 86 | ``` 87 | 88 | ### How do I upload an image? 89 | 90 | To upload something to Discord you have to use the object. 91 | 92 | A accepts two parameters, the file-like object (or file path) and the filename 93 | to pass to Discord when uploading. 94 | 95 | If you want to upload an image it's as simple as: 96 | 97 | ```python 98 | await channel.send(file=disnake.File("my_file.png")) 99 | ``` 100 | 101 | If you have a file-like object you can do as follows: 102 | 103 | ```python 104 | with open("my_file.png", "rb") as fp: 105 | await channel.send(file=disnake.File(fp, "new_filename.png")) 106 | ``` 107 | 108 | To upload multiple files, you can use the `files` keyword argument instead of `file`: 109 | 110 | ```python 111 | my_files = [ 112 | disnake.File("result.zip"), 113 | disnake.File("teaser_graph.png"), 114 | ] 115 | await channel.send(files=my_files) 116 | ``` 117 | 118 | If you want to upload something from a URL, you will have to use an HTTP request using [`aiohttp`](https://docs.aiohttp.org/en/stable/index.html) 119 | and then pass an [io.BytesIO](https://docs.python.org/3/library/io.html#io.BytesIO) instance 120 | to like so: 121 | 122 | ```python 123 | import io 124 | import aiohttp 125 | 126 | async with aiohttp.ClientSession() as session: 127 | async with session.get(my_url) as resp: 128 | if resp.status != 200: 129 | return await channel.send("Could not download file...") 130 | data = io.BytesIO(await resp.read()) 131 | await channel.send(file=disnake.File(data, "cool_image.png")) 132 | ``` 133 | 134 | ### How can I add a reaction to a message? 135 | 136 | You use the method. 137 | 138 | If you want to use unicode emoji, you must pass a valid unicode code point in a string. In your code, you can write this in a few different ways: 139 | 140 | - `'👍'` 141 | - `'\U0001F44D'` 142 | - `'\N{THUMBS UP SIGN}'` 143 | 144 | Quick example: 145 | 146 | ```python 147 | emoji = "\N{THUMBS UP SIGN}" 148 | # or '\U0001f44d' or '👍' 149 | await message.add_reaction(emoji) 150 | ``` 151 | 152 | In case you want to use emoji that come from a message, you already get their code points in the content without needing 153 | to do anything special. You **cannot** send `':thumbsup:'` style shorthands. 154 | 155 | For custom emoji, you should pass an instance of . You can also pass a `'<:name:id>'` string, but if you 156 | can use said emoji, you should be able to use to get an emoji via ID or use 157 | or on or collections. 158 | 159 | The name and ID of a custom emoji can be found with the client by prefixing `:custom_emoji:` with a backslash. 160 | For example, sending the message `\:python3:` with the client will result in `<:python3:232720527448342530>`. 161 | 162 | Quick example: 163 | 164 | ```python 165 | # If you have the ID already 166 | emoji = bot.get_emoji(310177266011340803) 167 | await message.add_reaction(emoji) 168 | 169 | # No ID, do a lookup 170 | emoji = disnake.utils.get(guild.emojis, name="LUL") 171 | if emoji: 172 | await message.add_reaction(emoji) 173 | 174 | # If you have the name and ID of a custom emoji: 175 | emoji = "<:python3:232720527448342530>" 176 | await message.add_reaction(emoji) 177 | ``` 178 | 179 | ### How do I pass a coroutine to the player's "after" function? 180 | 181 | The library's music player launches on a separate thread, ergo it does not execute inside a coroutine. 182 | This does not mean that it is not possible to call a coroutine in the `after` parameter. To do so you must pass a callable 183 | that wraps up a couple of aspects. 184 | 185 | The first gotcha that you must be aware of is that calling a coroutine is not a thread-safe operation. Since we are 186 | technically in another thread, we must take caution in calling thread-safe operations so things do not bug out. Luckily for 187 | us, [`asyncio`](https://docs.python.org/3/library/asyncio.html#module-asyncio) comes with a [`asyncio.run_coroutine_threadsafe`](https://docs.python.org/3/library/asyncio-task.html#asyncio.run_coroutine_threadsafe) function that allows us to call 188 | a coroutine from another thread. 189 | 190 | However, this function returns a [`Future`](https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Future) and to actually call it we have to fetch its result. Putting all of 191 | this together we can do the following: 192 | 193 | ```python 194 | def my_after(error): 195 | coro = some_channel.send("Song is done!") 196 | fut = asyncio.run_coroutine_threadsafe(coro, bot.loop) 197 | try: 198 | fut.result() 199 | except: 200 | # An error happened sending the message 201 | pass 202 | 203 | 204 | voice.play(disnake.FFmpegPCMAudio(url), after=my_after) 205 | ``` 206 | 207 | ### How do I run something in the background? 208 | 209 | Check the [`background_task.py` example](https://github.com/DisnakeDev/disnake/blob/master/examples/background_task.py). 210 | 211 | ### How do I get a specific model? 212 | 213 | There are multiple ways of doing this. If you have a specific model's ID then you can use 214 | one of the following functions: 215 | 216 | - 217 | - 218 | - 219 | - 220 | - 221 | - 222 | - 223 | 224 | The following use an HTTP request: 225 | 226 | - 227 | - 228 | - 229 | - 230 | - 231 | - 232 | - 233 | 234 | If the functions above do not help you, then use of or would serve some use in finding 235 | specific models. 236 | 237 | Quick example: 238 | 239 | ```python 240 | # Find a guild by name 241 | guild = disnake.utils.get(bot.guilds, name="My Server") 242 | 243 | # Make sure to check if it's found 244 | if guild is not None: 245 | # Find a channel by name 246 | channel = disnake.utils.get(guild.text_channels, name="cool-channel") 247 | ``` 248 | 249 | ### How do I make a web request? 250 | 251 | To make a request, you should use a non-blocking library. 252 | This library already uses and requires a 3rd party library for making requests, [`aiohttp`](https://docs.aiohttp.org/en/stable/index.html). 253 | 254 | Quick example: 255 | 256 | ```python 257 | async with aiohttp.ClientSession() as session: 258 | async with session.get("http://aws.random.cat/meow") as r: 259 | if r.status == 200: 260 | js = await r.json() 261 | ``` 262 | 263 | See [`aiohttp`'s full documentation](http://aiohttp.readthedocs.io/en/stable/) for more information. 264 | 265 | ### How do I use a local image file for an embed image? 266 | 267 | Setting a local image for the embed can be done using the `file` argument of method. It accepts a object. 268 | 269 | Quick example: 270 | 271 | ```python 272 | embed = disnake.Embed() 273 | embed.set_image(file=disnake.File("path/to/file.png")) 274 | await channel.send(embed=embed) 275 | ``` 276 | 277 | ### Is there an event for audit log entries being created? 278 | 279 | As of version 2.8, there's now an event for it, called . 280 | -------------------------------------------------------------------------------- /guide/docs/faq/good-practices.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: A section that lists practices you should follow while making your Discord bot. 3 | keywords: [disnake, bot, guide, tutorial, good practices, python] 4 | --- 5 | 6 | # Good practices 7 | 8 | ## Running code when a cog is loaded 9 | 10 | Most people are used to running everything in `__init__` but that doesn't allow running async code. In this case you can 11 | overwrite the special `cog_load` method. 12 | 13 | ```python {5,6} 14 | class MyCog(commands.Cog): 15 | def __init__(self, bot: commands.Bot): 16 | self.bot = bot 17 | 18 | async def cog_load(self): 19 | self.data = await bot.fetch_database_data() 20 | 21 | @commands.slash_command() 22 | async def command(self, interaction: disnake.ApplicationCommandInteraction, user: disnake.User): 23 | await interaction.response.send_message(self.data[user.id]) 24 | ``` 25 | 26 | ## Reloading your bot 27 | 28 | One of the lesser known disnake features is the `reload` kwarg for your bot. It reloads extensions every time they are 29 | edited; allowing you to test your code in real-time. This is especially useful if startup times for your bot are very 30 | slow, since only one extension will be reloaded at a time. 31 | 32 | ```python {3} 33 | from disnake.ext import commands 34 | 35 | bot = commands.Bot(..., reload=True) 36 | ``` 37 | 38 | :::caution 39 | 40 | This should be used purely for debugging. Please do not use this in production. 41 | 42 | ::: 43 | 44 | ## Converting arguments in commands 45 | 46 | discord.py used to have `Converter` classes to convert arguments if they are provided. These were however very hard to 47 | use with type-checkers because the type of the parameter is never actually the converter itself. 48 | 49 | Disnake aims to eliminate this issue by only allowing conversion of builtin types like `disnake.Member`, 50 | `disnake.Emoji`, etc. If you ever want to have your own converter you have to use the `converter` argument in `Param`. 51 | 52 | 53 | 54 | 55 | ```python {10} 56 | async def convert_data(inter: disnake.ApplicationCommandInteraction, arg: str): 57 | parts = arg.split(",") 58 | return {"a": parts[0], "b": int(parts[1]), "c": parts[2].lower()} 59 | 60 | 61 | @commands.slash_command() 62 | async def command( 63 | self, 64 | inter: disnake.ApplicationCommandInteraction, 65 | data: Dict[str, Any] = commands.Param(converter=convert_data), 66 | ): 67 | ... 68 | ``` 69 | 70 | 71 | 72 | 73 | ```python 74 | class DataConverter(commands.Converter): 75 | async def convert(self, ctx: commands.Context, arg: str): 76 | parts = arg.split(",") 77 | return {"a": parts[0], "b": int(parts[1]), "c": parts[2].lower()} 78 | 79 | 80 | @commands.command() 81 | async def command(self, ctx: commands.Context, data: DataConverter): 82 | ... 83 | ``` 84 | 85 | 86 | 87 | 88 | If you absolutely want to be able to use classes you may pass in a class method. Alternatively, set a method of the 89 | class to be the converter using `converter_method`. 90 | 91 | 92 | 93 | 94 | ```python {9-12,19} 95 | from dataclasses import dataclass 96 | 97 | 98 | @dataclass 99 | class Data: 100 | a: str 101 | b: int 102 | 103 | @classmethod 104 | async def from_option(cls, inter: disnake.CommandInteraction, arg: str): 105 | a, b = arg.split(",") 106 | return cls(a, int(b)) 107 | 108 | 109 | @commands.slash_command() 110 | async def command( 111 | self, 112 | inter: disnake.CommandInteraction, 113 | data: Data = commands.Param(converter=Data.from_option), 114 | ): 115 | ... 116 | ``` 117 | 118 | 119 | 120 | 121 | ```python {9-12,19} 122 | from dataclasses import dataclass 123 | 124 | 125 | @dataclass 126 | class Data: 127 | a: str 128 | b: int 129 | 130 | @commands.converter_method 131 | async def from_option(cls, inter: disnake.CommandInteraction, arg: str): 132 | a, b = arg.split(",") 133 | return cls(a, int(b)) 134 | 135 | 136 | @commands.slash_command() 137 | async def command( 138 | self, 139 | inter: disnake.CommandInteraction, 140 | data: Data, 141 | ): 142 | ... 143 | ``` 144 | 145 | 146 | 147 | 148 | ## Context command targets 149 | 150 | Instead of using `inter.target` you should be using a parameter of your command. 151 | 152 | ```python 153 | @commands.user_command() 154 | async def command(self, inter: disnake.ApplicationCommandInteraction, user: disnake.User): 155 | await inter.response.send_message(f"Used on {user.mention}") 156 | ``` 157 | 158 | ## Slash command descriptions 159 | 160 | You may use docstrings for command and option descriptions. Everything before `Parameters` is the command description. 161 | Everything after `Parameters` are the option descriptions. 162 | 163 | ```python 164 | @commands.slash_command() 165 | async def command( 166 | self, 167 | inter: disnake.ApplicationCommandInteraction, 168 | category: str, 169 | item: str, 170 | details: bool, 171 | ): 172 | """Show item info 173 | 174 | Parameters 175 | ---------- 176 | category: The category to search 177 | item: The item to display 178 | details: Whether to get the details of this time 179 | """ 180 | ``` 181 | 182 | ## Guild-only commands 183 | 184 | While disnake does provide a `@commands.guild_only()` decorator, it still makes you handle `guild` being optional in 185 | case you're using linters. To solve this you should be using `GuildCommandInteraction`. 186 | 187 | ```python 188 | # before 189 | @commands.slash_command() 190 | @commands.guild_only() 191 | async def command(inter: disnake.ApplicationCommandInteraction): 192 | assert inter.guild is not None 193 | await inter.send(inter.guild.name) 194 | 195 | 196 | # after 197 | @commands.slash_command() 198 | async def command(inter: disnake.GuildCommandInteraction): 199 | await inter.send(inter.guild.name) 200 | ``` 201 | -------------------------------------------------------------------------------- /guide/docs/faq/intro.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: /faq 3 | hide_table_of_contents: true 4 | description: This is a list of Frequently Asked Questions regarding using `disnake` and its extension modules. 5 | keywords: [disnake, bot, guide, tutorial, python, faq] 6 | --- 7 | 8 | # Frequently Asked Questions 9 | 10 | This is a list of Frequently Asked Questions regarding using `disnake` and its extension modules. Feel free to suggest a 11 | new at the [support server](https://discord.gg/disnake), or submit one via pull requests on [the repository](https://github.com/DisnakeDev/guide/pulls). 12 | 13 | ## Legend 14 | 15 | - `bot` refers to the commands.Bot object. For example, `bot = commands.Bot(command_prefix="!", ...)`. 16 | - `ctx` refers to the commands.Context object, while `inter` refers to a version of the disnake.Interaction object. For example, `async def command(ctx: commands.Context, ...):` 17 | -------------------------------------------------------------------------------- /guide/docs/getting-started/creating-commands.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: This article goes in depth on creating commands in your Discord bot. 3 | keywords: [disnake, bot, guide, tutorial, creating, commands, python] 4 | --- 5 | 6 | # Creating commands 7 | 8 | :::info 9 | 10 | This page is a follow-up, and the base code used is from the previous page ([Initial files](./initial-files.mdx)). The 11 | code can be found on the GitHub repository 12 | [here](https://github.com/DisnakeDev/guide/tree/main/code-samples/getting-started/initial-files). 13 | 14 | ::: 15 | 16 | Discord also allows developers to register 17 | [slash commands](https://discord.com/developers/docs/interactions/application-commands), which provides users a 18 | first-class way of interacting directly with your application. These slash commands shall be covered by the guide 19 | [here](/interactions/slash-commands), in the **Interactions** section. 20 | 21 | ## A note on prefix commands 22 | 23 | Bot commands that are initiated when a keyword is used along with a specified prefix (such as `!` or `$`) are known as 24 | **prefix commands** (are also often referred to as **text commands**). 25 | 26 | :::caution Message Intent - Privileged 27 | 28 | It is to be noted that handling prefix commands require the **message intent**, which allows the bot to get content and 29 | data of messages sent by users. This intent has recently been privileged, i.e., it needs to be manually enabled for the 30 | bot application, and its requirement will eventually be reviewed if your bot is in over 100 servers. 31 | 32 | You can read more about the message intent [here][message-intent-article]. 33 | 34 | ::: 35 | 36 | Therefore, to minimize the permissions your bot has to use, we will be covering prefix commands under the **Popular 37 | Topics** section, and advancing with the basics of slash commands in this article; more advanced topics of the same will 38 | be covered in the [**Interactions**](/interactions) section. 39 | 40 | ## Registering commands 41 | 42 | This section covers the bare minimum to get you started with registering slash commands. Once again, you can refer to 43 | [this page](/interactions/slash-commands) for an in-depth coverage of topics, including guild commands, 44 | global commands, options, option types, autocomplete and choices. 45 | 46 | Now, we shall continue with the base code used in the previous section. 47 | 48 | ```python title="main.py" 49 | import disnake 50 | from disnake.ext import commands 51 | 52 | bot = commands.Bot() 53 | 54 | 55 | @bot.event 56 | async def on_ready(): 57 | print("The bot is ready!") 58 | 59 | 60 | bot.run("YOUR_BOT_TOKEN") 61 | ``` 62 | 63 | The first step is to use the `@bot.slash_command` coroutine, along with an `async` function in order to define the code 64 | for your slash command. Below is a script demonstrating the same (focus on the use of `inter`, which is short for 65 | `interaction`). 66 | 67 | ```python title="main.py" {12-14} 68 | import disnake 69 | from disnake.ext import commands 70 | 71 | bot = commands.Bot() 72 | 73 | 74 | @bot.event 75 | async def on_ready(): 76 | print("The bot is ready!") 77 | 78 | 79 | @bot.slash_command() 80 | async def ping(inter): 81 | ... 82 | 83 | 84 | bot.run("YOUR_BOT_TOKEN") 85 | ``` 86 | 87 | The `inter` passed into the function is analogous to context, or `ctx` used in prefix commands - it passes through all 88 | information relative to the interaction - data regarding the user who initiated the command, as an example. It is also 89 | necessary for replying to the use of the command. 90 | 91 | :::note Using `ctx` vs. `inter` 92 | 93 | If you have experience with coding bots with [`discord.py`](https://discordpy.readthedocs.io/en/latest), you would be 94 | familiar with using `ctx` as an abbreviation for passing context into the function. This guide will primarily be using 95 | `inter`, as it is short for `interaction` and refers to . 96 | Of course, you're open to using your preferred abbreviation in code. 97 | 98 | ::: 99 | 100 | ### Registering commands in specific guilds 101 | 102 | Note that servers are referred to as "guilds" in the Discord API and disnake library. On running the above code, the 103 | slash command will be registered globally, and will be accessible on all servers the bot is in. 104 | 105 | You can also use the `guild_ids` argument for the command to only be registered in a list of specified servers, for example your development server. 106 | 107 | ```python title="main.py" {12-14} 108 | import disnake 109 | from disnake.ext import commands 110 | 111 | bot = commands.Bot() 112 | 113 | 114 | @bot.event 115 | async def on_ready(): 116 | print("The bot is ready!") 117 | 118 | 119 | @bot.slash_command(guild_ids=[1234, 5678]) # Your server IDs go here. 120 | async def ping(inter): 121 | ... 122 | 123 | 124 | bot.run("YOUR_BOT_TOKEN") 125 | ``` 126 | 127 | :::tip Using `test_guilds` in `commands.Bot()` 128 | 129 | When you have multiple commands registered under the same test guilds, it is convenient to only have your `guild_ids` 130 | defined once. Therefore, you can use the `test_guilds` argument in the `commands.Bot()` instance instead of passing 131 | `guild_ids` to every single command - 132 | 133 | ```python 134 | bot = commands.Bot(test_guilds=[1234, 5678]) 135 | ``` 136 | 137 | ::: 138 | 139 | Now that you're all set with registering the slash command, you can proceed with responding to the initiated command. 140 | 141 | ## Responding to commands 142 | 143 | You can respond to a slash command initiated by the user, using `inter.response.send_message()`. It is analogous to 144 | using `ctx.send()`, in that you can respond to the interaction with embeds, files, buttons/select menus or just plain 145 | text. 146 | 147 | ```python title="main.py" {14} 148 | import disnake 149 | from disnake.ext import commands 150 | 151 | bot = commands.Bot() 152 | 153 | 154 | @bot.event 155 | async def on_ready(): 156 | print("The bot is ready!") 157 | 158 | 159 | @bot.slash_command(guild_ids=[1234, 5678]) 160 | async def ping(inter): 161 | await inter.response.send_message("Pong!") 162 | 163 | 164 | bot.run("YOUR_BOT_TOKEN") 165 | ``` 166 | 167 |
168 | 169 | 170 |
171 | 172 | ping 173 | 174 |
175 | Pong! 176 |
177 |
178 |
179 | 180 | ### Server info command 181 | 182 | `inter.guild` refers to the guild the interaction was sent in (a Guild 183 | instance), which exposes properties such as `.name` or `.member_count`. 184 | 185 | ```python title="main.py" {12-16} 186 | import disnake 187 | from disnake.ext import commands 188 | 189 | bot = commands.Bot() 190 | 191 | 192 | @bot.event 193 | async def on_ready(): 194 | print("The bot is ready!") 195 | 196 | 197 | @bot.slash_command() 198 | async def server(inter): 199 | await inter.response.send_message( 200 | f"Server name: {inter.guild.name}\nTotal members: {inter.guild.member_count}" 201 | ) 202 | 203 | 204 | bot.run("YOUR_BOT_TOKEN") 205 | ``` 206 | 207 |
208 | 209 | 210 |
211 | 212 | server 213 | 214 |
215 | Server name: Disnake Guide
216 | Total members: 2 217 |
218 |
219 |
220 | 221 | :::tip 222 | 223 | Refer to the Guild documentation for a list of all the available 224 | properties and methods. 225 | 226 | ::: 227 | 228 | You could also display the date the server was created, or the server's verification level. You would do those in the 229 | same manner - use `inter.guild.created_at` or `inter.guild.verification_level`, respectively. 230 | 231 | ### User info command 232 | 233 | A "user" refers to a Discord user. `inter.author` refers to the user the interaction was sent by 234 | (a User instance in DM contexts, or a Member instance in server contexts), which exposes properties 235 | such as `.name` or `.id`. (Using just `inter.author` will give the user's full tag.) 236 | 237 | ```python title="main.py" {12-14} 238 | import disnake 239 | from disnake.ext import commands 240 | 241 | bot = commands.Bot() 242 | 243 | 244 | @bot.event 245 | async def on_ready(): 246 | print("The bot is ready!") 247 | 248 | 249 | @bot.slash_command() 250 | async def user(inter): 251 | await inter.response.send_message(f"Your tag: {inter.author}\nYour ID: {inter.author.id}") 252 | 253 | 254 | bot.run("YOUR_BOT_TOKEN") 255 | ``` 256 | 257 |
258 | 259 | 260 |
261 | 262 | server 263 | 264 |
265 | Your tag: AbhigyanTrips#1234
266 | Your ID: 123456789012345678 267 |
268 |
269 |
270 | 271 | :::tip 272 | 273 | Refer to the and documentation for a list 274 | of all the available properties and methods. 275 | 276 | ::: 277 | 278 | And there you have it! 279 | 280 | ## Resulting Code 281 | 282 | 283 | 284 | [message-intent-article]: https://support-dev.discord.com/hc/en-us/articles/4404772028055 285 | -------------------------------------------------------------------------------- /guide/docs/getting-started/initial-files.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: An in-depth article on initializing your Discord bot project. 3 | keywords: [disnake, bot, guide, tutorial, initial files, initialization, python] 4 | --- 5 | 6 | # Initial files 7 | 8 | Once you [add your bot to a server](../prerequisites/creating-your-application#inviting-your-bot), the next step is to 9 | start coding and get it online! Let's start by creating a `.env` file for your bot token and a main file for your bot 10 | application. 11 | 12 | ## Creating configuration files 13 | 14 | As explained in the "What is a token, anyway?" section, your token is essentially your bot's password, and you should 15 | protect it as best as possible. This can be done through a `.env` file, or by using environment variables. 16 | 17 | Open your application in the [Discord Developer Portal](https://discord.com/developers/applications) and go to the `Bot` 18 | page to copy your token. 19 | 20 | ### Using environment variables 21 | 22 | Environment variables are special values for your environment (e.g., terminal session, docker container, or environment 23 | variable file). You can pass these values into your code's scope so that you can use them. 24 | 25 | :::note 26 | 27 | When referring to a `.env` file, keep in mind that you can name this file whatever you prefer. For example, the file can 28 | be named `token.env` or `secret.env`. 29 | 30 | ::: 31 | 32 | Storing data in a `.env` file is a common way of keeping your sensitive values safe. Create a `.env` file in your 33 | project directory and paste in your token. You can access your token inside other files by using `os.environ`. 34 | 35 | 36 | 37 | 38 | ```python 39 | # Importing "os" module. 40 | import os 41 | 42 | # Getting .env value. 43 | # You can name this variable in the script however you like. 44 | YOUR_BOT_TOKEN = os.environ["YOUR_BOT_TOKEN"] 45 | ``` 46 | 47 | 48 | 49 | 50 | ```python 51 | # Each line in a .env file should hold a KEY=value pair. 52 | YOUR_BOT_TOKEN = OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg 53 | ``` 54 | 55 | 56 | 57 | 58 | :::danger 59 | 60 | If you're using Git, you should not commit this file and should ignore it via `.gitignore`. The values of a `.env` file are still accessible to anyone if they are able to view the file. 61 | 62 | ::: 63 | 64 | You can use the [python-dotenv package][python-dotenv] to either load the `env` variables into the environment, or make 65 | a `config` dict out of the env values. 66 | 67 | 68 | 69 | 70 | ```python 71 | import os 72 | from dotenv import load_dotenv 73 | 74 | load_dotenv() # Take environment variables from .env. 75 | 76 | # Using the variables in your application, which uses environment variables 77 | # (e.g. from 'os.environ()' or 'os.getenv()') 78 | # as if they came from the actual environment. 79 | YOUR_BOT_TOKEN = os.environ["YOUR_BOT_TOKEN"] 80 | ``` 81 | 82 | 83 | 84 | 85 | ```python 86 | from dotenv import dotenv_values 87 | 88 | config = dotenv_values(".env") # Makes a dict out of the values. 89 | 90 | # Thus, we get 91 | # config = {YOUR_BOT_TOKEN: OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg} 92 | # which can be used as: 93 | YOUR_BOT_TOKEN = config["YOUR_BOT_TOKEN"] 94 | ``` 95 | 96 | 97 | 98 | 99 | Keep in mind that the values imported from the `.env` file are **in string format**. Therefore if you would like to, 100 | say, use them for calculations, you'll have to convert them via `int()` 101 | 102 | :::info Online editors (Glitch, Replit, etc.) 103 | 104 | While we generally do not recommend using online editors as hosting solutions, but rather invest in a proper virtual 105 | private server, these services do offer ways to keep your credentials safe as well! Please see the respective service's 106 | documentation and help articles for more information on how to keep sensitive values safe: 107 | 108 | - Glitch: [Storing secrets in .env][glitch-article] 109 | - Replit: [Secrets and environment variables][replit-article] 110 | 111 | ::: 112 | 113 | ### Git and `.gitignore` 114 | 115 | Git is a fantastic tool to keep track of your code changes and allows you to upload progress to services like 116 | [GitHub][github], [GitLab][gitlab], or [Bitbucket][bitbucket]. While this is super useful to share code with other 117 | developers, it also bears the risk of uploading your configuration files with sensitive values! 118 | 119 | You can specify files that Git should ignore in its versioning systems with a `.gitignore` file. Create a `.gitignore` 120 | file in your project directory and add the names of the files and folders you want to ignore: 121 | 122 | ``` 123 | __pycache__/ 124 | secrets.env 125 | config.json 126 | ``` 127 | 128 | :::tip 129 | 130 | [`__pycache__/`][pycache] has been included in `.gitignore` as it is simply cache that helps loading and running your 131 | script faster (this is an oversimplification). As it is of no particular importance, and is recompiled every time a 132 | change is made in the script, it is better to not commit the directory. 133 | 134 | Also, you can specify certain extensions/directories in `.gitignore` files, as per the requirements of your project - 135 | [here is an example][gitignore-example]. Check out the [Git documentation on `.gitignore`][gitignore-article] for more 136 | information! 137 | 138 | ::: 139 | 140 | ## Creating the main file 141 | 142 | Open your code editor and create a new file. We suggest that you save the file as `main.py` or `bot.py`, but you may 143 | name it whatever you wish. 144 | 145 | Here's the base code to get you started: 146 | 147 | ```python title="main.py" 148 | # Import the necessary libraries. 149 | import disnake 150 | from disnake.ext import commands 151 | 152 | # Creating a commands.Bot() instance, and assigning it to "bot" 153 | bot = commands.Bot() 154 | 155 | # When the bot is ready, run this code. 156 | @bot.event 157 | async def on_ready(): 158 | print("The bot is ready!") 159 | 160 | 161 | # Login to Discord with the bot's token. 162 | bot.run("YOUR_BOT_TOKEN") 163 | ``` 164 | 165 | This is how you create a bot instance for your Discord bot and login to Discord. Open your terminal and run 166 | `python3 main.py` to start the process. If you see "The bot is ready!" after a few seconds, you're good to go! 167 | 168 | :::tip 169 | 170 | After closing the process with `Ctrl + C`, you can press the up arrow on your keyboard to bring up the latest commands 171 | you've run in the terminal. Pressing up and then enter after closing the process is a quick way to start it up again. 172 | 173 | ::: 174 | 175 | ## Resulting code 176 | 177 | 178 | 179 | [python-dotenv]: https://pypi.org/project/python-dotenv/ 180 | [glitch-article]: https://glitch.happyfox.com/kb/article/18 181 | [replit-article]: https://docs.replit.com/repls/secrets-environment-variables 182 | [github]: https://github.com/ 183 | [gitlab]: https://about.gitlab.com/ 184 | [bitbucket]: https://bitbucket.org/product 185 | [pycache]: https://stackoverflow.com/questions/16869024/what-is-pycache 186 | [gitignore-example]: https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore 187 | [gitignore-article]: https://git-scm.com/docs/gitignore 188 | -------------------------------------------------------------------------------- /guide/docs/getting-started/using-cogs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: An in-depth section on creating cogs for your Discord bot. 3 | keywords: [disnake, bot, guide, tutorial, cogs, extensions, python] 4 | --- 5 | 6 | # Creating cogs/extensions 7 | 8 | [**Cogs**](https://docs.disnake.dev/en/stable/ext/commands/cogs.html) are analogous to modules that extend the core of 9 | the bot - thus adding to its functions and abilities. They also allow you to break down and organize your bot's 10 | collection of commands/listeners (which is useful when your bot's command list becomes quite extensive). 11 | 12 | :::note 13 | 14 | Cogs are typically used alongside with **Extensions**. You can read more about them 15 | [in the documentation](https://docs.disnake.dev/en/stable/ext/commands/extensions.html). 16 | 17 | ::: 18 | 19 | ## Creating files 20 | 21 | What we code here will extend on the code from [**Initial Files**](./initial-files.mdx), and thus we assume that there 22 | already exists a `main.py` file in your directory which initiates a `commands.Bot()` instance. 23 | 24 | ```python title="main.py" 25 | import disnake 26 | from disnake.ext import commands 27 | 28 | bot = commands.Bot() 29 | 30 | 31 | @bot.event 32 | async def on_ready(): 33 | print("The bot is ready!") 34 | 35 | 36 | bot.run("YOUR_BOT_TOKEN") 37 | ``` 38 | 39 | Next, we will create a new Python file either in the same directory as `main.py` or in a subfolder (which is 40 | recommended). For the sake of being organized, we will create the cog file in a separate `cogs` folder, and name the 41 | file `ping.py` just for the "ping" command. If you're strictly following the guide, your file directory should look 42 | something like this: 43 | 44 | ``` 45 | . 46 | ├── cogs/ 47 | │ └── ping.py 48 | ├── main.py 49 | └── secrets.env 50 | ``` 51 | 52 | ## Initial code 53 | 54 | ### Inheriting a class 55 | 56 | The first step is to create a class in `ping.py` that inherits from the `commands.Cog`, along with a constructor that 57 | takes in the `bot` object as its only argument and saves it. This is where we will be putting in our commands. 58 | 59 | ```python title="ping.py" 60 | from disnake.ext import commands 61 | 62 | 63 | class PingCommand(commands.Cog): 64 | """This will be for a ping command.""" 65 | 66 | def __init__(self, bot: commands.Bot): 67 | self.bot = bot 68 | ``` 69 | 70 | :::tip 71 | 72 | We typehint `bot: commands.Bot` here to give the IDE an idea of what the argument type is. It is not necessary, but can 73 | be useful in development when adding certain arguments to your commands. 74 | 75 | ::: 76 | 77 | ### Registering commands 78 | 79 | Now, to define a slash command inside the class, we will use the `commands.slash_command()` decorator. It functions the same as 80 | `bot.slash_command()`, but works in cogs as well. 81 | 82 | ```python title="ping.py" {10-13} 83 | import disnake 84 | from disnake.ext import commands 85 | 86 | 87 | class PingCommand(commands.Cog): 88 | """This will be for a ping command.""" 89 | 90 | def __init__(self, bot: commands.Bot): 91 | self.bot = bot 92 | 93 | @commands.slash_command() 94 | async def ping(self, inter: disnake.ApplicationCommandInteraction): 95 | """Get the bot's current websocket latency.""" 96 | await inter.response.send_message(f"Pong! {round(self.bot.latency * 1000)}ms") 97 | ``` 98 | 99 | Note that we're using `self` as the first argument, since the command function is inside a class. 100 | 101 | ### Adding `setup` function 102 | 103 | The last thing that needs to be added to this file is a `setup` function, so that disnake can load the cog when it is 104 | added to `main.py`. 105 | 106 | ```python title="ping.py" {16,17} 107 | import disnake 108 | from disnake.ext import commands 109 | 110 | 111 | class PingCommand(commands.Cog): 112 | """This will be for a ping command.""" 113 | 114 | def __init__(self, bot: commands.Bot): 115 | self.bot = bot 116 | 117 | @commands.slash_command() 118 | async def ping(self, inter: disnake.ApplicationCommandInteraction): 119 | """Get the bot's current websocket latency.""" 120 | await inter.response.send_message(f"Pong! {round(self.bot.latency * 1000)}ms") 121 | 122 | 123 | def setup(bot: commands.Bot): 124 | bot.add_cog(PingCommand(bot)) 125 | ``` 126 | 127 | This cog file can now be added to the bot via the `bot.load_extension()` method in `main.py`. 128 | 129 | ```python title="main.py" 130 | import disnake 131 | from disnake.ext import commands 132 | 133 | bot = commands.Bot() 134 | 135 | 136 | @bot.event 137 | async def on_ready(): 138 | print("The bot is ready!") 139 | 140 | 141 | bot.load_extension("cogs.ping") # Note: We did not append the .py extension. 142 | 143 | bot.run("YOUR_BOT_TOKEN") 144 | ``` 145 | 146 | Note that we've input `"cogs.ping"` here, since the `ping.py` file is inside the `cogs` folder. Therefore, the basic 147 | syntax for loading an extension is `bot.load_extension(".")`. 148 | 149 | And that concludes the use of cogs with `disnake`! You can now create multiple cogs to group and organize your 150 | commands/events, and load/unload them via your main file. More information on special cog methods and meta options can 151 | be found [in the documentation](https://docs.disnake.dev/en/stable/ext/commands/cogs.html). 152 | 153 | ## Syntax changes 154 | 155 | Cogs represent a fairly drastic change in the way you write commands and bots, so here's a list you can come back to for 156 | the primary syntax used in cogs: 157 | 158 | - Each cog is a Python class that subclasses commands.Cog. 159 | - Decorators for commands in cogs: 160 | - Command - commands.command() 161 | - Slash command - commands.slash_command() 162 | - User command - commands.user_command() 163 | - Message command - commands.message_command() 164 | - Every listener is marked with the commands.Cog.listener() decorator. 165 | - Cogs are then registered with the Bot.add_cog call, and are subsequently removed with the Bot.remove_cog call. 166 | 167 | 168 | Source: Disnake Documentation 169 | 170 | 171 | Cogs represent a fairly drastic change in the way you write commands and bots. Thus, it is recommended that you get 172 | familiar with this syntax - since the code we use in this section is largely tailored for cogs. 173 | -------------------------------------------------------------------------------- /guide/docs/interactions/buttons.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: They refer to views, buttons and select menus that can be added to the messages your bot sends. 3 | --- 4 | 5 | # Buttons 6 | 7 | Components allow users to interact with your bot through interactive UI elements. In this section, we will discuss how bots can receive and process information from these elements through the library. 8 | 9 |
10 | 11 | 12 |
13 | 14 | buttons 15 | 16 |
17 | Need help? 18 |
19 | 20 | Yes 21 | No 22 | 23 |
24 |
25 |
26 |
27 | 28 | The code for this command is given below. 29 | 30 | ```python title="buttons.py" 31 | # At the top of the file. 32 | import disnake 33 | from disnake.ext import commands 34 | 35 | # The slash command that responds with a message. 36 | @bot.slash_command() 37 | async def buttons(inter: disnake.ApplicationCommandInteraction): 38 | await inter.response.send_message( 39 | "Need help?", 40 | components=[ 41 | disnake.ui.Button(label="Yes", style=disnake.ButtonStyle.success, custom_id="yes"), 42 | disnake.ui.Button(label="No", style=disnake.ButtonStyle.danger, custom_id="no"), 43 | ], 44 | ) 45 | 46 | 47 | @bot.listen("on_button_click") 48 | async def help_listener(inter: disnake.MessageInteraction): 49 | if inter.component.custom_id not in ["yes", "no"]: 50 | # We filter out any other button presses except 51 | # the components we wish to process. 52 | return 53 | 54 | if inter.component.custom_id == "yes": 55 | await inter.response.send_message("Contact us at https://discord.gg/disnake!") 56 | elif inter.component.custom_id == "no": 57 | await inter.response.send_message("Got it. Signing off!") 58 | ``` 59 | 60 | ## Building and sending buttons 61 | 62 | ## Button styles 63 | 64 | | Name | Syntax | Color | 65 | | --------- | ----------------------------------------------------------------- | ------- | 66 | | Primary | `ButtonStyle.primary` / `ButtonStyle.blurple` | Blurple | 67 | | Secondary | `ButtonStyle.secondary` / `ButtonStyle.grey` / `ButtonStyle.gray` | Grey | 68 | | Success | `ButtonStyle.success` / `ButtonStyle.green ` | Green | 69 | | Danger | `ButtonStyle.danger` / `ButtonStyle.red` | Red | 70 | | Link | `ButtonStyle.link` / `ButtonStyle.url` | Grey | 71 | 72 |
73 | 74 | 75 |
76 | 77 | Primary 78 | Secondary 79 | Success 80 | Danger 81 | 82 | Link 83 | 84 | 85 |
86 |
87 |
88 |
89 | 90 | :::note 91 | 92 | `Link` buttons _cannot_ have a `custom_id`, and _do not_ send an interaction event when clicked. 93 | 94 | ::: 95 | 96 | ### Disabled buttons 97 | 98 | ## Receiving button callback 99 | 100 | ## Views vs. low-level components 101 | -------------------------------------------------------------------------------- /guide/docs/interactions/context-menus.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: They are available under Apps in user/message context menus, responding with the resolved user or message on which the action was taken. 3 | --- 4 | 5 | # Context Menus 6 | 7 | These application commands are situated under the "Apps" section when you right-click a user or a message. They act like interactions, except they take a or object along with . 8 | 9 | 10 |
11 |

12 | Context Menu Introduction 13 |

14 |
15 | 16 | :::note 17 | 18 | For more specific annotations, you can use or instead of for the respective commands. 19 | 20 | ::: 21 | 22 | ## User commands 23 | 24 | 25 | 26 |
27 | 28 | User Info 29 | 30 |
31 | Username: AbhigyanTrips#2474 32 |
33 | Bot: False 34 |
35 |
36 |
37 | 38 | The function below should have only one required argument, which is an instance of . 39 | 40 | ```python 41 | @bot.user_command(name="User Info") 42 | async def avatar(inter: disnake.ApplicationCommandInteraction, user: disnake.User): 43 | await inter.response.send_message(f"Username: {user}\nBot: {user.bot}") 44 | ``` 45 | 46 | ## Message commands 47 | 48 | 49 | 50 |
51 | 52 | Reverse 53 | 54 |
55 | .desrever si egassem sihT 56 |
57 |
58 |
59 | 60 | The function below should have only one required argument, which is an instance of . 61 | 62 | ```python 63 | @bot.message_command(name="Reverse") 64 | async def reverse(inter: disnake.ApplicationCommandInteraction, message: disnake.Message): 65 | # Reversing the message and sending it back. 66 | await inter.response.send_message(message.content[::-1]) 67 | ``` 68 | 69 | ## Context menu notes 70 | 71 | - Context menu commands cannot have subcommands or any other options. 72 | - Responses to context menu commands and their permissions function the same as slash commands. 73 | -------------------------------------------------------------------------------- /guide/docs/interactions/images/context-menus-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/context-menus-intro.png -------------------------------------------------------------------------------- /guide/docs/interactions/images/message-components-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/message-components-intro.png -------------------------------------------------------------------------------- /guide/docs/interactions/images/modals-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/modals-preview.png -------------------------------------------------------------------------------- /guide/docs/interactions/images/slash-commands-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/slash-commands-intro.png -------------------------------------------------------------------------------- /guide/docs/interactions/images/string-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/interactions/images/string-select.png -------------------------------------------------------------------------------- /guide/docs/interactions/intro.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: /interactions 3 | hide_table_of_contents: true 4 | description: This section covers application commands and message components. 5 | --- 6 | 7 | # Interactions 8 | 9 | **Interactions** here refers to client-integrated elements that a bot can create - giving users a way to interact directly with your application. Currently, interactions can be classified under three categories: 10 | 11 |
12 |
13 |
14 |
15 |
16 | 17 | Slash Commands Intro Image 21 | 22 |
23 |
24 | 25 |

Slash Commands

26 |
27 | 28 | They can be accessed via the textbox, and return the values the user submitted with the command. 29 | 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | 38 | Context Menus Intro Image 42 | 43 |
44 |
45 | 46 |

Context Menus

47 |
48 | 49 | They are available under Apps in user/message context menus, responding with the resolved 50 | user or message on which the action was taken. 51 | 52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 60 | Buttons Intro Image 61 | 62 |
63 |
64 | 65 |

Buttons

66 |
67 | 68 | They refer to views, buttons and select menus that can be added to the messages your bot 69 | sends.{' '} 70 | 71 |
72 |
73 |
74 |
75 |
76 | 77 |
78 | 79 | :::note Interactions and Bot Users 80 | 81 | Interactions are essentially webhooks under the hood (and responding to interactions doesn't require a bot token). Thus, you can have an interaction-only application that can be accessed by users **without having a bot user** in the guild. 82 | 83 | Since `disnake` is primarily focused on using the gateway events, you will still require a bot user. You can check out other libraries like [`hikari`](https://www.hikari-py.dev/) for utilizing this feature. 84 | 85 | ::: 86 | -------------------------------------------------------------------------------- /guide/docs/interactions/modals.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: Modals act as client-integrated popups, that can prompt the user for text input. 3 | keywords: [disnake, bot, guide, tutorial, modals, text inputs, python] 4 | --- 5 | 6 | # Modals & Text Inputs 7 | 8 | Modals are how you can prompt users for further detailed input. They act as client-integrated popups, and work in tandem 9 | with interactive components called [Text Inputs](https://discord.com/developers/docs/interactions/message-components#text-inputs). 10 | These inputs can have various formats to accept information from the user on prompt, and use the callback to process that information. 11 | 12 | This section goes in-depth on using modals with other interactive components, and responding to interactions with them. 13 | 14 | ## Modal preview 15 | 16 | Here is an example of how a modal may look. We will go over modal construction in the next part of this article. 17 | 18 |
19 |

20 | Modal preview 21 |

22 |
23 | 24 | Once the user inputs information into the modal, the bot will receive a callback that includes the values. As an example, this information has been portrayed in an embed below. 25 | 26 |
27 | 28 | 29 | 30 | 31 | This is the tag's name. 32 | 33 | This is the tag's description. It can be as long as 4000 characters. 34 | 35 | 36 | 37 | 38 | 39 |
40 | 41 | This was done using the following code. 42 | 43 | ```python title="modals.py" 44 | # At the top of the file. 45 | import disnake 46 | from disnake.ext import commands 47 | from disnake import TextInputStyle 48 | 49 | # Subclassing the modal. 50 | class MyModal(disnake.ui.Modal): 51 | def __init__(self): 52 | # The details of the modal, and its components 53 | components = [ 54 | disnake.ui.TextInput( 55 | label="Name", 56 | placeholder="Foo Tag", 57 | custom_id="name", 58 | style=TextInputStyle.short, 59 | max_length=50, 60 | ), 61 | disnake.ui.TextInput( 62 | label="Description", 63 | placeholder="Lorem ipsum dolor sit amet.", 64 | custom_id="description", 65 | style=TextInputStyle.paragraph, 66 | ), 67 | ] 68 | super().__init__(title="Create Tag", components=components) 69 | 70 | # The callback received when the user input is completed. 71 | async def callback(self, inter: disnake.ModalInteraction): 72 | embed = disnake.Embed(title="Tag Creation") 73 | for key, value in inter.text_values.items(): 74 | embed.add_field( 75 | name=key.capitalize(), 76 | value=value[:1024], 77 | inline=False, 78 | ) 79 | await inter.response.send_message(embed=embed) 80 | 81 | 82 | bot = commands.Bot(command_prefix="!") 83 | 84 | 85 | @bot.slash_command() 86 | async def tags(inter: disnake.AppCmdInter): 87 | """Sends a Modal to create a tag.""" 88 | await inter.response.send_modal(modal=MyModal()) 89 | 90 | 91 | bot.run("YOUR_BOT_TOKEN") 92 | ``` 93 | 94 | The implementation of this command using the lower-level interface can be found [here](https://github.com/DisnakeDev/guide/tree/main/code-samples/interactions/modals/create-tag-low.py). 95 | 96 | ## Creating a modal 97 | 98 | A modal can be created and sent using two different methods, just like other message components: 99 | 100 | - Subclassing disnake.ui.Modal to define the components, and sending it through the `modal` parameter of interaction.send_modal (as done in the example above). 101 | - Defining the attributes and components of the modal inside interaction.send_modal itself. This is considered a "lower-level" implementation of modals - we will use the same term to refer to it in this section. 102 | 103 | A disnake.ui.Modal object has the following attributes: 104 | 105 | - `title` - The title of the modal. 106 | - `custom_id` - An ID specified to the modal. This can be accessed via . 107 | - `timeout` - The time (in seconds) after which the modal is removed from cache. Defaults to **600 seconds**. 108 | - `components` - The list of components to be displayed in the modal. Limited to **5 action rows**. 109 | 110 | :::caution 111 | 112 | Modals without timeouts are not supported. There is no event dispatched when the modal is closed by the user, and thus the modal would not be removed from cache without a timeout. 113 | 114 | ::: 115 | 116 | These attributes can be used while creating a modal, as follows. 117 | 118 | ```python title="modals.py" {4-9} 119 | # Subclassing the modal. 120 | class MyModal(disnake.ui.Modal): 121 | def __init__(self): 122 | super().__init__( 123 | title="Modal Title", 124 | custom_id="modal_id", 125 | timeout=300, 126 | components=[...], 127 | ) 128 | 129 | 130 | # Using the lower-level interface. (inside a function) 131 | await inter.response.send_modal( 132 | title="Modal Title", 133 | custom_id="modal_id", 134 | components=[...], 135 | ) 136 | ``` 137 | 138 | :::note 139 | 140 | It is recommended that you pseudo-randomize a modal's `custom_id` by storing the interaction's ID in it. The reason being that a user can close a modal without triggering an event, and reopen one with the same command. In such cases, the `wait_for` for the old modal will still be active, which will resume execution for both commands. 141 | 142 | In the lower-level interface, you can implement this like so: 143 | 144 | ```diff title="modals.py" 145 | - custom_id="create_tag_low", 146 | + custom_id=f"create_tag_low-{inter.id}", 147 | 148 | % in wait_for 149 | - check=lambda i: i.custom_id == "create_tag_low" and i.author.id == inter.author.id, 150 | + check=lambda i: i.custom_id == f"create_tag_low-{inter.id}", 151 | ``` 152 | 153 | ::: 154 | 155 | ## `TextInput` components 156 | 157 | `TextInput` is a component in itself, like buttons and select menus. It is responsible for receiving user input in the form of text, and cannot be used outside of modals. It has the following customizable attributes. 158 | 159 | - `label` - The label of the text input. 160 | - `custom_id` - An ID specified to the text input. 161 | - `style` - Utilizes one of for the component. Defaults to short. 162 | - `placeholder` - The placeholder text that is shown if nothing is entered. 163 | - `value` - The pre-filled value of the text input (up to `max_length`). 164 | - `required` - Whether the text input is required. Defaults to `True`. 165 | - `min_length` and `max_length` - Set the minimum and maximum length of the inputs respectively. 166 | 167 | These attributes can be used per `TextInput` component, as follows. 168 | 169 | ```python title="textinput.py" 170 | disnake.ui.TextInput( 171 | label="Name", 172 | placeholder="Foo Tag", 173 | custom_id="name", 174 | style=TextInputStyle.short, 175 | max_length=50, 176 | ) 177 | ``` 178 | 179 | The attribute currently has two styles, which have multiple aliases: 180 | 181 | - `short` - Represents a single-line text input component. Also called `single_line`. 182 | - `long` - Represents a multi-line text input component. Also called `multi_line` or `paragraph`. 183 | 184 | ## Modal callback methods 185 | 186 | ### `callback()` 187 | 188 | It refers to the callback associated with the modal, and returns a object which includes the values input by the user. It is overriden by subclasses, allowing the developer to define the action done on the modal input received. 189 | 190 | ```python title="modals.py" {5,6} 191 | class MyModal(disnake.ui.Modal): 192 | def __init__(self): 193 | super().__init__(...) 194 | 195 | async def callback(self, inter: disnake.ModalInteraction): 196 | await inter.response.send_message("User input was received!") 197 | ``` 198 | 199 | If you're using the lower-level interface for sending the modal, you'll have to utilize an event/listener to check for the custom ID and respond to the interaction. There is an event for this purpose. 200 | 201 | ```python title="modals.py" {10-14} 202 | @bot.slash_command() 203 | async def tags(inter: disnake.ApplicationCommandInteraction): 204 | ... 205 | await inter.response.send_modal(..., custom_id="modal_custom_id") 206 | 207 | 208 | ... 209 | 210 | 211 | @bot.event() 212 | async def on_modal_submit(inter: disnake.ModalInteraction): 213 | 214 | if inter.custom_id == "modal_custom_id": 215 | await do_stuff_here() 216 | ``` 217 | 218 | You can alternatively use bot.wait_for() for this purpose, inside the command itself. 219 | 220 | ```python title="modals.py" {} 221 | @bot.slash_command() 222 | async def tags(inter: disnake.ApplicationCommandInteraction): 223 | ... 224 | await inter.response.send_modal(..., custom_id="modal_custom_id") 225 | 226 | modal_inter: disnake.ModalInteraction = await bot.wait_for( 227 | "modal_submit", 228 | check=lambda i: i.custom_id == "modal_custom_id" and i.author.id == inter.author.id, 229 | timeout=300, 230 | ) 231 | ``` 232 | 233 | ### `on_error()` 234 | 235 | Received when the modal submission encounters an error. It returns the error as well as a object that can be used to respond to the user. The default implementation prints the traceback to `stderr`. 236 | 237 | ````python title="modals.py" {5,6} 238 | class MyModal(disnake.ui.Modal): 239 | def __init__(self): 240 | super().__init__(...) 241 | 242 | async def on_error(self, error: Exception, inter: disnake.ModalInteraction): 243 | await inter.response.send_message(f"An error occurred!\n```{error}```") 244 | ```` 245 | 246 | ### `on_timeout()` 247 | 248 | Called when the user does not respond before specified timeout; the modal is removed from cache without an interaction being made. 249 | 250 | :::note 251 | 252 | No interaction object is passed to the on_timeout() method. It is to be subclassed solely for backend functions. 253 | 254 | ::: 255 | 256 | ## Modal notes 257 | 258 | - The only component that can currently be used with modals is TextInput. Buttons and select menus cannot be used inside modals. 259 | - You can add components to a disnake.ui.Modal object using the append_component() method. To directly add text inputs, use add_text_input() instead. 260 | - The defaults to `short`, if not specified for the component. 261 | - All TextInput components are required by default. 262 | 263 | ## Modal limits 264 | 265 | There are a few limits to be aware of while using modals due to the API's limitations. Here is a quick reference you can come back to: 266 | 267 | - A modal can have a total of **5 action rows**. 268 | - A modal's `title` has a maximum length of **45 characters**. 269 | - The text input `label` has a maximum length of **45 characters**. 270 | - The text input `placeholder` is limited to **100 characters**. 271 | - The `custom_id` is limited to **100 characters**, for both modals and text inputs. 272 | - The pre-defined `value` has a maximum length of **4000 characters** (overriden by the developer-defined limit). 273 | - The minimum and maximum values of a text input can be set from **0-4000 characters** and **1-4000 characters** respectively. 274 | 275 | ## Resulting code 276 | 277 | 278 | -------------------------------------------------------------------------------- /guide/docs/interactions/select-menus.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: They refer to views, buttons and select menus that can be added to the messages your bot sends. 3 | --- 4 | 5 | # Select Menus 6 | 7 | Select Menus allow users to interact with your bot through an interactive dropdown component. This component provides selectable options for your users to choose from. 8 | You can require users to select a minimum/maximum number of options using min_values and max_values. 9 | 10 | Select Menus are available as the following types: 11 | 12 | - disnake.ui.StringSelect Allows you to input custom string options 13 | for users to select from, up to 25 14 | - disnake.ui.UserSelect User or Member objects as options 15 | - disnake.ui.RoleSelect Role objects as options 16 | - disnake.ui.ChannelSelect Channel objects as options 17 | 18 | The options for user, role, and channel select menus are populated by Discord, and currently cannot be limited to a specific subset. See [below](#other-selects) for details. 19 | 20 | This guide will walk you through a pretty basic use-case for a `StringSelect` dropdowns using [Views](#example-of-stringselects) and [low-level components](#views-vs-low-level-components). 21 | If you need more examples, you can always check the examples in the [disnake repo](https://github.com/DisnakeDev/disnake/tree/master/examples). 22 |   23 | :::note 24 | A message can have a maximum of 5 rows of components. Select components each take a single row, therefore you cannot have more than 5 Select components per message 25 | ::: 26 | 27 |
28 |

29 | StringSelect example 30 |

31 |
32 | 33 | ### Example of `StringSelect`s 34 | 35 | 36 | 37 | 38 | ```python 39 | import os 40 | 41 | import disnake 42 | from disnake.ext import commands 43 | 44 | 45 | # Defines the StringSelect that contains animals that your users can choose from 46 | class AnimalDropdown(disnake.ui.StringSelect): 47 | def __init__(self): 48 | 49 | # Define the options that will be displayed inside the dropdown. 50 | # You may not have more than 25 options. 51 | # There is a `value` keyword that is being omitted, which is useful if 52 | # you wish to display a label to the user, but handle a different value 53 | # here within the code, like an index number or database id. 54 | options = [ 55 | disnake.SelectOption(label="Dog", description="Dogs are your favorite type of animal"), 56 | disnake.SelectOption(label="Cat", description="Cats are your favorite type of animal"), 57 | disnake.SelectOption( 58 | label="Snake", description="Snakes are your favorite type of animal" 59 | ), 60 | disnake.SelectOption( 61 | label="Gerbil", description="Gerbils are your favorite type of animal" 62 | ), 63 | ] 64 | 65 | # We will include a placeholder that will be shown until an option has been selected. 66 | # The min and max values indicate the minimum and maximum number of options to be selected - 67 | # in this example we will only allow one option to be selected. 68 | super().__init__( 69 | placeholder="Choose an animal", 70 | min_values=1, 71 | max_values=1, 72 | options=options, 73 | ) 74 | 75 | # This callback is called each time a user has selected an option 76 | async def callback(self, inter: disnake.MessageInteraction): 77 | # Use the interaction object to respond to the interaction. 78 | # `self` refers to this StringSelect object, and the `values` 79 | # attribute contains a list of the user's selected options. 80 | # We only want the first (and in this case, only) one. 81 | await inter.response.send_message(f"Your favorite type of animal is: {self.values[0]}") 82 | 83 | 84 | # Now that we have created the Select object with its options and callback, we need to attach it to a 85 | # View that will be displayed to the user in the command response. 86 | # 87 | # Views have a default timeout of 180s, at which the bot will stop listening for those events. 88 | # You may pass any float here, or `None` if you wish to remove the timeout. 89 | # Note: If `None` is passed, this view will persist only until the bot is restarted. If you wish to have persistent views, 90 | # consider using low-level components or check out the persistent view example: 91 | # https://github.com/DisnakeDev/disnake/blob/master/examples/views/persistent.py 92 | class DropDownView(disnake.ui.View): 93 | def __init__(self): 94 | # You would pass a new `timeout=` if you wish to alter it, but 95 | # we will leave it empty for this example so that it uses the default 180s. 96 | super().__init__() 97 | 98 | # Now let's add the `StringSelect` object we created above to this view 99 | self.add_item(AnimalDropdown()) 100 | 101 | 102 | # Finally, we create a basic bot instance with a command that will utilize the created view and dropdown. 103 | bot = commands.Bot() 104 | 105 | 106 | @bot.listen() 107 | async def on_ready(): 108 | print(f"Logged in as {bot.user} (ID: {bot.user.id})\n------") 109 | 110 | 111 | @bot.slash_command() 112 | async def animals(inter: disnake.ApplicationCommandInteraction): 113 | """Sends a message with our dropdown containing the animals""" 114 | 115 | # Create the view with our dropdown object 116 | view = DropDownView() 117 | 118 | # Respond to the interaction with a message and our view 119 | await inter.response.send_message("What is your favorite type of animal?", view=view) 120 | 121 | 122 | if __name__ == "__main__": 123 | bot.run(os.getenv("BOT_TOKEN")) 124 | ``` 125 | 126 | 127 | 128 | 129 | ```python 130 | # Instead of subclassing `disnake.ui.StringSelect`, this example shows how to use the 131 | # `@disnake.ui.string_select` decorator directly inside the View to create the dropdown 132 | # component. 133 | class AnimalView(disnake.ui.View): 134 | def __init__(self): 135 | super().__init__() 136 | 137 | # If you wish to pass a previously defined sequence of values to this `View` so that 138 | # you may have dynamic options, you can do so by defining them within this __init__ method. 139 | # `self.animal_callback.options = [...]` 140 | 141 | @disnake.ui.string_select( 142 | placeholder="Choose an animal", 143 | options=[ 144 | disnake.SelectOption(label="Dog", description="Dogs are your favorite type of animal"), 145 | disnake.SelectOption(label="Cat", description="Cats are your favorite type of animal"), 146 | disnake.SelectOption( 147 | label="Snake", description="Snakes are your favorite type of animal" 148 | ), 149 | disnake.SelectOption( 150 | label="Gerbil", description="Gerbils are your favorite type of animal" 151 | ), 152 | ], 153 | min_values=1, 154 | max_values=1, 155 | ) 156 | async def animal_callback( 157 | self, select: disnake.ui.StringSelect, inter: disnake.MessageInteraction 158 | ): 159 | # The main difference in this callback is that we access the `StringSelect` through the 160 | # parameter passed to the callback, vs the subclass example where we access it via `self` 161 | await inter.response.send_message(f"You favorite type of animal is: {select.values[0]}") 162 | ``` 163 | 164 | 165 | 166 | 167 | ### Other Selects 168 | 169 | The three other select components available are constructed and can be used in the same manner. 170 | The main difference is that we do not create nor pass any options to these Select objects as Discord will provide these options to the user automatically. 171 | The selected option(s) that are available in `self.values` will be the selected object(s) rather than the string values. 172 | 173 | - `UserSelect.values` will return a list of disnake.User or disnake.Member 174 | - `RoleSelect.values` will return a list of disnake.Role 175 | - `ChannelSelect.values` returns a list of disnake.abc.GuildChannel, disnake.Thread, or disnake.PartialMessageable 176 | 177 | :::note 178 | 179 | disnake.ui.ChannelSelect has an extra keyword argument that is 180 | not available to the other SelectMenu types. 181 | 182 | `channel_types` will allow you to specify the types of channels made available as options (omit to show all available channels): 183 | 184 | - `channel_types=[ChannelType.text]` to display only guild text channels as options 185 | - `channel_types=[ChannelType.voice, ChannelType.stage_voice]` to display only guild voice and stage channels as options 186 | - etc. 187 | 188 | See disnake.ChannelType to see more channel types. 189 | ::: 190 | 191 | ### Handling View Timeouts 192 | 193 | When a View times out, the bot will no longer listen for these events, and your users will receive an error `This interaction failed`. 194 | 195 | To avoid this, you have a couple of options when the view times out: 196 | 197 | 1. Disable the components within the view so that they are no longer interactive. 198 | 2. Remove the view altogether, leaving only the original message without components. 199 | 200 | For this example we will disable the components using an `on_timeout` method. However, if you wish to remove the View completely you can pass `view=None` (instead of `view=self` like the example below). 201 | 202 | We'll continue with the `subclassing.py` example from above, only altering the relevant parts: 203 | 204 | ```python title="viewtimeout.py" 205 | ... 206 | 207 | 208 | class DropDownView(disnake.ui.View): 209 | 210 | message: disnake.Message # adding to typehint the future `message` attribute 211 | 212 | def __init__(self): 213 | # this time we will set the timeout to 30.0s 214 | super().__init__(timeout=30.0) 215 | # now let's add the `StringSelect` object we created above to this view 216 | self.add_item(AnimalDropdown()) 217 | 218 | # To handle this timeout, we'll need to override the default `on_timeout` method within the `View`` 219 | async def on_timeout(self): 220 | # Now we will edit the original command response so that it displays the disabled view. 221 | # Since `self.children` returns a list of the components attached to this `View` and we want 222 | # to prevent the components from remaining interactive after the timeout, we can easily loop 223 | # over its components and disable them. 224 | for child in self.children: 225 | if isinstance(child, (disnake.ui.Button, disnake.ui.BaseSelect)): 226 | child.disabled = True 227 | 228 | # Now, edit the message with this updated `View` 229 | await self.message.edit(view=self) 230 | 231 | 232 | ... 233 | # The only changes we need to make to handle the updated view is to fetch the original response after, 234 | # so that the message can be edited later. 235 | # This is necessary because `interaction.send` methods do not return a message object 236 | @bot.slash_command() 237 | async def animals(inter: disnake.ApplicationCommandInteraction): 238 | """Sends a message with our dropdown containing the animals""" 239 | 240 | # Create the view with our dropdown object. 241 | view = DropDownView() 242 | 243 | # Respond to the interaction with a message and our view. 244 | await inter.response.send_message("What is your favorite type of animal?", view=view) 245 | 246 | # This will add a new `message` attribute to the `DropDownView` that will be edited 247 | # once the view has timed out 248 | view.message = await inter.original_response() 249 | 250 | 251 | ... 252 | ``` 253 | 254 | ### Views vs. low-level components 255 | 256 | As an alternative to using `View`s, it is possible to use Select Menus as low-level components. 257 | These components do not need to be sent as part of a View and can be sent as is. 258 | 259 | Note that any component being sent in this manner must have a `custom_id` explicitly set. Component interactions are sent to all listeners, 260 | which means the `custom_id` should be unique for each component to be able to identify the component in your code. 261 | 262 | The main advantage of this is that listeners, by nature, are persistent and will remain fully functional over bot reloads. Listeners are stored in the bot 263 | strictly once, and are shared by all components. Because of this, the memory footprint will generally be smaller than that of an equivalent view. 264 | 265 | The example below will be similar to the above examples, however will be executed as a low level component instead. 266 | 267 | ```python title="low_level_dropdown.py" 268 | @bot.slash_command() 269 | async def animals(inter: disnake.ApplicationCommandInteraction): 270 | """Sends a message with our dropdown containing the animals""" 271 | 272 | await inter.response.send_message( 273 | "What is your favorite type of animal?", 274 | components=[ 275 | disnake.ui.StringSelect( 276 | custom_id="fav_animal", 277 | options=["Dog", "Cat", "Snake", "Gerbil"], 278 | ) 279 | ], 280 | ) 281 | 282 | 283 | # Now we create the listener that will handle the users's selection(s), similarly to the callback we used above. 284 | @bot.listen("on_dropdown") 285 | async def fav_animal_listener(inter: disnake.MessageInteraction): 286 | # First we should check if the interaction is for the `fav_animal` dropdown we created 287 | # and ignore if it isn't. 288 | if inter.component.custom_id != "fav_animal": 289 | return 290 | 291 | # Now we can respond with the user's favorite animal 292 | await inter.response.send_message(f"Your favorite type of animal is: {inter.values[0]}") 293 | ``` 294 | 295 | :::note 296 | These component listeners can be used inside cogs as well. Simply replace `@bot.listen()` with `@commands.Cog.listener()` and 297 | be sure to pass `self` as the first argument of the listener function 298 | ::: 299 | -------------------------------------------------------------------------------- /guide/docs/interactions/slash-commands.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: They can be accessed via the textbox, and return the values the user submitted with the command. 3 | --- 4 | 5 | # Slash Commands 6 | 7 | A **slash command** is one of the types of client-integrated interactions a bot can create. Unlike prefix commands, these do not require the message content intent. 8 | 9 | This section will go in-depth on the creation, types and handling of slash commands with `disnake`. 10 | 11 | :::note 12 | 13 | A bot needs to be authorized with the `applications.commands` scope in order to create slash commands in a guild. We recommend adding this scope to your bot invite links. 14 | 15 | ::: 16 | 17 | ## Defining slash commands 18 | 19 | To create a slash command, use the @Bot.slash_command decorator. 20 | 21 | ```python 22 | @bot.slash_command( 23 | name="ping", 24 | description="Returns websocket latency.", 25 | ) 26 | async def ping(inter: disnake.ApplicationCommandInteraction): 27 | ... 28 | ``` 29 | 30 |
31 | 32 | 33 |
34 | 35 | ping 36 | 37 |
38 | Latency: `42ms`! 39 |
40 |
41 |
42 | 43 | If you're using cogs, the @commands.slash_command decorator should be used instead. 44 | 45 | ```python 46 | class Meta(commands.Cog): 47 | """Meta commands.""" 48 | 49 | @commands.slash_command( 50 | name="ping", 51 | description="Returns websocket latency.", 52 | ) 53 | async def ping(self, inter: disnake.ApplicationCommandInteraction): 54 | ... 55 | ``` 56 | 57 | ### Parameters 58 | 59 | While some commands can exist without arguments, most commands will need to take user input to be useful. Adding an option is as simple as defining a parameter on the callback. 60 | 61 | Here's an example of a command with one integer option (without a description): 62 | 63 | ```python 64 | @bot.slash_command(description="Multiplies the number by 7") 65 | async def multiply(inter, number: int): 66 | await inter.response.send_message(number * 7) 67 | ``` 68 | 69 | To make a parameter optional, provide a default value for the option: 70 | 71 | ```python 72 | @bot.slash_command(description="Multiplies the number by a multiplier") 73 | async def multiply(inter, number: int, multiplier: int = 7): 74 | await inter.response.send_message(number * multiplier) 75 | ``` 76 | 77 | 80 | 81 | ## Registering commands 82 | 83 | Unlike prefix commands, slash commands must be registered with Discord first, otherwise they won't show up if you press "/". 84 | By default, the library registers and updates your slash commands automatically. 85 | It does so on bot start or when cogs are loaded, unloaded, or reloaded. 86 | 87 | :::note 88 | 89 | The library avoids unnecessary API requests during this process. 90 | If the registered commands match the commands in your code, no API requests are made. 91 | Otherwise only one bulk overwrite request is done. 92 | 93 | ::: 94 | 95 | If you want to disable the automatic registration, set the `sync_commands` parameter of `commands.Bot` to `False`. 96 | If you want to see how exactly the list of registered commands changes, set the `sync_commands_debug` parameter to `True`. 97 | 98 | ```python 99 | bot = commands.Bot("!", sync_commands_debug=True) 100 | ``` 101 | 102 | It will print (or use the logger, if enabled) which commands were added, edited, deleted or left untouched. 103 | 104 | ### Global commands 105 | 106 | Global slash commands are visible everywhere, including your bot DMs. 107 | If you don't specify the `test_guilds` parameter of `commands.Bot`, all your slash commands are global by default. 108 | 109 | Global commands are visible and usable on most devices right away. 110 | 111 | ### Guild commands 112 | 113 | If you specify the `test_guilds` parameter of `commands.Bot`, all slash commands in your code are no longer global. 114 | The following code will register all slash commands only in 2 guilds: 115 | 116 | ```python 117 | bot = commands.Bot("!", test_guilds=[123456789, 987654321]) # guild IDs 118 | ``` 119 | 120 | If you want to keep some of your slash commands global and make some of them local, specify the `guild_ids` parameter of `@bot.slash_command()`: 121 | 122 | ```python 123 | bot = commands.Bot("!") 124 | 125 | 126 | @bot.slash_command() 127 | async def global_command(inter): 128 | """This command is visible everywhere""" 129 | ... 130 | 131 | 132 | @bot.slash_command(guild_ids=[123456789]) 133 | async def local_command(inter): 134 | """This command is visible in one guild""" 135 | ... 136 | ``` 137 | 138 | ## Responding to commands {#responding} 139 | 140 | ### Sending responses {#response} 141 | 142 | The response attribute returns a InteractionResponse 143 | instance that has four ways of responding to an ApplicationCommandInteraction. 144 | A response can only be done once. 145 | If you want to send secondary messages, consider using a followup webhook instead. 146 | 147 | :::warning 148 | 149 | An interaction can only be responded to **once**. After a response is made, **no more responses can be made.** See the [followup](#followup) object for sending messages after responding. 150 | ::: 151 | 152 | 1. send_message - Sends response message. 153 | 1. edit_message - Edits message, for example in 154 | component or component+modal interactions. Cannot be used with application commands. 155 | 1. defer - Defers the interaction. 156 | 1. send_modal - Sends a 157 | Modal 158 | as a response. 159 | 160 | :::note 161 | 162 | If you're going to run long processes (more than 3 seconds) before responding, you should first defer the interaction, 163 | as interactions expire after 3 seconds and later responses will fail. 164 | Deferring an interaction response shows a loading indicator to the user, and gives you more time to prepare a complete response. 165 | Once your response is ready, you can edit the original response using the Interaction.edit_original_response method. 166 | 167 | ::: 168 | 169 | ```python title="example.py" 170 | @bot.slash_command() 171 | async def ping(inter: ApplicationCommandInteraction): 172 | await inter.response.send_message("Pong!") 173 | 174 | 175 | @bot.slash_command() 176 | async def defer(inter: ApplicationCommandInteraction): 177 | await inter.response.defer() 178 | await asyncio.sleep(10) 179 | await inter.edit_original_response(content="The wait is over, my comrades!") 180 | ``` 181 | 182 | ### Followups 183 | 184 | Followups are a way to send a message after responding. There are two important restrictions for when a followup can be used: 185 | 186 | - The interaction must have been responded to (see [responding](#responses)). 187 | - The interaction must not be expired (i.e. hasn't exceeded the 15 minute limit). 188 | Checking if an interaction has expired can be done with ApplicationCommandInteraction.is_expired. 189 | 190 | At their core, followups are simply Webhook instances. The only special thing about them is that the `wait` parameter is treated as if it is always set to `True`. 191 | 192 | Take this as an example of how followups could be used: 193 | 194 | ```python 195 | @bot.slash_command() 196 | async def timer(inter: disnake.ApplicationCommandInteraction, seconds: int): 197 | await inter.response.send_message(f"Setting a timer for {seconds} seconds.") 198 | await asyncio.sleep(seconds) 199 | await inter.followup.send(f"{inter.author.mention}, your timer expired!") 200 | ``` 201 | 202 |
203 | 204 | 205 |
206 | 207 | timer 208 | 209 |
210 | Setting a timer for 30 seconds. 211 |
212 | 213 |
214 | Setting a timer for 30 seconds. 215 |
216 | , your timer expired! 217 |
218 |
219 |
220 | -------------------------------------------------------------------------------- /guide/docs/intro.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: / 3 | description: The purpose of this guide is to make your journey with disnake easier. 4 | keywords: [disnake, bot, guide, api, python] 5 | --- 6 | 7 | # Introduction 8 | 9 | Welcome! The purpose of this guide is to make your journey with `disnake` easier, whether you're an experienced 10 | developer just getting into coding Discord bots, or an advanced bot developer who has decided to proceed with `disnake` 11 | as their library of choice. 12 | 13 | The concepts this guide will be going over include: 14 | 15 | - How to get started on working with bots 16 | - How to create and organize commands, using cogs/extensions 17 | - Working with databases (such as [`sqlite (aiosqlite)`][sqlite-docs] and [`mongodb (motor)`][motor-docs]) 18 | - Using the [`AutoShardedClient`](https://disnake.readthedocs.io/en/stable/api.html#disnake.AutoShardedClient) class 19 | to shard your bot 20 | - A plethora of examples with popular topics along with in-depth explanation, and much more! 21 | 22 | [sqlite-docs]: https://aiosqlite.omnilib.dev/en/latest/ 23 | [motor-docs]: https://motor.readthedocs.io/en/stable/tutorial-asyncio.html 24 | 25 | This guide will showcase the various features and events that the library has, 26 | while giving you an idea of how these functions work together as well as how a project might look in production. 27 | 28 | ## Disclaimer 29 | 30 | We appreciate the process of making Discord bots, but creating a bot with `disnake` requires a decent amount of 31 | experience on working with Python and object-oriented programming. While you _can_ go forward and make a bot with little 32 | to no knowledge of Python or programming, doing so may hinder your progress and cause issues in the future. 33 | 34 | If you don't know Python but would like to learn more, here are a few resources to get you started: 35 | 36 | - [Think Python](https://greenteapress.com/thinkpython/html/index.html), a free online book. 37 | - [Codecademy's course](https://www.codecademy.com/learn/learn-python-3) for learning Python 3. 38 | - [LearnPython](https://www.learnpython.org/), an interactive tutorial for absolute beginners. 39 | - [A Byte of Python by Swaroop C.H.](https://python.swaroopch.com/), an introductory text for people with no previous 40 | programming experience. 41 | 42 | An extensive list of these resources can be found on 43 | [Python's official website](https://wiki.python.org/moin/BeginnersGuide/NonProgrammers). Once you feel 44 | a little more experienced with Python, you can come back here to get started! 45 | 46 | ## Links & Credits 47 | 48 | This guide is made using [**Docusaurus**](https://docusaurus.io/), a static site generator geared towards building 49 | project documentation. The package utilized for Discord-like message elements is Danktuary's [`@discord-message-components/react`](https://www.npmjs.com/package/@discord-message-components/react). 50 | 51 | The idea of building a guide that showcases the use of `disnake`'s syntax and features was inspired from 52 | [**Discord.js**](https://discord.js.org/), one of the most popular JavaScript Discord libraries; their guide can be 53 | found [here](https://discordjs.guide/). The credit for the initial directory and articles structure goes to them. 54 | 55 | We also thank all contributors on the repository, who strive to make the guide better. 56 | 57 | Lastly, a huge thanks to **you**, the members of the `disnake` community, for supporting our library and choosing us for 58 | your bot development journey. We as the authors of this guide aim to familiarize users on coding Discord bots with our 59 | library, and your support gives us a boost to make the guide better, one commit at a time. 60 | -------------------------------------------------------------------------------- /guide/docs/popular-topics/errors.mdx: -------------------------------------------------------------------------------- 1 | # Error Handling 2 | 3 | :::note 4 | 5 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/ext/commands/commands.html#error-handling), and inherited from `discord.py`. It will most likely be rewritten in the future. 6 | 7 | ::: 8 | 9 | When our commands fail to parse we will, by default, receive a noisy error in `stderr` of our console that tells us 10 | that an error has happened and has been silently ignored. 11 | 12 | In order to handle our errors, we must use something called an error handler. There is a global error handler, called on_command_error() which works 13 | like any other event in the Event Reference. This global error handler is called for every error reached. 14 | 15 | Most of the time however, we want to handle an error local to the command itself. Luckily, commands come with local error 16 | handlers that allow us to do just that. First we decorate an error handler function with Command.error(): 17 | 18 | ```python 19 | @bot.command() 20 | async def info(ctx, *, member: disnake.Member): 21 | """Tells you some info about the member.""" 22 | msg = f"{member} joined on {member.joined_at} and has {len(member.roles)} roles." 23 | await ctx.send(msg) 24 | 25 | 26 | @info.error 27 | async def info_error(ctx, error): 28 | if isinstance(error, commands.BadArgument): 29 | await ctx.send("I could not find that member...") 30 | ``` 31 | 32 | The first parameter of the error handler is the Context while the second one is an exception that is derived from CommandError. A list of errors is found in the Exceptions page of the documentation. 33 | -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/embeds-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/embeds-intro.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/intents-bot-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/intents-bot-tab.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/intents-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/intents-intro.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/intents-privileged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/intents-privileged.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/monetization-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/monetization-intro.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/monetization-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/monetization-response.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/permissions-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/permissions-intro.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/reactions-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/reactions-intro.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/threads-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/threads-intro.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/images/webhooks-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/popular-topics/images/webhooks-intro.png -------------------------------------------------------------------------------- /guide/docs/popular-topics/intents.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: This section covers a list of intents your bot might require. 3 | keywords: [disnake, bot, guide, tutorial, python, gateway intents] 4 | --- 5 | 6 | # Gateway Intents 7 | 8 | :::note 9 | 10 | This content has been taken directly from the [documentation](https://docs.disnake.dev/en/stable/intents.html), and inherited from `discord.py`. It will most likely be rewritten in the future. 11 | 12 | ::: 13 | 14 | An intent basically allows a bot to subscribe to specific buckets of events. The events that correspond to each intent is documented in the individual attribute of the Intents documentation. 15 | 16 | These intents are passed to the constructor of commands.Bot or its subclasses (, , ) with the `intents` argument. 17 | 18 | If intents are not passed, then the library defaults to every intent being enabled except the privileged intents, currently , and . 19 | 20 | ## What intents are needed? 21 | 22 | The intents that are necessary for your bot can only be dictated by yourself. Each attribute in the class documents what events it corresponds to and what kind of cache it enables. 23 | 24 | For example, if you want a bot that functions without spammy events like presences or typing then we could do the following: 25 | 26 | ```python title="intents.py" {3-5} 27 | import disnake 28 | 29 | intents = disnake.Intents.default() 30 | intents.typing = False 31 | intents.presences = False 32 | 33 | # Somewhere else: 34 | client = disnake.Client(intents=intents) 35 | # or, 36 | from disnake.ext import commands 37 | 38 | bot = commands.Bot(command_prefix=commands.when_mentioned_or("!"), intents=intents) 39 | ``` 40 | 41 | :::note 42 | 43 | This doesn't enable since it's a privileged intent. 44 | 45 | ::: 46 | 47 | Another example showing a bot that only deals with messages and guild information: 48 | 49 | ```python title="intents.py" {3} 50 | import disnake 51 | 52 | intents = disnake.Intents(messages=True, guilds=True) 53 | # If you also want reaction events enable the following: 54 | # intents.reactions = True 55 | 56 | # Somewhere else: 57 | client = disnake.Client(intents=intents) 58 | # or 59 | from disnake.ext import commands 60 | 61 | bot = commands.Bot(command_prefix=commands.when_mentioned, intents=intents) 62 | ``` 63 | 64 | ## Privileged Intents 65 | 66 | With the API change requiring bot authors to specify intents, some intents were restricted further and require more manual steps. These intents are called **privileged intents**. 67 | 68 | A privileged intent is one that requires you to go to the developer portal and manually enable it. To enable privileged intents do the following: 69 | 70 | 1. Make sure you're logged on to the [Discord website](https://discord.com). 71 | 2. Navigate to the [application page](https://discord.com/developers/applications). 72 | 3. Click on the bot you want to enable privileged intents for. 73 | 4. Navigate to the bot tab on the left side of the screen. 74 | 75 |
76 | The bot tab in the application page. 81 |
82 | 83 | 5. Scroll down to the "Privileged Gateway Intents" section and enable the ones you want. 84 | 85 |
86 | The privileged gateway intents selector. 91 |
92 | 93 | :::warning 94 | 95 | Enabling privileged intents when your bot is in over 100 guilds requires going through [bot verification](https://support.discord.com/hc/en-us/articles/360040720412). If your bot is already verified and you would like to enable a privileged intent you must go through [Discord support](https://dis.gd/contact) and talk to them about it. 96 | 97 | ::: 98 | 99 | :::note 100 | 101 | Even if you enable intents through the developer portal, you still have to enable the intents through code as well. 102 | 103 | ::: 104 | 105 | ## Do I need privileged intents? 106 | 107 | This is a quick checklist to see if you need specific privileged intents. 108 | 109 | ### Message Content Intent 110 | 111 | - Whether you want a prefix that isn't the bot mention. 112 | - Whether you want to see what the content of a message was. This includes content, embeds, attachments, and components. 113 | 114 | ### Presence Intent 115 | 116 | - Whether you use at all to track member statuses. 117 | - Whether you use or to check member's activities. 118 | 119 | ### Member Intent 120 | 121 | - Whether you track member joins or member leaves, corresponds to and events. 122 | - Whether you want to track member updates such as nickname or role changes. 123 | - Whether you want to track user updates such as usernames, avatars, discriminators, etc. 124 | - Whether you want to request the guild member list through or . 125 | - Whether you want high accuracy member cache under . 126 | 127 | ## Member Cache 128 | 129 | Along with intents, Discord now further restricts the ability to cache members and expects bot authors to cache as little as is necessary. However, to properly maintain a cache the intent 130 | is required in order to track the members who left and properly evict them. 131 | 132 | To aid with member cache where we don't need members to be cached, the library now has a flag to control the member cache. The documentation page for the class goes over the specific policies that are possible. 133 | 134 | It should be noted that certain things do not need a member cache since Discord will provide full member information if possible. For example: 135 | 136 | - will have be a member even 137 | if cache is disabled. 138 | - will have the member parameter be a member even 139 | if cache is disabled. 140 | - will have the user parameter be a member when in a guild 141 | even if cache is disabled. 142 | - will have be 143 | a member when in a guild even if cache is disabled. 144 | - The reaction add events do not contain additional information when in direct messages. This is a Discord limitation. 145 | - The reaction removal events do not have member information. This is a Discord limitation. 146 | 147 | Other events that take a will require the use of the member cache. If absolute accuracy over the member cache is desirable, then it is advisable to have the intent enabled. 148 | 149 | ## Retrieving Members 150 | 151 | If the cache is disabled or you disable chunking guilds at startup, we might still need a way to load members. The library offers a few ways to do this: 152 | 153 | - 154 | 155 | - Used to query members by a prefix matching nickname or username. 156 | - This can also be used to query members by their user ID. 157 | - This uses the gateway and not the HTTP. 158 | 159 | - 160 | 161 | - This can be used to fetch the entire member list through the gateway. 162 | 163 | - 164 | 165 | - Used to fetch a member by ID through the HTTP API. 166 | 167 | - 168 | 169 | - Used to fetch a large number of members through the HTTP API. 170 | 171 | It should be noted that the gateway has a strict rate limit of 120 requests per 60 seconds. 172 | 173 | ## Troubleshooting 174 | 175 | Some common issues relating to the mandatory intent change. 176 | 177 | ### Where'd my members go? 178 | 179 | Due to an [API change](#member-cache) Discord is now forcing developers who want member caching to explicitly opt-in to it. This is a Discord mandated change and there is no way to bypass it. In order to get members back you have to explicitly enable the [members privileged intent](#privileged-intents) and 180 | change the attribute to true. 181 | 182 | For example: 183 | 184 | ```python title="intents.py" {4} 185 | import disnake 186 | 187 | intents = disnake.Intents.default() 188 | intents.members = True 189 | 190 | # Somewhere else: 191 | client = disnake.Client(intents=intents) 192 | # or 193 | from disnake.ext import commands 194 | 195 | bot = commands.Bot(command_prefix=commands.when_mentioned, intents=intents) 196 | ``` 197 | 198 | ### Why do most messages have no content? 199 | 200 | After August 31st 2022, Discord will start blocking message content from being sent to bots that don't have the message content intent enabled. 201 | 202 | If you are on version 2.4 or before, your bot will be able to access message content without the intent enabled in the code. However, as of version 2.5, it is required to enable message_content to receive message content over the gateway. 203 | 204 | :::note 205 | 206 | Starting with version 2.5, or any version once the intent deadline has passed, you will be required to turn on the message content intent in the [Discord Developer Portal](https://discord.com/developers/applications) as well. 207 | 208 | ::: 209 | 210 | Message content refers to four attributes on the object: 211 | 212 | - content 213 | - embeds 214 | - attachments 215 | - components 216 | 217 | You will always receive message content in the following cases even without the message content intent: 218 | 219 | - Messages the bot sends 220 | - Messages the bot receives in DMs 221 | - Messages in which the bot is mentioned 222 | - Messages received as part of an interaction (for example, a message command) 223 | 224 | ### Why does `on_ready` take so long to fire? 225 | 226 | As part of the API change regarding intents, Discord also changed how members are loaded in the beginning. Originally the library could request 75 guilds at once and only request members from guilds that have the Guild.large attribute set to `True`. With the new intent changes, Discord mandates that we can only send 1 guild per request. This causes a 75x slowdown which is further compounded by the fact that _all_ guilds, not just large guilds are being requested. 227 | 228 | There are a few solutions to fix this. 229 | 230 | 1. Request the privileged presences intent along with the privileged members intent and enable both of them. This allows the initial member list to contain online members just like the old gateway. Note that we're still limited to 1 guild per request but the number of guilds we request is significantly reduced. 231 | 232 | 2. Disable member chunking by setting `chunk_guilds_at_startup` to `False` when constructing a client. Then, when chunking for a guild is necessary you can use the various techniques to [retrieve members](#retrieving-members). 233 | 234 | To illustrate the slowdown caused by the API change, take a bot who is in 840 guilds and 95 of these guilds are "large" (over 250 members). 235 | 236 | Under the original system this would result in 2 requests to fetch the member list (75 guilds, 20 guilds) roughly taking 60 seconds. With but not this requires 840 requests, with a rate limit of 120 requests per 60 seconds means that due to waiting for the rate limit it totals to around 7 minutes of waiting for the rate limit to fetch all the members. With both Intents.members and Intents.presences we mostly get the old behaviour so we're only required to request for the 95 guilds that are large, this is slightly less than our rate limit so it's close to the original timing to fetch the member list. 237 | 238 | Unfortunately due to this change being required from Discord there is nothing that the library can do to mitigate this. 239 | -------------------------------------------------------------------------------- /guide/docs/popular-topics/intro.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: /popular-topics 3 | hide_table_of_contents: true 4 | description: 5 | This section covers several Discord client and gateway features extensively, including their methods and various 6 | use-cases. 7 | --- 8 | 9 | # Popular Topics 10 | 11 | This section covers several Discord client and gateway features extensively, including their methods and various 12 | use-cases. 13 | 14 |
15 |
16 |
17 |
18 |
19 | 20 | Threads Intro Image 21 | 22 |
23 |
24 | 25 |

Threads

26 |
27 |
Think of them as sub-channels inside a channel.
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 | Embeds Intro Image 37 | 38 |
39 |
40 | 41 |

Embeds

42 |
43 |
Present your content in an organized format.
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | Reactions Intro Image 53 | 54 |
55 |
56 | 57 |

Reactions

58 |
59 |
Create polls, paginate your commands, and more.
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 69 | Webhooks Intro Image 70 | 71 |
72 |
73 | 74 |

Webhooks

75 |
76 |
Send messages in a text channel without a bot user.
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | 85 | Permissions Intro Image 86 | 87 |
88 |
89 | 90 |

Permissions

91 |
92 |
Understand and utilize the permission system.
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | 101 | Intents Intro Image 102 | 103 |
104 |
105 | 106 |

Gateway Intents

107 |
108 |
Learn how intents limit your bot's functions.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | 118 | Monetization Intro Image 119 | 120 |
121 |
122 | 123 |

Monetization

124 |
125 |
Add premium features to your bot.
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | 136 | ## Resulting Code 137 | 138 | 139 | -------------------------------------------------------------------------------- /guide/docs/popular-topics/monetization.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: Add premium features to your bot. 3 | --- 4 | 5 | # Monetization 6 | 7 | :::note 8 | 9 | Monetization is limited to **verified** apps/bots. 10 | 11 | ::: 12 | 13 | Premium Apps offer developers the ability to monetize their application through either **monthly recurring subscriptions** or **one-time purchases**, natively on Discord. 14 | This allows you to e.g. limit specific commands or other functionality to premium users/guilds. 15 | 16 | Not all applications are eligible - your app must be verified, part of a developer team, and use [slash commands](/interactions/slash-commands) or the privileged `Message Content` intent, among other things. 17 | 18 | ## Initial setup 19 | 20 | To get started, visit the [official documentation](https://discord.com/developers/docs/monetization/overview) to see the full list of requirements, and configure your app for monetization by following the steps outlined there. 21 | 22 | ## Entitlements 23 | 24 | Entitlements represent access to the premium functionality of your application. These can be granted to users or guilds, depending on the type of SKU you set up in the previous step. 25 | 26 | In [interactions](/interactions) (e.g. slash commands), the entitlements for the invoking user/guild are easily accessible using . 27 | 28 | Outside of interactions, you can fetch entitlements using Client.entitlements(), optionally only fetching entitlements of a specific user or guild. Note that this may include expired entitlements, unless you pass the `exclude_ended` parameter. 29 | 30 | To check whether an entitlement is still active, you can use Entitlement.is_active(). 31 | 32 | ### Premium interactions 33 | 34 | This is usually the main way to provide premium functionality. 35 | Commands are not preemptively marked as "premium-only" - instead, you may respond with a premium button, prompting users to upgrade/purchase a specific SKU: 36 | 37 | ```python 38 | sku_id = 1234432112344321 39 | 40 | 41 | @bot.slash_command() 42 | async def command(inter: disnake.ApplicationCommandInteraction): 43 | if not any(e.sku_id == sku_id for e in inter.entitlements): 44 | await inter.response.send( 45 | content="Upgrade now to get access to this feature!", 46 | components=[disnake.ui.Button(sku_id=sku_id)], 47 | ) 48 | return # skip remaining code 49 | ... 50 | ``` 51 | 52 |

53 | Premium Interaction Response Type 58 |

59 | 60 | ### Events 61 | 62 | Whenever users make a purchase in your app, you will receive an event. For subscriptions, entitlements are granted indefinitely until the user decides to cancel their subscription, in which case you will receive a event when the subscription ends. 63 | Note that entitlement events are not emitted immediately when a subscription is canceled, only at the end of the subscription period. In this case, the entitlement's ends_at attribute reflects the date indicating when the subscription (and entitlement) ended. 64 | 65 | :::note 66 | While an event also exists, it will not fire when a subscription expires; it only occurs e.g. in case of refunds or due to manual removal by Discord. 67 | ::: 68 | 69 | ## Subscriptions 70 | 71 | Subscriptions are used for products with recurring monthly payments. These should not be used to determine access to premium features, they are only meant for lifecycle management purposes. 72 | 73 | Similarly to entitlements, you will receive an event whenever a subscription is created. An event is emitted when a user cancels their subscription; canceled subscriptions remain valid until the end of the subscription period. Further details about subscription lifecycles can be found in the [official documentation](https://discord.com/developers/docs/monetization/implementing-app-subscriptions#supporting-subscriptions). 74 | 75 | To obtain the subscriptions of a user to a particular SKU, you can use . 76 | 77 | ### Testing subscriptions 78 | 79 | For subscription SKUs, you can create test entitlements using and delete them using , which allows you to test your implementation in various subscription states. These entitlements do not expire and therefore have no start/end date. 80 | 81 | If you want to test the full payment flow, you can go through the same upgrade steps as any other user of your application would - all members of the app's associated team automatically receive a 100% discount on the subscription. Note that you cannot delete these entitlements, unlike the test entitlements mentioned before. 82 | 83 | ## One-time purchases 84 | 85 | One-time purchases can be durable (i.e. permanent) or consumable. 86 | Just like subscriptions, access to these items is represented by entitlements, which you can receive in entitlement events or interactions. 87 | 88 | Users may only have one unconsumed entitlement for an SKU at a time. To consume an entitlement, use and process/store the state of the item in your application where applicable. 89 | 90 | For further lifecycle details and other considerations, visit the [official documentation](https://discord.com/developers/docs/monetization/implementing-one-time-purchases#how-onetime-purchases-work) for one-time purchases. 91 | 92 | ### Testing one-time purchases 93 | 94 | Unlike subscriptions, one-time purchases may only be tested through the Application Test Mode, not via test entitlements. 95 | To test one-time purchases without being charged, [enable Application Test Mode](https://discord.com/developers/docs/monetization/implementing-one-time-purchases#using-application-test-mode) for your app and visit the app's store page. 96 | Entitlements tied to one-time purchases made this way will have a `type` of test_mode_purchase. 97 | -------------------------------------------------------------------------------- /guide/docs/popular-topics/reactions.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: Create polls, paginate your commands, and more. 3 | hide_table_of_contents: true 4 | --- 5 | 6 | # Reactions 7 | 8 | 9 | -------------------------------------------------------------------------------- /guide/docs/popular-topics/threads.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: An extensive article on methods and events to be used with Discord Threads. 3 | keywords: [disnake, bot, guide, tutorial, thread, python] 4 | --- 5 | 6 | # Threads 7 | 8 | Threads are Messageable objects, and can be thought of as 9 | sub-channels inside existing channels. They allow organization of multiple topics in a channel by temporarily giving 10 | them a separate space. 11 | 12 | ## Thread-related methods 13 | 14 | ### Creating and deleting threads 15 | 16 | A thread can be created by using the create_thread() 17 | method on a Message or TextChannel object. 18 | 19 | ```python title="threads.py" 20 | # Using the 'Message' object 21 | message = channel.fetch_message(1234567890) 22 | await message.create_thread( 23 | name="This message will act as the thread's starting message.", 24 | auto_archive_duration=60, 25 | ) 26 | 27 | # Using the 'TextChannel' object 28 | channel = bot.get_channel(...) 29 | await channel.create_thread( 30 | name="This thread requires a starting message to be specified.", 31 | auto_archive_duration=60, 32 | message=message, 33 | ) 34 | ``` 35 | 36 | In order to delete a thread, you can use the delete() method on 37 | the Thread object. 38 | 39 | ```python title="threads.py" 40 | thread = channel.get_thread(...) # You can also use bot.get_channel(...) 41 | 42 | await thread.delete() 43 | ``` 44 | 45 | ### Joining and leaving threads 46 | 47 | Both joining and leaving a thread require you to have a Thread object, 48 | on which you can use the join() and leave() methods for the respective action. 49 | 50 | ```python title="threads.py" 51 | thread = channel.get_thread(...) # You can also use bot.get_channel(...) 52 | 53 | # Joining a thread. 54 | await thread.join() 55 | 56 | # Leaving a thread. 57 | await thread.leave() 58 | ``` 59 | 60 | It is recommended to use a `try-except` loop here, in case that the thread is not joinable by the bot user; this can be 61 | due to missing permissions. 62 | 63 | ### Archiving, unarchiving and locking threads 64 | 65 | Archiving a thread essentially makes it "read-only" for non-moderators - where they can view older messages, but not 66 | send messages themselves. Locked threads can only be unarchived by users who have the `manage_threads` permission. 67 | 68 | Threads have an **auto-archive duration** - a period of time after which the thread is archived automatically without 69 | being configured by a moderator. This duration can also be set by a bot user while creating or editing a thread. 70 | 71 | ```python title="threads.py" 72 | thread = channel.get_thread(...) 73 | 74 | await thread.edit(auto_archive_duration=60) 75 | ``` 76 | 77 | Configuring a thread to be archived, unarchived or locked can be done using the edit() method, via the `archived` and `locked` parameters. Both of 78 | these attributes accept a boolean value. 79 | 80 | ```python title="threads.py" 81 | thread = channel.get_thread(...) 82 | 83 | # Archiving a thread 84 | await thread.edit(archived=True) # Set to 'False', to unarchive the thread 85 | 86 | # Locking a thread 87 | await thread.edit(locked=True) 88 | ``` 89 | 90 | ### Public and private threads 91 | 92 | Public threads are accessible to all members that can view the thread's parent channel. Such threads can be created 93 | using the create_thread() method, as covered in 94 | [a previous section](#creating-and-deleting-threads). 95 | 96 | Private threads are those which are only accessible to moderators and the members invited by the thread creator. A 97 | private thread can be created by specifying the `type` in the create_thread() method as private_thread. 98 | 99 | ```python title="threads.py" {6} 100 | channel = bot.get_channel(...) 101 | 102 | await channel.create_thread( 103 | name="Thread Title", 104 | auto_archive_duration=60, 105 | type=disnake.ChannelType.private_thread, 106 | ) 107 | ``` 108 | 109 | :::note 110 | 111 | A private thread can only be created on a TextChannel object. The 112 | type that can be specified under create_thread() can 113 | be `public_thread`, `private_thread` or `news_thread`. 114 | 115 | ::: 116 | 117 | ## Thread-related events 118 | 119 | Threads introduce some new gateway events, which are listed below. You can find more information on these 120 | [in the documentation](https://docs.disnake.dev/en/stable/api.html#disnake.on_thread_join). 121 | 122 | - on_thread_join 123 | 124 | - on_thread_remove 125 | 126 | - on_thread_member_join 127 | 128 | - on_thread_member_remove 129 | 130 | - on_thread_delete 131 | 132 | - on_thread_update 133 | -------------------------------------------------------------------------------- /guide/docs/popular-topics/webhooks.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: Send messages in a text channel without a bot user. 3 | hide_table_of_contents: true 4 | --- 5 | 6 | # Webhooks 7 | 8 | 9 | -------------------------------------------------------------------------------- /guide/docs/prerequisites/creating-your-application.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: A short article on how to create an application for your Discord bot. 3 | keywords: [disnake, bot, guide, tutorial, create, application, python] 4 | --- 5 | 6 | # Creating your application 7 | 8 | The steps mentioned further in this markdown are essentially a copy of the steps 9 | [mentioned in the documentation](https://docs.disnake.dev/en/stable/discord.html). Therefore, you can follow the steps 10 | from either resource. 11 | 12 | :::note 13 | 14 | If you have already made your bot application, you can skip this document and move on to 15 | [`initial-files`](../getting-started/initial-files.mdx). 16 | 17 | ::: 18 | 19 | ## Creating a bot account 20 | 21 | In order to work with the library and the Discord API in general, we must first create a Discord Bot account. 22 | 23 | Creating a Bot account is a pretty straightforward process. 24 | 25 | 1. Make sure you're logged on to the [Discord website](https://www.discord.com). 26 | 27 | 2. Navigate to the [application page](https://discord.com/developers/applications). 28 | 29 | 3. Click on the `New Application` button. 30 | 31 | 32 |
33 |

34 | New Application button 35 |

36 |
37 | 38 | 4. Give the application a name and click `Create`. 39 | 40 | 41 |
42 |

43 | Create Application modal 44 |

45 |
46 | 47 | 5. Navigate to the `Bot` tab. A bot account is added automatically when the application is created. 48 | 49 | 6. Make sure that `Public Bot` is ticked if you want others to invite your bot. 50 | 51 | 52 |
53 |

54 | New Bot 55 |

56 |
57 | 58 | - You should also make sure that `Require OAuth2 Code Grant` is unchecked unless you are developing a service that 59 | needs it. If you're unsure, then **leave it unchecked**. 60 | 61 | 7. Copy the token by clicking `Reset Token` and then using the `Copy` button. 62 | 63 | 64 |
65 |

66 | Copy Token 67 |

68 |
69 | 70 | - Click "Yes, do it!" when asked. 71 | - This is **not** the Client Secret at the General Information page. 72 | 73 | And that's it. You now have a bot account and you can login with that token. 74 | 75 | ## Regarding bot tokens 76 | 77 | :::danger 78 | 79 | This section is critical, so pay close attention. It explains what your bot token is, as well as the security aspects of 80 | it. 81 | 82 | ::: 83 | 84 | ### What is a token, anyway? 85 | 86 | It should be worth noting that the token is **essentially your bot's password**. You should **never** share this with 87 | someone else. In doing so, someone can log in to your bot and do malicious things, such as leaving servers, ban all 88 | members inside a server, or pinging everyone maliciously. 89 | 90 | Tokens look like this: `OTA4MjgxMjk4NTU1MTA5Mzk2.YYzc4A.TB7Ng6DOnVDlpMS4idjGptsreFg` (don't worry, we immediately reset 91 | this token before even posting it here!). If it's any shorter and looks more like this: 92 | `CR8UbizwLgyES9LuHK7eIrXHbRTuqoEs`, you copied your client secret instead. Make sure to copy the token if you want your 93 | bot to work! 94 | 95 | ### Token leak scenario 96 | 97 | Let's imagine that you have a bot on over 1,000 servers, and it took you many, many months of coding and patience to get 98 | it on that amount. Your token gets leaked somewhere, and now someone else has it. That person can: 99 | 100 | - Spam every server your bot is on; 101 | - DM spam as many users as possible; 102 | - Delete as many channels as possible; 103 | - Kick or ban as many server members as possible; 104 | - Make your bot leave all of the servers it has joined; 105 | - Access and damage the underlying infrastructure (your server). 106 | 107 | All that and much, much more. Sounds pretty terrible, right? So make sure to keep your token as safe as possible! In the 108 | [initial files](../getting-started/initial-files.mdx) page of the guide, we cover how to safely store your token in a 109 | configuration file. 110 | 111 | :::caution 112 | 113 | If you accidentally leaked your token, click the “Regenerate” button as soon as possible. This revokes your old token 114 | and re-generates a new one. Now you need to use the new token to login. 115 | 116 | ::: 117 | 118 | ### Discord's system messages 119 | 120 | System messages are official messages that Discord sends to alert you of an account issue. These messages can range from 121 | a variety of payment issues, support ticket updates, and ToS violation warnings. You can also expect a system message if 122 | your bot token has been posted on the internet publicly, and was found by Discord. 123 | 124 | **Such a message will look as follows:** 125 | 126 |
127 |

128 | System message 129 |

130 |
131 | 132 | :::caution 133 | 134 | Since we do not know the extent to which Discord searches for bot tokens, or the time taken to inform the user about 135 | where the bot was found, we recommend not fully depending on this feature. But incase your bot token _is_ found on the 136 | internet publicly (say, on [a GitHub repository](https://www.github.com/)), you can expect Discord to send a system 137 | message to the account the bot is linked to. 138 | 139 | ::: 140 | 141 | Note that Discord will never ask for your password or account token, and a system message will never request for the 142 | same. System messages from Discord will have a verified `SYSTEM` tag, and the bar at the bottom of the DM will mention - 143 | "This thread is reserved for official Discord notifications". 144 | 145 |
146 |

147 | System DM 148 |

149 |
150 | 151 | You can read more about the authenticity of these messages [in this article][discord-sys-msg-page]. 152 | 153 | [discord-sys-msg-page]: https://support.discord.com/hc/en-us/articles/360036118732-Discord-System-Messages 154 | 155 | ## Inviting your bot 156 | 157 | So, you've made the bot account, but it is not actually in any server. If you want to invite your bot you must create an 158 | invite URL for it. 159 | 160 | 1. Make sure you're logged on to the [Discord website][discord-page]. 161 | 162 | 2. Navigate to the [application page][discord-app-page]. 163 | 164 | 3. Click on your bot's page. 165 | 166 | 4. Go to the `OAuth2` tab. 167 | 168 | 169 |
170 |

171 | OAuth2 Page 172 |

173 |
174 | 175 | 5. Tick the `bot` checkbox under `scopes`. 176 | 177 | 178 |
179 |

180 | OAuth2 Scope 181 |

182 |
183 | 184 | - If you would like to integrate slash commands and other interactions into your bot, make sure to check the 185 | `applications.commands` scope as well. 186 | 187 | 188 |
189 |

190 | OAuth2 Application Commands 191 |

192 |
193 | 194 | 6. Tick the permissions required for your bot to function under `Bot Permissions`. 195 | 196 | - Please be aware of the consequences of requiring your bot to have the `Administrator` permission. 197 | 198 | - Bot owners must have 2FA enabled for certain actions and permissions when added in servers that have Server-Wide 199 | 2FA enabled. Check the [2FA support page][discord-2fa-page] for more information. 200 | 201 | 202 |
203 |

204 | OAuth2 Permissions 205 |

206 |
207 | 208 | 7. Now the resulting URL can be used to add your bot to a server. Copy and paste the URL into your browser, choose a 209 | server to invite the bot to, and click `Authorize`. 210 | 211 | :::info 212 | 213 | The person adding the bot needs "Manage Server" permissions to do so. 214 | 215 | ::: 216 | 217 | If you want to generate this URL dynamically at run-time inside your bot and using the interface, you can use . 218 | 219 | [discord-page]: https://www.discord.com 220 | [discord-app-page]: https://discord.com/developers/applications 221 | [discord-2fa-page]: https://support.discord.com/hc/en-us/articles/219576828-Setting-up-Two-Factor-Authentication 222 | -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/application-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/application-name.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/copy-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/copy-token.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/new-application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/new-application.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/new-bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/new-bot.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/oauth-application-commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/oauth-application-commands.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/oauth-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/oauth-page.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/oauth-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/oauth-permissions.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/oauth-scopes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/oauth-scopes.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/system-dm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/system-dm.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/images/system-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/docs/prerequisites/images/system-message.png -------------------------------------------------------------------------------- /guide/docs/prerequisites/installing-disnake.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: An article on the various ways you can install disnake. 3 | keywords: [disnake, bot, guide, tutorial, install, python] 4 | hide_table_of_contents: true 5 | --- 6 | 7 | # Installing disnake 8 | 9 | :::note 10 | 11 | To use disnake, you'll first need to install [Python][python]. Like most other 12 | [`discord.py`](https://discordpy.readthedocs.io/en/latest) forks, disnake supports Python 3.8 or higher. For a full 13 | walkthrough on installing Python, we suggest following 14 | [this Real Python article](https://realpython.com/installing-python/), or 15 | [The Hitchhiker's Guide to Python](https://docs.python-guide.org/starting/installation/). 16 | 17 | ::: 18 | 19 | To use disnake, you'll need to install it via `pip`, which is Python's standard package manager. Since pip comes 20 | standard with Python 3.4 and above, there is no need to separately install it. 21 | 22 | To install the library without full voice support, you can just run the following command: 23 | 24 | 25 | 26 | 27 | ``` 28 | py -3 -m pip install -U disnake 29 | ``` 30 | 31 | 32 | 33 | 34 | ``` 35 | python3 -m pip install -U disnake 36 | ``` 37 | 38 | 39 | 40 | 41 | ``` 42 | python3 -m pip install -U disnake 43 | ``` 44 | 45 | 46 | 47 | 48 | Otherwise to get voice support you should run the following command: 49 | 50 | 51 | 52 | 53 | ``` 54 | py -3 -m pip install -U "disnake[voice]" 55 | ``` 56 | 57 | 58 | 59 | 60 | ``` 61 | python3 -m pip install -U "disnake[voice]" 62 | ``` 63 | 64 | 65 | 66 | 67 | ``` 68 | python3 -m pip install -U "disnake[voice]" 69 | ``` 70 | 71 | 72 | 73 | 74 | To install the development version, do the following: 75 | 76 | ``` 77 | $ pip install -U git+https://github.com/DisnakeDev/disnake#egg=disnake[speed,voice] 78 | ``` 79 | 80 | :::info 81 | 82 | While installing voice on Linux, you must install the following packages via your favourite package manager (e.g. `apt`, 83 | `dnf`, etc.) before running the above commands: 84 | 85 | - libffi-dev (or `libffi-devel` on some systems) 86 | - python-dev (e.g. `python3.6-dev` for Python 3.6) 87 | 88 | ::: 89 | 90 | And that's it! With all the necessities installed, you're almost ready to start coding your bot. 91 | 92 | [python]: https://www.python.org/downloads/ 93 | [brew]: https://brew.sh/ 94 | [opensource-linux]: https://opensource.com/article/20/4/install-python-linux 95 | -------------------------------------------------------------------------------- /guide/docs/prerequisites/migrating-from-dpy.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn the syntax differences between `discord.py` and `disnake`, along with various methods to migrate. 3 | --- 4 | 5 | # Migrating from discord.py 6 | 7 | After the discontinuation of `discord.py` (refer 8 | [this gist](https://gist.github.com/Rapptz/4a2f62751b9600a31a0d3c78100287f1)), many forks of the API wrapper branched 9 | onward to maintain the library, in order to keep it updated with the latest features and Discord API changes - `disnake` 10 | is one such fork. 11 | 12 | Thus, if you've chosen `disnake` as your fork of choice in order to implement interactions/components and other 13 | features, this page will help you understand the changes in syntax, and aim for making your migrating process as smooth 14 | as possible. 15 | 16 | ## Differences between libraries 17 | 18 | `disnake` is based on `discord.py 2.0`, which had major syntax changes from its previous version. Therefore, if you're 19 | shifting to `disnake` from a version of `discord.py` lower than 2.0, you will have to make some important syntax changes 20 | in your code. You can refer [this page](https://gist.github.com/apple502j/f75b4f24652f04de85c7084ffd73ec58) for the full 21 | list of breaking changes in `discord.py 2.0`, though we will list some primary API reference changes here: 22 | 23 | - Methods and attributes that returned `TextChannel`, etc can now return `Thread`. 24 | - Attributes that returned `Asset` are renamed, e.g. attributes ending with `_url` (i.e. `avatar_url`) are changed to 25 | `avatar.url`. `User.avatar` returns `None` in case the default avatar is used. 26 | - `on_presence_update` replaces `on_member_update` for updates to `Member.status` and `Member.activities`. 27 | - Webhooks are changed significantly: `WebhookAdapter` is removed, and synchronous requests using `requests` is now 28 | inside `SyncWebhook`. 29 | - `edit` methods no longer updates cache and instead returns modified instance. 30 | - `Client.logout` is removed; use `Client.close` instead. 31 | - `Message.type` for replies are now `MessageType.reply`. 32 | - `Reaction.custom_emoji` property is changed to `Reaction.is_custom_emoji` method. 33 | - `missing_perms` attributes and arguments are renamed to `missing_permissions`. 34 | - Many arguments are now specified as positional-only or keyword-only; e.g. `oauth_url` now takes keyword-only 35 | arguments, and methods starting with `get_` or `fetch_` take positional-only arguments. 36 | 37 | ## Changing requirements 38 | 39 | In order to avoid conflicts between the libraries, you must uninstall `discord.py`. You can do so by using the following 40 | command in your terminal: 41 | 42 | 43 | 44 | 45 | ``` 46 | py -3 -m pip uninstall discord 47 | ``` 48 | 49 | 50 | 51 | 52 | ``` 53 | python3 -m pip uninstall discord 54 | ``` 55 | 56 | 57 | 58 | 59 | ``` 60 | python3 -m pip uninstall discord 61 | ``` 62 | 63 | 64 | 65 | 66 | To install `disnake`, you can follow the instructions on [this page](./installing-disnake). 67 | 68 | ## Rewriting your bot 69 | 70 | As discussed above, rewriting your code from an older `discord.py` version to `disnake` will require some major syntax 71 | changes. But if you're migrating from `discord.py 2.0`, all that's left now is changing the library references 72 | throughout the code, since the base code for both the libraries is practically the same. 73 | 74 | There are two ways to switch between libraries: 75 | 76 | #### Replace `discord` with `disnake` 77 | 78 | 1. Import `disnake` into your code (and delete the lines where you import `discord`). 79 | 80 | ```py 81 | import disnake 82 | from disnake.ext import commands 83 | ``` 84 | 85 | 2. With your favorite editor, replace every `discord` reference in your code with `disnake` (this is fairly simple, if 86 | your editor has a "Find & Replace" tool). 87 | 88 | #### Import `disnake as discord` 89 | 90 | Import `disnake as discord` into your code (and delete the lines where you import `discord`). This reduces the effort of 91 | changing all references throughout your code. 92 | 93 | ```py 94 | import disnake as discord 95 | from disnake.ext import commands 96 | ``` 97 | 98 | And that's it! Since `disnake` is a fork of `discord.py`, it inherits a lot of similarities - though we recommend you to 99 | always run your code to fix any possible issues. 100 | -------------------------------------------------------------------------------- /guide/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | module.exports = { 4 | title: 'Disnake Guide', 5 | url: 'https://guide.disnake.dev/', 6 | favicon: 'public/favicon.ico', 7 | 8 | organizationName: 'DisnakeDev', 9 | projectName: 'guide', 10 | baseUrl: '/', 11 | trailingSlash: false, 12 | 13 | onBrokenLinks: 'warn', 14 | onBrokenMarkdownLinks: 'warn', 15 | 16 | plugins: ['docusaurus-plugin-sass'], 17 | 18 | presets: [ 19 | [ 20 | 'classic', 21 | /** @type {import('@docusaurus/preset-classic').Options} */ 22 | ({ 23 | docs: { 24 | routeBasePath: '/', 25 | sidebarPath: require.resolve('./sidebars.js'), 26 | editUrl: 'https://github.com/DisnakeDev/guide/edit/main/guide', 27 | showLastUpdateAuthor: true, 28 | showLastUpdateTime: true, 29 | }, 30 | theme: { 31 | customCss: [require.resolve('./src/styles/index.scss')], 32 | }, 33 | }), 34 | ], 35 | ], 36 | 37 | themeConfig: { 38 | colorMode: { 39 | respectPrefersColorScheme: true, 40 | }, 41 | metadata: [ 42 | { 43 | name: 'og:image', 44 | content: 'https://guide.disnake.dev/public/disnake-meta-image.png', 45 | }, 46 | { 47 | name: 'theme-color', 48 | content: '#f0c43f', 49 | }, 50 | { 51 | name: 'twitter:card', 52 | content: 'summary', 53 | }, 54 | ], 55 | navbar: { 56 | title: 'Disnake', 57 | logo: { 58 | alt: 'Disnake Logo', 59 | src: 'public/disnake-logo.png', 60 | href: 'https://disnake.dev/', 61 | }, 62 | items: [ 63 | { 64 | to: 'https://docs.disnake.dev/', 65 | label: 'Docs', 66 | position: 'left', 67 | }, 68 | { 69 | to: '/', 70 | label: 'Guide', 71 | position: 'left', 72 | }, 73 | { 74 | to: 'https://docs.disnake.dev/en/stable/api/index.html', 75 | label: 'API Reference', 76 | position: 'left', 77 | }, 78 | { 79 | href: 'https://github.com/DisnakeDev/disnake', 80 | className: 'navbar-item-github', 81 | position: 'right', 82 | }, 83 | ], 84 | }, 85 | docs: { 86 | sidebar: { 87 | autoCollapseCategories: true, 88 | }, 89 | }, 90 | prism: { 91 | theme: require('./src/utils/prismLight'), 92 | darkTheme: require('./src/utils/prismDark'), 93 | }, 94 | algolia: { 95 | appId: 'WPCP8YA273', 96 | apiKey: '5a21886b115baa4f6819b23d0a1e87c0', 97 | indexName: 'guide-disnake', 98 | placeholder: 'Search', 99 | }, 100 | }, 101 | }; 102 | -------------------------------------------------------------------------------- /guide/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "disnake-guide", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "deploy": "docusaurus deploy", 9 | "serve": "docusaurus serve", 10 | "swizzle": "docusaurus swizzle" 11 | }, 12 | "repository": "https://github.com/DisnakeDev/guide.git", 13 | "author": "abhigyantrips ", 14 | "homepage": "https://guide.disnake.dev", 15 | "bugs": { 16 | "url": "https://github.com/DisnakeDev/guide/issues" 17 | }, 18 | "dependencies": { 19 | "@discord-message-components/react": "^0.2.1" 20 | }, 21 | "browserslist": { 22 | "production": [ 23 | ">0.5%", 24 | "not dead", 25 | "not op_mini all" 26 | ], 27 | "development": [ 28 | "last 1 chrome version", 29 | "last 1 firefox version", 30 | "last 1 safari version" 31 | ] 32 | }, 33 | "devDependencies": { 34 | "@docusaurus/core": "^2.2.0", 35 | "@docusaurus/preset-classic": "^2.2.0", 36 | "@mdx-js/react": "^1.6.22", 37 | "clsx": "^1.1.1", 38 | "docusaurus-plugin-sass": "^0.2.2", 39 | "eslint": "^8.6.0", 40 | "eslint-plugin-mdx": "^1.16.0", 41 | "prism-react-renderer": "^1.2.1", 42 | "react": "^17.0.2", 43 | "react-dom": "^17.0.2", 44 | "react-native": "^0.66.4", 45 | "sass": "^1.49.9" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /guide/sidebars.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 4 | const sidebars = { 5 | guideSidebar: [ 6 | { 7 | type: 'doc', 8 | label: 'Introduction', 9 | id: 'intro', 10 | }, 11 | { 12 | type: 'category', 13 | label: 'Prerequisites', 14 | items: [ 15 | 'prerequisites/installing-disnake', 16 | 'prerequisites/migrating-from-dpy', 17 | 'prerequisites/creating-your-application', 18 | ], 19 | }, 20 | { 21 | type: 'category', 22 | label: 'Getting Started', 23 | items: ['getting-started/initial-files', 'getting-started/creating-commands', 'getting-started/using-cogs'], 24 | }, 25 | { 26 | type: 'category', 27 | label: 'Interactions', 28 | link: { type: 'doc', id: 'interactions/intro' }, 29 | items: [ 30 | 'interactions/slash-commands', 31 | 'interactions/context-menus', 32 | 'interactions/buttons', 33 | 'interactions/select-menus', 34 | 'interactions/modals', 35 | ], 36 | }, 37 | { 38 | type: 'category', 39 | label: 'Popular Topics', 40 | link: { type: 'doc', id: 'popular-topics/intro' }, 41 | items: [ 42 | 'popular-topics/threads', 43 | 'popular-topics/embeds', 44 | 'popular-topics/reactions', 45 | 'popular-topics/webhooks', 46 | 'popular-topics/permissions', 47 | 'popular-topics/errors', 48 | 'popular-topics/intents', 49 | 'popular-topics/monetization', 50 | ], 51 | }, 52 | { 53 | type: 'category', 54 | label: 'Frequently Asked Questions', 55 | link: { type: 'doc', id: 'faq/intro' }, 56 | items: ['faq/general', 'faq/administrative', 'faq/coroutines', 'faq/extensions', 'faq/good-practices'], 57 | }, 58 | ], 59 | }; 60 | 61 | module.exports = sidebars; 62 | -------------------------------------------------------------------------------- /guide/src/components/DocsLink.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function DocsLink(props) { 4 | const basePath = 'https://docs.disnake.dev/en/stable' 5 | const docsPath = basePath + (props.ext ? `/ext/${props.ext}` : '') + (props.ext === 'tasks' ? '/index.html' : '/api.html') + (props.reference ? `#${props.reference}` : '') 6 | const docsText = (props.children ? props.children : (props.ext ? props.reference.replace(`disnake.ext.${props.ext}.`, "") : props.reference.replace("disnake.", ""))) 7 | 8 | return( 9 | {docsText} 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /guide/src/components/ResultingCode.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {useLocation} from "@docusaurus/router"; 3 | 4 | export default function ResultingCode() { 5 | const location = useLocation(); 6 | const codeSampleURL = 'https://github.com/DisnakeDev/guide/tree/main/code-samples/'; 7 | 8 | return( 9 | <> 10 |

11 | The code showcased in this section can be found on our GitHub repository{' '} 12 | here. 13 |

14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /guide/src/components/WorkInProgress.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function WorkInProgress() { 4 | 5 | return( 6 |
7 |
8 |
9 | 10 |
11 |
12 |

13 | WIP. 14 |

15 |
16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /guide/src/hooks/isDarkTheme.js: -------------------------------------------------------------------------------- 1 | import { useColorMode } from '@docusaurus/theme-common'; 2 | 3 | export default function isDarkTheme() { 4 | const { colorMode } = useColorMode(); 5 | return colorMode === 'dark'; 6 | } 7 | -------------------------------------------------------------------------------- /guide/src/styles/_layout.scss: -------------------------------------------------------------------------------- 1 | // Discord Message Components 2 | 3 | .discord-messages { 4 | border-radius: 0.4rem; 5 | } 6 | 7 | .discord-light-theme .discord-embed .discord-embed-content .discord-embed-title { 8 | color: #000; // Some weird bug that kept the title white, idk. 9 | } 10 | 11 | // Text Selection 12 | 13 | html[data-theme="dark"] ::selection { 14 | background: rgba(255, 255, 255, 0.2); 15 | } 16 | 17 | html[data-theme="dark"] ::-moz-selection { 18 | background: rgba(255, 255, 255, 0.2); 19 | } 20 | 21 | html[data-theme="light"] ::selection { 22 | background: rgba(0, 0, 0, 0.1); 23 | } 24 | 25 | html[data-theme="light"] ::-moz-selection { 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | 29 | // Sidebar 30 | 31 | :root { 32 | --doc-sidebar-width: 310px !important; 33 | } 34 | 35 | // Codeblocks 36 | 37 | .docusaurus-highlight-code-line { 38 | background-color: var(--custom-code-highlight-color); 39 | display: block; 40 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 41 | padding: 0 var(--ifm-pre-padding); 42 | } 43 | 44 | // Last Updated Span 45 | 46 | span.theme-last-updated { 47 | font-style: normal; 48 | } 49 | -------------------------------------------------------------------------------- /guide/src/styles/_navbar.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'uni-sans-caps'; 3 | src: url('/fonts/uni-sans.heavy-caps.otf') format('opentype'); 4 | } 5 | 6 | // Navbar Branding 7 | 8 | .navbar__title { 9 | color: var(--ifm-color-primary); 10 | font-family: 'uni-sans-caps'; 11 | font-size: 1.35rem; 12 | padding-left: 0.25rem; 13 | } 14 | 15 | // GitHub Social Icon 16 | 17 | .navbar-item-github:hover { 18 | opacity: 0.6; 19 | transition: ease 0.2s; 20 | } 21 | 22 | .navbar-item-github:before { 23 | margin-right: 0.5rem; 24 | content: ''; 25 | width: 24px; 26 | height: 24px; 27 | display: flex; 28 | align-items: center; 29 | justify-items: center; 30 | background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E"); 31 | background-repeat: no-repeat; 32 | background-position: center; 33 | } 34 | 35 | html[data-theme='dark'] .navbar-item-github:before { 36 | background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") 37 | no-repeat; 38 | background-repeat: no-repeat; 39 | background-position: center; 40 | } 41 | -------------------------------------------------------------------------------- /guide/src/styles/_search.scss: -------------------------------------------------------------------------------- 1 | html[data-theme='light'] .DocSearch { 2 | --docsearch-primary-color: var(--ifm-color-primary); 3 | --docsearch-text-color: var(--ifm-font-color-base); 4 | --docsearch-muted-color: var(--ifm-color-secondary-darkest); 5 | --docsearch-container-background: rgba(94, 100, 112, 0.7); 6 | /* Modal */ 7 | --docsearch-modal-background: var(--ifm-color-secondary-lighter); 8 | /* Search box */ 9 | --docsearch-searchbox-background: var(--ifm-color-secondary); 10 | --docsearch-searchbox-focus-background: var(--ifm-color-white); 11 | /* Hit */ 12 | --docsearch-hit-color: var(--ifm-font-color-base); 13 | --docsearch-hit-active-color: var(--ifm-color-white); 14 | --docsearch-hit-background: var(--ifm-color-white); 15 | /* Footer */ 16 | --docsearch-footer-background: var(--ifm-color-white); 17 | /* Keys */ 18 | --docsearch-key-gradient: var(--ifm-background-color); 19 | --docsearch-key-shadow: 0 0 0 1px var(--ifm-color-emphasis-500); 20 | } 21 | 22 | html[data-theme='dark'] .DocSearch { 23 | --docsearch-text-color: var(--ifm-font-color-base); 24 | --docsearch-muted-color: var(--ifm-color-secondary-darkest); 25 | --docsearch-container-background: rgba(47, 55, 69, 0.7); 26 | /* Modal */ 27 | --docsearch-modal-background: var(--ifm-background-color); 28 | /* Search box */ 29 | --docsearch-searchbox-background: var(--ifm-background-color); 30 | --docsearch-searchbox-focus-background: var(--ifm-color-black); 31 | /* Hit */ 32 | --docsearch-hit-color: var(--ifm-font-color-base); 33 | --docsearch-hit-active-color: var(--ifm-color-white); 34 | --docsearch-hit-background: var(--ifm-color-emphasis-100); 35 | /* Footer */ 36 | --docsearch-footer-background: var(--ifm-background-surface-color); 37 | /* Keys */ 38 | --docsearch-key-gradient: var(--ifm-background-color); 39 | --docsearch-key-shadow: 0 0 0 1px var(--ifm-color-emphasis-400); 40 | } 41 | -------------------------------------------------------------------------------- /guide/src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | html[data-theme="light"] { 2 | --ifm-color-primary: hsl(211, 44%, 40%); 3 | --ifm-color-primary-light: hsl(211, 43%, 44%); 4 | --ifm-color-primary-lighter: hsl(211, 43%, 45%); 5 | --ifm-color-primary-lightest: hsl(211, 46%, 52%); 6 | --ifm-color-primary-dark: hsl(212, 43%, 36%); 7 | --ifm-color-primary-darker: hsl(212, 43%, 34%); 8 | --ifm-color-primary-darkest: hsl(211, 43%, 28%); 9 | --ifm-code-font-size: 95%; 10 | 11 | --custom-code-highlight-color: rgba(0, 0, 0, 0.1); 12 | } 13 | 14 | html[data-theme="dark"] { 15 | --ifm-color-primary: hsl(47, 83%, 55%); 16 | --ifm-color-primary-light: hsl(47, 82%, 63%); 17 | --ifm-color-primary-lighter: hsl(47, 82%, 66%); 18 | --ifm-color-primary-lightest: hsl(47, 82%, 76%); 19 | --ifm-color-primary-dark: hsl(48, 100%, 46%); 20 | --ifm-color-primary-darker: hsl(47, 100%, 45%); 21 | --ifm-color-primary-darkest: hsl(47, 100%, 38%); 22 | --ifm-code-font-size: 95%; 23 | 24 | --custom-code-highlight-color: rgba(255, 255, 255, 0.1); 25 | } 26 | -------------------------------------------------------------------------------- /guide/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "layout"; 3 | @import "navbar"; 4 | @import "search"; 5 | -------------------------------------------------------------------------------- /guide/src/theme/MDXComponents/A.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from '@docusaurus/Link'; 3 | export default function MDXA(props) { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /guide/src/theme/MDXComponents/Code.js: -------------------------------------------------------------------------------- 1 | import React, { isValidElement } from 'react'; 2 | import CodeBlock from '@theme/CodeBlock'; 3 | export default function MDXCode(props) { 4 | const inlineElements = [ 5 | 'a', 6 | 'abbr', 7 | 'b', 8 | 'br', 9 | 'button', 10 | 'cite', 11 | 'code', 12 | 'del', 13 | 'dfn', 14 | 'em', 15 | 'i', 16 | 'img', 17 | 'input', 18 | 'ins', 19 | 'kbd', 20 | 'label', 21 | 'object', 22 | 'output', 23 | 'q', 24 | 'ruby', 25 | 's', 26 | 'small', 27 | 'span', 28 | 'strong', 29 | 'sub', 30 | 'sup', 31 | 'time', 32 | 'u', 33 | 'var', 34 | 'wbr', 35 | ]; 36 | const shouldBeInline = React.Children.toArray(props.children).every( 37 | (el) => 38 | (typeof el === 'string' && !el.includes('\n')) || 39 | (isValidElement(el) && inlineElements.includes(el.props?.mdxType)) 40 | ); 41 | return shouldBeInline ? : ; 42 | } 43 | -------------------------------------------------------------------------------- /guide/src/theme/MDXComponents/Details.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Details from '@theme/Details'; 3 | export default function MDXDetails(props) { 4 | const items = React.Children.toArray(props.children); 5 | // Split summary item from the rest to pass it as a separate prop to the 6 | // Details theme component 7 | const summary = items.find((item) => React.isValidElement(item) && item.props?.mdxType === 'summary'); 8 | const children = <>{items.filter((item) => item !== summary)}; 9 | return ( 10 |
11 | {children} 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /guide/src/theme/MDXComponents/Head.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Head from '@docusaurus/Head'; 3 | // MDX elements are wrapped through the MDX pragma. In some cases (notably usage 4 | // with Head/Helmet) we need to unwrap those elements. 5 | function unwrapMDXElement(element) { 6 | if (element.props?.mdxType && element.props.originalType) { 7 | const { mdxType, originalType, ...newProps } = element.props; 8 | return React.createElement(element.props.originalType, newProps); 9 | } 10 | return element; 11 | } 12 | export default function MDXHead(props) { 13 | const unwrappedChildren = React.Children.map(props.children, (child) => 14 | React.isValidElement(child) ? unwrapMDXElement(child) : child 15 | ); 16 | return {unwrappedChildren}; 17 | } 18 | -------------------------------------------------------------------------------- /guide/src/theme/MDXComponents/Heading.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Heading from '@theme/Heading'; 3 | export default function MDXHeading(props) { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /guide/src/theme/MDXComponents/Pre.js: -------------------------------------------------------------------------------- 1 | import React, { isValidElement } from 'react'; 2 | import CodeBlock from '@theme/CodeBlock'; 3 | export default function MDXPre(props) { 4 | return ( 5 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /guide/src/theme/MDXComponents/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MDXHead from '@theme/MDXComponents/Head'; 3 | import MDXCode from '@theme/MDXComponents/Code'; 4 | import MDXA from '@theme/MDXComponents/A'; 5 | import MDXPre from '@theme/MDXComponents/Pre'; 6 | import MDXDetails from '@theme/MDXComponents/Details'; 7 | import MDXHeading from '@theme/MDXComponents/Heading'; 8 | import MDXUl from '@theme/MDXComponents/Ul'; 9 | import MDXImg from '@theme/MDXComponents/Img'; 10 | import Admonition from '@theme/Admonition'; 11 | import Mermaid from '@theme/Mermaid'; 12 | 13 | import { 14 | DiscordButton, 15 | DiscordButtons, 16 | DiscordDefaultOptions, 17 | DiscordEmbed, 18 | DiscordEmbedField, 19 | DiscordEmbedFields, 20 | DiscordInteraction, 21 | DiscordMarkdown, 22 | DiscordMention, 23 | DiscordMessage, 24 | DiscordMessages, 25 | DiscordOptionsContext, 26 | DiscordReaction, 27 | DiscordReactions, 28 | } from '@discord-message-components/react'; 29 | import '@discord-message-components/react/styles'; 30 | import isDarkTheme from '../../hooks/isDarkTheme'; 31 | import useIsBrowser from '@docusaurus/useIsBrowser'; 32 | 33 | import Tabs from '@theme/Tabs'; 34 | import TabItem from '@theme/TabItem'; 35 | 36 | import DocsLink from '../../components/DocsLink'; 37 | import ResultingCode from '../../components/ResultingCode'; 38 | import WorkInProgress from '../../components/WorkInProgress'; 39 | 40 | const discordOptions = { 41 | ...DiscordDefaultOptions, 42 | profiles: { 43 | user: { 44 | author: 'AbhigyanTrips', 45 | avatar: '/public/message-author.png', 46 | }, 47 | bot: { 48 | author: 'Disnake Bot', 49 | avatar: '/public/disnake-logo.png', 50 | bot: true, 51 | }, 52 | }, 53 | }; 54 | 55 | const MDXComponents = { 56 | head: MDXHead, 57 | code: MDXCode, 58 | a: MDXA, 59 | pre: MDXPre, 60 | details: MDXDetails, 61 | ul: MDXUl, 62 | img: MDXImg, 63 | h1: (props) => , 64 | h2: (props) => , 65 | h3: (props) => , 66 | h4: (props) => , 67 | h5: (props) => , 68 | h6: (props) => , 69 | admonition: Admonition, 70 | mermaid: Mermaid, 71 | DocsLink: (props) => { 72 | return {props.children}; 73 | }, 74 | ResultingCode: () => { 75 | return ; 76 | }, 77 | WorkInProgress: () => { 78 | return ; 79 | }, 80 | Tabs: (props) => { 81 | return {props.children}; 82 | }, 83 | TabItem: (props) => { 84 | return {props.children}; 85 | }, 86 | DiscordMessages: (props) => { 87 | return ( 88 | 89 | 97 | {props.children} 98 | 99 | 100 | ); 101 | }, 102 | DiscordMessage: (props) => { 103 | return {props.children}; 104 | }, 105 | DiscordMention: (props) => { 106 | return {props.children}; 107 | }, 108 | DiscordEmbed: (props) => { 109 | return ( 110 | 116 | {props.children} 117 | 118 | ); 119 | }, 120 | DiscordEmbedFields: (props) => { 121 | return {props.children}; 122 | }, 123 | DiscordEmbedField: (props) => { 124 | return {props.children}; 125 | }, 126 | DiscordInteraction: (props) => { 127 | return {props.children}; 128 | }, 129 | DiscordMarkdown: (props) => { 130 | return {props.children}; 131 | }, 132 | DiscordButtons: (props) => { 133 | return {props.children}; 134 | }, 135 | DiscordButton: (props) => { 136 | return {props.children}; 137 | }, 138 | DiscordReactions: (props) => { 139 | return {props.children}; 140 | }, 141 | DiscordReaction: (props) => { 142 | return {props.children}; 143 | }, 144 | }; 145 | export default MDXComponents; 146 | -------------------------------------------------------------------------------- /guide/src/utils/prismDark.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-extraneous-dependencies 2 | const darkTheme = require('prism-react-renderer/themes/vsDark'); 3 | 4 | module.exports = { 5 | plain: { 6 | color: '#D4D4D4', 7 | backgroundColor: '#1E1E1E', 8 | }, 9 | styles: [ 10 | ...darkTheme.styles, 11 | { 12 | types: ['title'], 13 | style: { 14 | color: '#569CD6', 15 | fontWeight: 'bold', 16 | }, 17 | }, 18 | { 19 | types: ['property', 'parameter'], 20 | style: { 21 | color: '#9CDCFE', 22 | }, 23 | }, 24 | { 25 | types: ['script'], 26 | style: { 27 | color: '#D4D4D4', 28 | }, 29 | }, 30 | { 31 | types: ['boolean', 'arrow', 'atrule', 'tag'], 32 | style: { 33 | color: '#569CD6', 34 | }, 35 | }, 36 | { 37 | types: ['number', 'color', 'unit'], 38 | style: { 39 | color: '#B5CEA8', 40 | }, 41 | }, 42 | { 43 | types: ['font-matter'], 44 | style: { 45 | color: '#CE9178', 46 | }, 47 | }, 48 | { 49 | types: ['keyword', 'rule'], 50 | style: { 51 | color: '#C586C0', 52 | }, 53 | }, 54 | { 55 | types: ['regex'], 56 | style: { 57 | color: '#D16969', 58 | }, 59 | }, 60 | { 61 | types: ['maybe-class-name'], 62 | style: { 63 | color: '#4EC9B0', 64 | }, 65 | }, 66 | { 67 | types: ['constant'], 68 | style: { 69 | color: '#4FC1FF', 70 | }, 71 | }, 72 | ], 73 | }; 74 | -------------------------------------------------------------------------------- /guide/src/utils/prismLight.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-extraneous-dependencies 2 | const lightTheme = require('prism-react-renderer/themes/github'); 3 | 4 | module.exports = { 5 | ...lightTheme, 6 | styles: [ 7 | ...lightTheme.styles, 8 | { 9 | types: ['title'], 10 | style: { 11 | color: '#0550AE', 12 | fontWeight: 'bold', 13 | }, 14 | }, 15 | { 16 | types: ['parameter'], 17 | style: { 18 | color: '#953800', 19 | }, 20 | }, 21 | { 22 | types: ['boolean', 'rule', 'color', 'number', 'constant', 'property'], 23 | style: { 24 | color: '#005CC5', 25 | }, 26 | }, 27 | { 28 | types: ['atrule', 'tag'], 29 | style: { 30 | color: '#22863A', 31 | }, 32 | }, 33 | { 34 | types: ['script'], 35 | style: { 36 | color: '#24292E', 37 | }, 38 | }, 39 | { 40 | types: ['operator', 'unit', 'rule'], 41 | style: { 42 | color: '#D73A49', 43 | }, 44 | }, 45 | { 46 | types: ['font-matter', 'string', 'attr-value'], 47 | style: { 48 | color: '#E3116C', 49 | }, 50 | }, 51 | { 52 | types: ['class-name'], 53 | style: { 54 | color: '#116329', 55 | }, 56 | }, 57 | { 58 | types: ['attr-name'], 59 | style: { 60 | color: '#0550AE', 61 | }, 62 | }, 63 | { 64 | types: ['keyword'], 65 | style: { 66 | color: '#CF222E', 67 | }, 68 | }, 69 | { 70 | types: ['function'], 71 | style: { 72 | color: '#8250DF', 73 | }, 74 | }, 75 | { 76 | types: ['selector'], 77 | style: { 78 | color: '#6F42C1', 79 | }, 80 | }, 81 | { 82 | types: ['variable'], 83 | style: { 84 | color: '#E36209', 85 | }, 86 | }, 87 | ], 88 | }; 89 | -------------------------------------------------------------------------------- /guide/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/.nojekyll -------------------------------------------------------------------------------- /guide/static/CNAME: -------------------------------------------------------------------------------- 1 | guide.disnake.dev 2 | -------------------------------------------------------------------------------- /guide/static/fonts/uni-sans.heavy-caps.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/fonts/uni-sans.heavy-caps.otf -------------------------------------------------------------------------------- /guide/static/public/disnake-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/public/disnake-banner.png -------------------------------------------------------------------------------- /guide/static/public/disnake-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/public/disnake-logo.png -------------------------------------------------------------------------------- /guide/static/public/disnake-meta-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/public/disnake-meta-image.png -------------------------------------------------------------------------------- /guide/static/public/disnake-readme-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/public/disnake-readme-banner.png -------------------------------------------------------------------------------- /guide/static/public/disnake-thin-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/public/disnake-thin-banner.png -------------------------------------------------------------------------------- /guide/static/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/public/favicon.ico -------------------------------------------------------------------------------- /guide/static/public/message-author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DisnakeDev/guide/d2e0a9810f6e82fde20078d851c18c56ead4a9d1/guide/static/public/message-author.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "disnake-guide", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": {} 6 | } 7 | --------------------------------------------------------------------------------