diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b6a2cc72..6a7ed240f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,7 @@ We recommend also reading the following if you're unsure or not confident: * [Contributing To An Open Source Project For The First Time](https://firsttimersonly.com) TeX-Bot is written in [Python](https://python.org) using [Pycord](https://pycord.dev) and uses Discord's [slash-commands](https://support.discord.com/hc/articles/1500000368501-Slash-Commands-FAQ) & [user-commands](https://guide.pycord.dev/interactions/application-commands/context-menus). -We would recommend being somewhat familiar with the [Pycord library](https://docs.pycord.dev), [Python language](https://docs.python.org/3/reference/index) & [project terminology](README.md#terminology) before contributing. +We would recommend being somewhat familiar with the [Pycord library](https://docs.pycord.dev), [Python language](https://docs.python.org/3/reference/index) & [project terminology](TERMINOLOGY.md) before contributing. ## Using the Issue Tracker @@ -52,13 +52,13 @@ If you are submitting a feature request, please include the steps to implement t ### Top level files * [`main.py`](main.py): is the main entrypoint to instantiate the [`Bot` object](https://docs.pycord.dev/stable/api/clients.html#discord.Bot) & run it -* [`exceptions.py`](exceptions.py): contains common [exception](https://docs.python.org/3/tutorial/errors) subclasses that may be raised when certain errors occur -* [`config.py`](config.py): retrieves the [environment variables](README.md#setting-environment-variables) & populates the correct values into the `settings` object +* [`exceptions/`](exceptions): contains common [exception](https://docs.python.org/3/tutorial/errors) subclasses that may be raised when certain errors occur +* [`config.py`](config.py): retrieves the [environment variables](README.md#setting-environment-variables) and populates the correct values into the `settings` object ### Other significant directories * [`cogs/`](cogs): contains all the [cogs](https://guide.pycord.dev/popular-topics/cogs) within this project, see [below](#cogs) for more information -* [`utils/`](utils): contains common utility classes & functions used by the top-level modules & cogs +* [`utils/`](utils): contains common utility classes and functions used by the top-level modules and cogs * [`db/core/models/`](db/core/models): contains all the [database ORM models](https://docs.djangoproject.com/en/stable/topics/db/models) to interact with storing information longer-term (between individual command events) * [`tests/`](tests): contains the complete test suite for this project, based on the [Pytest framework](https://pytest.org) @@ -73,6 +73,8 @@ There are separate cog files for each activity, and one [`__init__.py`](cogs/__i * [`cogs/__init__.py`](cogs/__init__.py): instantiates all the cog classes within this directory +* [`cogs/annual_handover_and_reset.py`](cogs/annual_handover_and_reset.py): cogs for annual handover procedures and role resets + * [`cogs/archive.py`](cogs/archive.py): cogs for archiving categories of channels within your group's Discord guild * [`cogs/command_error.py`](cogs/command_error.py): cogs for sending error messages when commands fail to complete/execute @@ -81,10 +83,14 @@ There are separate cog files for each activity, and one [`__init__.py`](cogs/__i * [`cogs/edit_message.py`](cogs/edit_message.py): cogs for editing messages that were previously sent by TeX-Bot +* [`cogs/get_token_authorisation.py`](cogs/get_token_authorisation.py): cogs for retrieving the current status of the supplied authentication token + * [`cogs/induct.py`](cogs/induct.py): cogs for inducting people into your group's Discord guild * [`cogs/kill.py`](cogs/kill.py): cogs related to the shutdown of TeX-Bot +* [`cogs/make_applicant`](cogs/make_applicant.py): cogs related to making users into applicants + * [`cogs/make_member.py`](cogs/make_member.py): cogs related to making guests into members * [`cogs/ping.py`](cogs/ping.py): cog to request a [ping](https://wikipedia.org/wiki/Ping-pong_scheme#Internet) response @@ -101,11 +107,11 @@ There are separate cog files for each activity, and one [`__init__.py`](cogs/__i * [`cogs/startup.py`](cogs/startup.py): cogs for startup & bot initialisation -* [`cogs/stats.py`](cogs/stats.py): cogs for displaying stats about your group's Discord guild, as well as its channels & Discord members +* [`cogs/stats/`](cogs/stats): cogs for displaying stats about your group's Discord guild, as well as its channels & Discord members * [`cogs/strike.py`](cogs/strike.py): cogs for applying moderation actions to Discord members -* [`cogs/write_roles.py`](cogs/write_roles.py): cogs relating to sending the message that contains all the opt-in roles, into the "#**roles**" channel +* [`cogs/write_roles.py`](cogs/write_roles.py): cogs relating to sending the message, that contains all the opt-in roles, into the "#**roles**" channel ## Making Your First Contribution @@ -149,7 +155,7 @@ It can be run with the following command: uv run mypy . ``` -Although there is [a PyCharm plugin](https://github.com/leinardi/mypy-pycharm#mypy-pycharm) to provide GUI control & inline warnings for [mypy](https://mypy-lang.org), it has been rather temperamental recently. +Although there is [a PyCharm plugin](https://github.com/leinardi/mypy-pycharm#mypy-pycharm) to provide GUI control and inline warnings for [mypy](https://mypy-lang.org), it has been rather temperamental recently. So it is suggested to avoid using it, and run [mypy](https://mypy-lang.org) from the command-line instead. #### PyMarkdown @@ -200,3 +206,317 @@ If you see an error, we encourage you to **be bold** and fix it yourself, rather If you are stuck, need help, or have a question, the best place to ask is on our Discord. Happy contributing! + +## Guides + +### Creating a New Cog + +Cogs are modular components of TeX-Bot that group related commands and listeners into a single class. To create a new cog, follow these steps: + +1. **Create the Cog File** + - Navigate to the `cogs/` directory. + - Create a new Python file with a name that reflects the purpose of the cog (e.g., `example_cog.py`). + +2. **Define the Cog Class** + - Import the necessary modules, including `TeXBotBaseCog` from `utils`. + - Define a class that inherits from `TeXBotBaseCog`. + - Add a docstring to describe the purpose of the cog. + + Example: + ```python + from utils import TeXBotBaseCog + + class ExampleCog(TeXBotBaseCog): + """A cog for demonstrating functionality.""" + + def do_something(arguments): + print("do something") + ``` + +3. **Add Commands and Listeners** + - Define methods within the class for commands and event listeners. + - Use decorators like `@discord.slash_command()` or `@TeXBotBaseCog.listener()` to register the callback method to their [interaction](TERMINOLOGY.md#Interactions) type. + - Include any necessary checks using `CommandChecks` decorators. + + Example: + ```python + import discord + from utils import CommandChecks + + __all__: "Sequence[str]" = ( + "ExampleCog", + ) + + class ExampleCog(TeXBotBaseCog): + """A cog for demonstrating functionality.""" + + ``` + +4. **Register the Cog** + - Edit `cogs/__init__.py` to add your new cog class to the list of cogs in the `setup` function. + - Also, include the cog class in the `__all__` sequence to ensure it is properly exported. + + Example: + ```python + from .example_cog import ExampleCog + + __all__: "Sequence[str]" = ( + ...existing cogs... + "ExampleCog", + ) + + def setup(bot: "TeXBot") -> None: + """Add all the cogs to the bot, at bot startup.""" + cogs: Iterable[type[TeXBotBaseCog]] = ( + ...existing cogs... + ExampleCog, + ) + Cog: type[TeXBotBaseCog] + for Cog in cogs: + bot.add_cog(Cog(bot)) + ``` + +5. **Test the Cog** + - Run the bot with your changes and ensure the new cog is loaded without errors. + - Test the commands and listeners to verify they work as expected. + +6. **Document the Cog** + - Add comments and docstrings to explain the functionality of the cog. + - Update the `CONTRIBUTING.md` file or other relevant documentation if necessary. + +### Creating a New Environment Variable + +To add a new environment variable to the project, follow these steps: + +1. **Define the Variable in development `.env`** + - Open the `.env` file in the project root directory (or create one if it doesn't exist). + - Add the new variable in the format `VARIABLE_NAME=value`. + - Ensure the variable name is descriptive and uses uppercase letters with underscores. + +2. **Update `config.py`** + - Open the `config.py` file. + - Add a new private setup method in the `Settings` class to validate and load the variable. For example: + ```python + @classmethod + def _setup_new_variable(cls) -> None: + raw_value: str | None = os.getenv("NEW_VARIABLE") + + if not raw_value or not re.fullmatch(r"", raw_value): + raise ImproperlyConfiguredError("NEW_VARIABLE is invalid or missing.") + + cls._settings["NEW_VARIABLE"] = raw_value + ``` + - Replace `` with a regular expression to validate the variable's format, if applicable. + +3. **Call the Setup Method** + - Add the new setup method to the `_setup_env_variables` method in `config.py`: + ```python + @classmethod + def _setup_env_variables(cls) -> None: + if cls._is_env_variables_setup: + logger.warning("Environment variables have already been set up.") + return + + cls._settings = {} + + cls._setup_new_variable() + # Add other setup methods here + + cls._is_env_variables_setup = True + ``` + +4. **Document the Variable** + - Update the `README.md` file under the "Setting Environment Variables" section to include the new variable, its purpose and any valid values. + +5. **Test the Variable** + - Run the bot with your changes and ensure the new variable is loaded correctly. + - Test edge cases, such as missing, blank or invalid values in the `.env` file, to confirm that error handling functions correctly. + +### Creating a Response Button + +Response buttons are interactive UI components that allow users to respond to bot messages with predefined actions. To create a response button, follow these steps: + +1. **Define the Button Class** + - Create a new class in your cog file that inherits from `discord.ui.View`. + - Add button callback response methods using the `@discord.ui.button` decorator. + - Each button method should define the button's label, a custom response ID and style. + + Example: + ```python + from discord.ui import View + + class ConfirmActionView(View): + """A discord.View containing buttons to confirm or cancel an action.""" + + @discord.ui.button( + label="Yes", + style=discord.ButtonStyle.green, + custom_id="confirm_yes", + ) + async def confirm_yes(self, button: discord.Button, interaction: discord.Interaction) -> None: + # Handle the 'Yes' button click + await interaction.response.send_message("Action confirmed.", ephemeral=True) + + @discord.ui.button( + label="No", + style=discord.ButtonStyle.red, + custom_id="confirm_no", + ) + async def confirm_no(self, button: discord.Button, interaction: discord.Interaction) -> None: + # Handle the 'No' button click + await interaction.response.send_message("Action cancelled.", ephemeral=True) + ``` + +2. **Send the View with a Message** + - Use the `view` parameter of the `send` or `respond` method to attach the button view to a message. This could be sent in response to a command, event handler or scheduled task. + + Example: + ```python + await ctx.send( + content="Do you want to proceed?", + view=ConfirmActionView(), + ) + ``` + +3. **Handle Button Interactions** + - Define logic within each button method to handle user interactions. + - Use `interaction.response` to send feedback or perform actions based on the button clicked. + +4. **Test the Button** + - Run the bot with your changes and ensure the buttons appear and function as expected. + - Test edge cases, such as multiple users interacting with the buttons simultaneously. + +5. **Document the Button** + - Add comments and docstrings to explain the purpose and functionality of the button. + - Update relevant documentation if necessary. + +### Creating and Interacting with Django Models + +#### Data Protection Consideration + +When making changes to the database model, it is essential to consider the data protection implications of these changes. If personal data is being collected, stored or processed, it is essential that this is in compliance with the law. In the UK, the relevant law is the [Data Protection Act 2018](https://www.legislation.gov.uk/ukpga/2018/12/contents). As a general rule, any changes that have data protection implications should be checked and approved by the organisation responsible for running the application. +Django models are used to interact with the database in this project. They allow you to define the structure of your data and provide an API to query and manipulate it. To create and interact with Django models, follow these steps: + +1. **Define a Model** + - Navigate to the `db/core/models/` directory. + - Create a new Python file with a name that reflects the purpose of the model (e.g., `example_model.py`). + - If your model is a new property related to each Discord member (E.g. the number of smiley faces of each Discord member, then define a class that inherits from `BaseDiscordMemberWrapper` (found within the `.utils` module within the `db/core/models/` directory). + - If your model is unrelated to Discord members, then define a class that inherits from `AsyncBaseModel` (also found within the `.utils` module within the `db/core/models/` directory). + - Add your Django fields to the class to represent the model's data structure. + - If your model inherits from `BaseDiscordMemberWrapper` then you *must* declare a field called `discord_member` which must be either a Django `ForeignKey` field or a `OneToOneField`, depending upon the relationship between your model and each Discord member. + - Define the static class string holding the display name for multiple instances of your class (E.g., `INSTANCES_NAME_PLURAL: str = "Members' Smiley Faces"`) + + Example: + ```python + from django.db import models + + from .utils import AsyncBaseModel, BaseDiscordMemberWrapper + + class ExampleModel(AsyncBaseModel): + """A model for demonstrating functionality.""" + + INSTANCES_NAME_PLURAL: str = "Example Model objects" + + name = models.CharField(max_length=255) + created_at = models.DateTimeField(auto_now_add=True) + class MemberSmileyFaces(BaseDiscordMemberWrapper): + """Model to represent the number of smiley faces of each Discord member.""" + + INSTANCES_NAME_PLURAL: str = "Discord Members' Smiley Faces" + + discord_member = models.OneToOneField( + DiscordMember, + on_delete=models.CASCADE, + related_name="smiley_faces", + verbose_name="Discord Member", + blank=False, + null=False, + primary_key=True, + ) + count = models.IntegerField( + "Number of smiley faces", + null=False, + blank=True, + default=0, + ) + +2. **Apply Migrations** + - Run the following commands to create and apply migrations for your new model: + ```shell + uv run manage.py makemigrations + uv run manage.py migrate + ``` + +3. **Query the Model** + - Use Django's ORM to interact with the model. For example: + ```python + from db.core.models.example_model import ExampleModel + + class ExampleCog(TeXBotBaseCog): + """A cog for demonstrating model access.""" + + async def create_example(self, name: str) -> None: + """Create a new instance of ExampleModel.""" + await ExampleModel.objects.acreate(name=name) + + async def retrieve_examples(self) -> list[ExampleModel]: + """Retrieve all instances of ExampleModel.""" + return await ExampleModel.objects.all() + + async def filter_examples(self, name: str) -> list[ExampleModel]: + """Filter instances of ExampleModel by name.""" + return await ExampleModel.objects.filter(name=name) + + async def update_example(self, example: ExampleModel, new_name: str) -> None: + """Update the name of an ExampleModel instance.""" + example.name = new_name + await example.asave() + + async def delete_example(self, example: ExampleModel) -> None: + """Delete an ExampleModel instance.""" + await example.adelete() + ``` + +4. **Document the Model** + - Add comments and docstrings to explain the purpose and functionality of your new model. + +### Member Retrieval DB Queries via Hashed Discord ID + +To retrieve members from the database using their hashed Discord ID, follow these steps: + +1. **Hash the Discord ID** + - Use a consistent hashing algorithm to hash the Discord ID before storing or querying it in the database. + + Example: + ```python + import hashlib + + def hash_discord_id(discord_id: str) -> str: + return hashlib.sha256(discord_id.encode()).hexdigest() + ``` + +2. **Query the Database** + - A custom filter field is implemented for models that inherit from `BaseDiscordMemberWrapper`, this allows you to filter using the unhashed Discord ID, despite only the hashed Discord ID being stored in the database. + + Example: + ```python + from db.core.models.member_smiley_faces import MemberSmileyFaces + + class MyExampleCommandCog(TeXBotBaseCog): + @tasks.loop(minutes=5) + async def check_member_smiley_faces(self) -> None: + member_smiley_faces = await MemberSmileyFaces.objects.filter(discord_id=1234567).afirst() + + if member_smiley_faces: + print(f"Member's smiley faces found: {member_smiley_faces.discord_member.name}") + else: + print("Member's smiley facesnot found.") + ``` + + It is unlikely that you will need to query the `DiscordMember` model directly. Instead, the attributes of the member can be accessed by the relationship between each new Django model to the `DiscordMember` model. + +3. **Test the Query** + - Ensure the query works as expected by testing it with valid and invalid Discord IDs. + +4. **Document the Query** + - Add comments and docstrings to explain the purpose and functionality of the query. diff --git a/README.md b/README.md index f24eb69d5..99b9f2a1b 100644 --- a/README.md +++ b/README.md @@ -15,44 +15,10 @@ This is a [Discord bot](https://discord.com/developers/docs/topics/oauth2#bot-vs Featured in the [CSS Discord guild](https://cssbham.com/discord). -## Terminology - -### ["Guild"](https://discord.com/developers/docs/resources/guild) Vs ["Server"](https://wikipedia.org/wiki/Discord#Servers) - -Confusingly, [Discord](https://discord.com) uses the term ["guild"](https://discord.com/developers/docs/resources/guild) to refer to a [Discord "server"](https://wikipedia.org/wiki/Discord#Servers), when communicating with developers. -Therefore, the same terminology (["guild"](https://discord.com/developers/docs/resources/guild)) will be used across all documentation in this project. -(See [the Discord developer docs](https://discord.com/developers/docs/resources/guild) & [Pycord's docs](https://docs.pycord.dev/en/stable/api/models.html#discord.Guild) for more information.) - -The term "main guild" is used throughout the code in this repository to refer specifically to your community group's main [Discord guild](https://discord.com/developers/docs/resources/guild). - -### "User" Vs "Member" Vs "Guest" - -#### [Discord Objects](https://discord.com/developers/docs) - -In the context of [Discord](https://discord.com) itself, a ["user"](https://discord.com/developers/docs/resources/user) object represents a [Discord](https://discord.com) account not connected to any specific [guild](https://discord.com/developers/docs/resources/guild). -Therefore, it can be [messaged via DM](https://dictionary.com/browse/dm) or be retrieved via its [snowflake ID](https://discord.com/developers/docs/reference#snowflakes), but little else can be done with it. -(See [the Discord developer docs](https://discord.com/developers/docs/resources/user) & [Pycord's docs](https://docs.pycord.dev/en/stable/api/models.html#users) for more information.) - -In contrast, a [Discord "member" object](https://discord.com/developers/docs/resources/guild#guild-member-object) is a [user](https://discord.com/developers/docs/resources/user) attached to a specific [guild](https://discord.com/developers/docs/resources/guild). -Therefore, it can have [roles](https://discord.com/developers/docs/topics/permissions#role-object), be [banned](https://discord.com/developers/docs/resources/guild#ban-object) & have many other actions applied to it. -(See [the Discord developer docs](https://discord.com/developers/docs/resources/guild#guild-member-object) & [Pycord's docs](https://docs.pycord.dev/en/stable/api/models.html#discord.Member) for more information.) - -#### Community Group Membership - -In the context of your community group's membership structure, a "member" is a person that has purchased a membership to join your community group. -This is in contrast to a "guest", which is a person that has not purchased a membership. -Guests often can only attend events that are open to anyone (i.e. **not** members only), and have limited communication/perks within your [Discord guild](https://discord.com/developers/docs/resources/guild). -Some commands may require you to create [roles](https://discord.com/developers/docs/topics/permissions#role-object) within your [Discord guild](https://discord.com/developers/docs/resources/guild), to differentiate between these different types of users. - -#### Other Uses - -In some other contexts, the term "user" may be used to refer to any person/organisation making use of this project. -(E.g. the description within [the "Error Codes" section](#error-codes).) - ## Error Codes Users of TeX-Bot may encounter an error code when executing a slash-command fails. -If a user encounters any of these errors, please communicate the error to the committee member that has been assigned to upkeep & deployment of your instance of TeX-Bot. +If a user encounters any of these errors, please communicate the error to the committee member that has been assigned to upkeep and deployment of your instance of TeX-Bot. The meaning of each error code is given here: * `E1011` - The value for the [environment variable](https://wikipedia.org/wiki/Environment_variable) `DISCORD_GUILD_ID` is an [ID](https://discord.com/developers/docs/reference#snowflakes) that references a [Discord guild](https://discord.com/developers/docs/resources/guild) that does not exist @@ -124,7 +90,7 @@ The conditions for each [task](https://docs.pycord.dev/en/stable/ext/tasks) are ## Deploying in Production The only supported way to deploy TeX-Bot in production is by using our pre-built [docker container](https://docs.docker.com/resources/what-container). -It is can be pulled from the [GitHub Container Registry](https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-container-registry) with this identifier: [`ghcr.io/CSSUoB/tex-bot-py-v2:latest`](https://github.com/CSSUoB/TeX-Bot-Py-V2/pkgs/container/tex-bot-py-v2). +It can be pulled from the [GitHub Container Registry](https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-container-registry) with this identifier: [`ghcr.io/CSSUoB/tex-bot-py-v2:latest`](https://github.com/CSSUoB/TeX-Bot-Py-V2/pkgs/container/tex-bot-py-v2). (An introduction on how to use a [docker-compose deployment](https://docs.docker.com/compose) can be found [here](https://docs.docker.com/get-started/08_using_compose).) See [**Versioning**](#versioning) for the full list of available version tags for each release. @@ -170,7 +136,7 @@ You'll also need to set a number of [environment variables](https://wikipedia.or (This setting is optional. Error logs will **always** be sent to the [console](https://wikipedia.org/wiki/Terminal_emulator), this setting allows them to also be sent to a [Discord log channel](https://docs.pycord.dev/en/stable/api/models.html#discord.TextChannel).) -* `ORGANISATION_ID`: Your Guild society ID. This is used to dynamically create the members list among other needed URLs. +* `ORGANISATION_ID`: Your Guild society ID. This is used to dynamically fetch your community group's list of members, among other necessary URLs. * `MEMBERS_LIST_URL_SESSION_COOKIE`: The members-list [URL](https://wikipedia.org/wiki/URL) [session cookie](https://wikipedia.org/wiki/HTTP_cookie#Session_cookie). (If your group's members-list is stored at a [URL](https://wikipedia.org/wiki/URL) that requires [authentication](https://wikipedia.org/wiki/Authentication), this [session cookie](https://wikipedia.org/wiki/HTTP_cookie#Session_cookie) should [authenticate](https://wikipedia.org/wiki/Authentication) TeX-Bot to view your group's members-list, as if it were [logged in to the website](https://wikipedia.org/wiki/Login_session) as a Committee member. @@ -210,7 +176,7 @@ This will ensure your code meets the standard required for this project and give This project follows the [semantic versioning scheme](https://semver.org). We currently treat TeX-Bot as alpha software, and as such no numbered release has been made yet. -When selecting a version tag to use for [deploying TeX-Bot as a container image](#deploying-in-production) there are multiple tag schemes available: +When selecting a version tag to use for [deploying TeX-Bot as a container image](#deploying-in-production), there are multiple tag schemes available: * `latest` - The most recent numerically tagged version released * `br-` - The most recent commit from a given branch in this repository (E.g. `br-main`) (N.B. this does not include branches of forks of this repository) diff --git a/TERMINOLOGY.md b/TERMINOLOGY.md new file mode 100644 index 000000000..bb20f4f5f --- /dev/null +++ b/TERMINOLOGY.md @@ -0,0 +1,78 @@ +# TeX-Bot-Py-V2 Terminology + +## ["Guild"](https://discord.com/developers/docs/resources/guild) Vs ["Server"](https://wikipedia.org/wiki/Discord#Servers) + +Confusingly, [Discord](https://discord.com) uses the term ["guild"](https://discord.com/developers/docs/resources/guild) to refer to a [Discord "server"](https://wikipedia.org/wiki/Discord#Servers), when communicating with developers. +Therefore, the same terminology (["guild"](https://discord.com/developers/docs/resources/guild)) will be used across all documentation in this project. +(See [the Discord developer docs](https://discord.com/developers/docs/resources/guild) & [Pycord's docs](https://docs.pycord.dev/en/stable/api/models.html#discord.Guild) for more information.) + +The term "main guild" is used throughout the code in this repository to refer specifically to your community group's main [Discord guild](https://discord.com/developers/docs/resources/guild). + + +## [Interactions](https://discord.com/developers/docs/interactions/overview) + +Interactions in Discord are a way for bots to communicate with users in a structured and interactive manner. They are the initial entry point to allow users to begin interacting with the bot. (Note that a Discord bot's functionality could also be triggered via non-interaction methods like scheduled tasks or event handlers.) The primary types of interactions are: + +1. **Application Commands**: These include Slash Commands, Message Commands, and User Commands. They are predefined commands that users can invoke to perform specific actions. +2. **Message Components**: These are interactive elements like buttons, select menus, and modals that can be attached to messages to provide a richer user experience. +3. **Modal Submissions**: These are forms that users can fill out and submit, allowing bots to collect structured input from users, though TeX-Bot does not currently make use of these. + +For more details, refer to the [Discord Developer Documentation on Interactions](https://discord.com/developers/docs/interactions/overview). + + +## [Application Commands](https://discord.com/developers/docs/interactions/application-commands) + + +An [Application Command](https://discord.com/developers/docs/interactions/application-commands) can be a [Slash Command](#slash-commands), [Message Command](#message-commands) or [User Command](#user-commands). + +Bots are limited to only 100 [Slash Commands](#slash-commands), 5 [Message Commands](#message-commands) and 5 [User Commands](#user-commands). + +The primary difference is the way these commands are triggered. [Slash Commands](#slash-commands) are triggered by sending a message into the chat, while both [User Commands](#user-commands) and [Message Commands](#message-commands) are triggered via a UI context menu provided by right-clicking on a [User](https://discord.com/developers/docs/resources/user) or [Message](https://discord.com/developers/docs/resources/message) respectively. + + +### [Slash Commands](https://discord.com/developers/docs/interactions/application-commands#slash-commands) + + +Slash commands, also known as `CHAT_INPUT` commands are executed via sending a chat message and are made up of a name, description and a set of options. These can be defined using the `@discord.slash_command()` decorator. + + +For example usages, check the [Guides section](CONTRIBUTING.md#Guides) of the [CONTRIBUTING.md document](CONTRIBUTING.md). + + +### Context Commands + +#### [Message Commands](https://discord.com/developers/docs/interactions/application-commands#message-commands) + + +Message-context commands are executed via right-clicking on a [Discord *Message*](https://discord.com/developers/docs/resources/message), clicking "Apps", then selecting the command from the menu. The interaction callback method is provided information about which message was clicked, along with which user clicked it. + +The main difference between [Context Commands](#context-commands) and [Slash Commands](#slash-commands) is that [Context Commands](#context-commands) do not take user defined arguments and are limited to the [Message](https://discord.com/developers/docs/resources/message) that the command is issued on and the context which is passed alongside it. + + +#### [User Commands](https://discord.com/developers/docs/interactions/application-commands#user-commands) + + + +## "User" Vs "Member" Vs "Guest" + +### [Discord Objects](https://discord.com/developers/docs) + +In the context of [Discord](https://discord.com) itself, a ["user"](https://discord.com/developers/docs/resources/user) object represents a [Discord](https://discord.com) account not connected to any specific [guild](https://discord.com/developers/docs/resources/guild). +Therefore, it can be [messaged via DM](https://dictionary.com/browse/dm) or be retrieved via its [snowflake ID](https://discord.com/developers/docs/reference#snowflakes), but little else can be done with it. +(See [the Discord developer docs](https://discord.com/developers/docs/resources/user) & [Pycord's docs](https://docs.pycord.dev/en/stable/api/models.html#users) for more information.) + +In contrast, a [Discord "member" object](https://discord.com/developers/docs/resources/guild#guild-member-object) is a [user](https://discord.com/developers/docs/resources/user) attached to a specific [guild](https://discord.com/developers/docs/resources/guild). +Therefore, it can have [roles](https://discord.com/developers/docs/topics/permissions#role-object), be [banned](https://discord.com/developers/docs/resources/guild#ban-object) and have many other actions applied to it. +(See [the Discord developer docs](https://discord.com/developers/docs/resources/guild#guild-member-object) & [Pycord's docs](https://docs.pycord.dev/en/stable/api/models.html#discord.Member) for more information.) + +### Community Group Membership + +In the context of your community group's membership structure, a "member" is a person that has purchased a membership to join your community group. +This is in contrast to a "guest", which is a person that has not purchased a membership. +Guests often can only attend events that are open to anyone (i.e. **not** members only), and have limited communication/perks within your [Discord guild](https://discord.com/developers/docs/resources/guild). +Some commands may require you to create [roles](https://discord.com/developers/docs/topics/permissions#role-object) within your [Discord guild](https://discord.com/developers/docs/resources/guild), to differentiate between these different types of users. + +### Other Uses + +In some other contexts, the term "user" may be used to refer to any person/organisation making use of this project. +(E.g. the description within [the "Error Codes" section](README.md#error-codes).)