aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/content/resources/guides
diff options
context:
space:
mode:
Diffstat (limited to 'pydis_site/apps/content/resources/guides')
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/asking-good-questions.md2
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing.md90
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot-extended-configuration-options.md191
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md642
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/commit-messages.md15
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines.md25
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines/_info.yml2
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines/supplemental-information.md99
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/creating-bot-account.md6
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md2
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/linting.md14
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/logging.md31
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/pull-requests.md40
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot.md66
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot/env-var-reference.md92
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md49
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/style-guide.md21
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/contributing/working-with-git.md6
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md103
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md4
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md55
-rw-r--r--pydis_site/apps/content/resources/guides/pydis-guides/off-topic-etiquette.md2
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/app-commands.md418
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/creating-python-environment-windows.md2
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/discord-embed-limits.md21
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/discord-messages-with-colors.md79
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/discordpy-subclassing-context.md129
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/discordpy_help_command.md66
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/docker-hosting-guide.md323
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/fix-ssl-certificate.md23
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/keeping-tokens-safe.md29
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/proper-error-handling.md70
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md48
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/subclassing_bot.md58
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/vps-services.md58
-rw-r--r--pydis_site/apps/content/resources/guides/python-guides/why-not-json-as-database.md28
36 files changed, 2028 insertions, 881 deletions
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/asking-good-questions.md b/pydis_site/apps/content/resources/guides/pydis-guides/asking-good-questions.md
index 971989a9..b08ba7c6 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/asking-good-questions.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/asking-good-questions.md
@@ -26,7 +26,7 @@ If none of the above steps help you or you're not sure how to do some of the abo
# A Good Question
-When you're ready to ask a question, there's a few things you should have to hand before forming a query.
+When you're ready to ask a question, there are a few things you should have to hand before forming a query.
* A code example that illustrates your problem
* If possible, make this a minimal example rather than an entire application
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing.md
index fd322d08..5dc6408c 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing.md
@@ -4,7 +4,7 @@ description: A guide to contributing to our open source projects.
icon: fab fa-github
---
-Our projects on Python Discord are open source and [available on Github](https://github.com/python-discord). If you would like to contribute, consider one of the following projects:
+Our projects on Python Discord are open source and [available on GitHub](https://github.com/python-discord). If you would like to contribute, consider one of the following projects:
<!-- Project cards -->
<div class="columns is-multiline is-centered is-3 is-variable">
@@ -19,11 +19,7 @@ Our projects on Python Discord are open source and [available on Github](https:/
</div>
<div class="card-content">
<div class="content">
- Our community-driven Discord bot.
- </div>
- <div class="tags has-addons">
- <span class="tag is-dark">Difficulty</span>
- <span class="tag is-primary">Beginner</span>
+ Sir Lancebot has a collection of self-contained, for-fun features. If you're new to Discord bots or contributing, this is a great place to start!
</div>
</div>
<div class="card-footer has-background-white">
@@ -46,11 +42,7 @@ Our projects on Python Discord are open source and [available on Github](https:/
</div>
<div class="card-content">
<div class="content">
- The community and moderation Discord bot.
- </div>
- <div class="tags has-addons">
- <span class="tag is-dark">Difficulty</span>
- <span class="tag is-warning">Intermediate</span>
+ Called @Python on the server, this bot handles moderation tools, help channels, and other critical features for our community.
</div>
</div>
<div class="card-footer">
@@ -73,11 +65,7 @@ Our projects on Python Discord are open source and [available on Github](https:/
</div>
<div class="card-content">
<div class="content">
- The website, subdomains and API.
- </div>
- <div class="tags has-addons">
- <span class="tag is-dark">Difficulty</span>
- <span class="tag is-danger">Advanced</span>
+ This website itself! This project is built with Django and includes our API, which is used by various services such as @Python.
</div>
</div>
<div class="card-footer">
@@ -91,26 +79,64 @@ Our projects on Python Discord are open source and [available on Github](https:/
</div>
</div>
-If you don't understand anything or need clarification, feel free to ask any staff member with the **@PyDis Core Developers** role in the server. We're always happy to help!
+# How do I start contributing?
+Unsure of what contributing to open source projects involves? Have questions about how to use GitHub? Just need to know about our contribution etiquette? Completing these steps will have you ready to make your first contribution no matter your starting point.
+
+Feel free to skip any steps you're already familiar with, but please make sure not to miss the [Contributing Guidelines](#3-read-our-contributing-guidelines).
+
+If you are here looking for the answer to a specific question, check out the sub-articles in the top right of the page to see a list of our guides.
+
+**Note:** We use Git to keep track of changes to the files in our projects. Git allows you to make changes to your local code and then distribute those changes to the other people working on the project. You'll use Git in a couple steps of the contributing process. You can refer to this [**guide on using Git**](./working-with-git/).
+{: .notification }
+
+### 1. Fork and clone the repo
+GitHub is a website based on Git that stores project files in the cloud. We use GitHub as a central place for sending changes, reviewing others' changes, and communicating with each other. You'll need to create a copy under your own GitHub account, a.k.a. "fork" it. You'll make your changes to this copy, which can then later be merged into the Python Discord repository.
+
+*Note: Members of the Python Discord staff can create feature branches directly on the repo without forking it.*
+
+Check out our [**guide on forking a GitHub repo**](./forking-repository/).
+
+Now that you have your own fork you need to be able to make changes to the code. You can clone the repo to your local machine, commit changes to it there, then push those changes to GitHub.
+
+Check out our [**guide on cloning a GitHub repo**](./cloning-repository/).
+
+### 2. Set up the project
+You have the source code on your local computer, now how do you actually run it? We have detailed guides on setting up the environment for each of our main projects:
+
+* [**Sir Lancebot**](./sir-lancebot/)
+
+* [**Python Bot**](./bot/)
+
+* [**Site**](./site/)
+
+### 3. Read our Contributing Guidelines
+We have a few short rules that all contributors must follow. Make sure you read and follow them while working on our projects.
+
+[**Read our Contributing Guidelines here.**](./contributing-guidelines/)
+
+As mentioned in the Contributing Guidelines, we have a simple style guide for our projects based on PEP 8. Give it a read to keep your code consistent with the rest of the codebase.
+
+[**Read our Style Guide here.**](./style-guide/)
+
+### 4. Create an issue
+The first step to any new contribution is an issue describing a problem with the current codebase or proposing a new feature. All the open issues are viewable on the GitHub repositories, for instance here is the [issues page for Sir Lancebot](https://github.com/python-discord/sir-lancebot/issues). If you have something that you want to implement open a new issue to present your idea. Otherwise, you can browse the unassigned issues and ask to be assigned to one that you're interested in, either in the comments on the issue or in the [`#dev-contrib`](https://discord.gg/2h3qBv8Xaa) channel on Discord.
+
+[**Find out how to write a good issue here.**](./issues/)
-### Useful Resources
+Don't move forward until your issue is approved by a Core Developer. Issues are not guaranteed to be approved so your work may be wasted.
+{: .notification .is-warning }
-[Guidelines](./contributing-guidelines/) - General guidelines you should follow when contributing to our projects.<br>
-[Style Guide](./style-guide/) - Information regarding the code styles you should follow when working on our projects.<br>
-[Review Guide](../code-reviews-primer/) - A guide to get you started on doing code reviews.
+### 5. Make changes
+Now it is time to make the changes to fulfill your approved issue. You should create a new Git branch for your feature; that way you can keep your main branch up to date with ours and even work on multiple features at once in separate branches.
-## Contributors Community
-We are very happy to have many members in our community that contribute to [our open source projects](https://github.com/python-discord/).
-Whether it's writing code, reviewing pull requests, or contributing graphics for our events, it’s great to see so many people being motivated to help out.
-As a token of our appreciation, those who have made significant contributions to our projects will receive a special **@Contributors** role on our server that makes them stand out from other members.
-That way, they can also serve as guides to others who are looking to start contributing to our open source projects or open source in general.
+This is a good time to review [how to write good commit messages](./commit-messages) if you haven't already.
-#### Guidelines for the @Contributors Role
+### 6. Open a pull request
+After your issue has been approved and you've written your code and tested it, it's time to open a pull request. Pull requests are a feature in GitHub; you can think of them as asking the project maintainers to accept your changes. This gives other contributors a chance to review your code and make any needed changes before it's merged into the main branch of the project.
-One question we get a lot is what the requirements for the **@Contributors** role are.
-As it’s difficult to precisely quantify contributions, we’ve come up with the following guidelines for the role:
+Check out our [**Pull Request Guide**](./pull-requests/) for help with opening a pull request and going through the review process.
-- The member has made several significant contributions to our projects.
-- The member has a positive influence in our contributors subcommunity.
+Check out our [**Code Review Guide**](../code-reviews-primer/) to learn how to be a star reviewer. Reviewing PRs is a vital part of open source development, and we always need more reviewers!
-The role will be assigned at the discretion of the Admin Team in consultation with the Core Developers Team.
+### That's it!
+Thank you for contributing to our community projects. If there's anything you don't understand or you just want to discuss with other contributors, come visit the [`#dev-contrib`](https://discord.gg/2h3qBv8Xaa) channel to ask questions. Keep an eye out for staff members with the **@PyDis Core Developers** role in the server; we're always happy to help!
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot-extended-configuration-options.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot-extended-configuration-options.md
new file mode 100644
index 00000000..f5425d88
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot-extended-configuration-options.md
@@ -0,0 +1,191 @@
+---
+title: Extended options for configuring Bot
+description: A guide that lists all extended/optional options to configure the bot further.
+icon: fab fa-github
+toc: 3
+---
+
+## Manual constants configuration
+
+Reading this means that you're ready for a bit of manual labour.
+If for some reason you've missed the automatic server setup section, you can read about it in the bot contributing guide [here](../bot.md#envserver)
+
+To configure the bot manually, you will **only** need to set inside the `.env.server` file the values for the channels, roles, categories, etc.
+that are used by the component you are developing.
+
+For example, if we're testing a feature that only needs the `announcements` channel:
+
+`constants.py`
+
+```py
+
+class EnvConfig:
+ # Defines from where & how Pydantic will be looking for env variables
+ ...
+
+class _Channels(EnvConfig):
+
+ EnvConfig.Config.env_prefix = "channels_"
+
+ announcements = 1079790565794779156
+ changelog = 1077877318564991006
+
+# Instantiate the class & load the configuration
+Channels = _Channels()
+```
+
+`.env.server` file:
+
+```text
+# .env.server
+
+channels_announcements=1077875228002234398
+```
+
+When you launch your bot, `pydantic` will load up the server constants from the `.env.server` file if they exist.
+
+Each constants class will define its own prefix, which will make `pydantic` look for variables that will look like `{{env_prefix}}{{attribute_name}}` in the environment files
+
+In our example, this will imply that pydantic will look for both `channels_announcements` and `channels_changelog` in the `.env.server` file.
+
+As you can see here, only `channels_announcements` has been defined in the `.env.server` file since it's the only one needed, which will tell `pydantic`
+to use the value **1077875228002234398** for the `announcements` attribute instead of the default **1079790565794779156**, and use the default value for the `changelog` attribute
+
+```python
+>>> Channels.announcements
+1077875228002234398
+>>> Channels.changelong
+1077877318564991006
+```
+
+See [here](../obtaining-discord-ids) for help with obtaining Discord IDs.
+
+If you wish to set all values in your `env.server` for your testing server, you need to set **all** the ones prefixed with:
+
+* `guild_`
+* `categories_`
+* `channels_`
+* `roles_`
+* `webhooks_`
+* `emojis_`
+
+## Working with the help forum
+
+**Note**: This is only required when you're not configuring the bot [automatically](#automatic-configuration)
+
+If you will be working on a feature that includes the Python help forum, you will need to use `Forum Channels`.
+
+Forum channels cannot be included in a template, which is why this needs to be done by hand for the time being.
+
+To activate forum channels, your Discord server needs to have the community feature.
+If that's not the case already, here are the steps required to do it:
+
+1. Go to server settings
+2. Scroll down to the `COMMUNITY` section and click on `Enable Community`
+3. Click on `Get Started` and fill out the necessary info
+
+Once the previous steps are done, all that is left is to:
+
+1. Create a new channel
+2. Choose the `Forum` type
+3. [Copy its ID](../obtaining-discord-ids#channel-id)
+4. Add the following line to the `.env.server` file: `channels_python_help={newly_created_forum_channel_id}`
+
+---
+
+We understand this is tedious which is why we **heavily recommend** using the [automatic configuration setup](../bot.md#automatic-configuration)
+
+---
+
+## Reloading parts of the bot
+If you make changes to an extension, you might not need to restart the entire bot for the changes to take effect. The command `!ext reload <extension_name>` re-imports the files associated with the extension.
+Invoke `!ext list` for a full list of the available extensions. In this bot in particular, cogs are defined inside extensions.
+
+Note that if you changed code that is not associated with a particular extension, such as utilities, converters, and constants, you will need to restart the bot.
+
+## Adding new statistics
+
+Details on how to add new statistics can be found on the [statistic infrastructure page](https://blog.pythondiscord.com/statistics-infrastructure).
+We are always open to more statistics so add as many as you can!
+
+---
+
+## With the Bot Running Locally
+The advantage of this method is that you can run the bot's code in your preferred editor, with debugger and all, while keeping all the setup of the bot's various dependencies inside Docker.
+
+* Append the following line to your `.env` file: `API_KEYS_SITE_API=badbot13m0n8f570f942013fc818f234916ca531`.
+* In your `.env.server` file, set `urls_site_api="http://localhost:8000/api"`. If you wish to keep using `http://web:8000/api`, then [COMPOSE_PROJECT_NAME](../docker/#compose-project-names) has to be set.
+* To work with snekbox, set `urls_snekbox_eval_api="http://localhost:8060/eval"`.
+
+
+You will need to start the services separately, but if you got the previous section with Docker working, that's pretty simple:
+
+* `docker compose up web` to start the site container. This is required.
+* `docker compose up snekbox` to start the snekbox container. You only need this if you're planning on working on the snekbox cog.
+* `docker compose up redis` to start the Redis container. You only need this if you're not using fakeredis. For more info refer to [Working with Redis](#optional-working-with-redis).
+
+You can start several services together: `docker compose up web snekbox redis`.
+
+### Setting Up a Development Environment
+
+With at least the site running in Docker already (see the previous section on how to start services separately), you can now start the bot locally through the command line, or through your preferred IDE.
+<div class="card">
+ <button type="button" class="card-header collapsible">
+ <span class="card-header-title subtitle is-6 my-2 ml-2">Ways to run code</span>
+ <span class="card-header-icon">
+ <i class="fas fa-fw fa-angle-down title is-5" aria-hidden="true"></i>
+ </span>
+ </button>
+ <div class="collapsible-content collapsed">
+ <div class="card-content">
+ Notice that the bot is started as a module. There are several ways to do so:
+ <ul>
+ <li>Through the command line, inside the bot directory, with either <code>poetry run task start</code>, or directly <code>python -m bot</code>.</li>
+ <li>If using PyCharm, enter <code>Edit Configurations</code> and set everything according to this image: <img src="/static/images/content/contributing/pycharm_run_module.png"></li>
+ <li>If using Visual Studio Code, set the interpreter to the poetry environment you created. In <code>launch.json</code> create a new Python configuration, and set the name of the program to be run to <code>bot</code>. VSC will correctly run it as a module.</li>
+ </ul>
+ </div>
+ </div>
+</div>
+<br>
+
+### With More Things Running Locally
+You can run additional services on the host, but this guide won't go over how to install and start them in this way.
+If possible, prefer to start the services through Docker to replicate the production environment as much as possible.
+
+The site, however, is a mandatory service for the bot.
+Refer to the [previous section](#with-the-bot-running-locally) and the [site contributing guide](../site) to learn how to start it on the host, in which case you will need to change `urls.site` in `.env.server` to wherever the site is being hosted.
+
+---
+
+### Starting Redis Using Other Methods
+You can run your own instance of Redis, but in that case you will need to correctly set `redis_host` and `redis_port` in your `.env.server` file and the `REDIS_PASSWORD` in the `.env` file.
+**Note**: The previously mentioned variables **SHOULD NOT** be overriden or changed in `constants.py`
+
+---
+
+## Working with Metricity
+[Metricity](https://github.com/python-discord/metricity) is our home-grown bot for collecting metrics on activity within the server, such as what users are present, and IDs of the messages they've sent.
+Certain features in the Python bot rely on querying the Metricity database for information such as the number of messages a user has sent, most notably the voice verification system.
+
+If you wish to work on a feature that relies on Metricity, for your convenience we've made the process of using it relatively painless with Docker: Enter the `.env` file you've written for the Python bot, and append the line `USE_METRICITY=true`.
+Note that if you don't need Metricity, there's no reason to have it enabled as it is just unnecessary overhead.
+
+To make the Metricity bot work with your test server, you will need to override its configurations similarly to the Python bot.
+You can see the various configurations in [the Metricity repo](https://github.com/python-discord/metricity), but the bare minimum is the guild ID setting.
+In your local version of the Python bot repo, create a file called `metricity-config.toml` and insert the following lines:
+```yaml
+[bot]
+guild_id = replace_with_your_guild_id
+```
+To properly replicate production behavior, set the `staff_role_id`, `staff_categories`, and `ignore_categories` fields as well.
+
+Now, `docker compose up` will also start Metricity.
+
+If you want to run the bot locally, you can run `docker compose up metricity` instead.
+
+---
+
+## Working with bot moderation logs
+To be able to view moderation-related logs published by the bot to site, you will need to set `urls_site_logs_view=http://localhost:8000/staff/bot/logs` in your `.env.server`.
+This will work in both Docker and locally.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md
index 2aa10aa3..f54ee664 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/bot.md
@@ -5,44 +5,49 @@ icon: fab fa-github
toc: 3
---
The purpose of this guide is to get you a running local version of [the Python bot](https://github.com/python-discord/bot).
+You should have already forked the repository and cloned it to your local machine. If not, check out our [detailed walkthrough](../#1-fork-and-clone-the-repo).
+
This page will focus on the quickest steps one can take, with mentions of alternatives afterwards.
-### Clone The Repository
-First things first, to run the bot's code and make changes to it, you need a local version of it (on your computer).
+---
+## Setting up the project
+
+### Setup Project Dependencies
+Below are the dependencies you **must** have installed to get started with the bot.
+
+1. Make sure you have [Python 3.11](https://www.python.org/downloads/) installed. It helps if it is your system's default Python version.
+1. [Install Poetry](https://github.com/python-poetry/poetry#installation).
+1. [Install the project's dependencies](../installing-project-dependencies).
+1. Docker.
<div class="card">
<button type="button" class="card-header collapsible">
- <span class="card-header-title subtitle is-6 my-2 ml-2">Getting started with Git and GitHub</span>
+ <span class="card-header-title subtitle is-6 my-2 ml-2">Getting started with Docker</span>
<span class="card-header-icon">
<i class="fas fa-fw fa-angle-down title is-5" aria-hidden="true"></i>
</span>
</button>
<div class="collapsible-content collapsed">
<div class="card-content">
- <p>If you don't have Git on your computer already, <a href="https://git-scm.com/downloads">install it</a>. You can additionally install a Git GUI such as <a href="https://www.gitkraken.com/download">GitKraken</a>, or the <a href="https://cli.github.com/manual/installation">GitHub CLI</a>.</p>
- <p>To learn more about Git, you can look into <a href="../working-with-git">our guides</a>, as well as <a href="https://education.github.com/git-cheat-sheet-education.pdf">this cheatsheet</a>, <a href="https://learngitbranching.js.org">Learn Git Branching</a>, and otherwise any guide you can find on the internet. Once you got the basic idea though, the best way to learn Git is to use it.</p>
- <p>Creating a copy of a repository under your own account is called a <em>fork</em>. This is where all your changes and commits will be pushed to, and from where your pull requests will originate from.</p>
- <p><strong><a href="../forking-repository">Learn about forking a project</a></strong>.</p>
+ The requirements for Docker are:
+ <ul>
+ <li><a href="https://docs.docker.com/install">Docker CE</a></li>
+ <li>Docker Compose. If you're using macOS and Windows, this already comes bundled with the previous installation. Otherwise, you can download it either from the <a href="https://docs.docker.com/compose/install">website</a>, or by running <code>pip install docker-compose</code>.</li>
+ </ul>
+ <p class="notification is-warning">If you get any Docker related errors, reference the <a href="../docker#possible-issues">Possible Issue</a> section of the Docker page.</p>
</div>
</div>
</div>
<br>
-You will need to create a fork of [the project](https://github.com/python-discord/bot), and clone the fork.
-Once this is done, you will have completed the first step towards having a running version of the bot.
-
-#### Working on the Repository Directly
-If you are a member of the organisation (a member of [this list](https://github.com/orgs/python-discord/people), or in our particular case, server staff), you can clone the project repository without creating a fork, and work on a feature branch instead.
-
----
-
### Set Up a Test Server
The Python bot is tightly coupled with the Python Discord server, so to have a functional version of the bot you need a server with channels it can use.
It's possible to set the bot to use a single channel for all cogs, but that will cause extreme spam and will be difficult to work with.
You can start your own server and set up channels as you see fit, but for your convenience we have a template for a development server you can use: [https://discord.new/zmHtscpYN9E3](https://discord.new/zmHtscpYN9E3).
-Keep in mind that this is not a mirror of the Python server, but a reduced version for testing purposes. A lot of the channels in the Python server were merged.
+Keep in mind that this is not an exact mirror of the Python server, but a reduced version for testing purposes.
+The channels there are mostly the ones needed by the bot.
---
### Set Up a Bot Account
@@ -61,398 +66,44 @@ If your bot fails to start with a `PrivilegedIntentsRequired` exception, this in
---
### Configure the Bot
-You now have both the bot's code and a server to run it on. It's time you to connect the two by changing the bot's configurations.
+You now have both the bot's code and a server to run it on. It's time for you to connect the two by setting the bot's configuration.
-#### config.yml
-Entering the directory of the cloned code, you will find a file named `config-default.yml`.
-This file contains the various configurations we use to make the bot run on the Python Discord server, such as channel and role IDs, and the emojis it works with.
-It also contains configurations such as how long it takes for a help channel to time out, and how many messages a user needs to voice-verify.
+Both `.env` and `.env.server` files we talk about below are ignored by git, so they do not get accidentally commit to the repository.
-To run the bot in your test server, you will need to override some of those configurations.
-Create and open a new file in the directory called `config.yml`. Alternatively, copy the `config-default.yml` file and rename the copy to `config.yml`.
-The bot will first look at the items in `config.yml`, and will fall back to `config-default.yml` only if necessary. Note that you don't have to specify all items in `config.yml`, just the ones you want to override such as channel IDs.
+#### .env
+This file will contain sensitive information such as your bot's token, do not share it with anybody!
-See [here](../obtaining-discord-ids) for help with obtaining Discord IDs.
+To start, create a `.env` file in the project root with the below content.
-<div class="card">
- <button type="button" class="card-header collapsible">
- <span class="card-header-title subtitle is-6 my-2 ml-2">Optional config.yml</span>
- <span class="card-header-icon">
- <i class="fas fa-fw fa-angle-down title is-5" aria-hidden="true"></i>
- </span>
- </button>
- <div class="collapsible-content collapsed">
- <div class="card-content">
- <p>If you used the provided server template, and you're not sure which channels belong where in the config file, you can use the config below. Pay attention to the comments with several <code>#</code> symbols, and replace the <code>�</code> characters with the right IDs.</p>
- <pre>
- <code class="language-yaml">
-bot:
- prefix: "!"
-
- redis:
- host: "redis"
- password: null
- port: 6379
- use_fakeredis: true
-
- stats:
- presence_update_timeout: 300
- statsd_host: "graphite.default.svc.cluster.local"
-
-urls:
- # PyDis site vars
- site: &DOMAIN "web:8000"
- site_api: &API !JOIN [*DOMAIN, "/api"]
- site_api_schema: "http://"
- site_paste: &PASTE !JOIN ["paste.", "pythondiscord.com"]
- site_schema: &SCHEMA "http://"
- site_staff: &STAFF !JOIN [*DOMAIN, "/staff"]
-
- paste_service: !JOIN ["https://", *PASTE, "/{key}"]
- site_logs_view: !JOIN [*SCHEMA, *STAFF, "/bot/logs"]
-
- # Snekbox
- snekbox_eval_api: "http://localhost:8060/eval"
-
-##### << Replace the following � characters with the channel IDs in your test server >> #####
-# This assumes the template was used: https://discord.new/zmHtscpYN9E3
-dev_guild:
- id: &DEV_GUILD_ID �
-
- categories:
- logs: &DEV_LOGS �
- help_available: &DEV_HELP_AVAILABLE �
- help_occupied: &DEV_HELP_OCCUPIED �
- help_dormant: &DEV_HELP_DORMANT �
- voice: &DEV_VOICE �
-
- channels:
- # Staff
- admins_mods: &DEV_ADMINS_MODS �
- lounge_helpers_org: &DEV_LOUNGE_HELPERS_ORG �
- defcon: &DEV_DEFCON �
- incidents: &DEV_INCIDENTS �
- incidents_archive: &DEV_INCIDENTS_ARCHIVE �
- staff_announcements: &DEV_STAFF_ANNOUNCEMENTS �
- dev_logs: &DEV_DEV_LOGS �
-
- # Logs
- all_logs: &DEV_ALL_LOGS �
- bb_logs: &DEV_BB_LOGS �
- duck_pond: &DEV_DUCK_POND �
-
- # Available Help Channels
- how_to_get_help: &DEV_HTGH �
-
- # Miscellaneous
- bot_commands: &DEV_BOT_CMD �
- general_meta_voice: &DEV_GMV �
- dev_core_contrib: &DEV_DEV �
-
- # Voice
- voice-verification: &DEV_VOICE_VER �
- vc: &DEV_VC �
- staff_voice: &DEV_STAFF_VOICE �
-
- # News
- announcements: &DEV_ANNOUNCEMENTS �
- py_news: &DEV_PY_NEWS �
-
- # Off-topic
- off_topic_0: &DEV_OT_0 �
- off_topic_1: &DEV_OT_1 �
- off_topic_2: &DEV_OT_2 �
-
-guild:
- ##### << Replace the following � characters with the role and webhook IDs in your test server >> #####
- roles:
- announcements: �
- contributors: �
- help_cooldown: �
- muted: &MUTED_ROLE �
- partners: &PY_PARTNER_ROLE �
- python_community: &PY_COMMUNITY_ROLE �
- voice_verified: �
-
- # Staff
- admins: &ADMINS_ROLE �
- core_developers: �
- devops: �
- domain_leads: �
- helpers: &HELPERS_ROLE �
- moderators: &MODS_ROLE �
- mod_team: &MOD_TEAM_ROLE �
- owners: &OWNERS_ROLE �
- code_jam_event_team: �
- project_leads: �
-
- # Code Jam
- team_leaders: �
-
- # Streaming
- video: �
-
- webhooks:
- big_brother: �
- dev_log: �
- duck_pond: �
- incidents: �
- incidents_archive: �
- python_news: &PYNEWS_WEBHOOK �
- talent_pool: �
-
- ##### << At this point your test bot should be able to mostly work with your test server >> #####
- # The following is the actual configs the bot uses, don't delete these.
- id: *DEV_GUILD_ID
- invite: "https://discord.gg/python"
-
- categories:
- help_available: *DEV_HELP_AVAILABLE
- help_dormant: *DEV_HELP_DORMANT
- help_in_use: *DEV_HELP_OCCUPIED
- logs: *DEV_LOGS
- voice: *DEV_VOICE
-
- channels:
- # Public announcement and news channels
- announcements: *DEV_ANNOUNCEMENTS
- change_log: *DEV_ANNOUNCEMENTS
- mailing_lists: *DEV_ANNOUNCEMENTS
- python_events: *DEV_ANNOUNCEMENTS
- python_news: *DEV_PY_NEWS
-
- # Development
- dev_contrib: *DEV_DEV
- dev_core: *DEV_DEV
- dev_log: *DEV_DEV_LOGS
-
- # Discussion
- meta: *DEV_GMV
- python_general: *DEV_GMV
-
- # Python Help: Available
- cooldown: *DEV_HTGH
- how_to_get_help: *DEV_HTGH
-
- # Topical
- discord_py: *DEV_GMV
-
- # Logs
- attachment_log: *DEV_ALL_LOGS
- message_log: *DEV_ALL_LOGS
- mod_log: *DEV_ALL_LOGS
- user_log: *DEV_ALL_LOGS
- voice_log: *DEV_ALL_LOGS
-
- # Off-topic
- off_topic_0: *DEV_OT_0
- off_topic_1: *DEV_OT_1
- off_topic_2: *DEV_OT_2
-
- # Special
- bot_commands: *DEV_BOT_CMD
- voice_gate: *DEV_VOICE_VER
- code_jam_planning: *DEV_ADMINS_MODS
-
- # Staff
- admins: *DEV_ADMINS_MODS
- admin_spam: *DEV_ADMINS_MODS
- defcon: *DEV_DEFCON
- duck_pond: *DEV_DUCK_POND
- helpers: *DEV_LOUNGE_HELPERS_ORG
- incidents: *DEV_INCIDENTS
- incidents_archive: *DEV_INCIDENTS_ARCHIVE
- mods: *DEV_ADMINS_MODS
- mod_alerts: *DEV_ADMINS_MODS
- mod_meta: *DEV_ADMINS_MODS
- mod_spam: *DEV_ADMINS_MODS
- mod_tools: *DEV_ADMINS_MODS
- organisation: *DEV_LOUNGE_HELPERS_ORG
- staff_lounge: *DEV_LOUNGE_HELPERS_ORG
-
- # Staff announcement channels
- admin_announcements: *DEV_STAFF_ANNOUNCEMENTS
- mod_announcements: *DEV_STAFF_ANNOUNCEMENTS
- staff_announcements: *DEV_STAFF_ANNOUNCEMENTS
-
- # Voice Channels
- admins_voice: *DEV_STAFF_VOICE
- code_help_voice_1: *DEV_VC
- code_help_voice_2: *DEV_VC
- general_voice: *DEV_VC
- staff_voice: *DEV_STAFF_VOICE
-
- # Voice Chat
- code_help_chat_1: *DEV_GMV
- code_help_chat_2: *DEV_GMV
- staff_voice_chat: *DEV_ADMINS_MODS
- voice_chat: *DEV_GMV
-
- # Watch
- big_brother_logs: *DEV_BB_LOGS
-
- moderation_categories:
- - *DEV_LOGS
-
- moderation_channels:
- - *DEV_ADMINS_MODS
-
- # Modlog cog ignores events which occur in these channels
- modlog_blacklist:
- - *DEV_ADMINS_MODS
- - *DEV_ALL_LOGS
- - *DEV_STAFF_VOICE
-
- reminder_whitelist:
- - *DEV_BOT_CMD
- - *DEV_DEV
-
- moderation_roles:
- - *ADMINS_ROLE
- - *MODS_ROLE
- - *MOD_TEAM_ROLE
- - *OWNERS_ROLE
-
- staff_roles:
- - *ADMINS_ROLE
- - *HELPERS_ROLE
- - *MODS_ROLE
- - *OWNERS_ROLE
-
-##### << The bot shouldn't fail without these, but commands adding specific emojis won't work. >> #####
-# You should at least set the trashcan. Set the incidents emojis if relevant.
-style:
- emojis:
- badge_bug_hunter: "<:bug_hunter_lvl1:�>"
- badge_bug_hunter_level_2: "<:bug_hunter_lvl2:�>"
- badge_early_supporter: "<:early_supporter:�>"
- badge_hypesquad: "<:hypesquad_events:�>"
- badge_hypesquad_balance: "<:hypesquad_balance:�>"
- badge_hypesquad_bravery: "<:hypesquad_bravery:�>"
- badge_hypesquad_brilliance: "<:hypesquad_brilliance:�>"
- badge_partner: "<:partner:�>"
- badge_staff: "<:discord_staff:�>"
- badge_verified_bot_developer: "<:verified_bot_dev:�>"
-
- defcon_shutdown: "<:defcondisabled:�>"
- defcon_unshutdown: "<:defconenabled:�>"
- defcon_update: "<:defconsettingsupdated:�>"
-
- failmail: "<:failmail:�>"
-
- #incident_actioned: "<:incident_actioned:�>"
- incident_investigating: "<:incident_investigating:�>"
- incident_unactioned: "<:incident_unactioned:�>"
-
- status_dnd: "<:status_dnd:�>"
- status_idle: "<:status_idle:�>"
- status_offline: "<:status_offline:�>"
- status_online: "<:status_online:�>"
-
- trashcan: "<:trashcan:�>"
-
-##### << Optional - If you don't care about the filtering, help channel and py-news cogs, ignore the rest of this file >> #####
-filter:
- # What do we filter?
- filter_domains: true
- filter_everyone_ping: true
- filter_invites: true
- filter_zalgo: false
- watch_regex: true
- watch_rich_embeds: true
-
- # Notify user on filter?
- # Notifications are not expected for "watchlist" type filters
- notify_user_domains: false
- notify_user_everyone_ping: true
- notify_user_invites: true
- notify_user_zalgo: false
-
- # Filter configuration
- offensive_msg_delete_days: 7 # How many days before deleting an offensive message?
- ping_everyone: true
-
- # Censor doesn't apply to these
- channel_whitelist:
- - *DEV_ADMINS_MODS
- - *DEV_BB_LOGS
- - *DEV_ALL_LOGS
- - *DEV_LOUNGE_HELPERS_ORG
-
- role_whitelist:
- - *ADMINS_ROLE
- - *HELPERS_ROLE
- - *MODS_ROLE
- - *OWNERS_ROLE
- - *PY_COMMUNITY_ROLE
- - *PY_PARTNER_ROLE
-
-help_channels:
- enable: true
-
- # Minimum interval before allowing a certain user to claim a new help channel
- claim_minutes: 1
-
- # Roles which are allowed to use the command which makes channels dormant
- cmd_whitelist:
- - *HELPERS_ROLE
-
- # Allowed duration of inactivity before making a channel dormant
- idle_minutes: 1
-
- # Allowed duration of inactivity when channel is empty (due to deleted messages)
- # before message making a channel dormant
- deleted_idle_minutes: 1
-
- # Maximum number of channels to put in the available category
- max_available: 2
-
- # Maximum number of channels across all 3 categories
- # Note Discord has a hard limit of 50 channels per category, so this shouldn't be > 50
- max_total_channels: 20
-
- # Prefix for help channel names
- name_prefix: 'help-'
-
- # Notify if more available channels are needed but there are no more dormant ones
- notify: true
-
- # Channel in which to send notifications
- notify_channel: *DEV_LOUNGE_HELPERS_ORG
-
- # Minimum interval between helper notifications
- notify_minutes: 5
-
- # Mention these roles in notifications
- notify_roles:
- - *HELPERS_ROLE
-
-python_news:
- channel: *DEV_PY_NEWS
- webhook: *PYNEWS_WEBHOOK
-
-##### << Add any additional sections you need to override from config-default.yml >> #####
- </code>
- </pre>
-</div></div></div>
-<br>
+```text
+BOT_TOKEN=YourDiscordBotTokenHere
+GUILD_ID=YourDiscordTestServerIdHere
+BOT_PREFIX=YourDesiredPrefixHere
+```
+See [here](../creating-bot-account) for help with obtaining the bot token, and [here](../obtaining-discord-ids#guild-id) for help with obtaining the guild's ID.
-If you don't wish to use the provided `config.yml` above, these are the main sections in `config-default.yml` that need overriding:
+Other values will be added to your `.env` over time as you need to interact with other parts of the bot, but those are not needed for a basic setup. For a full list of support values see the ENV file option [appendix](#appendix-full-env-file-options)
-* `guild.id`
-* `guild.categories`
-* `guild.channels`
-* `guild.roles`
-* `guild.webhooks`
-* `style.emojis`
+#### .env.server
+All server related configuration values are saved in this file, which also needs to be at the root directory of the project.
-Additionally:
+We provide a script to automatically generate a server config.
+**Note**: The script **only** works with servers created with the template mentioned above.
-* At this stage, set `bot.redis.use_fakeredis` to `true`. If you're looking for instructions for working with Redis, see [Working with Redis](#optional-working-with-redis).
-* Set `urls.site_api` to `!JOIN [*DOMAIN, "/api"]`.
-* Set `urls.site_schema` and `urls.site_api_schema` to `"http://"`.
+If you want to setup the bot from an existing guild read out [manual configuration guide](../bot-extended-configuration-options#manual-constants-configuration). This is far more complicated and time consuming.
+
+Running the below command will use the `BOT_TOKEN` and `GUILD_ID` from the `.env` file you created above to download all of the relevant IDs from the template guild into your `.env.server`
+
+**Note**: This script will overwrite the `.env.server` file. We suggest you put any configuration not generated by this script in to `.env` instead
+```shell
+$ poetry run task configure
+```
+
+Once the script has finished running, you'll notice the creation of a new file called `.env.server` at your project's root directory.
+This file will contain the extracted IDs from your server which are necessary for your bot to run.
+
+**Congratulations**, you have finished the configuration and can now [run your bot](#run-it).
-We understand this is tedious and are working on a better solution for setting up test servers.
<div class="card">
<button type="button" class="card-header collapsible">
@@ -463,205 +114,86 @@ We understand this is tedious and are working on a better solution for setting u
</button>
<div class="collapsible-content collapsed">
<div class="card-content">
- While it's technically possible to edit <code>config-default.yml</code> to match your server, it is heavily discouraged.
+ While it's technically possible to edit the values in <code>constants.py</code> to match your server, it is heavily discouraged.
This file's purpose is to provide the configurations the Python bot needs to run in the Python server in production, and should remain as such.
- In contrast, the <code>config.yml</code> file can remain in your local copy of the code, and will be ignored by commits via the project's <code>.gitignore</code>.
+ In contrast, the <code>.env.server</code> file can remain in your local copy of the code, and will be ignored by commits via the project's <code>.gitignore</code>.
</div>
</div>
</div>
<br>
-#### .env
-The second file you need to create is the one containing the environment variables, and needs to be named `.env`.
-Inside, add the line `BOT_TOKEN=YourDiscordBotTokenHere`. See [here](../creating-bot-account) for help with obtaining the bot token.
-
-The `.env` file will be ignored by commits.
-
----
### Run it!
#### With Docker
You are now almost ready to run the Python bot. The simplest way to do so is with Docker.
-<div class="card">
- <button type="button" class="card-header collapsible">
- <span class="card-header-title subtitle is-6 my-2 ml-2">Getting started with Docker</span>
- <span class="card-header-icon">
- <i class="fas fa-fw fa-angle-down title is-5" aria-hidden="true"></i>
- </span>
- </button>
- <div class="collapsible-content collapsed">
- <div class="card-content">
- The requirements for Docker are:
- <ul>
- <li><a href="https://docs.docker.com/install">Docker CE</a></li>
- <li>Docker Compose. If you're using macOS and Windows, this already comes bundled with the previous installation. Otherwise, you can download it either from the <a href="https://docs.docker.com/compose/install">website</a>, or by running <code>pip install docker-compose</code>.</li>
- </ul>
- <p class="notification is-warning">If you get any Docker related errors, reference the <a href="../docker#possible-issues">Possible Issue</a> section of the Docker page.</p>
- </div>
- </div>
-</div>
-<br>
-
-In your `config.yml` file:
-
-* Set `urls.site` to `"web:8000"`.
-* If you wish to work with snekbox set `urls.snekbox_eval_api` to `"http://snekbox:8060/eval"`.
-
-Assuming you have Docker installed **and running**, enter the cloned repo in the command line and type `docker-compose up`.
-
-After pulling the images and building the containers, your bot will start. Enter your server and type `!help` (or whatever prefix you chose instead of `!`).
-
-Your bot is now running, but this method makes debugging with an IDE a fairly involved process. For additional running methods, continue reading the following sections.
-#### With the Bot Running Locally
-The advantage of this method is that you can run the bot's code in your preferred editor, with debugger and all, while keeping all the setup of the bot's various dependencies inside Docker.
+With all of the above setup, you can run The projec with `docker compose up`. This will start the bot an all required services! Enter your server and type `!help` (or whatever prefix you chose instead of `!`) to see the bot in action!
-* Append the following line to your `.env` file: `BOT_API_KEY=badbot13m0n8f570f942013fc818f234916ca531`.
-* In your `config.yml` file, set `urls.site` to `"localhost:8000"`. If you wish to keep using `web:8000`, then [COMPOSE_PROJECT_NAME](../docker/#compose-project-names) has to be set.
-* To work with snekbox, set `urls.snekbox_eval_api` to `"http://localhost:8060/eval"`
+Some other useful docker commands are as follows:
-You will need to start the services separately, but if you got the previous section with Docker working, that's pretty simple:
-
-* `docker-compose up web` to start the site container. This is required.
-* `docker-compose up snekbox` to start the snekbox container. You only need this if you're planning on working on the snekbox cog.
-* `docker-compose up redis` to start the Redis container. You only need this if you're not using fakeredis. For more info refer to [Working with Redis](#optional-working-with-redis).
-
-You can start several services together: `docker-compose up web snekbox redis`.
-
-##### Setting Up a Development Environment
-The bot's code is Python code like any other. To run it locally, you will need the right version of Python with the necessary packages installed:
-
-1. Make sure you have [Python 3.9](https://www.python.org/downloads/) installed. It helps if it is your system's default Python version.
-2. [Install Poetry](https://github.com/python-poetry/poetry#installation).
-3. [Install the dependencies](../installing-project-dependencies).
-
-With at least the site running in Docker already (see the previous section on how to start services separately), you can now start the bot locally through the command line, or through your preferred IDE.
-<div class="card">
- <button type="button" class="card-header collapsible">
- <span class="card-header-title subtitle is-6 my-2 ml-2">Ways to run code</span>
- <span class="card-header-icon">
- <i class="fas fa-fw fa-angle-down title is-5" aria-hidden="true"></i>
- </span>
- </button>
- <div class="collapsible-content collapsed">
- <div class="card-content">
- Notice that the bot is started as a module. There are several ways to do so:
- <ul>
- <li>Through the command line, inside the bot directory, with either <code>poetry run task start</code>, or directly <code>python -m bot</code>.</li>
- <li>If using PyCharm, enter <code>Edit Configurations</code> and set everything according to this image: <img src="/static/images/content/contributing/pycharm_run_module.png"></li>
- <li>If using Visual Studio Code, set the interpreter to the poetry environment you created. In <code>launch.json</code> create a new Python configuration, and set the name of the program to be run to <code>bot</code>. VSC will correctly run it as a module.</li>
- </ul>
- </div>
- </div>
-</div>
-<br>
+1. `docker compose pull` this pulls updates for all non-bot services, such as postgres, redis and our [site](../site) project!
+1. `docker compose build` this rebuilds the bot's docker image, this is only needed if you need to make changes to the bot's dependencies, or the Dockerfile itself.
-#### With More Things Running Locally
-You can run additional services on the host, but this guide won't go over how to install and start them in this way.
-If possible, prefer to start the services through Docker to replicate the production environment as much as possible.
+Your bot is now running, all inside Docker.
-The site, however, is a mandatory service for the bot.
-Refer to the [previous section](#with-the-bot-running-locally) and the [site contributing guide](../site) to learn how to start it on the host, in which case you will need to change `urls.site` in `config.yml` to wherever the site is being hosted.
+**Note**: If you want to read about how to make debugging with an IDE a easier, or for additional running methods, check out our [extended configuration guide](../bot-extended-configuration-options).
---
-### Development Tips
-Now that you have everything setup, it is finally time to make changes to the bot!
-#### Working with Git
+## Development Tips
+Now that you have everything setup, it is finally time to make changes to the bot!
-If you have not yet [read the contributing guidelines](../contributing-guidelines), now is a good time.
-Contributions that do not adhere to the guidelines may be rejected.
+### Working with Git
-Notably, version control of our projects is done using Git and Github.
+Version control of our projects is done using Git and Github.
It can be intimidating at first, so feel free to ask for any help in the server.
[**Click here to see the basic Git workflow when contributing to one of our projects.**](../working-with-git/)
-#### Running tests
+### Running tests
[This section](https://github.com/python-discord/bot/blob/main/tests/README.md#tools) of the README in the `tests` repository will explain how to run tests.
The whole document explains how unittesting works, and how it fits in the context of our project.
Make sure to run tests *before* pushing code.
-Even if you run the bot through Docker, you might want to [setup a development environment](#setting-up-a-development-environment) in order to run the tests locally.
+Even if you run the bot through Docker, you might want to [setup a development environment](../bot-extended-configuration-options#setting-up-a-development-environment) in order to run the tests locally.
-#### Lint before you push
+### Lint before you push
As mentioned in the [contributing guidelines](../contributing-guidelines), you should make sure your code passes linting for each commit you make.
For ease of development, you can install the pre-commit hook with `poetry run task precommit`, which will check your code every time you try to commit it.
-For that purpose, even if you run the bot through Docker, you might want to [setup a development environment](#setting-up-a-development-environment), as otherwise the hook installation will fail.
-
-#### Reloading parts of the bot
-If you make changes to an extension, you might not need to restart the entire bot for the changes to take effect. The command `!ext reload <extension_name>` re-imports the files associated with the extension.
-Invoke `!ext list` for a full list of the available extensions. In this bot in particular, cogs are defined inside extensions.
-
-Note that if you changed code that is not associated with a particular extension, such as utilities, converters, and constants, you will need to restart the bot.
+For that purpose, even if you run the bot through Docker, you might want to [setup a development environment](../bot-extended-configuration-options#setting-up-a-development-environment), as otherwise the hook installation will fail.
-#### Adding new statistics
-
-Details on how to add new statistics can be found on the [statistic infrastructure page](https://blog.pythondiscord.com/statistics-infrastructure).
-We are always open to more statistics so add as many as you can!
-
----
-
-### Optional: Working with Redis
-In [Configure the Bot](#configyml) you were asked to set `bot.redis.use_fakeredis` to `true`. If you do not need to work on features that rely on Redis, this is enough. Fakeredis will give the illusion that features relying on Redis are saving information properly, but restarting the bot or the specific cog will wipe that information.
-
-If you are working on a feature that relies on Redis, you will need to enable Redis to make sure persistency is achieved for the feature across restarts. The first step towards that is going to `config.yml` and setting `bot.redis.use_fakeredis` to `false`.
-
-#### Starting Redis in Docker (Recommended)
-If you're using the Docker image provided in the project's Docker Compose, open your `config.yml` file. If you're running the bot in Docker, set `bot.redis.host` to `redis`, and if you're running it on the host set it to `localhost`. Set `bot.redis.password` to `null`.
-
-#### Starting Redis Using Other Methods
-You can run your own instance of Redis, but in that case you will need to correctly set `bot.redis.host` and `bot.redis.port`, and the `bot.redis.password` value in `config-default.yml` should not be overridden. Then, enter the `.env` file, and set `REDIS_PASSWORD` to whatever password you set.
-
----
-
-### Optional: Working with Metricity
-[Metricity](https://github.com/python-discord/metricity) is our home-grown bot for collecting metrics on activity within the server, such as what users are present, and IDs of the messages they've sent.
-Certain features in the Python bot rely on querying the Metricity database for information such as the number of messages a user has sent, most notably the voice verification system.
-
-If you wish to work on a feature that relies on Metricity, for your convenience we've made the process of using it relatively painless with Docker: Enter the `.env` file you've written for the Python bot, and append the line `USE_METRICITY=true`.
-Note that if you don't need Metricity, there's no reason to have it enabled as it is just unnecessary overhead.
-
-To make the Metricity bot work with your test server, you will need to override its configurations similarly to the Python bot.
-You can see the various configurations in [the Metricity repo](https://github.com/python-discord/metricity), but the bare minimum is the guild ID setting.
-In your local version of the Python bot repo, create a file called `metricity-config.toml` and insert the following lines:
-```yaml
-[bot]
-guild_id = replace_with_your_guild_id
-```
-To properly replicate production behavior, set the `staff_role_id`, `staff_categories`, and `ignore_categories` fields as well.
-
-Now, `docker-compose up` will also start Metricity.
+### Issues?
+If you have any issues with setting up the bot, come discuss it with us on the [#dev-contrib](https://discord.gg/2h3qBv8Xaa) channel on our server.
-If you want to run the bot locally, you can run `docker-compose up metricity` instead.
+If you find any bugs in the bot or would like to request a feature, feel free to [open an issue](https://github.com/python-discord/bot/issues/new/choose) on the repository.
---
-### Issues?
-If you have any issues with setting up the bot, come discuss it with us on the [#dev-contrib](https://discord.gg/2h3qBv8Xaa) channel on our server.
+# Next steps
+Now that you have everything setup, it is finally time to make changes to the bot! If you have not yet read the [contributing guidelines](../contributing-guidelines.md), now is a good time. Contributions that do not adhere to the guidelines may be rejected.
-If you find any bugs in the bot or would like to request a feature, feel free to open an issue on the repository.
+Have fun!
---
-### Appendix: Full ENV File Options
+# Appendix: Full ENV File Options
The following is a list of all available environment variables used by the bot:
-| Variable | Required | Description |
-| -------- | -------- | -------- |
-| `BOT_TOKEN` | Always | Your Discord bot account's token (see [Set Up a Bot Account](#set-up-a-bot-account)). |
-| `BOT_API_KEY` | When running bot without Docker | Used to authenticate with the site's API. When using Docker to run the bot, this is automatically set. By default, the site will always have the API key shown in the example below. |
-| `BOT_SENTRY_DSN` | When connecting the bot to sentry | The DSN of the sentry monitor. |
-| `BOT_TRACE_LOGGERS ` | When you wish to see specific or all trace logs | Comma separated list that specifies which loggers emit trace logs through the listed names. If the ! prefix is used, all of the loggers except the listed ones are set to the trace level. If * is used, the root logger is set to the trace level. |
-| `BOT_DEBUG` | In production | `true` or `false`, depending on whether to enable debug mode, affecting the behavior of certain features. `true` by default.
-| `REDIS_PASSWORD` | When not using FakeRedis | The password to connect to the Redis database (see [Optional: Working with Redis](#optional-working-with-redis)). |
-| `USE_METRICITY` | When using Metricity | `true` or `false`, depending on whether to enable metrics collection using Metricity (see [Optional: Working with Metricity](#optional-working-with-metricity)). `false` by default. |
-| `GITHUB_API_KEY` | When you wish to interact with GitHub | The API key to interact with GitHub, for example to download files for the branding manager.
-| `METABASE_USERNAME` | When you wish to interact with Metabase | The username for a Metabase admin account.
-| `METABASE_PASSWORD` | When you wish to interact with Metabase | The password for a Metabase admin account.
-
-Have fun!
+| Variable | Required | Description |
+|----------------------|---------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `BOT_TOKEN` | Always | Your Discord bot account's token (see [Set Up a Bot Account](#set-up-a-bot-account)). |
+| `GUILD_ID` | Always | Your Discord test server's id (see [Set Up a Test Server](#set-up-a-test-server)). |
+| `BOT_PREFIX` | When you wish to use a prefix different than "!" | Your Discord bot command's prefix. |
+| `API_KEYS_SITE_API` | When running bot without Docker | Used to authenticate with the site's API. When using Docker to run the bot, this is automatically set. By default, the site will always have the API key shown in the example below. |
+| `BOT_SENTRY_DSN` | When connecting the bot to sentry | The DSN of the sentry monitor. |
+| `BOT_TRACE_LOGGERS ` | When you wish to see specific or all trace logs | Comma separated list that specifies which loggers emit trace logs through the listed names. If the ! prefix is used, all of the loggers except the listed ones are set to the trace level. If * is used, the root logger is set to the trace level. |
+| `DEBUG` | In production | `true` or `false`, depending on whether to enable debug mode, affecting the behavior of certain features. `true` by default. |
+| `REDIS_PASSWORD` | When not using FakeRedis | The password to connect to the Redis database (see [Staring Redis with other methods](../bot-extended-configuration-options#starting-redis-using-other-methods)). |
+| `USE_METRICITY` | When using Metricity | `true` or `false`, depending on whether to enable metrics collection using Metricity (see [Working with Metricity](../bot-extended-configuration-options#working-with-metricity)). `false` by default. |
+| `API_KEYS_GITHUB` | When you wish to interact with GitHub | The API key to interact with GitHub, for example to download files for the branding manager. |
+| `METABASE_USERNAME` | When you wish to interact with Metabase | The username for a Metabase admin account. |
+| `METABASE_PASSWORD` | When you wish to interact with Metabase | The password for a Metabase admin account. |
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/commit-messages.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/commit-messages.md
new file mode 100644
index 00000000..ba476b65
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/commit-messages.md
@@ -0,0 +1,15 @@
+---
+title: Writing Good Commit Messages
+description: Information about logging in our projects.
+---
+
+A well-structured git log is key to a project's maintainability; it provides insight into when and *why* things were done for future maintainers of the project.
+
+Commits should be as narrow in scope as possible.
+Commits that span hundreds of lines across multiple unrelated functions and/or files are very hard for maintainers to follow.
+After about a week they'll probably be hard for you to follow, too.
+
+Please also avoid making minor commits for fixing typos or linting errors.
+[Don’t forget to lint before you push!](https://soundcloud.com/lemonsaurusrex/lint-before-you-push)
+
+A more in-depth guide to writing great commit messages can be found in Chris Beam's [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/).
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines.md
index de1777f2..d1e4250d 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines.md
@@ -4,22 +4,15 @@ description: Guidelines to adhere to when contributing to our projects.
---
Thank you for your interest in our projects!
+This page contains the golden rules to follow when contributing. If you have questions about how to get started contributing, check out our [in-depth walkthrough](../../contributing/).
-If you are interested in contributing, **this page contains the golden rules to follow when contributing.**
-Supplemental information [can be found here](./supplemental-information/).
-Do note that failing to comply with our guidelines may lead to a rejection of the contribution.
-
-If you are confused by any of these rules, feel free to ask us in the `#dev-contrib` channel in our [Discord server.](https://discord.gg/python)
-
-# The Golden Rules of Contributing
-
-1. **Lint before you push.** We have simple but strict style rules that are enforced through linting.
-You must always lint your code before committing or pushing.
-[Using tools](./supplemental-information/#linting-and-pre-commit) such as `flake8` and `pre-commit` can make this easier.
-Make sure to follow our [style guide](../style-guide/) when contributing.
+1. **Lint before you push.**
+We have simple but strict style rules that are enforced through linting.
+[Set up a pre-commit hook](../linting/) to lint your code when you commit it.
+Not all of the style rules are enforced by linting, so make sure to read the [style guide](../style-guide/) as well.
2. **Make great commits.**
Great commits should be atomic, with a commit message explaining what and why.
-More on that can be found in [this section](./supplemental-information/#writing-good-commit-messages).
+Check out [Writing Good Commit Messages](../commit-messages/) for details.
3. **Do not open a pull request if you aren't assigned to the issue.**
If someone is already working on it, consider offering to collaborate with that person.
4. **Use assets licensed for public use.**
@@ -28,4 +21,8 @@ Whenever the assets are images, audio or even code, they must have a license com
We aim to foster a welcoming and friendly environment on our open source projects.
We take violations of our Code of Conduct very seriously, and may respond with moderator action.
-Welcome to our projects!
+<br/>
+
+Failing to comply with our guidelines may lead to a rejection of the contribution.
+If you have questions about any of the rules, feel free to ask us in the [`#dev-contrib`](https://discord.gg/2h3qBv8Xaa) channel in our [Discord server](https://discord.gg/python).
+{: .notification .is-warning }
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines/_info.yml b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines/_info.yml
deleted file mode 100644
index 80c8e772..00000000
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines/_info.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-title: Contributing Guidelines
-description: Guidelines to adhere to when contributing to our projects.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines/supplemental-information.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines/supplemental-information.md
deleted file mode 100644
index e64e4fc6..00000000
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/contributing-guidelines/supplemental-information.md
+++ /dev/null
@@ -1,99 +0,0 @@
----
-title: Supplemental Information
-description: Additional information related to our contributing guidelines.
----
-
-This page contains additional information concerning a specific part of our development pipeline.
-
-## Writing Good Commit Messages
-
-A well-structured git log is key to a project's maintainability; it provides insight into when and *why* things were done for future maintainers of the project.
-
-Commits should be as narrow in scope as possible.
-Commits that span hundreds of lines across multiple unrelated functions and/or files are very hard for maintainers to follow.
-After about a week they'll probably be hard for you to follow, too.
-
-Please also avoid making minor commits for fixing typos or linting errors.
-*[Don’t forget to lint before you push!](https://soundcloud.com/lemonsaurusrex/lint-before-you-push)*
-
-A more in-depth guide to writing great commit messages can be found in Chris Beam's *[How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/).*
-
-## Code Style
-
-All of our projects have a certain project-wide style that contributions should attempt to maintain consistency with.
-During PR review, it's not unusual for style adjustments to be requested.
-
-[This page](../../style-guide/) will reference the differences between our projects and what is recommended by [PEP 8.](https://www.python.org/dev/peps/pep-0008/)
-
-## Linting and Pre-commit
-
-On most of our projects, we use `flake8` and `pre-commit` to ensure that the code style is consistent across the code base.
-
-Running `flake8` will warn you about any potential style errors in your contribution.
-You must always check it **before pushing**.
-Your commit will be rejected by the build server if it fails to lint.
-
-**Some style rules are not enforced by flake8. Make sure to read the [style guide](../../style-guide/).**
-
-`pre-commit` is a powerful tool that helps you automatically lint before you commit.
-If the linter complains, the commit is aborted so that you can fix the linting errors before committing again.
-That way, you never commit the problematic code in the first place!
-
-Please refer to the project-specific documentation to see how to setup and run those tools.
-In most cases, you can install pre-commit using `poetry run task precommit`, and lint using `poetry run task lint`.
-
-## Type Hinting
-
-[PEP 484](https://www.python.org/dev/peps/pep-0484/) formally specifies type hints for Python functions, added to the Python Standard Library in version 3.5.
-Type hints are recognized by most modern code editing tools and provide useful insight into both the input and output types of a function, preventing the user from having to go through the codebase to determine these types.
-
-For example:
-
-```python
-import typing
-
-def foo(input_1: int, input_2: typing.Dict[str, str]) -> bool:
- ...
-```
-
-This tells us that `foo` accepts an `int` and a `dict`, with `str` keys and values, and returns a `bool`.
-
-If the project is running Python 3.9 or above, you can use `dict` instead of `typing.Dict`.
-See [PEP 585](https://www.python.org/dev/peps/pep-0585/) for more information.
-
-All function declarations should be type hinted in code contributed to the PyDis organization.
-
-## Logging
-
-Instead of using `print` statements for logging, we use the built-in [`logging`](https://docs.python.org/3/library/logging.html) module.
-Here is an example usage:
-
-```python
-import logging
-
-log = logging.getLogger(__name__) # Get a logger bound to the module name.
-# This line is usually placed under the import statements at the top of the file.
-
-log.trace("This is a trace log.")
-log.warning("BEEP! This is a warning.")
-log.critical("It is about to go down!")
-```
-
-Print statements should be avoided when possible.
-Our projects currently defines logging levels as follows, from lowest to highest severity:
-
-- **TRACE:** These events should be used to provide a *verbose* trace of every step of a complex process. This is essentially the `logging` equivalent of sprinkling `print` statements throughout the code.
-- **Note:** This is a PyDis-implemented logging level. It may not be available on every project.
-- **DEBUG:** These events should add context to what's happening in a development setup to make it easier to follow what's going while workig on a project. This is in the same vein as **TRACE** logging but at a much lower level of verbosity.
-- **INFO:** These events are normal and don't need direct attention but are worth keeping track of in production, like checking which cogs were loaded during a start-up.
-- **WARNING:** These events are out of the ordinary and should be fixed, but can cause a failure.
-- **ERROR:** These events can cause a failure in a specific part of the application and require urgent attention.
-- **CRITICAL:** These events can cause the whole application to fail and require immediate intervention.
-
-Any logging above the **INFO** level will trigger a [Sentry](https://sentry.io) issue and alert the Core Developer team.
-
-## Draft Pull Requests
-
-Github [provides a PR feature](https://github.blog/2019-02-14-introducing-draft-pull-requests/) that allows the PR author to mark it as a Draft when opening it. This provides both a visual and functional indicator that the contents of the PR are in a draft state and not yet ready for formal review.
-
-This feature should be utilized in place of the traditional method of prepending `[WIP]` to the PR title.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/creating-bot-account.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/creating-bot-account.md
index ee38baa3..51da3f34 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/creating-bot-account.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/creating-bot-account.md
@@ -9,9 +9,9 @@ icon: fab fa-discord
4. Change your bot's `Public Bot` setting off so only you can invite it, save, and then get your **Bot Token** with the `Copy` button.
> **Note:** **DO NOT** post your bot token anywhere public. If you do it can and will be compromised.
5. Save your **Bot Token** somewhere safe to use in the project settings later.
-6. In the `General Information` tab, grab the **Client ID**.
-7. Replace `<CLIENT_ID_HERE>` in the following URL and visit it in the browser to invite your bot to your new test server.
+6. In the `General Information` tab, grab the **Application ID**.
+7. Replace `<APPLICATION_ID_HERE>` in the following URL and visit it in the browser to invite your bot to your new test server.
```plaintext
-https://discordapp.com/api/oauth2/authorize?client_id=<CLIENT_ID_HERE>&permissions=8&scope=bot
+https://discordapp.com/api/oauth2/authorize?client_id=<APPLICATION_ID_HERE>&permissions=8&scope=bot
```
Optionally, you can generate your own invite url in the `OAuth` tab, after selecting `bot` as the scope.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md
index bba5722d..2da88b61 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/hosts-file.md
@@ -8,7 +8,7 @@ toc: 3
# What's a hosts file?
The hosts file maps a hostname/domain to an IP address, allowing you to visit a given domain on your browser and have it resolve by your system to the given IP address, even if it's pointed back to your own system or network.
-When staging a local [Site](https://pythondiscord.com/pages/contributing/site/) project, you may want to add an entries to your hosts file so you can visit the site with the domain `http://pythondiscord.local`. This is purely for convenience, and you can use `localhost` or `127.0.0.1` instead if you prefer.
+When staging a local [Site](https://pythondiscord.com/pages/guides/pydis-guides/contributing/site/) project, you may want to add an entries to your hosts file so you can visit the site with the domain `http://pythondiscord.local`. This is purely for convenience, and you can use `localhost` or `127.0.0.1` instead if you prefer.
# What to add
You would add the following entry to your hosts file.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/linting.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/linting.md
new file mode 100644
index 00000000..b634f513
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/linting.md
@@ -0,0 +1,14 @@
+---
+title: Linting
+description: A guide for linting and setting up pre-commit.
+---
+
+Your commit will be rejected by the build server if it fails to lint.
+On most of our projects, we use `ruff` and `pre-commit` to ensure that the code style is consistent across the code base.
+
+`pre-commit` is a powerful tool that helps you automatically lint before you commit.
+If the linter complains, the commit is aborted so that you can fix the linting errors before committing again.
+That way, you never commit the problematic code in the first place!
+
+Please refer to the project-specific documentation to see how to setup and run those tools.
+In most cases, you can install pre-commit using `poetry run task precommit`, and lint using `poetry run task lint` in the console.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/logging.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/logging.md
new file mode 100644
index 00000000..1291a7a4
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/logging.md
@@ -0,0 +1,31 @@
+---
+title: Logging
+description: Information about logging in our projects.
+---
+
+Instead of using `print` statements for logging, we use the built-in [`logging`](https://docs.python.org/3/library/logging.html) module.
+Here is an example usage:
+
+```python
+import logging
+
+log = logging.getLogger(__name__) # Get a logger bound to the module name.
+# This line is usually placed under the import statements at the top of the file.
+
+log.trace("This is a trace log.")
+log.warning("BEEP! This is a warning.")
+log.critical("It is about to go down!")
+```
+
+Print statements should be avoided when possible.
+Our projects currently defines logging levels as follows, from lowest to highest severity:
+
+- **TRACE:** These events should be used to provide a *verbose* trace of every step of a complex process. This is essentially the `logging` equivalent of sprinkling `print` statements throughout the code.
+- **Note:** This is a PyDis-implemented logging level. It may not be available on every project.
+- **DEBUG:** These events should add context to what's happening in a development setup to make it easier to follow what's going while workig on a project. This is in the same vein as **TRACE** logging but at a much lower level of verbosity.
+- **INFO:** These events are normal and don't need direct attention but are worth keeping track of in production, like checking which cogs were loaded during a start-up.
+- **WARNING:** These events are out of the ordinary and should be fixed, but can cause a failure.
+- **ERROR:** These events can cause a failure in a specific part of the application and require urgent attention.
+- **CRITICAL:** These events can cause the whole application to fail and require immediate intervention.
+
+Any logging above the **INFO** level will trigger a [Sentry](https://sentry.io) issue and alert the Core Developer team.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/pull-requests.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/pull-requests.md
new file mode 100644
index 00000000..d193a455
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/pull-requests.md
@@ -0,0 +1,40 @@
+---
+title: Pull Requests
+description: A guide for opening pull requests.
+---
+
+As stated in our [Contributing Guidelines](../contributing-guidelines/), do not open a pull request if you aren't assigned to an approved issue. You can check out our [Issues Guide](../issues/) for help with opening an issue or getting assigned to an existing one.
+{: .notification .is-warning }
+
+Before opening a pull request you should have:
+
+1. Committed your changes to your local repository
+2. [Linted](../linting/) your code
+3. Tested your changes
+4. Pushed the branch to your fork of the project on GitHub
+
+## Opening a Pull Request
+
+Navigate to your fork on GitHub and make sure you're on the branch with your changes. Click on `Contribute` and then `Open pull request`:
+
+![Pull Request UI](/static/images/content/contributing/pull_request.png)
+
+In the page that it opened, write an overview of the changes you made and why. This should explain how you resolved the issue that spawned this PR and highlight any differences from the proposed implementation. You should also [link the issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
+
+At this stage you can also request reviews from individual contributors. If someone showed interest in the issue or has specific knowledge about it, they may be a good reviewer. It isn't necessary to request your reviewers; someone will review your PR either way.
+
+## The Review Process
+
+Before your changes are merged, your PR needs to be reviewed by other contributors. They will read the issue and your description of your PR, look at your code, test it, and then leave comments on the PR if they find any problems, possibly with suggested changes. Sometimes this can feel intrusive or insulting, but remember that the reviewers are there to help you make your code better.
+
+#### If the PR is already open, how do I make changes to it?
+
+A pull request is between a source branch and a target branch. Updating the source branch with new commits will automatically update the PR to include those commits; they'll even show up in the comment thread of the PR. Sometimes for small changes the reviewer will even write the suggested code themself, in which case you can simply accept them with the click of a button.
+
+If you truly disagree with a reviewer's suggestion, leave a reply in the thread explaining why or proposing an alternative change. Also feel free to ask questions if you want clarification about suggested changes or just want to discuss them further.
+
+## Draft Pull Requests
+
+GitHub [provides a PR feature](https://github.blog/2019-02-14-introducing-draft-pull-requests/) that allows the PR author to mark it as a draft when opening it. This provides both a visual and functional indicator that the contents of the PR are in a draft state and not yet ready for formal review. This is helpful when you want people to see the changes you're making before you're ready for the final pull request.
+
+This feature should be utilized in place of the traditional method of prepending `[WIP]` to the PR title.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot.md
index e3cd8f0c..7861c3d9 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot.md
@@ -5,21 +5,24 @@ icon: fab fa-github
toc: 1
---
-> Before contributing, please ensure you read the [contributing guidelines](../contributing-guidelines) in full.
+You should have already forked the [`sir-lancebot`](https://github.com/python-discord/sir-lancebot) repository and cloned it to your local machine. If not, check out our [detailed walkthrough](../#1-fork-and-clone-the-repo).
----
-# Requirements
-- [Python 3.9](https://www.python.org/downloads/)
+Remember to ensure that you have read the [contributing guidelines](../contributing-guidelines) in full before you start contributing.
+
+### Requirements
+- [Python 3.10.*](https://www.python.org/downloads/)
- [Poetry](https://github.com/python-poetry/poetry#installation)
- [Git](https://git-scm.com/downloads)
- [Windows Installer](https://git-scm.com/download/win)
- [MacOS Installer](https://git-scm.com/download/mac) or `brew install git`
- [Linux](https://git-scm.com/download/linux)
+---
+
## Using Gitpod
Sir Lancebot can be edited and tested on Gitpod. Gitpod will automatically install the correct dependencies and Python version, so you can get straight to coding.
-To do this, you will need a Gitpod account, which you can get [here](https://www.gitpod.io/#get-started), and a fork of Sir Lancebot. This guide covers forking the repository [here](#fork-the-project).
+To do this, you will need a Gitpod account, which you can get [here](https://www.gitpod.io/#get-started), and a fork of Sir Lancebot. This guide covers forking the repository [here](../forking-repository).
Afterwards, click on [this link](https://gitpod.io/#/github.com/python-discord/sir-lancebot) to spin up a new workspace for Sir Lancebot. Then run the following commands in the terminal after the existing tasks have finished running:
```sh
@@ -41,19 +44,8 @@ The requirements for Docker are:
* This is only a required step for linux. Docker comes bundled with docker-compose on Mac OS and Windows.
---
-
-# Fork the Project
-You will need your own remote (online) copy of the project repository, known as a *fork*.
-
-- [**Learn how to create a fork of the repository here.**](../forking-repository)
-
-You will do all your work in the fork rather than directly in the main repository.
-
----
-
# Development Environment
-1. Once you have your fork, you will need to [**clone the repository to your computer**](../cloning-repository).
-2. After cloning, proceed to [**install the project's dependencies**](../installing-project-dependencies). (This is not required if using Docker)
+If you aren't using Docker, you will need to [install the project's dependencies](../installing-project-dependencies) yourself.
---
# Test Server and Bot Account
@@ -65,9 +57,10 @@ You will need your own test server and bot account on Discord to test your chang
3. Create the following text channels:
* `#announcements`
* `#dev-log`
- * `#sir-lancebot-commands`
+ * `#sir-lancebot-playground`
4. Create the following roles:
- * `@Admin`
+ * `@Admins`
+ * `@Helpers`
5. Note down the IDs for your server, as well as any channels and roles created.
* [**Learn how to obtain the ID of a server, channel or role here.**](../setting-test-server-and-bot-account#obtain-the-ids)
@@ -80,21 +73,21 @@ You will have to setup environment variables:
The following variables are needed for running Sir Lancebot:
-| Environment Variable | Description |
-| -------- | -------- |
-| `BOT_TOKEN` | Bot Token from the [Discord developer portal](https://discord.com/developers/applications) |
-| `BOT_GUILD` | ID of the Discord Server |
-| `BOT_ADMIN_ROLE_ID` | ID of the role `@Admins` |
-| `ROLE_HELPERS` | ID of the role `@Helpers` |
-| `CHANNEL_ANNOUNCEMENTS` | ID of the `#announcements` channel |
-| `CHANNEL_DEVLOG` | ID of the `#dev-log` channel |
-| `CHANNEL_COMMUNITY_BOT_COMMANDS` | ID of the `#sir-lancebot-commands` channel |
+| Environment Variable | Description |
+|------------------------------------|--------------------------------------------------------------------------------------------|
+| `CLIENT_TOKEN` | Bot Token from the [Discord developer portal](https://discord.com/developers/applications) |
+| `CLIENT_GUILD` | ID of the Discord Server |
+| `ROLES_ADMIN` | ID of the role `@Admins` |
+| `ROLES_HELPERS` | ID of the role `@Helpers` |
+| `CHANNELS_ANNOUNCEMENTS` | ID of the `#announcements` channel |
+| `CHANNELS_DEVLOG` | ID of the `#dev-log` channel |
+| `CHANNELS_SIR_LANCEBOT_PLAYGROUND` | ID of the `#sir-lancebot-playground` channel |
-[**Full environment variable reference for this project.**](./env-var-reference)
+[**Full environment variable reference for this project.**](../sir-lancebot/env-var-reference)
---
-While not required, we advise you set `USE_FAKEREDIS` to `true` in development to avoid the need of setting up a Redis server.
+While not required, we advise you set `REDIS_USE_FAKEREDIS` to `true` in development to avoid the need of setting up a Redis server.
It does mean you may lose persistent data on restart but this is non-critical.
Otherwise, please see the below linked guide for Redis related variables.
{: .notification .is-warning }
@@ -104,11 +97,11 @@ Otherwise, please see the below linked guide for Redis related variables.
The sections below describe the two ways you can run this project. We recommend Docker as it requires less setup.
## Run with Docker
-Make sure to have Docker running, then use the Docker command `docker-compose up` in the project root.
+Make sure to have Docker running, then use the Docker command `docker compose up` in the project root.
The first time you run this command, it may take a few minutes while Docker downloads and installs Sir Lancebot's dependencies.
```shell
-$ docker-compose up
+$ docker compose up
```
If you get any Docker related errors, reference the [Possible Issues](../docker#possible-issues) section of the Docker page.
@@ -120,14 +113,11 @@ After installing project dependencies use the poetry command `poetry run task st
```shell
$ poetry run task start
```
-
---
-# Working with Git
-Now that you have everything setup, it is finally time to make changes to the bot! If you have not yet [read the contributing guidelines](https://github.com/python-discord/sir-lancebot/blob/main/CONTRIBUTING.md), now is a good time. Contributions that do not adhere to the guidelines may be rejected.
-
-Notably, version control of our projects is done using Git and Github. It can be intimidating at first, so feel free to ask for any help in the server.
+# Next steps
+Now that you have everything setup, it is finally time to make changes to the bot! If you have not yet read the [contributing guidelines](../contributing-guidelines.md), now is a good time. Contributions that do not adhere to the guidelines may be rejected.
-[**Click here to see the basic Git workflow when contributing to one of our projects.**](../working-with-git/)
+If you're not sure where to go from here, our [detailed walkthrough](../#2-set-up-the-project) is for you.
Have fun!
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot/env-var-reference.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot/env-var-reference.md
index 51587aac..342da12b 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot/env-var-reference.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/sir-lancebot/env-var-reference.md
@@ -6,70 +6,58 @@ toc: 2
## General Variables
The following variables are needed for running Sir Lancebot:
-| Environment Variable | Description |
-| -------- | -------- |
-| `BOT_TOKEN` | Bot Token from the [Discord developer portal](https://discord.com/developers/applications) |
-| `BOT_GUILD` | ID of the Discord Server |
-| `BOT_ADMIN_ROLE_ID` | ID of the role @Admins |
-| `ROLE_HELPERS` | ID of the role @Helpers |
-| `CHANNEL_ANNOUNCEMENTS` | ID of the #announcements channel |
-| `CHANNEL_DEVLOG` | ID of the #dev-log channel |
-| `CHANNEL_COMMUNITY_BOT_COMMANDS` | ID of the #sir-lancebot-commands channel |
-| `CHANNEL_REDDIT` | ID of the #reddit channel |
+| Environment Variable | Description |
+|------------------------------------|--------------------------------------------------------------------------------------------|
+| `CLIENT_TOKEN` | Bot Token from the [Discord developer portal](https://discord.com/developers/applications) |
+| `CLIENT_GUILD` | ID of the Discord Server |
+| `ROLES_ADMINS` | ID of the role `@Admins` |
+| `ROLES_HELPERS` | ID of the role `@Helpers` |
+| `CHANNELS_ANNOUNCEMENTS` | ID of the `#announcements` channel |
+| `CHANNELS_DEVLOG` | ID of the `#dev-log` channel |
+| `CHANNELS_SIR_LANCEBOT_PLAYGROUND` | ID of the `#sir-lancebot-commands` channel |
+| `CHANNELS_REDDIT` | ID of the `#reddit` channel |
---
## Debug Variables
Additionally, you may find the following environment variables useful during development:
-| Environment Variable | Description |
-| -------- | -------- |
-| `BOT_DEBUG` | Debug mode of the bot | False |
-| `PREFIX` | The bot's invocation prefix | `.` |
-| `CYCLE_FREQUENCY` | Amount of days between cycling server icon | 3 |
-| `MONTH_OVERRIDE` | Integer in range `[0, 12]`, overrides current month w.r.t. seasonal decorators |
-| `REDIS_HOST` | The address to connect to for the Redis database. |
-| `REDIS_PORT` | |
-| `REDIS_PASSWORD` | |
-| `USE_FAKEREDIS` | If the FakeRedis module should be used. Set this to true if you don't have a Redis database setup. |
-| `BOT_SENTRY_DSN` | The DSN of the sentry monitor. |
-| `TRASHCAN_EMOJI` | The full emoji to use for the trashcan. Format should be like the output of `\:emoji:`. |
-
+| Environment Variable | Description |
+|----------------------------|------------------------------------------------------------------------------------------------------------|
+| `CLIENT_DEBUG` | Debug mode of the bot |
+| `CLIENT_PREFIX` | The bot's invocation prefix |
+| `CLIENT_MONTH_OVERRIDE` | Integer in range `[0, 12]`, overrides current month w.r.t. seasonal decorators |
+| `REDIS_HOST` | The address to connect to for the Redis database. |
+| `REDIS_PORT` | The port on which the Redis database is exposed. |
+| `REDIS_PASSWORD` | The password to connect to the Redis database. |
+| `REDIS_USE_FAKEREDIS` | If the FakeRedis module should be used. Set this to true if you don't have a Redis database setup. |
+| `BOT_SENTRY_DSN` | The DSN of the sentry monitor. |
+| `TRASHCAN_EMOJI` | The full emoji to use for the trashcan. Format should be like the output of sending `\:emoji:` on discord. |
---
## Tokens/APIs
If you will be working with an external service, you might have to set one of these tokens:
-| Token | Description |
-| -------- | -------- |
-| `GITHUB_TOKEN` | Personal access token for GitHub, raises rate limits from 60 to 5000 requests per hour. |
-| `GIPHY_TOKEN` | Required for API access. [Docs](https://developers.giphy.com/docs/api) |
-| `OMDB_API_KEY` | Required for API access. [Docs](https://www.omdbapi.com/) |
-| `REDDIT_CLIENT_ID` | OAuth2 client ID for authenticating with the [reddit API](https://github.com/reddit-archive/reddit/wiki/OAuth2). |
-| `REDDIT_SECRET` | OAuth2 secret for authenticating with the reddit API. *Leave empty if you're not using the reddit API.* |
-| `REDDIT_WEBHOOK` | Webhook ID for Reddit channel |
-| `YOUTUBE_API_KEY` | An OAuth Key or Token are required for API access. [Docs](https://developers.google.com/youtube/v3/docs#calling-the-api) |
-| `TMDB_API_KEY` | Required for API access. [Docs](https://developers.themoviedb.org/3/getting-started/introduction) |
-| `NASA_API_KEY` | Required for API access. [Docs](https://api.nasa.gov/) |
-| `WOLFRAM_API_KEY` | |
-| `UNSPLASH_KEY` | Required for API access. Use the `access_token` given by Unsplash. [Docs](https://unsplash.com/documentation) |
-| `IGDB_CLIENT_ID` | OAuth2 client ID for authenticating with the [IGDB API](https://api-docs.igdb.com/) |
-| `IGDB_CLIENT_SECRET` | OAuth2 secret for authenticating with the IGDB API. *Leave empty if you're not using the IGDB API.* |
+| Token | Description |
+|-----------------------------|--------------------------------------------------------------------------------------------------------------------------|
+| `TOKENS_GITHUB` | Personal access token for GitHub, raises rate limits from 60 to 5000 requests per hour. |
+| `TOKENS_GIPHY` | Required for API access. [Docs](https://developers.giphy.com/docs/api) |
+| `REDDIT_CLIENT_ID` | OAuth2 client ID for authenticating with the [reddit API](https://github.com/reddit-archive/reddit/wiki/OAuth2). |
+| `REDDIT_SECRET` | OAuth2 secret for authenticating with the reddit API. *Leave empty if you're not using the reddit API.* |
+| `REDDIT_WEBHOOK` | Webhook ID for Reddit channel |
+| `TOKENS_YOUTUBE` | An OAuth Key or Token are required for API access. [Docs](https://developers.google.com/youtube/v3/docs#calling-the-api) |
+| `TOKENS_TMDB` | Required for API access. [Docs](https://developers.themoviedb.org/3/getting-started/introduction) |
+| `TOKENS_NASA` | Required for API access. [Docs](https://api.nasa.gov/) |
+| `WOLFRAM_KEY` | Required for API access. [Docs](https://products.wolframalpha.com/simple-api/documentation) |
+| `TOKENS_UNSPLASH` | Required for API access. Use the `access_token` given by Unsplash. [Docs](https://unsplash.com/documentation) |
+| `TOKENS_IGDB_CLIENT_ID` | OAuth2 client ID for authenticating with the [IGDB API](https://api-docs.igdb.com/) |
+| `TOKENS_IGDB_CLIENT_SECRET` | OAuth2 secret for authenticating with the IGDB API. *Leave empty if you're not using the IGDB API.* |
---
## Seasonal Cogs
These variables might come in handy while working on certain cogs:
-| Cog | Environment Variable | Description |
-| -------- | -------- | -------- |
-| Advent of Code | `AOC_LEADERBOARDS` | List of leaderboards separated by `::`. Each entry should have an `id,session cookie,join code` separated by commas in that order. |
-| Advent of Code | `AOC_STAFF_LEADERBOARD_ID` | Integer ID of the staff leaderboard. |
-| Advent of Code | `AOC_ROLE_ID` | ID of the advent of code role.
-| Advent of Code | `AOC_IGNORED_DAYS` | Comma separated list of days to ignore while calculating score. |
-| Advent of Code | `AOC_YEAR` | Debug variable to change the year used for AoC. |
-| Advent of Code | `AOC_CHANNEL_ID` | The ID of the #advent-of-code channel |
-| Advent of Code | `AOC_COMMANDS_CHANNEL_ID` | The ID of the #advent-of-code-commands channel |
-| Advent of Code | `AOC_FALLBACK_SESSION` | |
-| Advent of Code | `AOC_SESSION_COOKIE` | |
-| Valentines | `LOVEFEST_ROLE_ID` | |
-| Wolfram | `WOLFRAM_USER_LIMIT_DAY` | |
-| Wolfram | `WOLFRAM_GUILD_LIMIT_DAY` | |
+| Cog | Environment Variable | Description |
+|------------|---------------------------|------------------------------------------------------------------|
+| Valentines | `ROLES_LOVEFEST` | ID of the role `@Lovefest` |
+| Wolfram | `WOLFRAM_USER_LIMIT_DAY` | The amount of requests a user can make per day |
+| Wolfram | `WOLFRAM_GUILD_LIMIT_DAY` | The amount of requests that can come from the same guild per day |
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md
index f2c3bd95..1927f449 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/site.md
@@ -5,9 +5,11 @@ icon: fab fa-github
toc: 1
---
-# Requirements
+You should have already forked the [`site`](https://github.com/python-discord/site) repository and cloned it to your local machine. If not, check out our [detailed walkthrough](../#1-fork-and-clone-the-repo).
-- [Python 3.9](https://www.python.org/downloads/)
+### Requirements
+
+- [Python 3.11](https://www.python.org/downloads/)
- [Poetry](https://python-poetry.org/docs/#installation)
- `pip install poetry`
- [Git](https://git-scm.com/downloads)
@@ -27,22 +29,9 @@ Without Docker:
- Note that if you wish, the webserver can run on the host and still use Docker for PostgreSQL.
---
-# Fork the project
-
-You will need access to a copy of the git repository of your own that will allow you to edit the code and push your commits to.
-Creating a copy of a repository under your own account is called a _fork_.
-
-- [Learn how to create a fork of the repository here.](../forking-repository/)
-
-This is where all your changes and commits will be pushed to, and from where your PRs will originate from.
-
-For any Core Developers, since you have write permissions already to the original repository, you can just create a feature branch to push your commits to instead.
-
----
# Development environment
-1. [Clone your fork to a local project directory](../cloning-repository/)
-2. [Install the project's dependencies](../installing-project-dependencies/)
+[Install the project's dependencies](../installing-project-dependencies/)
## Without Docker
@@ -64,7 +53,7 @@ CREATE DATABASE pysite WITH OWNER pysite;
CREATE DATABASE metricity WITH OWNER pysite;
```
-Finally, enter `/q` to exit psql.
+Finally, enter `\q` to exit psql.
### 2. Environment variables
@@ -84,7 +73,7 @@ detailed information about these settings.
#### Notes regarding `DATABASE_URL`
- If the database is hosted locally i.e. on the same machine as the webserver, then use `localhost` for the host. Windows and macOS users may need to use the [Docker host IP](https://stackoverflow.com/questions/22944631/how-to-get-the-ip-address-of-the-docker-host-from-inside-a-docker-container) instead.
-- If the database is running in Docker, use port `7777`. Otherwise, use `5432` as that is the default port used by PostegreSQL.
+- If the database is running in Docker, use port `7777`. Otherwise, use `5432` as that is the default port used by PostgreSQL.
- If you configured PostgreSQL in a different manner or you are not hosting it locally, then you will need to determine the correct host and port yourself.
The user, password, and database name should all still be `pysite` unless you deviated from the setup instructions in the previous section.
@@ -103,7 +92,7 @@ docker-compose up
The `-d` option can be appended to the command to run in detached mode. This runs the containers in the background so the current terminal session is available for use with other things.
-If you get any Docker related errors, reference the [Possible Issues](https://pythondiscord.com/pages/contributing/docker/#possible-issues") section of the Docker page.
+If you get any Docker related errors, reference the [Possible Issues](https://pythondiscord.com/pages/guides/pydis-guides/contributing/docker/#possible-issues") section of the Docker page.
{: .notification .is-warning }
## Run on the host
@@ -140,6 +129,14 @@ Unless you are editing the Dockerfile or docker-compose.yml, you shouldn't need
[**Click here to see the basic Git workflow when contributing to one of our projects.**](../working-with-git/)
---
+# Deploy previews
+
+When you open a pull request, the `netlify` bot will build and publish a static
+preview of your changes, which is very valuable if you made any changes to the
+content or styling of the website. An example deploy preview can be found on
+[pull request #773](https://github.com/python-discord/site/pull/773#issuecomment-1257224147).
+
+---
# Django admin site
Django provides an interface for administration with which you can view and edit the models among other things.
@@ -178,3 +175,17 @@ The website is configured through the following environment variables:
- **`STATIC_ROOT`**: The root in which `python manage.py collectstatic`
collects static files. Optional, defaults to `/app/staticfiles` for the
standard Docker deployment.
+
+---
+
+# Next steps
+Now that you have everything setup, it is finally time to make changes to the site! If you have not yet read the [contributing guidelines](../contributing-guidelines.md), now is a good time. Contributions that do not adhere to the guidelines may be rejected.
+
+If you're not sure where to go from here, our [detailed walkthrough](../#2-set-up-the-project), or the [guide on contributing a page](../../how-to-contribute-a-page) is for you.
+
+The site repository also contains `README.md` files in all major directories of
+interest, which explain where which functionality of the site is located. For
+example, see the [API app's
+README](https://github.com/python-discord/site/tree/main/pydis_site/apps/api).
+
+Have fun!
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/style-guide.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/style-guide.md
index f9962990..b26c467c 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/style-guide.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/style-guide.md
@@ -191,21 +191,14 @@ Present tense defines that the work being done is now, in the present, rather th
**Use:** "Build an information embed."<br>
**Don't use:** "Built an information embed." or "Will build an information embed."
-# Type Annotations
-Functions are required to have type annotations as per the style defined in [PEP 484](https://www.python.org/dev/peps/pep-0484/).
+# Type Hinting
+Functions are required to have type annotations as per the style defined in [PEP 484](https://www.python.org/dev/peps/pep-0484/). Type hints are recognized by most modern code editing tools and provide useful insight into both the input and output types of a function, preventing the user from having to go through the codebase to determine these types.
-A function without annotations might look like:
-```py
-def divide(a, b):
- """Divide the two given arguments."""
- return a / b
-```
-
-With annotations, the arguments and the function are annotated with their respective types:
-```py
-def divide(a: int, b: int) -> float:
- """Divide the two given arguments."""
- return a / b
+A function with type hints looks like:
+```python
+def foo(input_1: int, input_2: dict[str, int]) -> bool:
+ ...
```
+This tells us that `foo` accepts an `int` and a `dict`, with `str` keys and `int` values, and returns a `bool`.
In previous examples, we have purposely omitted annotations to keep focus on the specific points they represent.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/working-with-git.md b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/working-with-git.md
index 26c89b56..59c57859 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/contributing/working-with-git.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/contributing/working-with-git.md
@@ -19,5 +19,7 @@ Below are links to regular workflows for working with Git using PyCharm or the C
**Resources to learn Git**
* [The Git Book](https://git-scm.com/book)
-* [Corey Schafer's Youtube Tutorials](https://www.youtube.com/watch?v=HVsySz-h9r4&list=PL-osiE80TeTuRUfjRe54Eea17-YfnOOAx)
-* [GitHub Git Resources Portal](https://try.github.io/)
+* [Corey Schafer's YouTube tutorials](https://www.youtube.com/watch?v=HVsySz-h9r4&list=PL-osiE80TeTuRUfjRe54Eea17-YfnOOAx)
+* [GitHub Git resources portal](https://try.github.io/)
+* [Git cheatsheet](https://education.github.com/git-cheat-sheet-education.pdf)
+* [Learn Git branching](https://learngitbranching.js.org)
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md b/pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md
index 8b7c5584..bef2df9b 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md
@@ -6,73 +6,100 @@ relevant_links:
Asking Good Questions: ../asking-good-questions
Role Guide: /pages/server-info/roles
Helping Others: ../helping-others
+toc: 3
---
-On the 5th of April 2020, we introduced a new help channel system at Python Discord. This article is a supplementary guide to explain precisely where to go to find help.
-
-We have two different kinds of help channels in our community - **Topical help channels**, and **general help channels**.
-Where you should go depends on what you need help with.
-These channels also attract different helpers, and move at different speeds, which affects the kind of help you're likely to receive, and how fast you get that help.
+At Python Discord we have two different kinds of help channels: **topical help channels** and **help forum posts**.
# Topical Help Channels
-The topical help channels move at a slower pace than the general help channels.
-They also sometimes attract domain experts - for example, `#async-and-concurrency` has CPython contributors who helped write asyncio, and in `#game-development` you can find the creators and maintainers of several game frameworks.
+In topical channels, users can ask for help regarding specific domains or areas of Python.
+These channels also sometimes attract domain experts. For example, `#async-and-concurrency` has CPython contributors who helped write asyncio, and in `#game-development` you can find the creators and maintainers of several game frameworks.
If your question fits into the domain of one of our topical help channels, and if you're not in a big hurry, then this is probably the best place to ask for help.
![List of topical help channels](/static/images/content/help_channels/topical_channels.png)
-Some of the topical help channels have a broad scope, so they can cover many (somewhat) related topics.
+Some of the topical help channels have a broad scope, so they can cover many related topics.
For example, `#data-science-and-ai` covers scientific Python, statistics, and machine learning, while `#algos-and-data-structs` covers everything from data structures and algorithms to maths.
-To help you navigate this, we've added a list of suggested topics in the topic of every channel.
-If you're not sure where to post, feel free to ask us which channel is relevant for a topic in `#community-meta`.
+Each channel on the server has a channel description which briefly describes the topics covered by that channel. If you're not sure where to post, feel free to ask us which channel is appropriate in `#community-meta`.
+
+# Help Forum Posts
+
+Help forum posts can be used for all Python-related help, and have the advantage of attracting a more diverse spectrum of helpers. There is also the added benefit of receiving individual focus and attention on your question. These posts are a great choice for generic Python help, but can be used for domain-specific Python help as well.
-# General Help Channels
+## How to Create A New Post
-Our general help channels move at a fast pace, and attract a far more diverse spectrum of helpers.
-This is a great choice for a generic Python question, and a good choice if you need an answer as soon as possible.
-It's particularly important to [ask good questions](../asking-good-questions) when asking in these channels, or you risk not getting an answer and having your help channel be claimed by someone else.
+There are 4 easy needed steps to make this happen
-## How To Claim a Channel
+1. Navigate to the **Python Help System** category.<br>
+![Python help system category](/static/images/content/help_channels/help-system-category.png)
+2. Open the **python-help** forum channel.
+3. Click on the **New Post** button in the top-right corner.<br>
+![New post button](/static/images/content/help_channels/new-post-button.png)
+4. Choose a brief title that best describes your issue, along with a message explaining it more in details, and **post** it.
+Note that you can also choose one or more tags which can help attract experts of that tag easily.<br>
+![New post form](/static/images/content/help_channels/new-post-form.png)
-There are always 3 available help channels waiting to be claimed in the **Python Help: Available** category.
+Be sure to [ask questions with enough information](../asking-good-questions) in order to give yourself the best chances of getting help!
-![Available help channels](/static/images/content/help_channels/available_channels.png)
+At this point you will have the **Help Cooldown** role which will remain on your profile until you close your newly created post. This ensures that users can only have one post at any given time, giving everyone a chance to have their question seen.
-In order to claim one, simply start typing your question into one of these channels. Once your question has been posted, you have claimed this channel, and the channel will be moved down to the **Python Help: Occupied** category.
+# Frequently Asked Questions
-If you're unable to type into these channels, this means you're currently **on cooldown**. In order to prevent someone from claiming all the channels for themselves, **we only allow someone to claim a new help channel every 15 minutes**. However, if you close your help channel using the `!dormant` command, this cooldown is reset early.
+### I created a new help post, what happens now?
+Once you click on `Post`, these events take place:<br>
+1. A new channel will be created for you, and you'll have an `OP` next to you username, which tells people you're the `Original Poster`, or in other words, the owner of the help topic in that channel.<br>
+2. Your original question/message will always be the first one in that channel.<br>
+3. Our Python bot will send a message reminding you of what you should include in your question/message in case you could have missed anything.<br>
+4. People will be able to jump on that channel, and you can have a discussion with anyone who's volunteering to help you by asking as many followup questions as you want.<br>
-![Channel available message](/static/images/content/help_channels/available_message.png)
-*This message is always posted when a channel becomes available for use.*
+#### Example
+Suppose we're trying to find the minimum value in a list of integers.
+Once we've chosen our title and message content, we are ready to make a new post.<br><br>
+![Filled form example](/static/images/content/help_channels/question-example.png)<br><br>
+Note how we've checked the **Algos & data structs** tag here, whose circumference is highlighted in blue, since this is a question about an algorithm to find the minimum.<br>
+This will greatly help others pinpoint where they can help you best based on a combination of your title and tag from a first glance.<br><br>
+Once you click on post, a new channel is created, and you can see the original message on top along with the `OP` tag next to the poster's avatar.<br>
+You will also see the message that our Python bot sends instantly right after yours.<br><br>
+![Newly created thread example](/static/images/content/help_channels/newly-created-thread-example.png)
-## Q: For how long is the channel mine?
+### How long does my help post stay active?
-The channel is yours until it has been inactive for **30 minutes**. When this happens, we move the channel down to the **Python Help: Dormant** category, and make the channel read-only. After a while, the channel will be rotated back into **Python Help: Available** for the next question. Please try to resist the urge to continue bumping the channel so that it never gets marked as inactive. If nobody is answering your question, you should try to reformulate the question to increase your chances of getting help.
+The post remains open for **30 minutes** after your last message, or 10 minutes after the last message sent by another user (whichever time comes later).
![Channel dormant message](/static/images/content/help_channels/dormant_message.png)
-*You'll see this message in your channel when the channel is marked as inactive.*
+*You'll see this message in your post once it goes dormant.*
-## Q: I don't need my help channel anymore, my question was answered. What do I do?
+### No one answered my question. How come?
-Once you have finished with your help channel you or a staff member can run `!dormant`. This will move the channel to the **Python Help: Dormant** category where it will sit until it is returned to circulation. You will only be able to run the command if you claimed the channel from the available category, you cannot close channels belonging to others.
+The server has users active all over the world and all hours of the day, but some time periods are less active than others. It's also possible that the users that read your question didn't have the knowledge required to help you. If no one responded, feel free to open another post a little later, or try an appropriate topical channel.
-## Q: Are only Helpers supposed to answer questions?
+If you feel like your question is continuously being overlooked, read our guide on [asking good questions](../asking-good-questions) to increase your chances of getting a response.
-Absolutely not. We strongly encourage all members of the community to help answer questions. If you'd like to help answer some questions, simply head over to one of the help channels that are currently in use. These can be found in the **Python Help: Occupied** category.
+### My question was answered. What do I do?
-![Occupied help channels](/static/images/content/help_channels/occupied_channels.png)
+Go ahead and use one of the `!close` or `!solved` commands if you've satisfactorily solved your problem. You will only be able to run this command in your own post, and no one (outside of staff) will be able to close your post for you.
-Anyone can type in these channels, and users who are particularly helpful [may be offered a chance to join the staff on Python Discord](/pages/server-info/roles/#note-regarding-staff-roles).
+Closing your post once you are finished leads to less occupied ones, which means more attention can be given to other users that still need help.
-## Q: I lost my help channel!
+### Can only Helpers answer help questions?
-No need to panic.
-Your channel was probably just marked as dormant.
-All the dormant help channels are still available at the bottom of the channel list, in the **Python Help: Dormant** category, and also through search.
-If you're not sure what the name of your help channel was, you can easily find it by using the Discord Search feature.
-Try searching for `from:<your nickname>` to find the last messages sent by yourself, and from there you will be able to jump directly into the channel by pressing the Jump button on your message.
+Definitely not! We encourage all members of the community to participate in giving help. If you'd like to help answer some questions, you can either browse all posts in the **python-help** forum channel or head over to the **Topical Chat/Help** category.
+
+Before jumping in, please read our guide on [helping others](../helping-others) which explains our expectations for the culture and quailty of help that we aim for on the server.
+
+Tip: run the `!helpdm on` command in the `#bot-commands` channel to get notified via DM with jumplinks to help posts you're participating in.
-![Dormant help channels](/static/images/content/help_channels/dormant_channels.png)
-*The dormant help channels can be found at the bottom of the channel list.*
+### Can I save my help session for future reference?
+
+Yes! Because the help posts are only closed without being deleted, this means you can always refer to a previous help session if you found one particularly helpful.
+
+Tip: reply to a message and run the `.bm` command to get bookmarks sent to you via DM for future reference.
+
+### I lost my help post!
+
+No need to panic. Your post was probably just closed due to inactivity.
+All the dormant help posts are still available at the bottom of the **python-help** forum channel and also through search in the **Python Help System** category.
+If you're not sure what the title of your help post was, you can easily find it by using the Discord Search feature.
+Try searching for `from:<your nickname>` to find the last messages sent by yourself, and from there you will be able to jump directly into the channel by pressing the Jump button on your message.
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md b/pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md
index a7f1ce1d..9f0d947f 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md
@@ -9,7 +9,7 @@ relevant_links:
toc: 2
---
-Python Discord has a lot of people asking questions, be it in the help channels, topical channels, or any other part of the server.
+Python Discord has a lot of people asking questions, be it in the help forum, topical channels, or any other part of the server.
Therefore, you might sometimes want to give people the answers you have in mind.
But you might not be sure how best to approach the issue, or maybe you'd like to see how others handle it.
This article aims to present a few of the general principles which guide the staff on a day-to-day basis on the server.
@@ -64,7 +64,7 @@ At other times, it might not be as obvious, and it might be a good idea to kindl
The path is often more important than the answer.
Your goal should primarily be to allow the helpee to apply, at least to a degree, the concepts you introduce in your answer.
Otherwise, they might keep struggling with the same problem over and over again.
-That means that simply showing your answer might close the help channel for the moment, but won't be very helpful in the long-term.
+That means that simply showing your answer might close the help post for the moment, but won't be very helpful in the long-term.
A common approach is to walk the helpee through to an answer:
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md b/pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md
index 716250b1..65a402fd 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/how-to-contribute-a-page.md
@@ -3,8 +3,8 @@ title: How to Contribute a Page
description: Learn how to write and publish a page to this website.
icon: fas fa-info
relevant_links:
- Contributing to Site: https://pythondiscord.com/pages/contributing/site/
- Using Git: https://pythondiscord.com/pages/contributing/working-with-git/
+ Contributing to Site: https://pythondiscord.com/pages/guides/pydis-guides/contributing/site/
+ Using Git: https://pythondiscord.com/pages/guides/pydis-guides/contributing/working-with-git/
toc: 4
---
@@ -14,8 +14,8 @@ If you are interested in writing or modifying pages seen here on the site, follo
For further assistance and help with contributing pages, send a message to the `#dev-contrib` channel in the Discord server!
## Prerequisites
-Before working on a new page, you have to [setup the site project locally](https://pythondiscord.com/pages/contributing/site/).
-It is also a good idea to familiarize yourself with the [git workflow](https://pythondiscord.com/pages/contributing/working-with-git/), as it is part of the contribution workflow.
+Before working on a new page, you have to [setup the site project locally](https://pythondiscord.com/pages/guides/pydis-guides/contributing/site/).
+It is also a good idea to familiarize yourself with the [git workflow](https://pythondiscord.com/pages/guides/pydis-guides/contributing/working-with-git/), as it is part of the contribution workflow.
Additionally, please submit your proposed page or modification to a page as an [issue in the site repository](https://github.com/python-discord/site/issues), or discuss it in the `#dev-contrib` channel in the server.
As website changes require staff approval, discussing the page content beforehand helps with accelerating the contribution process, and avoids wasted work in the event the proposed page is not accepted.
@@ -68,8 +68,8 @@ title: How to Contribute a Page
description: Learn how to write and publish a page to this website.
icon: fas fa-info
relevant_links:
- Contributing to Site: https://pythondiscord.com/pages/contributing/site/
- Using Git: https://pythondiscord.com/pages/contributing/working-with-git/
+ Contributing to Site: https://pythondiscord.com/pages/guides/pydis-guides/contributing/site/
+ Using Git: https://pythondiscord.com/pages/guides/pydis-guides/contributing/working-with-git/
---
Pages, which include guides, articles, and other static content,...
@@ -169,7 +169,7 @@ path = os.path.join("foo", "bar")
### HTML Attributes
To add HTML attributes to certain lines/paragraphs, [see this page](https://python-markdown.github.io/extensions/attr_list/#the-list) for the format and where to put it.
-This can be useful for setting the image size when adding an image using markdown (see the [Image Captions](#image-captions) section for an example), or for adding bulma styles to certain elements (like the warning notification [here](/pages/guides/pydis-guides/contributing/sir-lancebot#setup-instructions)).<br>
+This can be useful for setting the image size when adding an image using markdown (see the [Image Captions](#image-captions) section for an example), or for adding bulma styles to certain elements (like the warning notification [here](/pages/guides/pydis-guides/contributing/sir-lancebot#run-with-docker)).<br>
**This should be used sparingly, as it reduces readability and simplicity of the article.**
---
@@ -215,3 +215,44 @@ To use a custom label in the table of contents for a heading, set the `data-toc-
```markdown
# Header 1 {: data-toc-label="Header One" }
```
+
+## Tips
+
+### Nested/Unhighlighted Code Blocks
+To nest code blocks, increase the number of ticks in the outer block by 1. To remove highlighting from code blocks (ie. no dark background), you can use the `nohighlight` language.
+`````nohighlight
+````nohighlight
+```python
+print("Some inner code block text.")
+```
+````
+`````
+
+### Images in Lists
+To properly indent images in lists, keep the image on the line directly after the previous line and add `<br>` to the end of the text, like this:
+
+```markdown
+1. List item text one.<br>
+![Image text one](image/link/one)
+
+2. List item text two.<br>
+![Image text two](image/link/two)
+```
+
+### Keeping Text In The Same Paragraph
+You can also use `<br>` to break lines while keeping them in the same paragraph (avoiding the vertical spacing added between paragraphs).
+
+```nohighlight
+##### Same line, same paragraph
+Line A
+Line B
+
+##### Different line, different paragraph
+Line A
+
+Line B
+
+##### Different line, same paragraph
+Line A<br>
+Line B
+```
diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/off-topic-etiquette.md b/pydis_site/apps/content/resources/guides/pydis-guides/off-topic-etiquette.md
index f8031834..5e785cd9 100644
--- a/pydis_site/apps/content/resources/guides/pydis-guides/off-topic-etiquette.md
+++ b/pydis_site/apps/content/resources/guides/pydis-guides/off-topic-etiquette.md
@@ -5,7 +5,7 @@ icon: fab fa-discord
---
## Why do we need off-topic etiquette?
-Everyone wants to have good conversations in our off-topic channels, but with tens of thousands of members, this might mean different things to different people.
+Everyone wants to have good conversations in our off-topic channels, but with hundreds of thousands of members, this might mean different things to different people.
To facilitate the best experience for everyone, here are some guidelines on conversation etiquette.
## Three things you shouldn't do
diff --git a/pydis_site/apps/content/resources/guides/python-guides/app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/app-commands.md
new file mode 100644
index 00000000..713cd650
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/app-commands.md
@@ -0,0 +1,418 @@
+---
+title: Discord.py 2.0 changes
+description: Changes and new features in version 2.0 of discord.py
+---
+
+Upon the return of the most popular discord API wrapper library for Python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with additions of features to the library. Additions to the library include support for Buttons, Select Menus, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py Gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6).
+
+
+# Install the latest version of discord.py
+
+Before you can make use of any of the new 2.0 features, you need to install the latest version of discord.py. Make sure that the version is 2.0 or above!
+Also, make sure to uninstall any third party libraries intended to add slash-command support to pre-2.0 discord.py, as they are no longer necessary and will likely cause issues.
+
+The latest and most up-to-date stable discord.py version can be installed using `pip install -U discord.py`.
+
+**Before migrating to discord.py 2.0, make sure you read the migration guide [here](https://discordpy.readthedocs.io/en/latest/migrating.html) as there are lots of breaking changes.**.
+{: .notification .is-warning }
+
+# What are Slash Commands?
+
+Slash Commands are an exciting new way to build and interact with bots on Discord. As soon as you type "/", you can easily see all the commands a bot has. It also comes with autocomplete, validation and error handling, which will all help users of your bot get the command right the first time.
+
+# Basic structure for discord.py Slash Commands!
+
+### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*.
+Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create Slash Commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command.
+
+# Fundamentals for this Gist!
+
+One new feature added in discord.py v2 is `setup_hook`. `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway.
+Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point.
+A full list of commands you can't use in setup_hook can be found [here](https://discord.com/developers/docs/topics/gateway-events#send-events).
+
+__**THE FOLLOWING ARE EXAMPLES OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__
+
+Note that the default intents are defined [here](https://discordpy.readthedocs.io/en/stable/api.html?highlight=discord%20intents%20default#discord.Intents.default) to have all intents enabled except presences, members, and message_content.
+
+```python
+import discord
+
+# You can create the setup_hook directly in the class definition
+
+class SlashClient(discord.Client):
+ def __init__(self) -> None:
+ super().__init__(intents=discord.Intents.default())
+
+ async def setup_hook(self) -> None:
+ ...
+
+# Or add it to the client after creating it
+
+client = discord.Client(intents=discord.Intents.default())
+async def my_setup_hook() -> None:
+ ...
+
+client.setup_hook = my_setup_hook
+```
+
+# Basic Slash Command application using discord.py.
+
+#### The `CommandTree` class resides within the `app_commands` of the discord.py package.
+
+## Slash Command Application with a Client
+
+```python
+import discord
+
+class SlashClient(discord.Client):
+ def __init__(self) -> None:
+ super().__init__(intents=discord.Intents.default())
+ self.tree = discord.app_commands.CommandTree(self)
+
+ async def setup_hook(self) -> None:
+ self.tree.copy_global_to(guild=discord.Object(id=12345678900987654))
+ await self.tree.sync()
+
+client = SlashClient()
+
[email protected](name="ping", description="...")
+async def _ping(interaction: discord.Interaction) -> None:
+ await interaction.response.send_message("pong")
+
+client.run("token")
+```
+
+
+__**EXPLANATION**__
+
+- `import discord` imports the **discord.py** package.
+- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid.
+- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commands.
+- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. **Essential to creation of commands** Further up, `self.tree.sync()` updates the API with any changes to the Slash Commands.
+- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`.
+- Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker.
+- And the classic old `client.run("token")` is used to connect the client to the discord gateway.
+- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. We will discuss how to handle this issue later following the Gist.
+
+## Slash Command application with the Bot class
+
+```python
+import discord
+
+class SlashBot(commands.Bot):
+ def __init__(self) -> None:
+ super().__init__(command_prefix=".", intents=discord.Intents.default())
+
+ async def setup_hook(self) -> None:
+ self.tree.copy_global_to(guild=discord.Object(id=12345678900987654))
+ await self.tree.sync()
+
+bot = SlashBot()
+
[email protected](name="ping", description="...")
+async def _ping(interaction: discord.Interaction) -> None:
+ await interaction.response.send_message("pong")
+
+bot.run("token")
+```
+
+The above example shows a basic Slash Commands within discord.py using the Bot class.
+
+__**EXPLANATION**__
+
+Most of the explanation is the same as the prior example that featured `SlashClient` which was a subclass of **discord.Client**. Though some minor changes are discussed below.
+
+- The `SlashBot` class now subclasses `discord.ext.commands.Bot` following the passing in of the required arguments to its `__init__` method.
+- `discord.ext.commands.Bot` already consists of an instance of the `CommandTree` class which can be accessed using the `tree` property.
+
+# Slash Commands within a Cog!
+
+A cog is a collection of commands, listeners, and optional state to help group commands together. More information on them can be found on the [Cogs](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs) page.
+
+## An Example to using cogs with discord.py for Slash Commands!
+
+```python
+import discord
+from discord.ext import commands
+from discord import app_commands
+
+class MySlashCog(commands.Cog):
+ def __init__(self, bot: commands.Bot) -> None:
+ self.bot = bot
+
+ @app_commands.command(name="ping", description="...")
+ async def _ping(self, interaction: discord.Interaction):
+ await interaction.response.send_message("pong!")
+
+class MySlashBot(commands.Bot):
+ def __init__(self) -> None:
+ super().__init__(command_prefix="!", intents=discord.Intents.default())
+
+ async def setup_hook(self) -> None:
+ await self.add_cog(MySlashCog(self))
+ await self.tree.copy_global_to(discord.Object(id=123456789098765432))
+ await self.tree.sync()
+
+bot = MySlashBot()
+
+bot.run("token")
+```
+
+__**EXPLANATION**__
+
+- Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` module from the **discord.py** root module. `from discord.ext import commands` imports the commands extension.
+- Further up, `class MySlashCog(commands.Cog)` is a class subclassing the `Cog` class. You can read more about this [here](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs).
+- `def __init__(self, bot: commands.Bot): self.bot = bot` is the constructor method of the class that is always run when the class is instantiated and that is why we pass in a **Bot** object whenever we create an instance of the cog class.
+- Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for Slash Commands with **self** added as the first parameter to the function as it is a method of a class.
+- The next up lines are mostly the same.
+- Talking about the first line inside the `setup_hook` is the `add_cog` method of the **Bot** class. And since **self** acts as the **instance** of the current class, we use **self** to use the `add_cog` method of the **Bot** class as we are inside a subclassed class of the **Bot** class. Then we pass in **self** to the `add_cog` method as the `__init__` function of the **MySlashCog** cog accepts a `Bot` object.
+- After that we instantiate the `MySlashBot` class and run the bot using the **run** method which executes our setup_hook function and our commands get loaded and synced. The bot is now ready to use!
+
+# An Example to using groups with discord.py for Slash Commands!
+
+## An example with optional group!
+
+```python
+import discord
+from discord.ext import commands
+from discord import app_commands
+
+class MySlashGroupCog(commands.Cog):
+ def __init__(self, bot: commands.Bot) -> None:
+ self.bot = bot
+
+ #--------------------------------------------------------
+ group = app_commands.Group(name="uwu", description="...")
+ #--------------------------------------------------------
+
+ @app_commands.command(name="ping", description="...")
+ async def _ping(self, interaction: discord.) -> None:
+ await interaction.response.send_message("pong!")
+
+ @group.command(name="command", description="...")
+ async def _cmd(self, interaction: discord.Interaction) -> None:
+ await interaction.response.send_message("uwu")
+
+class MySlashBot(commands.Bot):
+ def __init__(self) -> None:
+ super().__init__(command_prefix="!", intents=discord.Intents.default())
+
+ async def setup_hook(self) -> None:
+ await self.add_cog(MySlashGroupCog(self))
+ await self.tree.copy_global_to(discord.Object(id=123456789098765432))
+ await self.tree.sync()
+
+bot = MySlashBot()
+
+bot.run("token")
+```
+
+__**EXPLANATION**__
+- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them.
+
+## An example with a **Group** subclass!
+
+```python
+import discord
+from discord.ext import commands
+from discord import app_commands
+
+class MySlashGroup(app_commands.Group, name="uwu"):
+ def __init__(self, bot: commands.Bot) -> None:
+ self.bot = bot
+ super().__init__()
+
+ @app_commands.command(name="ping", description="...")
+ async def _ping(self, interaction: discord.) -> None:
+ await interaction.response.send_message("pong!")
+
+ @app_commands.command(name="command", description="...")
+ async def _cmd(self, interaction: discord.Interaction) -> None:
+ await interaction.response.send_message("uwu")
+
+class MySlashBot(commands.Bot):
+ def __init__(self) -> None:
+ super().__init__(command_prefix="!", intents=discord.Intents.default())
+
+ async def setup_hook(self) -> None:
+ await self.add_cog(MySlashGroup(self))
+ await self.tree.copy_global_to(discord.Object(id=123456789098765432))
+ await self.tree.sync()
+
+bot = MySlashBot()
+
+bot.run("token")
+```
+
+__**EXPLANATION**__
+- The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**.
+
+# Some common methods and features used for Slash Commands.
+
+### A common function used for Slash Commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below.
+
+```python
+from discord.ext import commands
+from discord import app_commands
+import discord
+
+bot = commands.Bot(command_prefix=".", intents=discord.Intents.default())
+#sync the commands
+
[email protected](name="echo", description="...")
+@app_commands.describe(text="The text to send!", channel="The channel to send the message in!")
+async def _echo(interaction: discord.Interaction, text: str, channel: discord.TextChannel=None):
+ channel = channel or interaction.channel
+ await channel.send(text)
+```
+
+### Another common issue that most people come across is the time duration of sending a message with `send_message`. This issue can be tackled by deferring the interaction response using the `defer` method of the `InteractionResponse` class. An example for fixing this issue is shown below.
+
+```python
+import discord
+from discord.ext import commands
+import asyncio
+
+bot = commands.Bot(command_prefix="!", intents=discord.Intents.default())
+#sync the commands
+
[email protected](name="time", description="...")
+async def _time(interaction: discord.Interaction, time_to_wait: int):
+ # -------------------------------------------------------------
+ await interaction.response.defer(ephemeral=True)
+ # -------------------------------------------------------------
+ await interaction.edit_original_response(content=f"I will notify you after {time_to_wait} seconds have passed!")
+ await asyncio.sleep(time_to_wait)
+ await interaction.edit_original_response(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!")
+```
+
+# Checking for Permissions and Roles!
+
+To add a permissions check to a command, the methods are imported through `discord.app_commands.checks`. To check for a member's permissions, the function can be decorated with the [discord.app_commands.checks.has_permissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=has_permissions#discord.app_commands.checks.has_permissions) method. An example to this as follows.
+
+```py
+from discord import app_commands
+from discord.ext import commands
+import discord
+
+bot = commands.Bot(command_prefix="!", intents=discord.Intents.default())
+#sync commands
+
[email protected](name="ping")
+@app_commands.checks.has_permissions(manage_messages=True, manage_channels=True) #example permissions
+async def _ping(interaction: discord.Interaction):
+ await interaction.response.send_message("pong!")
+
+```
+
+If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! We will discuss making an error handler later in the Gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions).
+
+Other methods that you can decorate the commands with are -
+- `bot_has_permissions` | This checks if the bot has the required permissions for executing the slash command. This raises a [BotMissingPermissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.BotMissingPermissions) exception.
+- `has_role` | This checks if the slash command user has the required role or not. Only **ONE** role name or role ID can be passed to this. If the name is being passed, make sure to have the exact same name as the role name. This raises a [MissingRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingRole) exception.
+- To pass in several role names or role IDs, `has_any_role` can be used to decorate a command. This raises two exceptions -> [MissingAnyRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingAnyRole) and [NoPrivateMessage](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.NoPrivateMessage)
+
+
+# Adding cooldowns to Slash Commands!
+
+Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown.
+An example is as follows.
+
+```python
+from discord.ext import commands
+import discord
+
+class Bot(commands.Bot):
+ def __init__(self):
+ super().__init__(command_prefix="uwu", intents=discord.Intents.all())
+
+ async def setup_hook(self):
+ self.tree.copy_global_to(guild=discord.Object(id=12345678909876543))
+ await self.tree.sync()
+
+
+bot = Bot()
+
[email protected](name="ping")
+# -----------------------------------------
[email protected]_commands.checks.cooldown(1, 30)
+# -----------------------------------------
+async def ping(interaction: discord.Interaction):
+ await interaction.response.send_message("pong!")
+
+bot.run("token")
+```
+
+__**EXPLANATION**__
+- The first argument is the number of times this command can be invoked before the cooldown is triggered.
+- The second argument it takes is the period of time in which the command can be run the specified number of times.
+- The `CommandOnCooldown` exception can be handled using an error handler. We will discuss making an error handler for Slash Commands later in the Gist.
+
+
+# Handling errors for Slash Commands!
+
+The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the Slash Commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException).
+An example to creating an error handler for Slash Commands is as follows.
+
+```python
+from discord.ext import commands
+from discord import app_commands
+import discord
+
+bot = commands.Bot(command_prefix="!", intents=discord.Intents.default())
+#sync commands
+
[email protected](name="ping")
+@app_commands.checks.cooldown(1, 30)
+async def ping(interaction: discord.Interaction):
+ await interaction.response.send_message("pong!")
+
+async def on_tree_error(interaction: discord.Interaction, error: app_commands.AppCommandError):
+ if isinstance(error, app_commands.CommandOnCooldown):
+ return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!")
+ elif isinstance(error, ...):
+ ...
+ else:
+ raise error
+
+bot.tree.on_error = on_tree_error
+
+bot.run("token")
+```
+
+__**EXPLANATION**__
+
+First we create a simple asynchronous function named `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here we have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displaying unhandled errors, i.e. the ones which have not been handled manually. If this is **removed**, you will not be able to see any exceptions raised by Slash Commands and makes debugging the code harder.
+After creating the error handler function, we set the function as the error handler for the Slash Commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here.
+
+### Creating an error handler for a specific error!
+
+```python
+from discord.ext import commands
+from discord import app_commands
+import discord
+
+bot = commands.Bot(command_prefix="!", intents=discord.Intents.default())
+#sync commands
+
[email protected](name="ping")
+@app_commands.checks.cooldown(1, 30)
+async def ping(interaction: discord.Interaction):
+ await interaction.response.send_message("pong!")
+
+async def ping_error(interaction: discord.Interaction, error: app_commands.AppCommandError):
+ if isinstance(error, app_commands.CommandOnCooldown):
+ return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!")
+ elif isinstance(error, ...):
+ ...
+ else:
+ raise error
+
+bot.run("token")
+```
+
+__**EXPLANATION**__
+
+Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. You should not need to call the `error` method manually.
diff --git a/pydis_site/apps/content/resources/guides/python-guides/creating-python-environment-windows.md b/pydis_site/apps/content/resources/guides/python-guides/creating-python-environment-windows.md
index 356d63bd..635c384f 100644
--- a/pydis_site/apps/content/resources/guides/python-guides/creating-python-environment-windows.md
+++ b/pydis_site/apps/content/resources/guides/python-guides/creating-python-environment-windows.md
@@ -29,7 +29,7 @@ You will also need a text editor for writing Python programs, and for subsequent
Powerful programs called integrated development environments (IDEs) like PyCharm and Visual Studio Code contain text editors, but they also contain many other features with uses that aren't immediately obvious to new programmers.
[Notepad++](https://notepad-plus-plus.org/) is a popular text editor for both beginners and advanced users who prefer a simpler interface.
-Other editors we recommend can be found (https://pythondiscord.com/resources/tools/#editors)[here].
+Other editors we recommend can be found [here](https://pythondiscord.com/resources/tools/#editors).
## Installing Git Bash
Git is a command line program that helps you keep track of changes to your code, among other things.
diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-embed-limits.md b/pydis_site/apps/content/resources/guides/python-guides/discord-embed-limits.md
new file mode 100644
index 00000000..ca97462b
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/discord-embed-limits.md
@@ -0,0 +1,21 @@
+---
+title: Discord Embed Limits
+description: A guide that shows the limits of embeds in Discord and how to avoid them.
+---
+
+If you plan on using embed responses for your bot you should know the limits of the embeds on Discord or you will get `Invalid Form Body` errors:
+
+- Embed **title** is limited to **256 characters**
+- Embed **description** is limited to **4096 characters**
+- An embed can contain a maximum of **25 fields**
+- A **field name/title** is limited to **256 character** and the **value of the field** is limited to **1024 characters**
+- Embed **footer** is limited to **2048 characters**
+- Embed **author name** is limited to **256 characters**
+- The **total of characters** allowed in an embed is **6000**
+
+Now if you need to get over this limit (for example for a help command), you would need to use pagination.
+There are several ways to do that:
+
+- A library called **[disputils](https://pypi.org/project/disputils)**
+- An experimental library made by the discord.py developer called **[discord-ext-menus](https://github.com/Rapptz/discord-ext-menus)**
+- Make your own setup using **[wait_for()](https://discordpy.readthedocs.io/en/stable/ext/commands/api.html#discord.ext.commands.Bot.wait_for)** and wait for a reaction to be added
diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-messages-with-colors.md b/pydis_site/apps/content/resources/guides/python-guides/discord-messages-with-colors.md
new file mode 100644
index 00000000..0e88490e
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/discord-messages-with-colors.md
@@ -0,0 +1,79 @@
+---
+title: Discord Messages with Colors
+description: A guide on how to add colors to your codeblocks on Discord
+---
+
+Discord is now *slowly rolling out* the ability to send colored messages within code blocks. It uses the ANSI color codes, so if you've tried to print colored text in your terminal or console with Python or other languages then it will be easy for you.
+
+## Quick Explanation
+To be able to send a colored text, you need to use the `ansi` language for your code block and provide a prefix of this format before writing your text:
+```ansi
+\u001b[{format};{color}m
+```
+*`\u001b` is the unicode escape for ESCAPE/ESC, meant to be used in the source of your bot (see <http://www.unicode-symbol.com/u/001B.html>).* ***If you wish to send colored text without using your bot you need to copy the character from the website.***
+
+After you've written this, you can now type the text you wish to color. If you want to reset the color back to normal, then you need to use the `\u001b[0m` prefix again.
+
+## Formats
+Here is the list of values you can use to replace `{format}`:
+
+* 0: Normal
+* 1: **Bold**
+* 4: <ins>Underline</ins>
+
+## Colors
+Here is the list of values you can use to replace `{color}`:
+
+### Text Colors
+
+* 30: Gray
+* 31: Red
+* 32: Green
+* 33: Yellow
+* 34: Blue
+* 35: Pink
+* 36: Cyan
+* 37: White
+
+### Background Colors
+
+* 40: Firefly dark blue
+* 41: Orange
+* 42: Marble blue
+* 43: Greyish turquoise
+* 44: Gray
+* 45: Indigo
+* 46: Light gray
+* 47: White
+
+## Example
+
+Let's take an example, I want a bold green colored text with the firefly dark blue background.
+I simply use `\u001b[0;40m` (background color) and `\u001b[1;32m` (text color) as prefix. Note that the order is **important**, first you give the background color and then the text color.
+
+Alternatively you can also directly combine them into a single prefix like the following: `\u001b[1;40;32m` and you can also use multiple values. Something like `\u001b[1;40;4;32m` would underline the text, make it bold, make it green and have a dark blue background.
+
+Raw message:
+````nohighlight
+```ansi
+\u001b[0;40m\u001b[1;32mThat's some cool formatted text right?
+or
+\u001b[1;40;32mThat's some cool formatted text right?
+```
+````
+
+Result:
+
+![Background and text color result](/static/images/content/discord_colored_messages/result.png)
+
+### ANSI Colors Showcase
+
+The way the colors look like on Discord is shown in the image below:
+
+![ANSI Colors](/static/images/content/discord_colored_messages/ansi-colors.png)
+
+*Message sent to get the output of above can be found [here](https://gist.github.com/kkrypt0nn/a02506f3712ff2d1c8ca7c9e0aed7c06#file-ansi-colors-showcase-md).*
+
+#### Disclaimer
+
+***Note**: The change has been brought to all stable desktop clients. Since syntax highlighting on mobile is far behind, ANSI is not supported on mobile as well. Refer to [this gist](https://gist.github.com/matthewzring/9f7bbfd102003963f9be7dbcf7d40e51) for other syntax highlighting methods.*
diff --git a/pydis_site/apps/content/resources/guides/python-guides/discordpy-subclassing-context.md b/pydis_site/apps/content/resources/guides/python-guides/discordpy-subclassing-context.md
new file mode 100644
index 00000000..5e5f05c1
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/discordpy-subclassing-context.md
@@ -0,0 +1,129 @@
+---
+title: Subclassing Context in discord.py
+description: "Subclassing the default `commands.Context` class to add more functionability and customizability."
+---
+
+Start by reading the guide on [subclassing the `Bot` class](../subclassing_bot). A subclass of Bot has to be used to
+inject your custom context subclass into discord.py.
+
+## Overview
+
+The way this works is by creating a subclass of discord.py's [`Context` class](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Context)
+adding whatever functionality you wish. Usually this is done by adding custom methods or properties, so that you don't need to
+copy it around or awkwardly import it elsewhere.
+
+This guide will show you how to add a `prompt()` method to the context and how to use it in a command.
+
+## Example subclass and code
+
+The first part - of course - is creating the actual context subclass. This is done similarly to creating a bot
+subclass, it will look like this:
+
+```python
+import asyncio
+from typing import Optional
+
+from discord import RawReactionActionEvent
+from discord.ext import commands
+
+
+class CustomContext(commands.Context):
+ async def prompt(
+ self,
+ message: str,
+ *,
+ timeout=30.0,
+ delete_after=True
+ ) -> Optional[bool]:
+ """Prompt the author with an interactive confirmation message.
+
+ This method will send the `message` content, and wait for max `timeout` seconds
+ (default is `30`) for the author to react to the message.
+
+ If `delete_after` is `True`, the message will be deleted before returning a
+ True, False, or None indicating whether the author confirmed, denied,
+ or didn't interact with the message.
+ """
+ msg = await self.send(message)
+
+ for reaction in ('✅', '❌'):
+ await msg.add_reaction(reaction)
+
+ confirmation = None
+
+ # This function is a closure because it is defined inside of another
+ # function. This allows the function to access the self and msg
+ # variables defined above.
+
+ def check(payload: RawReactionActionEvent):
+ # 'nonlocal' works almost like 'global' except for functions inside of
+ # functions. This means that when 'confirmation' is changed, that will
+ # apply to the variable above
+ nonlocal confirmation
+
+ if payload.message_id != msg.id or payload.user_id != self.author.id:
+ return False
+
+ emoji = str(payload.emoji)
+
+ if emoji == '✅':
+ confirmation = True
+ return True
+
+ elif emoji == '❌':
+ confirmation = False
+ return True
+
+ # This means that it was neither of the two emojis added, so the author
+ # added some other unrelated reaction.
+ return False
+
+ try:
+ await self.bot.wait_for('raw_reaction_add', check=check, timeout=timeout)
+ except asyncio.TimeoutError:
+ # The 'confirmation' variable is still None in this case
+ pass
+
+ if delete_after:
+ await msg.delete()
+
+ return confirmation
+```
+
+After creating your context subclass, you need to override the `get_context()` method on your
+Bot class and change the default of the `cls` parameter to this subclass:
+
+```python
+from discord.ext import commands
+
+
+class CustomBot(commands.Bot):
+ async def get_context(self, message, *, cls=CustomContext): # From the above codeblock
+ return await super().get_context(message, cls=cls)
+```
+
+Now that discord.py is using your custom context, you can use it in a command. For example:
+
+```python
+import discord
+from discord.ext import commands
+
+# Enable the message intent so that we get message content. This is needed for
+# the commands we define below
+intents = discord.Intents.default()
+intents.message_content = True
+
+
+# Replace '...' with any additional arguments for the bot
+bot = CustomBot(intents=intents, ...)
+
+
+async def massban(ctx: CustomContext, members: commands.Greedy[discord.Member]):
+ prompt = await ctx.prompt(f"Are you sure you want to ban {len(members)} members?")
+ if not prompt:
+ # Return if the author cancelled, or didn't react in time
+ return
+
+ ... # Perform the mass-ban, knowing the author has confirmed this action
+```
diff --git a/pydis_site/apps/content/resources/guides/python-guides/discordpy_help_command.md b/pydis_site/apps/content/resources/guides/python-guides/discordpy_help_command.md
new file mode 100644
index 00000000..4b475146
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/discordpy_help_command.md
@@ -0,0 +1,66 @@
+---
+title: Custom Help Command
+description: "Overwrite discord.py's help command to implement custom functionality"
+---
+
+First, a basic walkthrough can be found [here](https://gist.github.com/InterStella0/b78488fb28cadf279dfd3164b9f0cf96) by Stella#2000 on subclassing the HelpCommand. It will provide some foundational knowledge that is required before attempting a more customizable help command.
+
+## Custom Subclass of Help Command
+If the types of classes of the HelpCommand do not fit your needs, you can subclass HelpCommand and use the class mehods to customize the output. Below is a simple demonstration using the following methods that can also be found on the documenation:
+
+- [filter_commands](https://discordpy.readthedocs.io/en/stable/ext/commands/api.html#discord.ext.commands.HelpCommand.filter_commands)
+
+- [send_group_help](https://discordpy.readthedocs.io/en/stable/ext/commands/api.html#discord.ext.commands.HelpCommand.send_bot_help)
+
+- [send_command_help](https://discordpy.readthedocs.io/en/stable/ext/commands/api.html#discord.ext.commands.HelpCommand.send_command_help)
+
+- [send_group_help](https://discordpy.readthedocs.io/en/stable/ext/commands/api.html#discord.ext.commands.HelpCommand.send_group_help)
+
+- [send_error_message](https://discordpy.readthedocs.io/en/stable/ext/commands/api.html#discord.ext.commands.HelpCommand.send_error_message)
+
+```python
+class MyHelp(commands.HelpCommand):
+
+ async def send_bot_help(self, mapping):
+ """
+ This is triggered when !help is invoked.
+
+ This example demonstrates how to list the commands that the member invoking the help command can run.
+ """
+ filtered = await self.filter_commands(self.context.bot.commands, sort=True) # returns a list of command objects
+ names = [command.name for command in filtered] # iterating through the commands objects getting names
+ available_commands = "\n".join(names) # joining the list of names by a new line
+ embed = disnake.Embed(description=available_commands)
+ await self.context.send(embed=embed)
+
+ async def send_command_help(self, command):
+ """This is triggered when !help <command> is invoked."""
+ await self.context.send("This is the help page for a command")
+
+ async def send_group_help(self, group):
+ """This is triggered when !help <group> is invoked."""
+ await self.context.send("This is the help page for a group command")
+
+ async def send_cog_help(self, cog):
+ """This is triggered when !help <cog> is invoked."""
+ await self.context.send("This is the help page for a cog")
+
+ async def send_error_message(self, error):
+ """If there is an error, send a embed containing the error."""
+ channel = self.get_destination() # this defaults to the command context channel
+ await channel.send(error)
+
+bot.help_command = MyHelp()
+```
+
+You can handle when a user does not pass a command name when invoking the help command and make a fancy and customized embed; here a page that describes the bot and shows a list of commands is generally used. However if a command is passed in, you can display detailed information of the command. Below are references from the documentation below that can be utilised:
+
+- [Get the command object](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Bot.get_command)
+
+- [Get the command name](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Command.name)
+
+- [Get the command aliases](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Command.aliases)
+
+- [Get the command brief](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Command.brief)
+
+- [Get the command usage](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Command.usage)
diff --git a/pydis_site/apps/content/resources/guides/python-guides/docker-hosting-guide.md b/pydis_site/apps/content/resources/guides/python-guides/docker-hosting-guide.md
new file mode 100644
index 00000000..7af6a0bb
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/docker-hosting-guide.md
@@ -0,0 +1,323 @@
+---
+title: How to host a bot with Docker and GitHub Actions on Ubuntu VPS
+description: This guide shows how to host a bot with Docker and GitHub Actions on Ubuntu VPS
+---
+
+## Contents
+
+1. [You will learn](#you-will-learn)
+2. [Introduction](#introduction)
+3. [Installing Docker](#installing-docker)
+4. [Creating Dockerfile](#creating-dockerfile)
+5. [Building Image and Running Container](#building-image-and-running-container)
+6. [Creating Volumes](#creating-volumes)
+7. [Using GitHub Actions for full automation](#using-github-actions-for-full-automation)
+
+## You will learn how to
+
+- write Dockerfile
+- build Docker image and run the container
+- use Docker Compose
+- make docker keep the files throughout the container's runs
+- parse environment variables into container
+- use GitHub Actions for automation
+- set up self-hosted runner
+- use runner secrets
+
+## Introduction
+
+Let's say you have got a nice discord bot written in Python and you have a VPS to host it on. Now the only question is
+how to run it 24/7. You might have been suggested to use *screen multiplexer*, but it has some disadvantages:
+
+1. Every time you update the bot you have to SSH to your server, attach to screen, shutdown the bot, run `git pull` and
+ run the bot again. You might have good extensions management that allows you to update the bot without restarting it,
+ but there are some other cons as well
+2. If you update some dependencies, you have to update them manually
+3. The bot doesn't run in an isolated environment, which is not good for security.
+
+But there's a nice and easy solution to these problems - **Docker**! Docker is a containerization utility that automates
+some stuff like dependencies update and running the application in the background. So let's get started.
+
+## Installing Docker
+
+The best way to install Docker is to use
+the [convenience script](https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script) provided
+by Docker developers themselves. You just need 2 lines:
+
+```shell
+$ curl -fsSL https://get.docker.com -o get-docker.sh
+$ sudo sh get-docker.sh
+```
+
+## Creating Dockerfile
+
+To tell Docker what it has to do to run the application, we need to create a file named `Dockerfile` in our project's
+root.
+
+1. First we need to specify the *base image*, which is the OS that the docker container will be running. Doing that will
+ make Docker install some apps we need to run our bot, for
+ example the Python interpreter
+
+```dockerfile
+FROM python:3.10-bullseye
+```
+
+2. Next, we need to copy our Python project's external dependencies to some directory *inside the container*. Let's call
+ it `/app`
+
+```dockerfile
+COPY requirements.txt /app/
+```
+
+3. Now we need to set the directory as working and install the requirements
+
+```dockerfile
+WORKDIR /app
+RUN pip install -r requirements.txt
+```
+
+4. The only thing that is left to do is to copy the rest of project's files and run the main executable
+
+```dockerfile
+COPY . .
+CMD ["python3", "main.py"]
+```
+
+The final version of Dockerfile looks like this:
+
+```dockerfile
+FROM python:3.10-bullseye
+COPY requirements.txt /app/
+WORKDIR /app
+RUN pip install -r requirements.txt
+COPY . .
+CMD ["python3", "main.py"]
+```
+
+## Building Image and Running Container
+
+Now update the project on your VPS, so we can run the bot with Docker.
+
+1. Build the image (dot at the end is very important)
+
+```shell
+$ docker build -t mybot .
+```
+
+- the `-t` flag specifies a **tag** that will be assigned to the image. With it, we can easily run the image that the
+ tag was assigned to.
+- the dot at the end is basically the path to search for Dockerfile. The dot means current directory (`./`)
+
+2. Run the container
+
+```shell
+$ docker run -d --name mybot mybot:latest
+```
+
+- `-d` flag tells Docker to run the container in detached mode, meaning it will run the container in the background of
+ your terminal and not give us
+ any output from it. If we don't
+ provide it, the `run` will be giving us the output until the application exits. Discord bots aren't supposed to exit
+ after certain time, so we do need this flag
+- `--name` assigns a name to the container. By default, container is identified by id that is not human-readable. To
+ conveniently refer to container when needed,
+ we can assign it a name
+- `mybot:latest` means "latest version of `mybot` image"
+
+3. Read bot logs (keep in mind that this utility only allows to read STDERR)
+
+```shell
+$ docker logs -f mybot
+```
+
+- `-f` flag tells the docker to keep reading logs as they appear in container and is called "follow mode". To exit
+ press `CTRL + C`.
+
+If everything went successfully, your bot will go online and will keep running!
+
+## Using Docker Compose
+
+Just 2 commands to run a container is cool, but we can shorten it down to just 1 simple command. For that, create
+a `docker-compose.yml` file in project's root and fill it with the following contents:
+
+```yml
+version: "3.8"
+services:
+ main:
+ build: .
+ container_name: mybot
+```
+
+- `version` tells Docker what version of Compose to use. You may check all the
+ versions [here](https://docs.docker.com/compose/compose-file/compose-versioning/)
+- `services` contains services to build and run. Read more about
+ services [here](https://docs.docker.com/compose/compose-file/#services-top-level-element)
+- `main` is a service. We can call it whatever we would like to, not necessarily `main`
+- `build: .` is a path to search for Dockerfile, just like `docker build` command's dot
+- `container_name: mybot` is a container name to use for a bot, just like `docker run --name mybot`
+
+Update the project on VPS, remove the previous container with `docker rm -f mybot` and run this command
+
+```shell
+docker compose up -d --build
+```
+
+Now the docker will automatically build the image for you and run the container.
+
+### Why docker-compose
+
+The main purpose of Compose is to run several services at once. Mostly we
+don't need this in discord bots, however.
+For us, it has the following benefits:
+
+- we can build and run the container with just one command
+- if we need to parse some environment variables or volumes (more about them further in tutorial) our run command would
+ look like this
+
+```shell
+$ docker run -d --name mybot -e TOKEN=... -e WEATHER_API_APIKEY=... -e SOME_USEFUL_ENVIRONMENT_VARIABLE=... --net=host -v /home/exenifix/bot-data/data:/app/data -v /home/exenifix/bot-data/images:/app/data/images
+```
+
+This is pretty long and unreadable. Compose allows us to transfer those flags into single config file and still
+use just one short command to run the container.
+
+## Creating Volumes
+
+The files creating during container run are destroyed after its recreation. To prevent some files from getting
+destroyed, we need to use *volumes* that basically save the files from directory inside of container somewhere on drive.
+
+1. Create a new directory somewhere and copy path to it
+
+```shell
+$ mkdir mybot-data
+$ echo $(pwd)/mybot-data
+```
+
+My path is `/home/exenifix/mybot-data`, yours is most likely **different**!
+
+2. In your project, store the files that need to be persistent in a separate directory (eg. `data`)
+3. Add `volumes` to `docker-compose.yaml` so it looks like this:
+
+```yml
+version: "3.8"
+services:
+ main:
+ build: .
+ container_name: mybot
+ volumes:
+ - /home/exenifix/mybot-data:/app/data
+```
+
+The path before the colon `:` is the directory *on server's drive, outside of container*, and the second path is the
+directory *inside of container*.
+All the files saved in container in that directory will be saved on drive's directory as well and Docker will be
+accessing them *from drive*.
+
+## Using GitHub Actions for full automation
+
+Now it's time to fully automate the process and make Docker update the bot automatically on every commit or release. For
+that, we will use a **GitHub Actions workflow**, which basically runs some commands when we need to. You may read more
+about them [here](https://docs.github.com/en/actions/using-workflows).
+
+### Create repository secret
+
+We will not have the ability to use `.env` files with the workflow, so it's better to store the environment variables
+as **actions secrets**. Let's add your discord bot's token as a secret
+
+1. Head to your repository page -> Settings -> Secrets -> Actions
+2. Press `New repository secret`
+3. Give it a name like `TOKEN` and paste the token.
+ Now we will be able to access its value in workflow like `${{ secrets.TOKEN }}`. However, we also need to parse the
+ variable into container now. Edit `docker-compose` so it looks like this:
+
+```yml
+version: "3.8"
+services:
+ main:
+ build: .
+ container_name: mybot
+ volumes:
+ - /home/exenifix/mybot-data:/app/data
+ environment:
+ - TOKEN
+```
+
+### Setup self-hosted runner
+
+To run the workflow on our VPS, we will need to register it as *self-hosted runner*.
+
+1. Head to Settings -> Actions -> Runners
+2. Press `New self-hosted runner`
+3. Select runner image and architecture
+4. Follow the instructions but don't run the runner
+5. Instead, create a service
+
+```shell
+$ sudo ./svc.sh install
+$ sudo ./svc.sh start
+```
+
+Now we have registered our VPS as a self-hosted runner and we can run the workflow on it now.
+
+### Write a workflow
+
+Create a new file `.github/workflows/runner.yml` and paste the following content into it. Please pay attention to
+the `branches` instruction.
+The GitHub's standard main branch name is `main`, however it may be named `master` or something else if you edited its
+name. Make sure to put
+the correct branch name, otherwise it won't work. More about GitHub workflows
+syntax [here](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions)
+
+```yml
+name: Docker Runner
+
+on:
+ push:
+ branches: [ main ]
+
+jobs:
+ run:
+ runs-on: self-hosted
+ environment: production
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Run Container
+ run: docker compose up -d --build
+ env:
+ TOKEN: ${{ secrets.TOKEN }}
+
+ - name: Cleanup Unused Images
+ run: docker image prune -f
+```
+
+Run `docker rm -f mybot` (it only needs to be done once) and push to GitHub. Now if you open `Actions` tab on your
+repository, you should see a workflow running your bot. Congratulations!
+
+### Displaying logs in actions terminal
+
+There's a nice utility for reading docker container's logs and stopping upon meeting a certain phrase and it might be
+useful for you as well.
+
+1. Install the utility on your VPS with
+
+```shell
+$ pip install exendlr
+```
+
+2. Add a step to your workflow that would show the logs until it meets `"ready"` phrase. I recommend putting it before
+ the cleanup.
+
+```yml
+- name: Display Logs
+ run: python3 -m exendlr mybot "ready"
+```
+
+Now you should see the logs of your bot until the stop phrase is met.
+
+**WARNING**
+> The utility only reads from STDERR and redirects to STDERR, if you are using STDOUT for logs, it will not work and
+> will be waiting for stop phrase forever. The utility automatically exits if bot's container is stopped (e.g. error
+> occurred during starting) or if a log line contains a stop phrase. Make sure that your bot 100% displays a stop phrase
+> when it's ready otherwise your workflow will get stuck.
diff --git a/pydis_site/apps/content/resources/guides/python-guides/fix-ssl-certificate.md b/pydis_site/apps/content/resources/guides/python-guides/fix-ssl-certificate.md
new file mode 100644
index 00000000..096e3a90
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/fix-ssl-certificate.md
@@ -0,0 +1,23 @@
+---
+title: Fixing an SSL Certificate Verification Error
+description: A guide on fixing verification of an SSL certificate.
+---
+
+We're fixing the error Python specifies as [ssl.SSLCertVerificationError](https://docs.python.org/3/library/ssl.html#ssl.SSLCertVerificationError).
+
+# How to fix SSL Certificate issue on Windows
+
+Firstly, try updating your OS, wouldn't hurt to try.
+
+Now, if you're still having an issue, you would need to download the certificate for the SSL.
+
+The SSL Certificate, Sectigo (cert vendor) provides a download link of an [SSL certificate](https://crt.sh/?id=2835394). You should find it in the bottom left corner, shown below:
+
+A picture where to find the certificate in the website is:
+![location of certificate](/static/images/content/fix-ssl-certificate/pem.png)
+
+You have to setup the certificate yourself. To do that you can just click on it, or if that doesn't work, refer to [this link](https://portal.threatpulse.com/docs/sol/Solutions/ManagePolicy/SSL/ssl_chrome_cert_ta.htm)
+
+# How to fix SSL Certificate issue on Mac
+
+Navigate to your `Applications/Python 3.x/` folder and double-click the `Install Certificates.command` to fix this.
diff --git a/pydis_site/apps/content/resources/guides/python-guides/keeping-tokens-safe.md b/pydis_site/apps/content/resources/guides/python-guides/keeping-tokens-safe.md
new file mode 100644
index 00000000..92eb52a3
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/keeping-tokens-safe.md
@@ -0,0 +1,29 @@
+---
+title: Keeping Discord Bot Tokens Safe
+description: How to keep your bot tokens safe and safety measures you can take.
+---
+It's **very** important to keep a bot token safe,
+primarily because anyone who has the bot token can do whatever they want with the bot --
+such as destroying servers your bot has been added to and getting your bot banned from the API.
+
+# How to Avoid Leaking your Token
+To help prevent leaking your token,
+you should ensure that you don't upload it to an open source program/website,
+such as replit and github, as they show your code publicly.
+The best practice for storing tokens is generally utilising .env files
+([click here](https://tutorial.vco.sh/tips/tokens/) for more information on storing tokens safely).
+
+# What should I do if my token does get leaked?
+
+If for whatever reason your token gets leaked, you should immediately follow these steps:
+- Go to the list of [Discord Bot Applications](https://discord.com/developers/applications) you have and select the bot application that had the token leaked.
+- Select the Bot (1) tab on the left-hand side, next to a small image of a puzzle piece. After doing so you should see a small section named TOKEN (under your bot USERNAME and next to his avatar image)
+- Press the Regenerate button to regenerate your bot token and invalidate the old one.
+
+![Steps to Take to Reset your Discord Bot](/static/images/content/regenerating_token.jpg)
+
+Following these steps will create a new token for your bot, making it secure again and terminating any connections from the leaked token.
+The old token will stop working though, so make sure to replace the old token with the new one in your code if you haven't already.
+
+# Summary
+Make sure you keep your token secure by storing it safely, not sending it to anyone you don't trust, and regenerating your token if it does get leaked.
diff --git a/pydis_site/apps/content/resources/guides/python-guides/proper-error-handling.md b/pydis_site/apps/content/resources/guides/python-guides/proper-error-handling.md
new file mode 100644
index 00000000..74b0f59b
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/proper-error-handling.md
@@ -0,0 +1,70 @@
+---
+title: Proper error handling in discord.py
+description: Are you not getting any errors? This might be why!
+---
+If you're not recieving any errors in your console, even though you know you should be, try this:
+
+# With bot subclass:
+```py
+import discord
+from discord.ext import commands
+
+import traceback
+import sys
+
+class MyBot(commands.Bot):
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ async def on_command_error(self, ctx: commands.Context, error):
+ # Handle your errors here
+ if isinstance(error, commands.MemberNotFound):
+ await ctx.send("I could not find member '{error.argument}'. Please try again")
+
+ elif isinstance(error, commands.MissingRequiredArgument):
+ await ctx.send(f"'{error.param.name}' is a required argument.")
+ else:
+ # All unhandled errors will print their original traceback
+ print(f'Ignoring exception in command {ctx.command}:', file=sys.stderr)
+ traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
+
+bot = MyBot(command_prefix="!", intents=discord.Intents.default())
+
+bot.run("token")
+```
+
+# Without bot subclass
+```py
+import discord
+from discord.ext import commands
+
+import traceback
+import sys
+
+async def on_command_error(self, ctx: commands.Context, error):
+ # Handle your errors here
+ if isinstance(error, commands.MemberNotFound):
+ await ctx.send("I could not find member '{error.argument}'. Please try again")
+
+ elif isinstance(error, commands.MissingRequiredArgument):
+ await ctx.send(f"'{error.param.name}' is a required argument.")
+ else:
+ # All unhandled errors will print their original traceback
+ print(f'Ignoring exception in command {ctx.command}:', file=sys.stderr)
+ traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
+
+bot = commands.Bot(command_prefix="!", intents=discord.Intents.default())
+bot.on_command_error = on_command_error
+
+bot.run("token")
+```
+
+
+Make sure to import `traceback` and `sys`!
+
+-------------------------------------------------------------------------------------------------------------
+
+Useful Links:
+- [FAQ](https://discordpy.readthedocs.io/en/latest/faq.html)
+- [Simple Error Handling](https://gist.github.com/EvieePy/7822af90858ef65012ea500bcecf1612)
diff --git a/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md
new file mode 100644
index 00000000..45c7b37c
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md
@@ -0,0 +1,48 @@
+---
+title: Setting Different Statuses on Your Bot
+description: How to personalize your Discord bot status
+---
+
+You've probably seen a bot or two have a status message under their username in the member bar set to something such as `Playing Commands: .help`.
+
+This guide shows how to set such a status, so your bot can have one as well.
+
+**Please note:**
+
+If you want to change the bot status, it is suggested to not do so during the `on_ready` event, since it would be called many times and making an API call on that event has a chance to disconnect the bot.
+
+The status should not have a problem being set during runtime with `change_presence`, in the examples shown below.
+
+Instead, set the desired status using the activity / status kwarg of commands.Bot, for example:
+```python
+bot = commands.Bot(command_prefix="!", activity=..., status=...)
+```
+
+The following are examples of what you can put into the `activity` keyword argument.
+
+#### Setting 'Playing' Status
+```python
+await client.change_presence(activity=discord.Game(name="a game"))
+```
+
+#### Setting 'Streaming' Status
+```python
+await client.change_presence(activity=discord.Streaming(name="My Stream", url=my_twitch_url))
+```
+
+#### Setting 'Listening' Status
+```python
+await client.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="a song"))
+```
+
+#### Setting 'Watching' Status
+```python
+await client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="a movie"))
+```
+
+### Add Optional Status as Well:
+
+* `discord.Status.online` (default, green icon)
+* `discord.Status.idle` (yellow icon)
+* `discord.Status.do_not_disturb` (red icon)
+* `discord.Status.offline` (gray icon)
diff --git a/pydis_site/apps/content/resources/guides/python-guides/subclassing_bot.md b/pydis_site/apps/content/resources/guides/python-guides/subclassing_bot.md
new file mode 100644
index 00000000..8982a4f6
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/subclassing_bot.md
@@ -0,0 +1,58 @@
+---
+title: Subclassing Bot
+description: "Subclassing the discord.py `Bot` class to add more functionality and customizability."
+---
+
+## Basic Subclassing
+First, a [basic article](https://www.codesdope.com/course/python-subclass-of-a-class/) on subclassing will provide some fundamental knowledge, which is highly suggested before moving on to this topic, as subclassing [`Bot`](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Bot) can ultimately be a complicated task.
+
+## The Benefits of Subclassing Bot
+Subclassing `Bot` can be very beneficial as it provides you with more control and customizability of how your bot functions, also allowing you to add extra features, such as custom bot attributes or methods. For example, the default [Context](https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Context) can be [overridden](../discordpy-subclassing-context) to add more functionality.
+
+You can subclass `commands.Bot` as shown below:
+```python
+class CustomBot(commands.Bot):
+ def __init__(self, *args, **kwargs) -> None:
+ # Forward all arguments, and keyword-only arguments to commands.Bot
+ super().__init__(*args, **kwargs)
+
+ # Custom bot attributes can be set here.
+ self.launch_time = datetime.datetime.utcnow()
+ self.example_integer = 5
+
+ # Here you are overriding the default start method and write your own code.
+ async def start(self, *args, **kwargs) -> None:
+ """Establish a database connection."""
+ self.db = await aiosqlite.connect('sqlite.db')
+ await super().start(*args, **kwargs)
+
+ # Example of a custom bot method
+ def get_launch_time_str(self) -> str:
+ """Get bot launch datetime without milliseconds in UTC and status."""
+ return f"Bot started at: {self.launch_time.strftime('%F %T')} UTC."
+
+# All arguments as passed to commands.Bot can be passed here.
+bot = CustomBot(
+ command_prefix="!", # Prefix can be set to any string.
+ # Discord intents, refer to https://discordpy.readthedocs.io/en/stable/intents.html
+ intents=discord.Intents.default()
+)
+
+
+# Example bot command
+async def start_time(ctx):
+ """
+ Creates a command with the name `start_time`.
+
+ When invoked, sends the output of the custom method `get_launch_time_str`.
+ """
+ await ctx.send(bot.get_launch_time_str())
+
+
+# Having the token as an environment variable is recommended.
+# Refer to https://www.pythondiscord.com/pages/guides/python-guides/keeping-tokens-safe/
+token = YOUR_TOKEN_HERE
+bot.run(token)
+```
+The next step would be to look into discord.py cogs as they help in organizing collections of commands into various files and folders. Refer to [the official docs](https://discordpy.readthedocs.io/en/stable/ext/commands/cogs.html) for more on them.
diff --git a/pydis_site/apps/content/resources/guides/python-guides/vps-services.md b/pydis_site/apps/content/resources/guides/python-guides/vps-services.md
new file mode 100644
index 00000000..710fd914
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/vps-services.md
@@ -0,0 +1,58 @@
+---
+title: VPS and Free Hosting Service for Discord bots
+description: This article lists recommended VPS services and covers the disasdvantages of utilising a free hosting service to run a discord bot.
+toc: 2
+---
+
+## Recommended VPS services
+
+If you need to run your bot 24/7 (with no downtime), you should consider using a virtual private server (VPS). Here is a list of VPS services that are sufficient for running Discord bots.
+
+* Europe
+ * [netcup](https://www.netcup.eu/)
+ * Germany & Austria data centres.
+ * Great affiliate program.
+ * [Yandex Cloud](https://cloud.yandex.ru/)
+ * Vladimir, Ryazan, and Moscow region data centres.
+ * [Scaleway](https://www.scaleway.com/)
+ * France data centre.
+ * [Time 4 VPS](https://www.time4vps.eu/)
+ * Lithuania data centre.
+* US
+ * [GalaxyGate](https://galaxygate.net/)
+ * New York data centre.
+ * Great affiliate program.
+* Global
+ * [Linode](https://www.linode.com/)
+ * [Digital Ocean](https://www.digitalocean.com/)
+ * [OVHcloud](https://www.ovhcloud.com/)
+ * [Vultr](https://www.vultr.com/)
+
+
+## Why not to use free hosting services for bots?
+While these may seem like nice and free services, it has a lot more caveats than you may think. For example, the drawbacks of using common free hosting services to host a discord bot are discussed below.
+
+### Replit
+
+- The machines are super underpowered, resulting in your bot lagging a lot as it gets bigger.
+
+- You need to run a webserver alongside your bot to prevent it from being shut off. This uses extra machine power.
+
+- Repl.it uses an ephemeral file system. This means any file you saved through your bot will be overwritten when you next launch.
+
+- They use a shared IP for everything running on the service.
+This one is important - if someone is running a user bot on their service and gets banned, everyone on that IP will be banned. Including you.
+
+### Heroku
+- Bots are not what the platform is designed for. Heroku is designed to provide web servers (like Django, Flask, etc). This is why they give you a domain name and open a port on their local emulator.
+
+- Heroku's environment is heavily containerized, making it significantly underpowered for a standard use case.
+
+- Heroku's environment is volatile. In order to handle the insane amount of users trying to use it for their own applications, Heroku will dispose your environment every time your application dies unless you pay.
+
+- Heroku has minimal system dependency control. If any of your Python requirements need C bindings (such as PyNaCl
+ binding to libsodium, or lxml binding to libxml), they are unlikely to function properly, if at all, in a native
+ environment. As such, you often need to resort to adding third-party buildpacks to facilitate otherwise normal
+ CPython extension functionality. (This is the reason why voice doesn't work natively on heroku)
+
+- Heroku only offers a limited amount of time on their free programme for your applications. If you exceed this limit, which you probably will, they'll shut down your application until your free credit resets.
diff --git a/pydis_site/apps/content/resources/guides/python-guides/why-not-json-as-database.md b/pydis_site/apps/content/resources/guides/python-guides/why-not-json-as-database.md
new file mode 100644
index 00000000..6d9f433e
--- /dev/null
+++ b/pydis_site/apps/content/resources/guides/python-guides/why-not-json-as-database.md
@@ -0,0 +1,28 @@
+---
+title: Why JSON is unsuitable as a database
+description: The many reasons why you shouldn't use JSON as a database, and instead opt for SQL.
+relevant_links:
+ Tips on Storing Data: https://tutorial.vco.sh/tips/storage/
+---
+
+JSON, quite simply, is not a database. It's not designed to be a data storage format,
+rather a wayof transmitting data over a network. It's also often used as a way of doing configuration files for programs.
+
+There is no redundancy built in to JSON. JSON is just a format, and Python has libraries for it
+like json and ujson that let you load and dump it, sometimes to files, but that's all it does, write data to a file.
+There is no sort of DBMS (Database Management System), which means no sort of sophistication in how the data is stored,
+or built in ways to keep it safe and backed up, there's no built in encryption either - bear in mind
+in larger applications encryption may be necessary for GDPR/relevant data protection regulations compliance.
+
+JSON, unlike relational databases, has no way to store relational data,
+which is a very commonly needed way of storing data.
+Relational data, as the name may suggest, is data that relates to other data.
+For example if you have a table of users and a table of servers, the server table will probably have an owner field,
+where you'd reference a user from the users table. (**This is only relevant for relational data**).
+
+JSON is primarily a KV (key-value) format, for example `{"a": "b"}` where `a` is the key and `b` is the value,
+but what if you want to search not by that key but by a sub-key? Well, instead of being able to quickly use `var[key]`,
+which in a Python dictionary has a constant return time (for more info look up hash tables),
+you now have to iterate through every object in the dictionary and compare to find what you're looking for.
+Most relational database systems, like MySQL, MariaDB, and PostgreSQL have ways of indexing secondary fields
+apart from the primary key so that you can easily search by multiple attributes.