Skip to content
Open
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
e26957f
Fix invite url docs
MattyTheHacker Aug 23, 2024
9a23142
move terminology to new file
MattyTheHacker Aug 23, 2024
e99b152
start contributing guides
MattyTheHacker Aug 23, 2024
36bcb41
formatting
MattyTheHacker Aug 23, 2024
f228d3d
Merge branch 'main' into docs-updates
MattyTheHacker Aug 23, 2024
3cf257e
add some stuff
MattyTheHacker Aug 23, 2024
a60e460
add
MattyTheHacker Aug 23, 2024
36097b3
add links
MattyTheHacker Aug 23, 2024
1f0b222
add example
MattyTheHacker Aug 26, 2024
a085679
add missing cogs from list
MattyTheHacker Aug 26, 2024
37c545e
Merge branch 'main' into docs-updates
MattyTheHacker Aug 26, 2024
4e3d8a7
add link
MattyTheHacker Aug 26, 2024
da2f219
fix link
MattyTheHacker Aug 26, 2024
c007fbe
add backtick
MattyTheHacker Aug 26, 2024
b6f7362
add some stuff
MattyTheHacker Aug 26, 2024
1d5dd11
Merge branch 'main' into docs-updates
MattyTheHacker Sep 25, 2024
bb15019
Merge branch 'main' into docs-updates
MattyTheHacker Dec 23, 2024
c5baccc
Merge branch 'main' into docs-updates
CarrotManMatt Mar 5, 2025
9092bde
Merge branch 'main' into docs-updates
MattyTheHacker Mar 5, 2025
7b7d944
Merge branch 'main' into docs-updates
MattyTheHacker Mar 18, 2025
e60baa3
Merge branch 'main' into docs-updates
MattyTheHacker Apr 2, 2025
22ebb49
Add some stuff
MattyTheHacker Apr 2, 2025
826ac38
Merge branch 'main' into docs-updates
MattyTheHacker Apr 9, 2025
51d79e7
fix warnings
MattyTheHacker Apr 9, 2025
9bf2ebe
remove empty whitespace
MattyTheHacker Apr 9, 2025
8a53cf4
Merge branch 'main' into docs-updates
MattyTheHacker May 10, 2025
a5423b3
more terms
MattyTheHacker May 10, 2025
149c811
Add example cog
MattyTheHacker May 10, 2025
0c24b54
env example
MattyTheHacker May 10, 2025
ef4615d
response button
MattyTheHacker May 10, 2025
b9810d6
Merge branch 'main' into docs-updates
MattyTheHacker May 12, 2025
64207e9
Merge branch 'main' into docs-updates
MattyTheHacker May 13, 2025
dd61e51
blah blah
MattyTheHacker May 13, 2025
a8ab5c8
db stuff first run
MattyTheHacker May 13, 2025
1e5355b
add gdpr warning
MattyTheHacker May 15, 2025
581fb4c
Merge branch 'main' into docs-updates
MattyTheHacker May 18, 2025
762d7f8
Merge branch 'main' into docs-updates
MattyTheHacker May 20, 2025
e0c23a1
Merge branch 'main' into docs-updates
MattyTheHacker May 23, 2025
1a1c6c9
Merge branch 'main' into docs-updates
MattyTheHacker May 25, 2025
9523681
Merge branch 'main' into docs-updates
MattyTheHacker May 27, 2025
88e2ab5
Merge branch 'main' into docs-updates
MattyTheHacker May 27, 2025
84430ff
Apply suggestions from code review
MattyTheHacker Jun 3, 2025
c2087b5
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Jun 3, 2025
d1a247e
Merge branch 'main' into docs-updates
MattyTheHacker Jun 3, 2025
03c02fe
Merge main into docs-updates
cssbhamdev Jun 12, 2025
eea9b23
Merge main into docs-updates
cssbhamdev Jun 13, 2025
5edaf39
Fix minor docs errors
CarrotManMatt Jun 13, 2025
c7016f6
Merge main into docs-updates
cssbhamdev Jun 13, 2025
61671d3
Merge main into docs-updates
cssbhamdev Jun 14, 2025
1a6ff75
Allow committee-elect to update actions (and appear in auto-complete)…
Thatsmusic99 Jun 15, 2025
06ee380
Merge main into docs-updates
cssbhamdev Jun 15, 2025
a8ab0d3
Merge main into docs-updates
cssbhamdev Jun 15, 2025
8a669f9
Merge main into docs-updates
cssbhamdev Jun 15, 2025
e116a5a
Merge main into docs-updates
cssbhamdev Jun 15, 2025
6cb7e66
Merge main into docs-updates
cssbhamdev Jun 16, 2025
6afec1e
Merge main into docs-updates
cssbhamdev Jun 16, 2025
428a7a4
Merge main into docs-updates
cssbhamdev Jun 17, 2025
a9e3edb
Merge main into docs-updates
cssbhamdev Jun 19, 2025
125cd01
Merge main into docs-updates
cssbhamdev Jun 19, 2025
451d226
Merge main into docs-updates
cssbhamdev Jun 22, 2025
af03fd9
Merge main into docs-updates
cssbhamdev Jun 24, 2025
546bb76
Merge main into docs-updates
cssbhamdev Jun 24, 2025
f989c5f
Merge main into docs-updates
cssbhamdev Jun 24, 2025
32e7170
Merge main into docs-updates
cssbhamdev Jun 25, 2025
4c7f321
Merge main into docs-updates
cssbhamdev Jun 30, 2025
9d4d4de
Merge main into docs-updates
automatic-pr-updater[bot] Jun 30, 2025
0f1a935
Merge main into docs-updates
automatic-pr-updater[bot] Jul 2, 2025
377b675
Merge main into docs-updates
automatic-pr-updater[bot] Jul 2, 2025
36f7c3f
Merge main into docs-updates
automatic-pr-updater[bot] Jul 3, 2025
2c2381b
Merge main into docs-updates
automatic-pr-updater[bot] Jul 3, 2025
434f1f5
Merge main into docs-updates
automatic-pr-updater[bot] Jul 3, 2025
8a29e7e
Merge main into docs-updates
automatic-pr-updater[bot] Jul 4, 2025
62377c6
Merge main into docs-updates
automatic-pr-updater[bot] Jul 4, 2025
af7aefd
Merge main into docs-updates
automatic-pr-updater[bot] Jul 4, 2025
0bec33f
Merge main into docs-updates
automatic-pr-updater[bot] Jul 4, 2025
548c205
Merge main into docs-updates
automatic-pr-updater[bot] Jul 4, 2025
45017c3
Merge main into docs-updates
automatic-pr-updater[bot] Jul 4, 2025
83bea74
Merge main into docs-updates
automatic-pr-updater[bot] Jul 5, 2025
edcd5d0
Merge main into docs-updates
automatic-pr-updater[bot] Jul 5, 2025
d7be59c
Merge main into docs-updates
automatic-pr-updater[bot] Jul 5, 2025
8c31a1f
Merge main into docs-updates
automatic-pr-updater[bot] Jul 5, 2025
08b22be
Merge main into docs-updates
automatic-pr-updater[bot] Jul 5, 2025
1942666
Merge main into docs-updates
automatic-pr-updater[bot] Jul 6, 2025
8920378
Merge main into docs-updates
automatic-pr-updater[bot] Jul 7, 2025
6a4b9bc
Merge main into docs-updates
automatic-pr-updater[bot] Jul 9, 2025
e065577
Merge main into docs-updates
automatic-pr-updater[bot] Jul 9, 2025
d8bd8b7
Merge main into docs-updates
automatic-pr-updater[bot] Jul 11, 2025
a98f5a9
Merge main into docs-updates
automatic-pr-updater[bot] Jul 14, 2025
23580ea
Merge main into docs-updates
automatic-pr-updater[bot] Jul 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
295 changes: 295 additions & 0 deletions CONTRIBUTING.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add inline hyperlinks from any defined terms to their definitions within TERMINOLOGY.md.
I have provided one example in a suggestion.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -200,3 +206,292 @@ 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 the 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` folder.
- Create a new Python file with a name that reflects the purpose of the cog (e.g., `example_cog.py`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't suggest an example file name that includes the word cog.


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 specify their purpose.
- 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."""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing methods with decorators in example.

```

4. **Register the Cog**
- Open `cogs/__init__.py`.
- Add your new cog class to the list of cogs in the `setup` function.
- Also, include the cog 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 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 `.env`**
- Open the `.env` file in the 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 setup method in the `Settings` class to validate and load the variable. For example:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more description to "load the variable". Discuss saving as a key in the internal settings dict, along with the requirement that all keys must be present in every circumstance, but some values may be None

```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"<validation_regex>", raw_value):
raise ImproperlyConfiguredError("NEW_VARIABLE is invalid or missing.")

cls._settings["NEW_VARIABLE"] = raw_value
```
- Replace `<validation_regex>` 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention adding documentation of variable to .env.example


5. **Test the Variable**
- Run the bot and ensure the new variable is loaded correctly.
- Test edge cases, such as missing or invalid values, to confirm proper error handling.

### 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 class that inherits from `discord.ui.View`.
- Add button methods using the `@discord.ui.button` decorator.
- Each button method should define the button's label, style, and custom ID.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Use docstrings rather than comments
  • Use double speech marks

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Use docstrings rather than comments
  • Use double speech marks

await interaction.response.send_message("Action canceled.", 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.

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.
Comment on lines +382 to +383
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add example code of handling user interaction in a button method


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discuss alternative interaction handling where processing continues inside the original cog method that called the button, rather than inside the button handling function.

E.g., the strike commands create have buttons where the callback methods only functionality is to remove the button from the current message, and processing continues inside the command callback with self.bot.wait_for() and button_interaction.data["custom_id"] ==:

async def yes_strike_member_button_callback( # type: ignore[misc]
self, _: discord.Button, interaction: discord.Interaction
) -> None:
"""
Delete the message associated with the view when the Yes button is pressed.
This function is attached as a button's callback, so will run whenever the button
is pressed.
The actual handling of the event is done by the command that sent the view,
so all that is required is to delete the original message that sent this view.
"""
logger.debug('"Yes" button pressed. %s', interaction)
await interaction.response.edit_message(
view=None
) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error
@discord.ui.button(
label="No",
style=discord.ButtonStyle.grey,
custom_id="no_strike_member",
)
async def no_strike_member_button_callback( # type: ignore[misc]
self, _: discord.Button, interaction: discord.Interaction
) -> None:
"""
Delete the message associated with the view when the No button is pressed.
This function is attached as a button's callback, so will run whenever the button
is pressed.
The actual handling of the event is done by the command that sent the view,
so all that is required is to delete the original message that sent this view.
"""
logger.debug('"No" button pressed. %s', interaction)
await interaction.response.edit_message(
view=None
) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error

await message_sender_component.send(
content=confirm_strike_message,
view=ConfirmStrikeMemberView(),
)
button_interaction: discord.Interaction = await self.bot.wait_for(
"interaction",
check=lambda interaction: (
interaction.type == discord.InteractionType.component
and interaction.user == interaction_user
and interaction.channel == button_callback_channel
and "custom_id" in interaction.data
and interaction.data["custom_id"] in {"yes_strike_member", "no_strike_member"}
),
)
if button_interaction.data["custom_id"] == "no_strike_member": # type: ignore[index, typeddict-item]
await button_interaction.edit_original_response(
content=(
"Aborted performing "
f"{self.SUGGESTED_ACTIONS[actual_strike_amount]} action "
f"on {strike_user.mention}."
),
view=None,
)
return
if button_interaction.data["custom_id"] == "yes_strike_member": # type: ignore[index, typeddict-item]

4. **Test the Button**
- Run the bot 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`).
- Define a class that inherits from `django.db.models.Model`.
- Add fields to the class to represent the data structure.

Example:
```python
from django.db import models

class ExampleModel(models.Model):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename model name to not include the term "model"

"""A model for demonstrating functionality."""
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
```

2. **Apply Migrations**
- Run the following commands to create and apply migrations for your new model:
```shell
uv run python manage.py makemigrations
uv run python 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename model to not include the term "model"


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()
Comment on lines +458 to +477
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These examples are a bit too simplistic to correctly guide the usage of the ORM query methods. This could work better as a single example cog command, which creates, filters, updates and deletes based on some command options.

```

4. **Document the Model**
- Add comments and docstrings to explain the purpose and functionality of the 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()
```
Comment on lines +487 to +496
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hashing algorithm of Discord IDs is already implemented and will not need to be changed in new models. This section can be removed from the guide because editing the functionality of the hashing algorithm presumes a level of knowledge beyond these starter guides.


2. **Query the Database**
- Use the hashed Discord ID to retrieve the corresponding member from the database.

Example:
```python
from db.core.models.member import Member

hashed_id = hash_discord_id("123456789012345678")
member = Member.objects.filter(hashed_discord_id=hashed_id).first()

if member:
print(f"Member found: {member.name}")
else:
print("Member not found.")
```

3. **Test the Query**
- Ensure the query works as expected by testing it with valid and invalid hashed Discord IDs.

4. **Document the Query**
- Add comments and docstrings to explain the purpose and functionality of the query.
Comment on lines +521 to +522
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary suggestion for documentation. (Code should be self commenting and only require comments when functionality is unexpected or surprising.)




36 changes: 1 addition & 35 deletions README.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is intended to resolve #182, then the list of features is missing. If this PR is not intended to resolve that issue, please unlink it from this PR.

Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,6 @@ 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.
Expand Down Expand Up @@ -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.

Expand Down
Loading