Compare commits
32 commits
73d3449894
...
fb9a208dd4
Author | SHA1 | Date | |
---|---|---|---|
fb9a208dd4 | |||
f5b591c31e | |||
335882befd | |||
8aaa5bd33e | |||
afc6735e90 | |||
aa34b36608 | |||
65c35a422c | |||
9637f5209f | |||
39d286b559 | |||
ca78a197b9 | |||
719e040f2e | |||
6e0b13bb20 | |||
1a78475bc6 | |||
470b4d86d7 | |||
4d79320b39 | |||
ec08130f99 | |||
523a3a1e89 | |||
90e04cb70a | |||
9fb796d7f2 | |||
2ecc2cd318 | |||
d2c4b19610 | |||
6060c78056 | |||
25a70b925e | |||
03c43710e0 | |||
14fe976a0d | |||
f3f99209da | |||
2c7b0f5441 | |||
eb331faf55 | |||
3e4efa9220 | |||
b80dedbda2 | |||
872e1aabff | |||
bab80d6344 |
42 changed files with 2423 additions and 3347 deletions
34
.devcontainer/Dockerfile
Normal file
34
.devcontainer/Dockerfile
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
FROM ghcr.io/astral-sh/uv:0.5.24@sha256:2381d6aa60c326b71fd40023f921a0a3b8f91b14d5db6b90402e65a635053709 AS uv
|
||||||
|
FROM python:3.11-slim@sha256:6ed5bff4d7d377e2a27d9285553b8c21cfccc4f00881de1b24c9bc8d90016e82 AS python
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/vscode/devcontainers/base:bookworm@sha256:6155a486f236fd5127b76af33086029d64f64cf49dd504accb6e5f949098eb7e
|
||||||
|
LABEL repository="www.coastalcommits.com/cswimr/SeaCogs"
|
||||||
|
LABEL maintainer="cswimr <seaswimmerthefsh@gmail.com>"
|
||||||
|
|
||||||
|
RUN apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
# Red-DiscordBot
|
||||||
|
build-essential \
|
||||||
|
git \
|
||||||
|
# PyNaCl
|
||||||
|
libsodium-dev \
|
||||||
|
# CFFI
|
||||||
|
libffi-dev \
|
||||||
|
# SSH repository support
|
||||||
|
openssh-client \
|
||||||
|
# Cog dependencies
|
||||||
|
# Audio
|
||||||
|
openjdk-17-jre-headless \
|
||||||
|
# PyLav
|
||||||
|
libaio1 \
|
||||||
|
libaio-dev \
|
||||||
|
# SeaUtils
|
||||||
|
dnsutils; \
|
||||||
|
apt-get clean; \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=uv --chown=vscode: /uv /uvx /bin/
|
||||||
|
COPY --from=python --chown=vscode: /usr/local /usr/local
|
||||||
|
|
||||||
|
RUN ln -s /usr/local/bin/python3.11 /usr/local/bin/python; \
|
||||||
|
python --version
|
35
.devcontainer/devcontainer.json
Normal file
35
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "Red-DiscordBot: SeaCogs",
|
||||||
|
"build": {
|
||||||
|
"context": "..",
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
},
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"charliermarsh.ruff",
|
||||||
|
"ms-azuretools.vscode-docker",
|
||||||
|
"ms-python.python",
|
||||||
|
"tekumara.typos-vscode",
|
||||||
|
"tamasfe.even-better-toml",
|
||||||
|
"redhat.vscode-yaml",
|
||||||
|
"DavidAnson.vscode-markdownlint",
|
||||||
|
"yy0931.vscode-sqlite3-editor",
|
||||||
|
"aaron-bond.better-comments",
|
||||||
|
"donjayamanne.githistory",
|
||||||
|
"eamodio.gitlens"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"containerEnv": {
|
||||||
|
"DISPLAY": "dummy",
|
||||||
|
"PYTHONUNBUFFERED": "True",
|
||||||
|
"UV_LINK_MODE": "copy",
|
||||||
|
"UV_PYTHON_PREFERENCE": "only-system",
|
||||||
|
"UV_PYTHON_DOWNLOADS": "never",
|
||||||
|
"PROJECT_DIR": "/workspaces/SeaCogs"
|
||||||
|
},
|
||||||
|
"mounts": ["source=seacogs-persistent-data,target=/workspaces/SeaCogs/.data,type=volume"],
|
||||||
|
"postCreateCommand": "uv sync --frozen",
|
||||||
|
"remoteUser": "vscode"
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ Aurora is a fully-featured moderation system. It is heavily inspired by Galactic
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs
|
||||||
[p]cog install seacogs aurora
|
[p]cog install seacogs aurora
|
||||||
[p]cog load aurora
|
[p]cog load aurora
|
||||||
```
|
```
|
||||||
|
|
|
@ -5,14 +5,14 @@ Backup allows you to export a JSON list of all of your installed repositories an
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs
|
||||||
[p]cog install seacogs backup
|
[p]cog install seacogs backup
|
||||||
[p]cog load backup
|
[p]cog load backup
|
||||||
```
|
```
|
||||||
|
|
||||||
## Version Compatibility
|
## Version Compatibility
|
||||||
|
|
||||||
As of commit [1edb08a](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/commit/1edb08a1271f12098ca0bed11a735f7162cedd14), the Backup cog no longer supports Red versions older than 3.5.6. If you want to use the cog on an earlier version (3.5.0 - 3.5.5), install the cog pinned to this commit: `43464db6a7c51bc69282b1ae3dc507a4aae851de`.
|
As of commit [1edb08a](https://www.coastalcommits.com/cswimr/SeaCogs/commit/1edb08a1271f12098ca0bed11a735f7162cedd14), the Backup cog no longer supports Red versions older than 3.5.6. If you want to use the cog on an earlier version (3.5.0 - 3.5.5), install the cog pinned to this commit: `43464db6a7c51bc69282b1ae3dc507a4aae851de`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[p]cog installversion sea-cogs 43464db6a7c51bc69282b1ae3dc507a4aae851de backup
|
[p]cog installversion sea-cogs 43464db6a7c51bc69282b1ae3dc507a4aae851de backup
|
||||||
|
|
|
@ -6,7 +6,7 @@ This cog does require an api key to work.
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs
|
||||||
[p]cog install seacogs bible
|
[p]cog install seacogs bible
|
||||||
[p]cog load bible
|
[p]cog load bible
|
||||||
```
|
```
|
||||||
|
|
|
@ -5,7 +5,7 @@ EmojiInfo allows you to retrieve information about an emoji.
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs
|
||||||
[p]cog install seacogs emojiinfo
|
[p]cog install seacogs emojiinfo
|
||||||
[p]cog load emojiinfo
|
[p]cog load emojiinfo
|
||||||
```
|
```
|
||||||
|
|
|
@ -5,7 +5,7 @@ Nerdify allows you to nerdify other people's text.
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs
|
||||||
[p]cog install seacogs nerdify
|
[p]cog install seacogs nerdify
|
||||||
[p]cog load nerdify
|
[p]cog load nerdify
|
||||||
```
|
```
|
||||||
|
|
|
@ -28,7 +28,7 @@ The Downloader cog allows you to add Git repositories to your bot in order to do
|
||||||
Now, use Downloader to add my repository to your bot:
|
Now, use Downloader to add my repository to your bot:
|
||||||
|
|
||||||
```
|
```
|
||||||
[p]repo add sea-cogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
[p]repo add sea-cogs https://www.coastalcommits.com/cswimr/SeaCogs
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, install the Pterodactyl cog:
|
Now, install the Pterodactyl cog:
|
||||||
|
|
|
@ -10,7 +10,7 @@ Pterodactyl allows for connecting to a Pterodactyl server through websockets. It
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs
|
||||||
[p]cog install seacogs pterodactyl
|
[p]cog install seacogs pterodactyl
|
||||||
[p]cog load aurora
|
[p]cog load aurora
|
||||||
```
|
```
|
||||||
|
|
|
@ -10,7 +10,7 @@ There are a few caveats to running an instance of Red on Pterodactyl.
|
||||||
|
|
||||||
- You will not receive any support from the Red developers.
|
- You will not receive any support from the Red developers.
|
||||||
- The built-in Audio cog will not work.
|
- The built-in Audio cog will not work.
|
||||||
- Depending on your host, you might have to request a [`tmpfs` size increase](https://github.com/ign-gg/Pterodactyl-Eggs/tree/master/bots/discord/redbot#additional-requirements).
|
- Depending on your host, you might have to request a [`tmpfs` size increase](https://github.com/pelican-eggs/eggs/tree/master/bots/discord/redbot#additional-requirements).
|
||||||
|
|
||||||
If these are unacceptable to you, you should [install Red normally](https://docs.discord.red/en/stable/install_guides/index.html).
|
If these are unacceptable to you, you should [install Red normally](https://docs.discord.red/en/stable/install_guides/index.html).
|
||||||
///
|
///
|
||||||
|
@ -64,7 +64,7 @@ Red is quite a large bot, so I'll focus on the specifics of getting the bot work
|
||||||
```
|
```
|
||||||
2. Add my repository to the bot
|
2. Add my repository to the bot
|
||||||
```bash
|
```bash
|
||||||
[p]repo add sea-cogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
[p]repo add sea-cogs https://www.coastalcommits.com/cswimr/SeaCogs
|
||||||
```
|
```
|
||||||
3. Install and load the Pterodactyl cog
|
3. Install and load the Pterodactyl cog
|
||||||
```bash
|
```bash
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
name: Bug Report
|
name: Bug Report
|
||||||
about: File a bug report
|
about: File a bug report
|
||||||
title: "[Cog Name] "
|
|
||||||
labels: [bug]
|
labels: [bug]
|
||||||
ref: master
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
@ -13,7 +11,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Please confirm that;
|
label: Please confirm that;
|
||||||
options:
|
options:
|
||||||
- label: I have checked that this bug does not already have an opened/closed [issue](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/issues) or [pull request](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/pulls) associated with it.
|
- label: I have checked that this bug does not already have an opened/closed [issue](https://www.coastalcommits.com/cswimr/SeaCogs/issues) or [pull request](https://www.coastalcommits.com/cswimr/SeaCogs/pulls) associated with it.
|
||||||
required: true
|
required: true
|
||||||
- label: I have checked that I am on the latest version of [Red-DiscordBot](https://github.com/CogCreators/Red-DiscordBot), and SeaCogs.
|
- label: I have checked that I am on the latest version of [Red-DiscordBot](https://github.com/CogCreators/Red-DiscordBot), and SeaCogs.
|
||||||
required: true
|
required: true
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
name: Suggestion
|
name: Suggestion
|
||||||
about: Trying to suggest something for SeaCogs? Use this.
|
about: Trying to suggest something for SeaCogs? Use this.
|
||||||
title: "[Cog Name] "
|
labels: [enhancement]
|
||||||
labels: enhancement
|
|
||||||
ref: master
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
@ -13,7 +11,7 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: What cog is your feature request for?
|
label: What cog is your feature request for?
|
||||||
description: Specify the cog within the repository.
|
description: Specify the cog within the repository.
|
||||||
placeholder: E.g., ModerationCog
|
placeholder: E.g., Pterodactyl
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
<!-- Create a new issue, if it doesn't exist yet -->
|
<!-- Create a new issue, if it doesn't exist yet -->
|
||||||
|
|
||||||
- [ ] By submitting this pull request, I permit SeaswimmerTheFsh to license my work under
|
- [ ] By submitting this pull request, I permit cswimr to license my work under
|
||||||
the [Mozilla Public License Version 2.0](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/src/branch/main/LICENSE).
|
the [Mozilla Public License Version 2.0](https://www.coastalcommits.com/cswimr/SeaCogs/src/branch/main/LICENSE).
|
||||||
|
|
|
@ -1,39 +1,46 @@
|
||||||
name: Actions
|
name: Actions
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Lint Code (Ruff & Pylint):
|
lint:
|
||||||
|
name: Lint Code (Ruff & Pylint)
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container: www.coastalcommits.com/seaswimmerthefsh/actionscontainers-seacogs:latest
|
container: www.coastalcommits.com/cswimr/actions:uv@sha256:211aaf7d9ac98087579ebf9fab87a9122f51b2697e3a3649ac9f4bd3b03b8e5d
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
|
||||||
|
- name: Install python
|
||||||
|
run: uv python install 3.11
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: poetry install --with dev --no-root
|
run: uv sync
|
||||||
|
|
||||||
- name: Analysing code with Ruff
|
- name: Analysing code with Ruff
|
||||||
run: ./.venv/bin/ruff check $(git ls-files '*.py')
|
run: uv run ruff check $(git ls-files '*.py')
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Analysing code with Pylint
|
- name: Analysing code with Pylint
|
||||||
run: ./.venv/bin/pylint --rcfile=.forgejo/workflows/config/.pylintrc $(git ls-files '*.py')
|
run: uv run pylint --rcfile=.forgejo/workflows/config/.pylintrc $(git ls-files '*.py')
|
||||||
|
|
||||||
Build Documentation (MkDocs):
|
docs:
|
||||||
|
name: Build Documentation (MkDocs)
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container: www.coastalcommits.com/seaswimmerthefsh/actionscontainers-seacogs:latest
|
container: www.coastalcommits.com/cswimr/actions:docs@sha256:e405cd6b9b1182a570ddee32ed8dd1b2f899edc625d006c8b4b2f18c100e724f
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install python
|
||||||
|
run: uv python install 3.11
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: poetry install --with docs --no-root
|
run: uv sync --no-dev --extra=documentation
|
||||||
|
|
||||||
- name: Set environment variables
|
- name: Set environment variables
|
||||||
uses: actions/env@v2
|
uses: actions/env@v2
|
||||||
|
@ -42,7 +49,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
export SITE_URL="https://$CI_ACTION_REF_NAME_SLUG.seacogs.coastalcommits.com"
|
export SITE_URL="https://$CI_ACTION_REF_NAME_SLUG.seacogs.coastalcommits.com"
|
||||||
export EDIT_URI="src/branch/$CI_ACTION_REF_NAME/.docs"
|
export EDIT_URI="src/branch/$CI_ACTION_REF_NAME/.docs"
|
||||||
./.venv/bin/mkdocs build -v
|
uv run mkdocs build -v
|
||||||
|
|
||||||
- name: Deploy documentation
|
- name: Deploy documentation
|
||||||
run: |
|
run: |
|
||||||
|
@ -58,7 +65,7 @@ jobs:
|
||||||
npx -p "@getmeli/cli" meli upload ./site \
|
npx -p "@getmeli/cli" meli upload ./site \
|
||||||
--url "https://pages.coastalcommits.com" \
|
--url "https://pages.coastalcommits.com" \
|
||||||
--site "${{ vars.MELI_SITE_ID }}" \
|
--site "${{ vars.MELI_SITE_ID }}" \
|
||||||
--token "${{ secrets.MELI_SECRET }}" \
|
--token "${{ secrets.MELI_TOKEN }}" \
|
||||||
--release "$CI_ACTION_REF_NAME_SLUG/${{ env.GITHUB_SHA }}" \
|
--release "$CI_ACTION_REF_NAME_SLUG/${{ env.GITHUB_SHA }}" \
|
||||||
--branch "$CI_ACTION_REF_NAME_SLUG"
|
--branch "$CI_ACTION_REF_NAME_SLUG"
|
||||||
|
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,5 @@
|
||||||
.cache
|
.cache
|
||||||
.vscode
|
|
||||||
site
|
site
|
||||||
.venv
|
.venv
|
||||||
|
.data
|
||||||
__pycache__
|
__pycache__
|
||||||
|
|
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Red-DiscordBot",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "redbot",
|
||||||
|
"args": ["local"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
12
.vscode/settings.json
vendored
Normal file
12
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"[python]": {
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit"
|
||||||
|
},
|
||||||
|
"editor.defaultFormatter": "charliermarsh.ruff"
|
||||||
|
},
|
||||||
|
"[json]": {
|
||||||
|
"editor.defaultFormatter": "vscode.json-language-features"
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ My assorted cogs for Red-DiscordBot.
|
||||||
To get started with a development environment, first clone this repository.
|
To get started with a development environment, first clone this repository.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs.git
|
git clone https://coastalcommits.com/cswimr/SeaCogs.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, install Poetry.
|
Then, install Poetry.
|
||||||
|
|
|
@ -9,14 +9,15 @@ import discord
|
||||||
from red_commons.logging import getLogger
|
from red_commons.logging import getLogger
|
||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
from redbot.core.bot import Config, Red
|
from redbot.core.bot import Config, Red
|
||||||
from redbot.core.utils.chat_formatting import humanize_list
|
from redbot.core.utils.chat_formatting import bold, humanize_list
|
||||||
|
|
||||||
|
|
||||||
class AntiPolls(commands.Cog):
|
class AntiPolls(commands.Cog):
|
||||||
"""AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages)."""
|
"""AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages)."""
|
||||||
|
|
||||||
__author__ = ["SeaswimmerTheFsh"]
|
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"]
|
||||||
__version__ = "1.0.0"
|
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs"
|
||||||
|
__version__ = "1.0.1"
|
||||||
__documentation__ = "https://seacogs.coastalcommits.com/antipolls/"
|
__documentation__ = "https://seacogs.coastalcommits.com/antipolls/"
|
||||||
|
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
|
@ -38,9 +39,9 @@ class AntiPolls(commands.Cog):
|
||||||
n = "\n" if "\n\n" not in pre_processed else ""
|
n = "\n" if "\n\n" not in pre_processed else ""
|
||||||
text = [
|
text = [
|
||||||
f"{pre_processed}{n}",
|
f"{pre_processed}{n}",
|
||||||
f"Cog Version: **{self.__version__}**",
|
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})",
|
||||||
f"Author: {humanize_list(self.__author__)}",
|
f"{bold('Author:')} {humanize_list(self.__author__)}",
|
||||||
f"Documentation: {self.__documentation__}",
|
f"{bold('Documentation:')} {self.__documentation__}",
|
||||||
]
|
]
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"author" : ["SeaswimmerTheFsh (seasw.)"],
|
"author" : ["cswimr"],
|
||||||
"install_msg" : "Thank you for installing AntiPolls!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
|
"install_msg" : "Thank you for installing AntiPolls!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).",
|
||||||
"name" : "AntiPolls",
|
"name" : "AntiPolls",
|
||||||
"short" : "AntiPolls deletes messages that contain polls.",
|
"short" : "AntiPolls deletes messages that contain polls.",
|
||||||
"description" : "AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages).",
|
"description" : "AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages).",
|
||||||
|
|
390
aurora/aurora.py
390
aurora/aurora.py
|
@ -70,9 +70,7 @@ class Aurora(commands.Cog):
|
||||||
if requester == "user_strict":
|
if requester == "user_strict":
|
||||||
await config.user_from_id(user_id).clear()
|
await config.user_from_id(user_id).clear()
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
logger.warning("Invalid requester passed to red_delete_data_for_user: %s", requester)
|
||||||
"Invalid requester passed to red_delete_data_for_user: %s", requester
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, bot: Red) -> None:
|
def __init__(self, bot: Red) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -85,7 +83,7 @@ class Aurora(commands.Cog):
|
||||||
# and the information that aiosqlite logs is not useful to the bot owner.
|
# and the information that aiosqlite logs is not useful to the bot owner.
|
||||||
# This is a bad solution though as it overrides it for any other cogs that are using aiosqlite too.
|
# This is a bad solution though as it overrides it for any other cogs that are using aiosqlite too.
|
||||||
# If there's a better solution that you're aware of, please let me know in Discord or in a CoastalCommits issue.
|
# If there's a better solution that you're aware of, please let me know in Discord or in a CoastalCommits issue.
|
||||||
py_logging.getLogger('aiosqlite').setLevel(py_logging.INFO)
|
py_logging.getLogger("aiosqlite").setLevel(py_logging.INFO)
|
||||||
|
|
||||||
def format_help_for_context(self, ctx: commands.Context) -> str:
|
def format_help_for_context(self, ctx: commands.Context) -> str:
|
||||||
pre_processed = super().format_help_for_context(ctx) or ""
|
pre_processed = super().format_help_for_context(ctx) or ""
|
||||||
|
@ -140,13 +138,7 @@ class Aurora(commands.Cog):
|
||||||
if dm_users is None:
|
if dm_users is None:
|
||||||
dm_users = await config.guild(ctx.guild).dm_users()
|
dm_users = await config.guild(ctx.guild).dm_users()
|
||||||
silent = not dm_users
|
silent = not dm_users
|
||||||
return await moderation_type.handler(
|
return await moderation_type.handler(ctx=ctx, target=target, silent=silent, **kwargs)
|
||||||
ctx=ctx,
|
|
||||||
target=target,
|
|
||||||
silent=silent,
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@commands.Cog.listener("on_guild_join")
|
@commands.Cog.listener("on_guild_join")
|
||||||
async def db_generate_on_guild_join(self, guild: discord.Guild):
|
async def db_generate_on_guild_join(self, guild: discord.Guild):
|
||||||
|
@ -189,27 +181,23 @@ class Aurora(commands.Cog):
|
||||||
reason = "This action was performed without the bot."
|
reason = "This action was performed without the bot."
|
||||||
|
|
||||||
if entry.action == discord.AuditLogAction.kick:
|
if entry.action == discord.AuditLogAction.kick:
|
||||||
moderation_type = type_registry['kick']
|
moderation_type = type_registry["kick"]
|
||||||
|
|
||||||
elif entry.action == discord.AuditLogAction.ban:
|
elif entry.action == discord.AuditLogAction.ban:
|
||||||
moderation_type = type_registry['ban']
|
moderation_type = type_registry["ban"]
|
||||||
|
|
||||||
elif entry.action == discord.AuditLogAction.unban:
|
elif entry.action == discord.AuditLogAction.unban:
|
||||||
moderation_type = type_registry['unban']
|
moderation_type = type_registry["unban"]
|
||||||
|
|
||||||
elif entry.action == discord.AuditLogAction.member_update:
|
elif entry.action == discord.AuditLogAction.member_update:
|
||||||
if entry.after.timed_out_until is not None:
|
if entry.after.timed_out_until is not None:
|
||||||
timed_out_until_aware = entry.after.timed_out_until.replace(
|
timed_out_until_aware = entry.after.timed_out_until.replace(tzinfo=timezone.utc)
|
||||||
tzinfo=timezone.utc
|
duration_datetime = timed_out_until_aware - datetime.now(tz=timezone.utc)
|
||||||
)
|
|
||||||
duration_datetime = timed_out_until_aware - datetime.now(
|
|
||||||
tz=timezone.utc
|
|
||||||
)
|
|
||||||
minutes = round(duration_datetime.total_seconds() / 60)
|
minutes = round(duration_datetime.total_seconds() / 60)
|
||||||
duration = timedelta(minutes=minutes)
|
duration = timedelta(minutes=minutes)
|
||||||
moderation_type = type_registry['mute']
|
moderation_type = type_registry["mute"]
|
||||||
else:
|
else:
|
||||||
moderation_type = type_registry['unmute']
|
moderation_type = type_registry["unmute"]
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -254,7 +242,7 @@ class Aurora(commands.Cog):
|
||||||
target=target,
|
target=target,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
permissions=["moderate_members"],
|
permissions=["moderate_members"],
|
||||||
moderation_type=type_registry['note'],
|
moderation_type=type_registry["note"],
|
||||||
reason=reason,
|
reason=reason,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -281,7 +269,7 @@ class Aurora(commands.Cog):
|
||||||
target=target,
|
target=target,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
permissions=["moderate_members"],
|
permissions=["moderate_members"],
|
||||||
moderation_type=type_registry['warn'],
|
moderation_type=type_registry["warn"],
|
||||||
reason=reason,
|
reason=reason,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -309,16 +297,7 @@ class Aurora(commands.Cog):
|
||||||
How long are you adding this role for?
|
How long are you adding this role for?
|
||||||
silent: bool
|
silent: bool
|
||||||
Should the user be messaged?"""
|
Should the user be messaged?"""
|
||||||
await self.moderate(
|
await self.moderate(ctx=interaction, target=target, silent=silent, permissions=["moderate_members", "manage_roles"], moderation_type=type_registry["addrole"], reason=reason, role=role, duration=duration)
|
||||||
ctx=interaction,
|
|
||||||
target=target,
|
|
||||||
silent=silent,
|
|
||||||
permissions=["moderate_members", "manage_roles"],
|
|
||||||
moderation_type=type_registry['addrole'],
|
|
||||||
reason=reason,
|
|
||||||
role=role,
|
|
||||||
duration=duration
|
|
||||||
)
|
|
||||||
|
|
||||||
@app_commands.command(name="removerole")
|
@app_commands.command(name="removerole")
|
||||||
async def removerole(
|
async def removerole(
|
||||||
|
@ -344,16 +323,7 @@ class Aurora(commands.Cog):
|
||||||
How long are you removing this role for?
|
How long are you removing this role for?
|
||||||
silent: bool
|
silent: bool
|
||||||
Should the user be messaged?"""
|
Should the user be messaged?"""
|
||||||
await self.moderate(
|
await self.moderate(ctx=interaction, target=target, silent=silent, permissions=["moderate_members", "manage_roles"], moderation_type=type_registry["removerole"], reason=reason, role=role, duration=duration)
|
||||||
ctx=interaction,
|
|
||||||
target=target,
|
|
||||||
silent=silent,
|
|
||||||
permissions=["moderate_members", "manage_roles"],
|
|
||||||
moderation_type=type_registry['removerole'],
|
|
||||||
reason=reason,
|
|
||||||
role=role,
|
|
||||||
duration=duration
|
|
||||||
)
|
|
||||||
|
|
||||||
@app_commands.command(name="mute")
|
@app_commands.command(name="mute")
|
||||||
async def mute(
|
async def mute(
|
||||||
|
@ -381,7 +351,7 @@ class Aurora(commands.Cog):
|
||||||
target=target,
|
target=target,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
permissions=["moderate_members"],
|
permissions=["moderate_members"],
|
||||||
moderation_type=type_registry['mute'],
|
moderation_type=type_registry["mute"],
|
||||||
duration=duration,
|
duration=duration,
|
||||||
reason=reason,
|
reason=reason,
|
||||||
)
|
)
|
||||||
|
@ -409,7 +379,7 @@ class Aurora(commands.Cog):
|
||||||
target=target,
|
target=target,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
permissions=["moderate_members"],
|
permissions=["moderate_members"],
|
||||||
moderation_type=type_registry['unmute'],
|
moderation_type=type_registry["unmute"],
|
||||||
reason=reason,
|
reason=reason,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -436,7 +406,7 @@ class Aurora(commands.Cog):
|
||||||
target=target,
|
target=target,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
permissions=["kick_members"],
|
permissions=["kick_members"],
|
||||||
moderation_type=type_registry['kick'],
|
moderation_type=type_registry["kick"],
|
||||||
reason=reason,
|
reason=reason,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -480,7 +450,7 @@ class Aurora(commands.Cog):
|
||||||
target=target,
|
target=target,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
permissions=["ban_members"],
|
permissions=["ban_members"],
|
||||||
moderation_type=type_registry['tempban'],
|
moderation_type=type_registry["tempban"],
|
||||||
reason=reason,
|
reason=reason,
|
||||||
duration=duration,
|
duration=duration,
|
||||||
delete_messages=delete_messages,
|
delete_messages=delete_messages,
|
||||||
|
@ -491,7 +461,7 @@ class Aurora(commands.Cog):
|
||||||
target=target,
|
target=target,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
permissions=["ban_members"],
|
permissions=["ban_members"],
|
||||||
moderation_type=type_registry['ban'],
|
moderation_type=type_registry["ban"],
|
||||||
reason=reason,
|
reason=reason,
|
||||||
delete_messages=delete_messages,
|
delete_messages=delete_messages,
|
||||||
)
|
)
|
||||||
|
@ -519,7 +489,7 @@ class Aurora(commands.Cog):
|
||||||
target=target,
|
target=target,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
permissions=["ban_members"],
|
permissions=["ban_members"],
|
||||||
moderation_type=type_registry['unban'],
|
moderation_type=type_registry["unban"],
|
||||||
reason=reason,
|
reason=reason,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -549,7 +519,7 @@ class Aurora(commands.Cog):
|
||||||
target=channel,
|
target=channel,
|
||||||
silent=True,
|
silent=True,
|
||||||
permissions=["manage_channel"],
|
permissions=["manage_channel"],
|
||||||
moderation_type=type_registry['slowmode'],
|
moderation_type=type_registry["slowmode"],
|
||||||
interval=interval,
|
interval=interval,
|
||||||
reason=reason,
|
reason=reason,
|
||||||
)
|
)
|
||||||
|
@ -600,46 +570,26 @@ class Aurora(commands.Cog):
|
||||||
export: bool
|
export: bool
|
||||||
Exports the server's moderation history to a JSON file"""
|
Exports the server's moderation history to a JSON file"""
|
||||||
if ephemeral is None:
|
if ephemeral is None:
|
||||||
ephemeral = (
|
ephemeral = await config.user(interaction.user).history_ephemeral() or await config.guild(interaction.guild).history_ephemeral() or False
|
||||||
await config.user(interaction.user).history_ephemeral()
|
|
||||||
or await config.guild(interaction.guild).history_ephemeral()
|
|
||||||
or False
|
|
||||||
)
|
|
||||||
|
|
||||||
if inline is None:
|
if inline is None:
|
||||||
inline = (
|
inline = await config.user(interaction.user).history_inline() or await config.guild(interaction.guild).history_inline() or False
|
||||||
await config.user(interaction.user).history_inline()
|
|
||||||
or await config.guild(interaction.guild).history_inline()
|
|
||||||
or False
|
|
||||||
)
|
|
||||||
|
|
||||||
if pagesize is None:
|
if pagesize is None:
|
||||||
if inline is True:
|
if inline is True:
|
||||||
pagesize = (
|
pagesize = await config.user(interaction.user).history_inline_pagesize() or await config.guild(interaction.guild).history_inline_pagesize() or 6
|
||||||
await config.user(interaction.user).history_inline_pagesize()
|
|
||||||
or await config.guild(interaction.guild).history_inline_pagesize()
|
|
||||||
or 6
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
pagesize = (
|
pagesize = await config.user(interaction.user).history_pagesize() or await config.guild(interaction.guild).history_pagesize() or 5
|
||||||
await config.user(interaction.user).history_pagesize()
|
|
||||||
or await config.guild(interaction.guild).history_pagesize()
|
|
||||||
or 5
|
|
||||||
)
|
|
||||||
|
|
||||||
if before and not on:
|
if before and not on:
|
||||||
try:
|
try:
|
||||||
before = parse(before)
|
before = parse(before)
|
||||||
except (ParserError, OverflowError) as e:
|
except (ParserError, OverflowError) as e:
|
||||||
if e == ParserError:
|
if e == ParserError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error("Invalid date format for `before` parameter!"), ephemeral=True)
|
||||||
content=error("Invalid date format for `before` parameter!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
if e == OverflowError:
|
if e == OverflowError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error("Date is too far in the future!"), ephemeral=True)
|
||||||
content=error("Date is too far in the future!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if after and not on:
|
if after and not on:
|
||||||
|
@ -647,14 +597,10 @@ class Aurora(commands.Cog):
|
||||||
after = parse(after)
|
after = parse(after)
|
||||||
except (ParserError, OverflowError) as e:
|
except (ParserError, OverflowError) as e:
|
||||||
if e == ParserError:
|
if e == ParserError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error("Invalid date format for `after` parameter!"), ephemeral=True)
|
||||||
content=error("Invalid date format for `after` parameter!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
if e == OverflowError:
|
if e == OverflowError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error("Date is too far in the future!"), ephemeral=True)
|
||||||
content=error("Date is too far in the future!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if on:
|
if on:
|
||||||
|
@ -662,14 +608,10 @@ class Aurora(commands.Cog):
|
||||||
on = parse(on)
|
on = parse(on)
|
||||||
except (ParserError, OverflowError) as e:
|
except (ParserError, OverflowError) as e:
|
||||||
if e == ParserError:
|
if e == ParserError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error("Invalid date format for `on` parameter!"), ephemeral=True)
|
||||||
content=error("Invalid date format for `on` parameter!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
if e == OverflowError:
|
if e == OverflowError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error("Date is too far in the future!"), ephemeral=True)
|
||||||
content=error("Date is too far in the future!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
before = datetime.combine(on, datetime.max.time())
|
before = datetime.combine(on, datetime.max.time())
|
||||||
|
@ -677,20 +619,16 @@ class Aurora(commands.Cog):
|
||||||
|
|
||||||
await interaction.response.defer(ephemeral=ephemeral)
|
await interaction.response.defer(ephemeral=ephemeral)
|
||||||
|
|
||||||
permissions = check_permissions(
|
permissions = check_permissions(interaction.client.user, ["embed_links"], interaction)
|
||||||
interaction.client.user, ["embed_links"], interaction
|
|
||||||
)
|
|
||||||
if permissions:
|
if permissions:
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
error(
|
error(f"I do not have the `{permissions}` permission, required for this action."),
|
||||||
f"I do not have the `{permissions}` permission, required for this action."
|
|
||||||
),
|
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if export and not types:
|
if export and not types:
|
||||||
types = 'all'
|
types = "all"
|
||||||
|
|
||||||
type_list = []
|
type_list = []
|
||||||
registry_values = type_registry.values()
|
registry_values = type_registry.values()
|
||||||
|
@ -722,29 +660,20 @@ class Aurora(commands.Cog):
|
||||||
|
|
||||||
if export:
|
if export:
|
||||||
try:
|
try:
|
||||||
filepath = (
|
filepath = str(data_manager.cog_data_path(cog_instance=self)) + str(os.sep) + filename
|
||||||
str(data_manager.cog_data_path(cog_instance=self))
|
|
||||||
+ str(os.sep)
|
|
||||||
+ filename
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(filepath, "w", encoding="utf-8") as f:
|
with open(filepath, "w", encoding="utf-8") as f:
|
||||||
dump(obj=moderations, fp=f)
|
dump(obj=moderations, fp=f)
|
||||||
|
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
file=discord.File(
|
file=discord.File(fp=filepath, filename=filename),
|
||||||
fp=filepath, filename=filename
|
|
||||||
),
|
|
||||||
ephemeral=ephemeral,
|
ephemeral=ephemeral,
|
||||||
)
|
)
|
||||||
|
|
||||||
os.remove(filepath)
|
os.remove(filepath)
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
content=error(
|
content=error("An error occurred while exporting the moderation history.\nError:\n") + box(text=e, lang="py"),
|
||||||
"An error occurred while exporting the moderation history.\nError:\n"
|
|
||||||
)
|
|
||||||
+ box(text=e, lang="py"),
|
|
||||||
ephemeral=ephemeral,
|
ephemeral=ephemeral,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
@ -756,23 +685,17 @@ class Aurora(commands.Cog):
|
||||||
|
|
||||||
embed = discord.Embed(color=await self.bot.get_embed_color(interaction.channel))
|
embed = discord.Embed(color=await self.bot.get_embed_color(interaction.channel))
|
||||||
embed.set_author(icon_url=interaction.guild.icon.url, name="Infraction History")
|
embed.set_author(icon_url=interaction.guild.icon.url, name="Infraction History")
|
||||||
embed.set_footer(
|
embed.set_footer(text=f"Page {page:,}/{page_quantity:,} | {case_quantity:,} Results")
|
||||||
text=f"Page {page:,}/{page_quantity:,} | {case_quantity:,} Results"
|
|
||||||
)
|
|
||||||
|
|
||||||
memory_dict = {}
|
memory_dict = {}
|
||||||
|
|
||||||
for mod in moderations[start_index:end_index]:
|
for mod in moderations[start_index:end_index]:
|
||||||
if mod.target_id not in memory_dict:
|
if mod.target_id not in memory_dict:
|
||||||
memory_dict.update({
|
memory_dict.update({str(mod.target_id): await mod.get_target()})
|
||||||
str(mod.target_id): await mod.get_target()
|
|
||||||
})
|
|
||||||
target = memory_dict[str(mod.target_id)]
|
target = memory_dict[str(mod.target_id)]
|
||||||
|
|
||||||
if mod.moderator_id not in memory_dict:
|
if mod.moderator_id not in memory_dict:
|
||||||
memory_dict.update({
|
memory_dict.update({str(mod.moderator_id): await mod.get_moderator()})
|
||||||
str(mod.moderator_id): await mod.get_moderator()
|
|
||||||
})
|
|
||||||
moderator = memory_dict[str(mod.moderator_id)]
|
moderator = memory_dict[str(mod.moderator_id)]
|
||||||
|
|
||||||
field_name = f"Case #{mod.id:,} ({mod.type.string.title()})"
|
field_name = f"Case #{mod.id:,} ({mod.type.string.title()})"
|
||||||
|
@ -784,16 +707,10 @@ class Aurora(commands.Cog):
|
||||||
field_value += f"\n**Reason:** `{str(mod.reason)}`"
|
field_value += f"\n**Reason:** `{str(mod.reason)}`"
|
||||||
|
|
||||||
if mod.duration:
|
if mod.duration:
|
||||||
duration_embed = (
|
duration_embed = f"{humanize_timedelta(timedelta=mod.duration)} | <t:{int(mod.end_timestamp.timestamp())}:R>" if mod.expired is False else f"{humanize_timedelta(timedelta=mod.duration)} | Expired"
|
||||||
f"{humanize_timedelta(timedelta=mod.duration)} | <t:{int(mod.end_timestamp.timestamp())}:R>"
|
|
||||||
if mod.expired is False
|
|
||||||
else f"{humanize_timedelta(timedelta=mod.duration)} | Expired"
|
|
||||||
)
|
|
||||||
field_value += f"\n**Duration:** {duration_embed}"
|
field_value += f"\n**Duration:** {duration_embed}"
|
||||||
|
|
||||||
field_value += (
|
field_value += f"\n**Timestamp:** <t:{int(mod.timestamp.timestamp())}> | <t:{int(mod.timestamp.timestamp())}:R>"
|
||||||
f"\n**Timestamp:** <t:{int(mod.timestamp.timestamp())}> | <t:{int(mod.timestamp.timestamp())}:R>"
|
|
||||||
)
|
|
||||||
|
|
||||||
if mod.role_id:
|
if mod.role_id:
|
||||||
role = await mod.get_role()
|
role = await mod.get_role()
|
||||||
|
@ -806,8 +723,8 @@ class Aurora(commands.Cog):
|
||||||
|
|
||||||
await interaction.followup.send(embed=embed, ephemeral=ephemeral)
|
await interaction.followup.send(embed=embed, ephemeral=ephemeral)
|
||||||
|
|
||||||
@history.autocomplete('types')
|
@history.autocomplete("types")
|
||||||
async def _history_types(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]: # pylint: disable=unused-argument
|
async def _history_types(self, interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]: # pylint: disable=unused-argument
|
||||||
types: List[str] = sorted(self.type_registry.keys())
|
types: List[str] = sorted(self.type_registry.keys())
|
||||||
choices = []
|
choices = []
|
||||||
if current.endswith(","):
|
if current.endswith(","):
|
||||||
|
@ -815,7 +732,7 @@ class Aurora(commands.Cog):
|
||||||
if c in types:
|
if c in types:
|
||||||
types.remove(c)
|
types.remove(c)
|
||||||
for t in types:
|
for t in types:
|
||||||
choices.append(app_commands.Choice(name=current+t, value=current+t))
|
choices.append(app_commands.Choice(name=current + t, value=current + t))
|
||||||
else:
|
else:
|
||||||
choices.append(app_commands.Choice(name="all", value="all"))
|
choices.append(app_commands.Choice(name="all", value="all"))
|
||||||
for t in types:
|
for t in types:
|
||||||
|
@ -824,9 +741,7 @@ class Aurora(commands.Cog):
|
||||||
return choices[:25]
|
return choices[:25]
|
||||||
|
|
||||||
@app_commands.command(name="resolve")
|
@app_commands.command(name="resolve")
|
||||||
async def resolve(
|
async def resolve(self, interaction: discord.Interaction, case: int, reason: str = "No reason provided."):
|
||||||
self, interaction: discord.Interaction, case: int, reason: str = "No reason provided."
|
|
||||||
):
|
|
||||||
"""Resolve a specific case.
|
"""Resolve a specific case.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
@ -842,9 +757,7 @@ class Aurora(commands.Cog):
|
||||||
)
|
)
|
||||||
if permissions:
|
if permissions:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
error(
|
error(f"I do not have the `{permissions}` permission, required for this action."),
|
||||||
f"I do not have the `{permissions}` permission, required for this action."
|
|
||||||
),
|
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
@ -852,15 +765,11 @@ class Aurora(commands.Cog):
|
||||||
try:
|
try:
|
||||||
moderation = await Moderation.find_by_id(interaction.client, case, interaction.guild.id)
|
moderation = await Moderation.find_by_id(interaction.client, case, interaction.guild.id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error(f"Case #{case:,} does not exist!"), ephemeral=True)
|
||||||
content=error(f"Case #{case:,} does not exist!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
if len(moderation.changes) > 25:
|
if len(moderation.changes) > 25:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
content=error(
|
content=error("Due to limitations with Discord's embed system, you cannot edit a case more than 25 times."),
|
||||||
"Due to limitations with Discord's embed system, you cannot edit a case more than 25 times."
|
|
||||||
),
|
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
@ -869,21 +778,15 @@ class Aurora(commands.Cog):
|
||||||
success, msg = await moderation.resolve(interaction.user.id, reason)
|
success, msg = await moderation.resolve(interaction.user.id, reason)
|
||||||
except (ValueError, TypeError) as e:
|
except (ValueError, TypeError) as e:
|
||||||
if e == ValueError:
|
if e == ValueError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error("This case has already been resolved!"), ephemeral=True)
|
||||||
content=error("This case has already been resolved!"), ephemeral=True
|
|
||||||
)
|
|
||||||
elif e == TypeError:
|
elif e == TypeError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error("This case type cannot be resolved!"), ephemeral=True)
|
||||||
content=error("This case type cannot be resolved!"), ephemeral=True
|
|
||||||
)
|
|
||||||
|
|
||||||
embed = await case_factory(
|
embed = await case_factory(
|
||||||
interaction=interaction,
|
interaction=interaction,
|
||||||
moderation=moderation,
|
moderation=moderation,
|
||||||
)
|
)
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=f"✅ Moderation #{case:,} resolved!\n" + error(f"Resolve handler returned an error message: `{msg}`") if success is False else "", embed=embed)
|
||||||
content=f"✅ Moderation #{case:,} resolved!\n" + error(f"Resolve handler returned an error message: `{msg}`") if success is False else "", embed=embed
|
|
||||||
)
|
|
||||||
ctx = await self.bot.get_context(interaction, cls=commands.Context)
|
ctx = await self.bot.get_context(interaction, cls=commands.Context)
|
||||||
await log(ctx=ctx, moderation_id=case, resolved=True)
|
await log(ctx=ctx, moderation_id=case, resolved=True)
|
||||||
|
|
||||||
|
@ -917,47 +820,31 @@ class Aurora(commands.Cog):
|
||||||
List the changes made to the case
|
List the changes made to the case
|
||||||
raw: bool
|
raw: bool
|
||||||
Export the case to a JSON file or codeblock"""
|
Export the case to a JSON file or codeblock"""
|
||||||
permissions = check_permissions(
|
permissions = check_permissions(interaction.client.user, ["embed_links"], interaction)
|
||||||
interaction.client.user, ["embed_links"], interaction
|
|
||||||
)
|
|
||||||
if permissions:
|
if permissions:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
error(
|
error(f"I do not have the `{permissions}` permission, required for this action."),
|
||||||
f"I do not have the `{permissions}` permission, required for this action."
|
|
||||||
),
|
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if ephemeral is None:
|
if ephemeral is None:
|
||||||
ephemeral = (
|
ephemeral = await config.user(interaction.user).history_ephemeral() or await config.guild(interaction.guild).history_ephemeral() or False
|
||||||
await config.user(interaction.user).history_ephemeral()
|
|
||||||
or await config.guild(interaction.guild).history_ephemeral()
|
|
||||||
or False
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mod = await Moderation.find_by_id(interaction.client, case, interaction.guild.id)
|
mod = await Moderation.find_by_id(interaction.client, case, interaction.guild.id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error(f"Case #{case:,} does not exist!"), ephemeral=True)
|
||||||
content=error(f"Case #{case:,} does not exist!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if raw:
|
if raw:
|
||||||
if raw.value == "file" or len(mod.to_json(2)) > 1800:
|
if raw.value == "file" or len(mod.to_json(2)) > 1800:
|
||||||
filename = (
|
filename = str(data_manager.cog_data_path(cog_instance=self)) + str(os.sep) + f"moderation_{interaction.guild.id}_case_{case}.json"
|
||||||
str(data_manager.cog_data_path(cog_instance=self))
|
|
||||||
+ str(os.sep)
|
|
||||||
+ f"moderation_{interaction.guild.id}_case_{case}.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(filename, "w", encoding="utf-8") as f:
|
with open(filename, "w", encoding="utf-8") as f:
|
||||||
mod.to_json(2, f)
|
mod.to_json(2, f)
|
||||||
if raw.value == "codeblock":
|
if raw.value == "codeblock":
|
||||||
content = f"Case #{case:,} exported.\n" + warning(
|
content = f"Case #{case:,} exported.\n" + warning("Case was too large to export as codeblock, so it has been uploaded as a `.json` file.")
|
||||||
"Case was too large to export as codeblock, so it has been uploaded as a `.json` file."
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
content = f"Case #{case:,} exported."
|
content = f"Case #{case:,} exported."
|
||||||
|
|
||||||
|
@ -973,29 +860,19 @@ class Aurora(commands.Cog):
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
return
|
return
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
content=box(mod.to_json(2), 'json'),
|
content=box(mod.to_json(2), "json"),
|
||||||
ephemeral=ephemeral,
|
ephemeral=ephemeral,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if changes:
|
if changes:
|
||||||
embed = await changes_factory(
|
embed = await changes_factory(interaction=interaction, moderation=mod)
|
||||||
interaction=interaction, moderation=mod
|
await interaction.response.send_message(embed=embed, ephemeral=ephemeral)
|
||||||
)
|
|
||||||
await interaction.response.send_message(
|
|
||||||
embed=embed, ephemeral=ephemeral
|
|
||||||
)
|
|
||||||
elif evidenceformat:
|
elif evidenceformat:
|
||||||
content = await evidenceformat_factory(moderation=mod)
|
content = await evidenceformat_factory(moderation=mod)
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=content, ephemeral=ephemeral)
|
||||||
content=content, ephemeral=ephemeral
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
embed = await case_factory(
|
embed = await case_factory(interaction=interaction, moderation=mod)
|
||||||
interaction=interaction, moderation=mod
|
await interaction.response.send_message(embed=embed, ephemeral=ephemeral)
|
||||||
)
|
|
||||||
await interaction.response.send_message(
|
|
||||||
embed=embed, ephemeral=ephemeral
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@app_commands.command(name="edit")
|
@app_commands.command(name="edit")
|
||||||
|
@ -1017,14 +894,10 @@ class Aurora(commands.Cog):
|
||||||
duration: str
|
duration: str
|
||||||
What is the new duration? Does not reapply the moderation if it has already expired.
|
What is the new duration? Does not reapply the moderation if it has already expired.
|
||||||
"""
|
"""
|
||||||
permissions = check_permissions(
|
permissions = check_permissions(interaction.client.user, ["embed_links"], interaction)
|
||||||
interaction.client.user, ["embed_links"], interaction
|
|
||||||
)
|
|
||||||
if permissions:
|
if permissions:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
error(
|
error(f"I do not have the `{permissions}` permission, required for this action."),
|
||||||
f"I do not have the `{permissions}` permission, required for this action."
|
|
||||||
),
|
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
@ -1033,16 +906,12 @@ class Aurora(commands.Cog):
|
||||||
moderation = await Moderation.find_by_id(interaction.client, case, interaction.guild.id)
|
moderation = await Moderation.find_by_id(interaction.client, case, interaction.guild.id)
|
||||||
old_moderation = moderation.model_copy()
|
old_moderation = moderation.model_copy()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(content=error(f"Case #{case:,} does not exist!"), ephemeral=True)
|
||||||
content=error(f"Case #{case:,} does not exist!"), ephemeral=True
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(moderation.changes) > 25:
|
if len(moderation.changes) > 25:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
content=error(
|
content=error("Due to limitations with Discord's embed system, you cannot edit a case more than 25 times."),
|
||||||
"Due to limitations with Discord's embed system, you cannot edit a case more than 25 times."
|
|
||||||
),
|
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1053,18 +922,14 @@ class Aurora(commands.Cog):
|
||||||
raise commands.BadArgument()
|
raise commands.BadArgument()
|
||||||
moderation.duration = timedelta_from_relativedelta(relativedelta=parsed_time)
|
moderation.duration = timedelta_from_relativedelta(relativedelta=parsed_time)
|
||||||
except (commands.BadArgument, ValueError):
|
except (commands.BadArgument, ValueError):
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(error("Please provide a valid duration!"), ephemeral=True)
|
||||||
error("Please provide a valid duration!"), ephemeral=True
|
|
||||||
)
|
|
||||||
|
|
||||||
moderation.end_timestamp = moderation.timestamp + timedelta(seconds=moderation.duration.total_seconds())
|
moderation.end_timestamp = moderation.timestamp + timedelta(seconds=moderation.duration.total_seconds())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
success = await moderation.type.duration_edit_handler(interaction=interaction, old_moderation=old_moderation, new_moderation=moderation)
|
success = await moderation.type.duration_edit_handler(interaction=interaction, old_moderation=old_moderation, new_moderation=moderation)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(error("This case type does not support duration editing!"), ephemeral=True)
|
||||||
error("This case type does not support duration editing!"), ephemeral=True
|
|
||||||
)
|
|
||||||
if not success:
|
if not success:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -1072,27 +937,35 @@ class Aurora(commands.Cog):
|
||||||
moderation.reason = reason
|
moderation.reason = reason
|
||||||
|
|
||||||
if not reason and not duration:
|
if not reason and not duration:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(error("Please provide a new reason or duration to edit this case!"), ephemeral=True)
|
||||||
error("Please provide a new reason or duration to edit this case!"), ephemeral=True
|
|
||||||
)
|
|
||||||
|
|
||||||
if not moderation.changes:
|
if not moderation.changes:
|
||||||
moderation.changes.append(Change.from_dict(interaction.client, {
|
moderation.changes.append(
|
||||||
"type": "ORIGINAL",
|
Change.from_dict(
|
||||||
"timestamp": old_moderation.timestamp,
|
interaction.client,
|
||||||
"reason": old_moderation.reason,
|
{
|
||||||
"user_id": old_moderation.moderator_id,
|
"type": "ORIGINAL",
|
||||||
"duration": timedelta_to_string(old_moderation.duration) if old_moderation.duration else None,
|
"timestamp": old_moderation.timestamp,
|
||||||
"end_timestamp": old_moderation.end_timestamp,
|
"reason": old_moderation.reason,
|
||||||
}))
|
"user_id": old_moderation.moderator_id,
|
||||||
moderation.changes.append(Change.from_dict(interaction.client, {
|
"duration": timedelta_to_string(old_moderation.duration) if old_moderation.duration else None,
|
||||||
"type": "EDIT",
|
"end_timestamp": old_moderation.end_timestamp,
|
||||||
"timestamp": int(time.time()),
|
},
|
||||||
"reason": reason if reason else None,
|
)
|
||||||
"user_id": interaction.user.id,
|
)
|
||||||
"duration": timedelta_to_string(moderation.duration) if duration else None,
|
moderation.changes.append(
|
||||||
"end_timestamp": moderation.end_timestamp if duration else None,
|
Change.from_dict(
|
||||||
}))
|
interaction.client,
|
||||||
|
{
|
||||||
|
"type": "EDIT",
|
||||||
|
"timestamp": int(time.time()),
|
||||||
|
"reason": reason if reason else None,
|
||||||
|
"user_id": interaction.user.id,
|
||||||
|
"duration": timedelta_to_string(moderation.duration) if duration else None,
|
||||||
|
"end_timestamp": moderation.end_timestamp if duration else None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
await moderation.update()
|
await moderation.update()
|
||||||
embed = await case_factory(interaction=interaction, moderation=moderation)
|
embed = await case_factory(interaction=interaction, moderation=moderation)
|
||||||
|
@ -1135,7 +1008,7 @@ class Aurora(commands.Cog):
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
logger.warning("Expiry handler not implemented for expirable moderation type %s", moderation.type.key)
|
logger.warning("Expiry handler not implemented for expirable moderation type %s", moderation.type.key)
|
||||||
continue
|
continue
|
||||||
except Exception as e: # pylint: disable=broad-except
|
except Exception as e: # pylint: disable=broad-except
|
||||||
logger.exception("Expiry handler failed for moderation %s with the type %s", moderation.id, moderation.type.key, exc_info=e)
|
logger.exception("Expiry handler failed for moderation %s with the type %s", moderation.id, moderation.type.key, exc_info=e)
|
||||||
error_num += 1
|
error_num += 1
|
||||||
continue
|
continue
|
||||||
|
@ -1153,34 +1026,15 @@ class Aurora(commands.Cog):
|
||||||
await Moderation.execute(bot=self.bot, guild_id=guild.id, query=expiry_query, parameters=(time.time(),), return_obj=False)
|
await Moderation.execute(bot=self.bot, guild_id=guild.id, query=expiry_query, parameters=(time.time(),), return_obj=False)
|
||||||
|
|
||||||
per_guild_completion_time = (time.time() - time_per_guild) * 1000
|
per_guild_completion_time = (time.time() - time_per_guild) * 1000
|
||||||
logger.debug(
|
logger.debug("Completed expiry loop for %s (%s) in %sms with %s errors, %s users unbanned, %s roles added, and %s roles removed (%s other cases expired)", guild.name, guild.id, f"{per_guild_completion_time:.6f}", error_num, unban_num, addrole_num, removerole_num, other_num)
|
||||||
"Completed expiry loop for %s (%s) in %sms with %s errors, %s users unbanned, %s roles added, and %s roles removed (%s other cases expired)",
|
|
||||||
guild.name,
|
|
||||||
guild.id,
|
|
||||||
f"{per_guild_completion_time:.6f}",
|
|
||||||
error_num,
|
|
||||||
unban_num,
|
|
||||||
addrole_num,
|
|
||||||
removerole_num,
|
|
||||||
other_num
|
|
||||||
)
|
|
||||||
global_unban_num = global_unban_num + unban_num
|
global_unban_num = global_unban_num + unban_num
|
||||||
global_addrole_num = global_addrole_num + addrole_num
|
global_addrole_num = global_addrole_num + addrole_num
|
||||||
global_removerole_num = global_removerole_num + removerole_num
|
global_removerole_num = global_removerole_num + removerole_num
|
||||||
global_other_num = global_other_num + other_num
|
global_other_num = global_other_num + other_num
|
||||||
global_err_num = global_err_num + error_num
|
global_err_num = global_err_num + error_num
|
||||||
|
|
||||||
|
|
||||||
completion_time = (time.time() - current_time) * 1000
|
completion_time = (time.time() - current_time) * 1000
|
||||||
logger.debug(
|
logger.debug("Completed expiry loop in %sms with %s errors, %s users unbanned, %s roles added, and %s roles removed (%s other cases expired)", f"{completion_time:.6f}", global_err_num, global_unban_num, global_addrole_num, global_removerole_num, global_other_num)
|
||||||
"Completed expiry loop in %sms with %s errors, %s users unbanned, %s roles added, and %s roles removed (%s other cases expired)",
|
|
||||||
f"{completion_time:.6f}",
|
|
||||||
global_err_num,
|
|
||||||
global_unban_num,
|
|
||||||
global_addrole_num,
|
|
||||||
global_removerole_num,
|
|
||||||
global_other_num
|
|
||||||
)
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
### Configuration Commands #
|
### Configuration Commands #
|
||||||
|
@ -1252,18 +1106,10 @@ class Aurora(commands.Cog):
|
||||||
@commands.admin()
|
@commands.admin()
|
||||||
async def aurora_import_aurora(self, ctx: commands.Context):
|
async def aurora_import_aurora(self, ctx: commands.Context):
|
||||||
"""Import moderation history from another bot using Aurora."""
|
"""Import moderation history from another bot using Aurora."""
|
||||||
if (
|
if ctx.message.attachments and ctx.message.attachments[0].content_type == "application/json; charset=utf-8":
|
||||||
ctx.message.attachments
|
|
||||||
and ctx.message.attachments[0].content_type
|
|
||||||
== "application/json; charset=utf-8"
|
|
||||||
):
|
|
||||||
file = await ctx.message.attachments[0].read()
|
file = await ctx.message.attachments[0].read()
|
||||||
data: list[dict] = sorted(json.loads(file), key=lambda x: x["moderation_id"])
|
data: list[dict] = sorted(json.loads(file), key=lambda x: x["moderation_id"])
|
||||||
message = await ctx.send(
|
message = await ctx.send(warning("Are you sure you want to import moderations from another bot?\n**This will overwrite any moderations that already exist in this guild's moderation table.**\n*The import process will block the rest of your bot until it is complete.*"))
|
||||||
warning(
|
|
||||||
"Are you sure you want to import moderations from another bot?\n**This will overwrite any moderations that already exist in this guild's moderation table.**\n*The import process will block the rest of your bot until it is complete.*"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
await message.edit(view=ImportAuroraView(60, ctx, message, data))
|
await message.edit(view=ImportAuroraView(60, ctx, message, data))
|
||||||
else:
|
else:
|
||||||
await ctx.send(error("Please provide a valid Aurora export file."))
|
await ctx.send(error("Please provide a valid Aurora export file."))
|
||||||
|
@ -1272,21 +1118,11 @@ class Aurora(commands.Cog):
|
||||||
@commands.admin()
|
@commands.admin()
|
||||||
async def aurora_import_galacticbot(self, ctx: commands.Context):
|
async def aurora_import_galacticbot(self, ctx: commands.Context):
|
||||||
"""Import moderation history from GalacticBot."""
|
"""Import moderation history from GalacticBot."""
|
||||||
if (
|
if ctx.message.attachments and ctx.message.attachments[0].content_type == "application/json; charset=utf-8":
|
||||||
ctx.message.attachments
|
message = await ctx.send(warning("Are you sure you want to import GalacticBot moderations?\n**This will overwrite any moderations that already exist in this guild's moderation table.**\n*The import process will block the rest of your bot until it is complete.*"))
|
||||||
and ctx.message.attachments[0].content_type
|
|
||||||
== "application/json; charset=utf-8"
|
|
||||||
):
|
|
||||||
message = await ctx.send(
|
|
||||||
warning(
|
|
||||||
"Are you sure you want to import GalacticBot moderations?\n**This will overwrite any moderations that already exist in this guild's moderation table.**\n*The import process will block the rest of your bot until it is complete.*"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
await message.edit(view=ImportGalacticBotView(60, ctx, message))
|
await message.edit(view=ImportGalacticBotView(60, ctx, message))
|
||||||
else:
|
else:
|
||||||
await ctx.send(
|
await ctx.send(error("Please provide a valid GalacticBot moderation export file."))
|
||||||
error("Please provide a valid GalacticBot moderation export file.")
|
|
||||||
)
|
|
||||||
|
|
||||||
@aurora.group(autohelp=True, name="convert")
|
@aurora.group(autohelp=True, name="convert")
|
||||||
async def aurora_convert(self, ctx: commands.Context):
|
async def aurora_convert(self, ctx: commands.Context):
|
||||||
|
@ -1353,14 +1189,8 @@ class Aurora(commands.Cog):
|
||||||
timestamp=datetime.now(),
|
timestamp=datetime.now(),
|
||||||
)
|
)
|
||||||
embed.set_thumbnail(url=self.bot.user.avatar.url)
|
embed.set_thumbnail(url=self.bot.user.avatar.url)
|
||||||
embed.add_field(
|
embed.add_field(name="Version", value=f"[{self.__version__}]({self.__git__})")
|
||||||
name="Version",
|
embed.add_field(name="Author", value=", ".join(self.__author__))
|
||||||
value=f"[{self.__version__}]({self.__git__})"
|
|
||||||
)
|
|
||||||
embed.add_field(
|
|
||||||
name="Author",
|
|
||||||
value=', '.join(self.__author__)
|
|
||||||
)
|
|
||||||
if ctx.author.id in self.bot.owner_ids:
|
if ctx.author.id in self.bot.owner_ids:
|
||||||
results = await Moderation.execute(query="SELECT name FROM sqlite_master WHERE type='table';", return_obj=False)
|
results = await Moderation.execute(query="SELECT name FROM sqlite_master WHERE type='table';", return_obj=False)
|
||||||
tables = [table[0] for table in results]
|
tables = [table[0] for table in results]
|
||||||
|
@ -1378,9 +1208,5 @@ class Aurora(commands.Cog):
|
||||||
name="Database Stats",
|
name="Database Stats",
|
||||||
value=f"{bold('Table Count:')} {table_count:,}\n{bold('Row Count:')} {row_count:,}\n{bold('File Size:')} {filesize:,.0f} KB",
|
value=f"{bold('Table Count:')} {table_count:,}\n{bold('Row Count:')} {row_count:,}\n{bold('File Size:')} {filesize:,.0f} KB",
|
||||||
)
|
)
|
||||||
embed.add_field(
|
embed.add_field(name="Moderation Types", value=f"{len(type_registry)} registered types\n{box(', '.join(type_registry.keys()))}", inline=False)
|
||||||
name="Moderation Types",
|
|
||||||
value=f"{len(type_registry)} registered types\n{box(', '.join(type_registry.keys()))}",
|
|
||||||
inline=False
|
|
||||||
)
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"author" : ["Seaswimmer (cswimr)"],
|
"author" : ["cswimr"],
|
||||||
"install_msg" : "Thank you for installing Aurora!\nMost of this cog's functionality requires enabling slash commands.\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
|
"install_msg" : "Thank you for installing Aurora!\nMost of this cog's functionality requires enabling slash commands.\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).",
|
||||||
"name" : "Aurora",
|
"name" : "Aurora",
|
||||||
"short" : "A full replacement for Red's core Mod cogs.",
|
"short" : "A full replacement for Red's core Mod cogs.",
|
||||||
"description" : "Aurora is a fully-featured moderation system. It is heavily inspired by GalacticBot, and is designed to be a more user-friendly alternative to Red's core Mod cogs. This cog stores all of its data in an SQLite database.",
|
"description" : "Aurora is a fully-featured moderation system. It is heavily inspired by GalacticBot, and is designed to be a more user-friendly alternative to Red's core Mod cogs. This cog stores all of its data in an SQLite database.",
|
||||||
|
|
|
@ -14,15 +14,16 @@ from redbot.cogs.downloader import errors
|
||||||
from redbot.cogs.downloader.converters import InstalledCog
|
from redbot.cogs.downloader.converters import InstalledCog
|
||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
from redbot.core.utils.chat_formatting import error, humanize_list, text_to_file
|
from redbot.core.utils.chat_formatting import bold, error, humanize_list, text_to_file
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
class Backup(commands.Cog):
|
class Backup(commands.Cog):
|
||||||
"""A utility to make reinstalling repositories and cogs after migrating the bot far easier."""
|
"""A utility to make reinstalling repositories and cogs after migrating the bot far easier."""
|
||||||
|
|
||||||
__author__ = ["SeaswimmerTheFsh"]
|
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"]
|
||||||
__version__ = "1.1.0"
|
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs"
|
||||||
|
__version__ = "1.1.1"
|
||||||
__documentation__ = "https://seacogs.coastalcommits.com/backup/"
|
__documentation__ = "https://seacogs.coastalcommits.com/backup/"
|
||||||
|
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
|
@ -35,9 +36,9 @@ class Backup(commands.Cog):
|
||||||
n = "\n" if "\n\n" not in pre_processed else ""
|
n = "\n" if "\n\n" not in pre_processed else ""
|
||||||
text = [
|
text = [
|
||||||
f"{pre_processed}{n}",
|
f"{pre_processed}{n}",
|
||||||
f"Cog Version: **{self.__version__}**",
|
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})",
|
||||||
f"Author: {humanize_list(self.__author__)}",
|
f"{bold('Author:')} {humanize_list(self.__author__)}",
|
||||||
f"Documentation: {self.__documentation__}",
|
f"{bold('Documentation:')} {self.__documentation__}",
|
||||||
]
|
]
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
|
@ -196,7 +197,7 @@ class Backup(commands.Cog):
|
||||||
cog_modules = []
|
cog_modules = []
|
||||||
for cog in cogs:
|
for cog in cogs:
|
||||||
# If you're forking this cog, make sure to change these strings!
|
# If you're forking this cog, make sure to change these strings!
|
||||||
if cog["name"] == "backup" and "SeaswimmerTheFsh/SeaCogs" in url:
|
if cog["name"] == "backup" and "cswimr/SeaCogs" in url:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
cog_module = await InstalledCog.convert(ctx, cog["name"])
|
cog_module = await InstalledCog.convert(ctx, cog["name"])
|
||||||
|
@ -232,7 +233,7 @@ class Backup(commands.Cog):
|
||||||
commit = None
|
commit = None
|
||||||
|
|
||||||
# If you're forking this cog, make sure to change these strings!
|
# If you're forking this cog, make sure to change these strings!
|
||||||
if cog_name == "backup" and "SeaswimmerTheFsh/SeaCogs" in url:
|
if cog_name == "backup" and "cswimr/SeaCogs" in url:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
async with repository.checkout(
|
async with repository.checkout(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"author" : ["SeaswimmerTheFsh (seasw.)"],
|
"author" : ["cswimr"],
|
||||||
"install_msg" : "Thank you for installing Backup!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
|
"install_msg" : "Thank you for installing Backup!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).",
|
||||||
"name" : "Backup",
|
"name" : "Backup",
|
||||||
"short" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.",
|
"short" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.",
|
||||||
"description" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.",
|
"description" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.",
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"min_bot_version": "3.5.6",
|
"min_bot_version": "3.5.6",
|
||||||
"max_bot_version": "3.5.12",
|
"max_bot_version": "3.5.13",
|
||||||
"min_python_version": [3, 9, 0],
|
"min_python_version": [3, 9, 0],
|
||||||
"tags": [
|
"tags": [
|
||||||
"utility",
|
"utility",
|
||||||
|
|
|
@ -15,7 +15,7 @@ from PIL import Image
|
||||||
from red_commons.logging import getLogger
|
from red_commons.logging import getLogger
|
||||||
from redbot.core import Config, commands, data_manager
|
from redbot.core import Config, commands, data_manager
|
||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
from redbot.core.utils.chat_formatting import error, humanize_list
|
from redbot.core.utils.chat_formatting import bold, error, humanize_list
|
||||||
|
|
||||||
import bible.errors
|
import bible.errors
|
||||||
from bible.models import Version
|
from bible.models import Version
|
||||||
|
@ -24,9 +24,10 @@ from bible.models import Version
|
||||||
class Bible(commands.Cog):
|
class Bible(commands.Cog):
|
||||||
"""Retrieve Bible verses from the API.bible API."""
|
"""Retrieve Bible verses from the API.bible API."""
|
||||||
|
|
||||||
__author__ = ["SeaswimmerTheFsh"]
|
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"]
|
||||||
__version__ = "1.1.0"
|
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs"
|
||||||
__documentation__ = "https://seacogs.coastalcommits.com/bible/"
|
__version__ = "1.1.1"
|
||||||
|
__documentation__ = "https://seacogs.coastalcommits.com/pterodactyl/"
|
||||||
|
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -44,12 +45,13 @@ class Bible(commands.Cog):
|
||||||
n = "\n" if "\n\n" not in pre_processed else ""
|
n = "\n" if "\n\n" not in pre_processed else ""
|
||||||
text = [
|
text = [
|
||||||
f"{pre_processed}{n}",
|
f"{pre_processed}{n}",
|
||||||
f"Cog Version: **{self.__version__}**",
|
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})",
|
||||||
f"Author: {humanize_list(self.__author__)}",
|
f"{bold('Author:')} {humanize_list(self.__author__)}",
|
||||||
f"Documentation: {self.__documentation__}",
|
f"{bold('Documentation:')} {self.__documentation__}",
|
||||||
]
|
]
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
|
|
||||||
def get_icon(self, color: Colour) -> File:
|
def get_icon(self, color: Colour) -> File:
|
||||||
"""Get the docs.api.bible favicon with a given color."""
|
"""Get the docs.api.bible favicon with a given color."""
|
||||||
image_path = data_manager.bundled_data_path(self) / "api.bible-logo.png"
|
image_path = data_manager.bundled_data_path(self) / "api.bible-logo.png"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"author" : ["SeaswimmerTheFsh (seasw.)"],
|
"author" : ["cswimr"],
|
||||||
"install_msg" : "Thank you for installing Bible!\nThis cog requires setting an API key for API.Bible. Please read the [documentation](https://seacogs.coastalcommits.com/bible/#setup) for more information.\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
|
"install_msg" : "Thank you for installing Bible!\nThis cog requires setting an API key for API.Bible. Please read the [documentation](https://seacogs.coastalcommits.com/bible/#setup) for more information.\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).",
|
||||||
"name" : "Bible",
|
"name" : "Bible",
|
||||||
"short" : "Retrieve Bible verses from API.Bible.",
|
"short" : "Retrieve Bible verses from API.Bible.",
|
||||||
"description" : "Retrieve Bible verses from the API.Bible API. This cog requires an API.Bible api key.",
|
"description" : "Retrieve Bible verses from the API.Bible API. This cog requires an API.Bible api key.",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import io
|
import io
|
||||||
from typing import Any, Literal
|
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import discord
|
import discord
|
||||||
|
@ -15,9 +14,10 @@ from .model import PartialEmoji
|
||||||
class EmojiInfo(commands.Cog):
|
class EmojiInfo(commands.Cog):
|
||||||
"""Retrieve information about emojis."""
|
"""Retrieve information about emojis."""
|
||||||
|
|
||||||
__author__: list[str] = ["SeaswimmerTheFsh"]
|
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"]
|
||||||
__version__: str = "1.0.0"
|
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs"
|
||||||
__documentation__: str = "https://seacogs.coastalcommits.com/emojiinfo/"
|
__version__ = "1.0.1"
|
||||||
|
__documentation__ = "https://seacogs.coastalcommits.com/emojiinfo/"
|
||||||
|
|
||||||
def __init__(self, bot: Red) -> None:
|
def __init__(self, bot: Red) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -25,16 +25,17 @@ class EmojiInfo(commands.Cog):
|
||||||
self.logger: RedTraceLogger = getLogger(name="red.SeaCogs.Emoji")
|
self.logger: RedTraceLogger = getLogger(name="red.SeaCogs.Emoji")
|
||||||
|
|
||||||
def format_help_for_context(self, ctx: commands.Context) -> str:
|
def format_help_for_context(self, ctx: commands.Context) -> str:
|
||||||
pre_processed: Any | Literal[''] = super().format_help_for_context(ctx) or ""
|
pre_processed = super().format_help_for_context(ctx) or ""
|
||||||
n: Literal['\n'] | Literal[''] = "\n" if "\n\n" not in pre_processed else ""
|
n = "\n" if "\n\n" not in pre_processed else ""
|
||||||
text: list[str] = [
|
text = [
|
||||||
f"{pre_processed}{n}",
|
f"{pre_processed}{n}",
|
||||||
f"Cog Version: **{self.__version__}**",
|
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})",
|
||||||
f"Author: {humanize_list(items=self.__author__)}",
|
f"{bold('Author:')} {humanize_list(self.__author__)}",
|
||||||
f"Documentation: {self.__documentation__}",
|
f"{bold('Documentation:')} {self.__documentation__}",
|
||||||
]
|
]
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
|
|
||||||
async def fetch_twemoji(self, unicode_emoji) -> str:
|
async def fetch_twemoji(self, unicode_emoji) -> str:
|
||||||
base_url = "https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/72x72/"
|
base_url = "https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/72x72/"
|
||||||
emoji_codepoint = "-".join([hex(ord(char))[2:] for char in unicode_emoji])
|
emoji_codepoint = "-".join([hex(ord(char))[2:] for char in unicode_emoji])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"author" : ["SeaswimmerTheFsh (seasw.)"],
|
"author" : ["cswimr"],
|
||||||
"install_msg" : "Thank you for installing Emoji!",
|
"install_msg" : "Thank you for installing Emoji!",
|
||||||
"name" : "Emoji",
|
"name" : "Emoji",
|
||||||
"short" : "Retrieve information about emojis.",
|
"short" : "Retrieve information about emojis.",
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"author": [
|
"author": [
|
||||||
"SeaswimmerTheFsh (seasw.)"
|
"cswimr"
|
||||||
],
|
],
|
||||||
"install_msg": "Thanks for installing my repo!\n\nIf you have any issues with any of the cogs, please create an issue [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs/issues) or join my [Discord Server](https://discord.gg/eMUMe77Yb8 ).",
|
"install_msg": "Thanks for installing my repo!\n\nIf you have any issues with any of the cogs, please create an issue [here](https://coastalcommits.com/cswimr/SeaCogs/issues) or join my [Discord Server](https://discord.gg/eMUMe77Yb8 ).",
|
||||||
"index_name": "sea-cogs",
|
"index_name": "sea-cogs",
|
||||||
"short": "Various cogs for Red, by SeaswimmerTheFsh (seasw.)",
|
"short": "Various cogs for Red, by cswimr",
|
||||||
"description": "Various cogs for Red, by SeaswimmerTheFsh (seasw.)"
|
"description": "Various cogs for Red, by cswimr"
|
||||||
}
|
}
|
||||||
|
|
10
mkdocs.yml
10
mkdocs.yml
|
@ -1,12 +1,12 @@
|
||||||
site_name: SeaCogs Documentation
|
site_name: SeaCogs Documentation
|
||||||
site_url: !ENV [SITE_URL, 'https://seacogs.coastalcommits.com']
|
site_url: !ENV [SITE_URL, 'https://seacogs.coastalcommits.com']
|
||||||
repo_name: CoastalCommits
|
repo_name: CoastalCommits
|
||||||
repo_url: https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs
|
repo_url: https://coastalcommits.com/cswimr/SeaCogs
|
||||||
edit_uri: !ENV [EDIT_URI, 'src/branch/main/.docs']
|
edit_uri: !ENV [EDIT_URI, 'src/branch/main/.docs']
|
||||||
copyright: Copyright © 2023-2024, SeaswimmerTheFsh
|
copyright: Copyright © 2023-2024, cswimr
|
||||||
docs_dir: .docs
|
docs_dir: .docs
|
||||||
|
|
||||||
site_author: SeaswimmerTheFsh
|
site_author: cswimr
|
||||||
site_description: Documentation for my Red-DiscordBot Cogs.
|
site_description: Documentation for my Red-DiscordBot Cogs.
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
|
@ -30,7 +30,7 @@ nav:
|
||||||
plugins:
|
plugins:
|
||||||
- git-authors
|
- git-authors
|
||||||
- search
|
- search
|
||||||
#- social
|
- social
|
||||||
- git-revision-date-localized:
|
- git-revision-date-localized:
|
||||||
enable_creation_date: true
|
enable_creation_date: true
|
||||||
type: timeago
|
type: timeago
|
||||||
|
@ -113,3 +113,5 @@ watch:
|
||||||
- ./bible
|
- ./bible
|
||||||
- ./nerdify
|
- ./nerdify
|
||||||
- ./pterodactyl
|
- ./pterodactyl
|
||||||
|
- ./emojiinfo
|
||||||
|
- ./antipolls
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"author" : ["SeaswimmerTheFsh (seasw.)"],
|
"author" : ["cswimr"],
|
||||||
"install_msg" : "Thank you for installing Nerdify!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs). Based off of PhasecoreX's [UwU](<https://github.com/PhasecoreX/PCXCogs/tree/master/uwu>) cog.",
|
"install_msg" : "Thank you for installing Nerdify!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs). Based off of PhasecoreX's [UwU](<https://github.com/PhasecoreX/PCXCogs/tree/master/uwu>) cog.",
|
||||||
"name" : "Nerdify",
|
"name" : "Nerdify",
|
||||||
"short" : "Nerdify your text!",
|
"short" : "Nerdify your text!",
|
||||||
"description" : "Nerdify your text!",
|
"description" : "Nerdify your text!",
|
||||||
|
|
|
@ -12,13 +12,15 @@ from typing import Any, Optional, Union
|
||||||
import discord
|
import discord
|
||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
from redbot.core.utils import chat_formatting, common_filters
|
from redbot.core.utils import chat_formatting, common_filters
|
||||||
|
from redbot.core.utils.chat_formatting import bold, humanize_list
|
||||||
|
|
||||||
|
|
||||||
class Nerdify(commands.Cog):
|
class Nerdify(commands.Cog):
|
||||||
"""Nerdify your text."""
|
"""Nerdify your text."""
|
||||||
|
|
||||||
__author__ = ["SeaswimmerTheFsh"]
|
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"]
|
||||||
__version__ = "1.3.4"
|
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs"
|
||||||
|
__version__ = "1.3.5"
|
||||||
__documentation__ = "https://seacogs.coastalcommits.com/nerdify/"
|
__documentation__ = "https://seacogs.coastalcommits.com/nerdify/"
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
|
@ -29,12 +31,13 @@ class Nerdify(commands.Cog):
|
||||||
n = "\n" if "\n\n" not in pre_processed else ""
|
n = "\n" if "\n\n" not in pre_processed else ""
|
||||||
text = [
|
text = [
|
||||||
f"{pre_processed}{n}",
|
f"{pre_processed}{n}",
|
||||||
f"Cog Version: **{self.__version__}**",
|
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})",
|
||||||
f"Author: {chat_formatting.humanize_list(self.__author__)}",
|
f"{bold('Author:')} {humanize_list(self.__author__)}",
|
||||||
f"Documentation: {self.__documentation__}"
|
f"{bold('Documentation:')} {self.__documentation__}",
|
||||||
]
|
]
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
|
|
||||||
@commands.command(aliases=["nerd"])
|
@commands.command(aliases=["nerd"])
|
||||||
async def nerdify(
|
async def nerdify(
|
||||||
self, ctx: commands.Context, *, text: Optional[str] = None
|
self, ctx: commands.Context, *, text: Optional[str] = None
|
||||||
|
|
2850
poetry.lock
generated
2850
poetry.lock
generated
File diff suppressed because it is too large
Load diff
BIN
pterodactyl/data/unknown.png
Normal file
BIN
pterodactyl/data/unknown.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"author" : ["SeaswimmerTheFsh (seasw.)"],
|
"author" : ["cswimr"],
|
||||||
"install_msg" : "Thank you for installing Pterodactyl!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).\nDocumentation can be found [here](https://seacogs.coastalcommits.com/pterodactyl ).",
|
"install_msg" : "Thank you for installing Pterodactyl!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).\nDocumentation can be found [here](https://seacogs.coastalcommits.com/pterodactyl ).",
|
||||||
"name" : "Pterodactyl",
|
"name" : "Pterodactyl",
|
||||||
"short" : "Interface with Pterodactyl through websockets.",
|
"short" : "Interface with Pterodactyl through websockets.",
|
||||||
"description" : "Interface with Pterodactyl through websockets.",
|
"description" : "Interface with Pterodactyl through websockets.",
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"min_bot_version": "3.5.0",
|
"min_bot_version": "3.5.0",
|
||||||
"min_python_version": [3, 8, 0],
|
"min_python_version": [3, 8, 0],
|
||||||
"requirements": ["git+https://github.com/SeaswimmerTheFsh/pydactyl", "websockets"],
|
"requirements": ["git+https://github.com/cswimr/pydactyl", "websockets"],
|
||||||
"tags": [
|
"tags": [
|
||||||
"pterodactyl",
|
"pterodactyl",
|
||||||
"minecraft",
|
"minecraft",
|
||||||
|
|
|
@ -9,7 +9,7 @@ from pydactyl import PterodactylClient
|
||||||
from redbot.core import app_commands, commands
|
from redbot.core import app_commands, commands
|
||||||
from redbot.core.app_commands import Choice
|
from redbot.core.app_commands import Choice
|
||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
from redbot.core.utils.chat_formatting import box, error, humanize_list
|
from redbot.core.utils.chat_formatting import bold, box, error, humanize_list
|
||||||
from redbot.core.utils.views import ConfirmView
|
from redbot.core.utils.views import ConfirmView
|
||||||
|
|
||||||
from pterodactyl import mcsrvstatus
|
from pterodactyl import mcsrvstatus
|
||||||
|
@ -20,8 +20,9 @@ from pterodactyl.logger import logger
|
||||||
class Pterodactyl(commands.Cog):
|
class Pterodactyl(commands.Cog):
|
||||||
"""Pterodactyl allows you to manage your Pterodactyl Panel from Discord."""
|
"""Pterodactyl allows you to manage your Pterodactyl Panel from Discord."""
|
||||||
|
|
||||||
__author__ = ["SeaswimmerTheFsh"]
|
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"]
|
||||||
__version__ = "2.0.0"
|
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs"
|
||||||
|
__version__ = "2.0.4"
|
||||||
__documentation__ = "https://seacogs.coastalcommits.com/pterodactyl/"
|
__documentation__ = "https://seacogs.coastalcommits.com/pterodactyl/"
|
||||||
|
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
|
@ -39,12 +40,29 @@ class Pterodactyl(commands.Cog):
|
||||||
n = "\n" if "\n\n" not in pre_processed else ""
|
n = "\n" if "\n\n" not in pre_processed else ""
|
||||||
text = [
|
text = [
|
||||||
f"{pre_processed}{n}",
|
f"{pre_processed}{n}",
|
||||||
f"Cog Version: **{self.__version__}**",
|
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})",
|
||||||
f"Author: {humanize_list(self.__author__)}",
|
f"{bold('Author:')} {humanize_list(self.__author__)}",
|
||||||
f"Documentation: {self.__documentation__}",
|
f"{bold('Documentation:')} {self.__documentation__}",
|
||||||
]
|
]
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
|
async def cog_load(self) -> None:
|
||||||
|
pterodactyl_keys = await self.bot.get_shared_api_tokens("pterodactyl")
|
||||||
|
api_key = pterodactyl_keys.get("api_key")
|
||||||
|
if api_key is None:
|
||||||
|
self.task.cancel()
|
||||||
|
raise ValueError("Pterodactyl API key not set. Please set it using `[p]set api`.")
|
||||||
|
base_url = await config.base_url()
|
||||||
|
if base_url is None:
|
||||||
|
self.task.cancel()
|
||||||
|
raise ValueError("Pterodactyl base URL not set. Please set it using `[p]pterodactyl config url`.")
|
||||||
|
server_id = await config.server_id()
|
||||||
|
if server_id is None:
|
||||||
|
self.task.cancel()
|
||||||
|
raise ValueError("Pterodactyl server ID not set. Please set it using `[p]pterodactyl config serverid`.")
|
||||||
|
|
||||||
|
self.client = PterodactylClient(base_url, api_key).client
|
||||||
|
|
||||||
async def cog_unload(self) -> None:
|
async def cog_unload(self) -> None:
|
||||||
self.update_topic.cancel()
|
self.update_topic.cancel()
|
||||||
self.task.cancel()
|
self.task.cancel()
|
||||||
|
@ -175,76 +193,47 @@ class Pterodactyl(commands.Cog):
|
||||||
|
|
||||||
async def power(self, ctx: Union[discord.Interaction, commands.Context], action: str, action_ing: str, warning: str = '') -> None:
|
async def power(self, ctx: Union[discord.Interaction, commands.Context], action: str, action_ing: str, warning: str = '') -> None:
|
||||||
if isinstance(ctx, discord.Interaction):
|
if isinstance(ctx, discord.Interaction):
|
||||||
author = ctx.user
|
ctx = await self.bot.get_context(ctx)
|
||||||
else:
|
|
||||||
author = ctx.author
|
|
||||||
|
|
||||||
current_status = await config.current_status()
|
current_status = await config.current_status()
|
||||||
|
|
||||||
if current_status == action_ing:
|
if current_status == action_ing:
|
||||||
if isinstance(ctx, discord.Interaction):
|
return await ctx.send(f"Server is already {action_ing}.", ephemeral=True)
|
||||||
return await ctx.response.send_message(f"Server is already {action_ing}.", ephemeral=True)
|
|
||||||
return await ctx.send(f"Server is already {action_ing}.")
|
|
||||||
|
|
||||||
if current_status in ["starting", "stopping"] and action != "kill":
|
if current_status in ["starting", "stopping"] and action != "kill":
|
||||||
if isinstance(ctx, discord.Interaction):
|
return await ctx.send("Another power action is already in progress.", ephemeral=True)
|
||||||
return await ctx.response.send_message("Another power action is already in progress.", ephemeral=True)
|
|
||||||
return await ctx.send("Another power action is already in progress.")
|
|
||||||
|
|
||||||
view = ConfirmView(author, disable_buttons=True)
|
view = ConfirmView(ctx.author, disable_buttons=True)
|
||||||
|
|
||||||
if isinstance(ctx, discord.Interaction):
|
message = await ctx.send(f"{warning}Are you sure you want to {action} the server?", view=view)
|
||||||
await ctx.response.send_message(f"{warning}Are you sure you want to {action} the server?", view=view)
|
|
||||||
else:
|
|
||||||
message = await ctx.send(f"{warning}Are you sure you want to {action} the server?", view=view)
|
|
||||||
|
|
||||||
await view.wait()
|
await view.wait()
|
||||||
|
|
||||||
if view.result is True:
|
if view.result is True:
|
||||||
if isinstance(ctx, discord.Interaction):
|
await message.edit(content=f"Sending websocket command to {action} server...", view=None)
|
||||||
await ctx.edit_original_response(content=f"Sending websocket command to {action} server...", view=None)
|
|
||||||
else:
|
|
||||||
await message.edit(content=f"Sending websocket command to {action} server...", view=None)
|
|
||||||
|
|
||||||
await self.websocket.send(json.dumps({"event": "set state", "args": [action]}))
|
await self.websocket.send(json.dumps({"event": "set state", "args": [action]}))
|
||||||
|
|
||||||
if isinstance(ctx, discord.Interaction):
|
await message.edit(content=f"Server {action_ing}", view=None)
|
||||||
await ctx.edit_original_response(content=f"Server {action_ing}", view=None)
|
|
||||||
else:
|
|
||||||
await message.edit(content=f"Server {action_ing}", view=None)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if isinstance(ctx, discord.Interaction):
|
await message.edit(content="Cancelled.", view=None)
|
||||||
await ctx.edit_original_response(content="Cancelled.", view=None)
|
|
||||||
else:
|
|
||||||
await message.edit(content="Cancelled.", view=None)
|
|
||||||
|
|
||||||
async def send_command(self, ctx: Union[discord.Interaction, commands.Context], command: str):
|
async def send_command(self, ctx: Union[discord.Interaction, commands.Context], command: str):
|
||||||
channel = self.bot.get_channel(await config.console_channel())
|
channel = self.bot.get_channel(await config.console_channel())
|
||||||
if isinstance(ctx, discord.Interaction):
|
if isinstance(ctx, discord.Interaction):
|
||||||
if channel:
|
ctx = await self.bot.get_context(ctx)
|
||||||
await channel.send(f"Received console command from {ctx.user.id}: {command[:1900]}", allowed_mentions=discord.AllowedMentions.none())
|
if channel:
|
||||||
try:
|
await channel.send(f"Received console command from {ctx.author.id}: {command[:1900]}", allowed_mentions=discord.AllowedMentions.none())
|
||||||
await self.websocket.send(json.dumps({"event": "send command", "args": [command]}))
|
try:
|
||||||
await ctx.response.send_message(f"Command sent to server. {box(command, 'json')}", ephemeral=True)
|
await self.websocket.send(json.dumps({"event": "send command", "args": [command]}))
|
||||||
except websockets.exceptions.ConnectionClosed as e:
|
await ctx.send(f"Command sent to server. {box(command, 'json')}")
|
||||||
logger.error("WebSocket connection closed: %s", e)
|
except websockets.exceptions.ConnectionClosed as e:
|
||||||
await ctx.response.send_message(error("WebSocket connection closed."))
|
logger.error("WebSocket connection closed: %s", e)
|
||||||
self.task.cancel()
|
await ctx.send(error("WebSocket connection closed."))
|
||||||
self.retry_counter = 0
|
self.task.cancel()
|
||||||
self.task = self.get_task()
|
self.retry_counter = 0
|
||||||
else:
|
self.task = self.get_task()
|
||||||
if channel:
|
|
||||||
await channel.send(f"Received console command from {ctx.author.id}: {command[:1900]}", allowed_mentions=discord.AllowedMentions.none())
|
|
||||||
try:
|
|
||||||
await self.websocket.send(json.dumps({"event": "send command", "args": [command]}))
|
|
||||||
await ctx.send(f"Command sent to server. {box(command, 'json')}")
|
|
||||||
except websockets.exceptions.ConnectionClosed as e:
|
|
||||||
logger.error("WebSocket connection closed: %s", e)
|
|
||||||
await ctx.send(error("WebSocket connection closed."))
|
|
||||||
self.task.cancel()
|
|
||||||
self.retry_counter = 0
|
|
||||||
self.task = self.get_task()
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_red_api_tokens_update(self, service_name: str, api_tokens: Mapping[str,str]): # pylint: disable=unused-argument
|
async def on_red_api_tokens_update(self, service_name: str, api_tokens: Mapping[str,str]): # pylint: disable=unused-argument
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# pylint: disable=cyclic-import
|
# pylint: disable=cyclic-import
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from typing import Optional, Union
|
from pathlib import Path
|
||||||
|
from typing import Optional, Tuple, Union
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import discord
|
import discord
|
||||||
import websockets
|
import websockets
|
||||||
from pydactyl import PterodactylClient
|
from pydactyl import PterodactylClient
|
||||||
|
from redbot.core.data_manager import bundled_data_path
|
||||||
from redbot.core.utils.chat_formatting import bold, pagify
|
from redbot.core.utils.chat_formatting import bold, pagify
|
||||||
|
|
||||||
from pterodactyl.config import config
|
from pterodactyl.config import config
|
||||||
|
@ -78,7 +80,12 @@ async def establish_websocket_connection(coginstance: Pterodactyl) -> None:
|
||||||
if join_message:
|
if join_message:
|
||||||
if chat_channel is not None:
|
if chat_channel is not None:
|
||||||
if coginstance.bot.embed_requested(chat_channel):
|
if coginstance.bot.embed_requested(chat_channel):
|
||||||
await chat_channel.send(embed=await generate_join_leave_embed(join_message, True))
|
embed, img = await generate_join_leave_embed(coginstance=coginstance, username=join_message,join=True)
|
||||||
|
if img:
|
||||||
|
with open(img, 'rb') as file:
|
||||||
|
await chat_channel.send(embed=embed, file=file)
|
||||||
|
else:
|
||||||
|
await chat_channel.send(embed=embed)
|
||||||
else:
|
else:
|
||||||
await chat_channel.send(f"{join_message} joined the game", allowed_mentions=discord.AllowedMentions.none())
|
await chat_channel.send(f"{join_message} joined the game", allowed_mentions=discord.AllowedMentions.none())
|
||||||
|
|
||||||
|
@ -86,7 +93,12 @@ async def establish_websocket_connection(coginstance: Pterodactyl) -> None:
|
||||||
if leave_message:
|
if leave_message:
|
||||||
if chat_channel is not None:
|
if chat_channel is not None:
|
||||||
if coginstance.bot.embed_requested(chat_channel):
|
if coginstance.bot.embed_requested(chat_channel):
|
||||||
await chat_channel.send(embed=await generate_join_leave_embed(leave_message, False))
|
embed, img = await generate_join_leave_embed(coginstance=coginstance, username=leave_message,join=False)
|
||||||
|
if img:
|
||||||
|
with open(img, 'rb') as file:
|
||||||
|
await chat_channel.send(embed=embed, file=file)
|
||||||
|
else:
|
||||||
|
await chat_channel.send(embed=embed)
|
||||||
else:
|
else:
|
||||||
await chat_channel.send(f"{leave_message} left the game", allowed_mentions=discord.AllowedMentions.none())
|
await chat_channel.send(f"{leave_message} left the game", allowed_mentions=discord.AllowedMentions.none())
|
||||||
|
|
||||||
|
@ -94,7 +106,7 @@ async def establish_websocket_connection(coginstance: Pterodactyl) -> None:
|
||||||
if achievement_message:
|
if achievement_message:
|
||||||
if chat_channel is not None:
|
if chat_channel is not None:
|
||||||
if coginstance.bot.embed_requested(chat_channel):
|
if coginstance.bot.embed_requested(chat_channel):
|
||||||
await chat_channel.send(embed=await generate_achievement_embed(achievement_message['username'], achievement_message['achievement'], achievement_message['challenge']))
|
await chat_channel.send(embed=await generate_achievement_embed(coginstance, achievement_message['username'], achievement_message['achievement'], achievement_message['challenge']))
|
||||||
else:
|
else:
|
||||||
await chat_channel.send(f"{achievement_message['username']} has {'completed the challenge' if achievement_message['challenge'] else 'made the advancement'} {achievement_message['achievement']}")
|
await chat_channel.send(f"{achievement_message['username']} has {'completed the challenge' if achievement_message['challenge'] else 'made the advancement'} {achievement_message['achievement']}")
|
||||||
|
|
||||||
|
@ -153,7 +165,7 @@ async def check_if_server_message(text: str) -> Union[bool, str]:
|
||||||
regex = await config.server_regex()
|
regex = await config.server_regex()
|
||||||
match: Optional[re.Match[str]] = re.match(regex, text)
|
match: Optional[re.Match[str]] = re.match(regex, text)
|
||||||
if match:
|
if match:
|
||||||
logger.debug("Message is a server message")
|
logger.trace("Message is a server message")
|
||||||
return match.group(1)
|
return match.group(1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -162,7 +174,7 @@ async def check_if_chat_message(text: str) -> Union[bool, dict]:
|
||||||
match: Optional[re.Match[str]] = re.match(regex, text)
|
match: Optional[re.Match[str]] = re.match(regex, text)
|
||||||
if match:
|
if match:
|
||||||
groups = {"username": match.group(1), "message": match.group(2)}
|
groups = {"username": match.group(1), "message": match.group(2)}
|
||||||
logger.debug("Message is a chat message\n%s", json.dumps(groups))
|
logger.trace("Message is a chat message\n%s", json.dumps(groups))
|
||||||
return groups
|
return groups
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -170,7 +182,7 @@ async def check_if_join_message(text: str) -> Union[bool, str]:
|
||||||
regex = await config.join_regex()
|
regex = await config.join_regex()
|
||||||
match: Optional[re.Match[str]] = re.match(regex, text)
|
match: Optional[re.Match[str]] = re.match(regex, text)
|
||||||
if match:
|
if match:
|
||||||
logger.debug("Message is a join message")
|
logger.trace("Message is a join message")
|
||||||
return match.group(1)
|
return match.group(1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -178,7 +190,7 @@ async def check_if_leave_message(text: str) -> Union[bool, str]:
|
||||||
regex = await config.leave_regex()
|
regex = await config.leave_regex()
|
||||||
match: Optional[re.Match[str]] = re.match(regex, text)
|
match: Optional[re.Match[str]] = re.match(regex, text)
|
||||||
if match:
|
if match:
|
||||||
logger.debug("Message is a leave message")
|
logger.trace("Message is a leave message")
|
||||||
return match.group(1)
|
return match.group(1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -191,23 +203,23 @@ async def check_if_achievement_message(text: str) -> Union[bool, dict]:
|
||||||
groups["challenge"] = True
|
groups["challenge"] = True
|
||||||
else:
|
else:
|
||||||
groups["challenge"] = False
|
groups["challenge"] = False
|
||||||
logger.debug("Message is an achievement message")
|
logger.trace("Message is an achievement message")
|
||||||
return groups
|
return groups
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def get_info(username: str) -> Optional[dict]:
|
async def get_info(username: str) -> Optional[dict]:
|
||||||
logger.debug("Retrieving player info for %s", username)
|
logger.verbose("Retrieving player info for %s", username)
|
||||||
endpoint = await config.api_endpoint()
|
endpoint = await config.api_endpoint()
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(f"https://playerdb.co/api/player/{endpoint}/{username}") as response:
|
async with session.get(f"https://playerdb.co/api/player/{endpoint}/{username}") as response:
|
||||||
if response.status == 200:
|
if response.status == 200:
|
||||||
logger.debug("Player info retrieved for %s", username)
|
logger.verbose("Player info retrieved for %s", username)
|
||||||
return await response.json()
|
return await response.json()
|
||||||
logger.error("Failed to retrieve player info for %s: %s", username, response.status)
|
logger.warning("Failed to retrieve player info for %s: %s", username, response.status)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def send_chat_discord(coginstance: Pterodactyl, username: str, message: str, avatar_url: str) -> None:
|
async def send_chat_discord(coginstance: Pterodactyl, username: str, message: str, avatar_url: str) -> None:
|
||||||
logger.debug("Sending chat message to Discord")
|
logger.trace("Sending chat message to Discord")
|
||||||
channel = coginstance.bot.get_channel(await config.chat_channel())
|
channel = coginstance.bot.get_channel(await config.chat_channel())
|
||||||
if channel is not None:
|
if channel is not None:
|
||||||
webhooks = await channel.webhooks()
|
webhooks = await channel.webhooks()
|
||||||
|
@ -215,33 +227,37 @@ async def send_chat_discord(coginstance: Pterodactyl, username: str, message: st
|
||||||
if webhook is None:
|
if webhook is None:
|
||||||
webhook = await channel.create_webhook(name="Pterodactyl Chat")
|
webhook = await channel.create_webhook(name="Pterodactyl Chat")
|
||||||
await webhook.send(content=message, username=username, avatar_url=avatar_url, allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=True))
|
await webhook.send(content=message, username=username, avatar_url=avatar_url, allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=True))
|
||||||
logger.debug("Chat message sent to Discord")
|
logger.trace("Chat message sent to Discord")
|
||||||
else:
|
else:
|
||||||
logger.warning("Chat channel not set. Skipping sending chat message to Discord")
|
logger.warning("Chat channel not set. Skipping sending chat message to Discord")
|
||||||
|
|
||||||
async def generate_join_leave_embed(username: str, join: bool) -> discord.Embed:
|
async def generate_join_leave_embed(coginstance: Pterodactyl, username: str, join: bool) -> Tuple[discord.Embed, Optional[Union[str, Path]]]:
|
||||||
embed = discord.Embed()
|
embed = discord.Embed()
|
||||||
embed.color = discord.Color.green() if join else discord.Color.red()
|
embed.color = discord.Color.green() if join else discord.Color.red()
|
||||||
embed.description = await config.join_msg() if join else await config.leave_msg()
|
embed.description = await config.join_msg() if join else await config.leave_msg()
|
||||||
info = await get_info(username)
|
info = await get_info(username)
|
||||||
if info:
|
if info:
|
||||||
|
img = None
|
||||||
embed.set_author(name=username, icon_url=info['data']['player']['avatar'])
|
embed.set_author(name=username, icon_url=info['data']['player']['avatar'])
|
||||||
else:
|
else:
|
||||||
embed.set_author(name=username, icon_url='https://seafsh.cc/u/j3AzqQ.png')
|
img = bundled_data_path(coginstance) / "unknown.png"
|
||||||
|
embed.set_author(name=username, icon_url='attachment://unknown.png')
|
||||||
embed.timestamp = discord.utils.utcnow()
|
embed.timestamp = discord.utils.utcnow()
|
||||||
return embed
|
return embed, img
|
||||||
|
|
||||||
async def generate_achievement_embed(username: str, achievement: str, challenge: bool) -> discord.Embed:
|
async def generate_achievement_embed(coginstance: Pterodactyl, username: str, achievement: str, challenge: bool) -> Tuple[discord.Embed, Optional[Union[str, Path]]]:
|
||||||
embed = discord.Embed()
|
embed = discord.Embed()
|
||||||
embed.color = discord.Color.from_str('#a800a7') if challenge else discord.Color.from_str('#54fb54')
|
embed.color = discord.Color.from_str('#a800a7') if challenge else discord.Color.from_str('#54fb54')
|
||||||
embed.description = f"{bold(username)} has {'completed the challenge' if challenge else 'made the advancement'} {bold(achievement)}"
|
embed.description = f"{bold(username)} has {'completed the challenge' if challenge else 'made the advancement'} {bold(achievement)}"
|
||||||
info = await get_info(username)
|
info = await get_info(username)
|
||||||
if info:
|
if info:
|
||||||
|
img = None
|
||||||
embed.set_author(name=username, icon_url=info['data']['player']['avatar'])
|
embed.set_author(name=username, icon_url=info['data']['player']['avatar'])
|
||||||
else:
|
else:
|
||||||
embed.set_author(name=username, icon_url='https://seafsh.cc/u/j3AzqQ.png')
|
img = bundled_data_path(coginstance) / "unknown.png"
|
||||||
|
embed.set_author(name=username, icon_url='attachment://unknown.png')
|
||||||
embed.timestamp = discord.utils.utcnow()
|
embed.timestamp = discord.utils.utcnow()
|
||||||
return embed
|
return embed, img
|
||||||
|
|
||||||
def mask_ip(string: str) -> str:
|
def mask_ip(string: str) -> str:
|
||||||
def check(match: re.Match[str]):
|
def check(match: re.Match[str]):
|
||||||
|
|
|
@ -1,49 +1,45 @@
|
||||||
[tool.poetry]
|
[project]
|
||||||
name = "seacogs"
|
name = "seacogs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "My assorted cogs for Red-DiscordBot."
|
description = "My assorted cogs for Red-DiscordBot."
|
||||||
authors = ["SeaswimmerTheFsh"]
|
authors = [{name = "cswimr", email = "seaswimmerthefsh@gmail.com"}]
|
||||||
license = "MPL 2"
|
license = {file="LICENSE"}
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
package-mode = false
|
requires-python = ">=3.11"
|
||||||
|
dependencies = [
|
||||||
|
"aiosqlite>=0.20.0",
|
||||||
|
"beautifulsoup4>=4.12.3",
|
||||||
|
"colorthief>=0.2.1",
|
||||||
|
"markdownify>=0.13.1",
|
||||||
|
"numpy>=2.1.2",
|
||||||
|
"phx-class-registry>=5.0.0",
|
||||||
|
"pillow>=10.4.0",
|
||||||
|
"pip>=24.3.1",
|
||||||
|
"py-dactyl",
|
||||||
|
"pydantic>=2.9.2",
|
||||||
|
"red-discordbot>=3.5.14",
|
||||||
|
"websockets>=13.1",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[project.optional-dependencies]
|
||||||
python = ">=3.11,<3.12"
|
documentation = [
|
||||||
Red-DiscordBot = "^3.5.9"
|
"mkdocs>=1.6.1",
|
||||||
py-dactyl = "^2.0.4"
|
"mkdocs-git-authors-plugin>=0.9.0",
|
||||||
websockets = "^12.0"
|
"mkdocs-git-revision-date-localized-plugin>=1.2.9",
|
||||||
pillow = "^10.3.0"
|
"mkdocs-material[imaging]>=9.5.40",
|
||||||
numpy = "^1.26.4"
|
"mkdocstrings[python]>=0.26.1",
|
||||||
pydantic = "^2.7.1"
|
"mkdocs-redirects>=1.2.1",
|
||||||
colorthief = "^0.2.1"
|
]
|
||||||
beautifulsoup4 = "^4.12.3"
|
|
||||||
markdownify = "^0.12.1"
|
|
||||||
aiosqlite = "^0.20.0"
|
|
||||||
phx-class-registry = "^5.0.0"
|
|
||||||
|
|
||||||
[tool.poetry.group.dev]
|
[tool.uv]
|
||||||
optional = true
|
dev-dependencies = [
|
||||||
|
"pylint>=3.3.1",
|
||||||
|
"ruff>=0.6.9",
|
||||||
|
"sqlite-web>=0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.uv.sources]
|
||||||
ruff = "^0.3.1"
|
py-dactyl = { git = "https://github.com/cswimr/pydactyl" }
|
||||||
pylint = "^3.1.0"
|
|
||||||
pipx = "^1.5.0"
|
|
||||||
sqlite-web = "^0.6.4"
|
|
||||||
|
|
||||||
[tool.poetry.group.docs]
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[tool.poetry.group.docs.dependencies]
|
|
||||||
mkdocs = "1.5.3"
|
|
||||||
mkdocstrings = {extras = ["python"], version = "0.24.0"}
|
|
||||||
mkdocs-git-authors-plugin = "0.7.2"
|
|
||||||
mkdocs-git-revision-date-localized-plugin = "1.2.2"
|
|
||||||
mkdocs-material = {extras = ["imaging"], version = "^9.5.2"}
|
|
||||||
mkdocs-redirects = "^1.2.1"
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core"]
|
|
||||||
build-backend = "poetry.core.masonry.api"
|
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
# Exclude a variety of commonly ignored directories.
|
# Exclude a variety of commonly ignored directories.
|
||||||
|
|
5
renovate.json
Normal file
5
renovate.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"local>cc/renovate-config"
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"author" : ["SeaswimmerTheFsh (seasw.)"],
|
"author" : ["cswimr"],
|
||||||
"install_msg" : "Thank you for installing SeaUtils!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
|
"install_msg" : "Thank you for installing SeaUtils!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).",
|
||||||
"name" : "SeaUtils",
|
"name" : "SeaUtils",
|
||||||
"short" : "A collection of useful utilities.",
|
"short" : "A collection of useful utilities.",
|
||||||
"description" : "A collection of useful utilities.",
|
"description" : "A collection of useful utilities.",
|
||||||
|
|
|
@ -38,22 +38,26 @@ def format_rfc_text(text: str, number: int) -> str:
|
||||||
class SeaUtils(commands.Cog):
|
class SeaUtils(commands.Cog):
|
||||||
"""A collection of random utilities."""
|
"""A collection of random utilities."""
|
||||||
|
|
||||||
__author__ = ["SeaswimmerTheFsh"]
|
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"]
|
||||||
__version__ = "1.0.0"
|
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs"
|
||||||
|
__version__ = "1.0.1"
|
||||||
|
__documentation__ = "https://seacogs.coastalcommits.com/seautils/"
|
||||||
|
|
||||||
def __init__(self, bot: Red) -> None:
|
def __init__(self, bot: Red) -> None:
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
def format_help_for_context(self, ctx: commands.Context) -> str:
|
def format_help_for_context(self, ctx: commands.Context) -> str:
|
||||||
pre_processed = super().format_help_for_context(ctx=ctx) or ""
|
pre_processed = super().format_help_for_context(ctx) or ""
|
||||||
n = "\n" if "\n\n" not in pre_processed else ""
|
n = "\n" if "\n\n" not in pre_processed else ""
|
||||||
text = [
|
text = [
|
||||||
f"{pre_processed}{n}",
|
f"{pre_processed}{n}",
|
||||||
f"Cog Version: **{self.__version__}**",
|
f"{cf.bold('Cog Version:')} [{self.__version__}]({self.__git__})",
|
||||||
f"Author: {cf.humanize_list(items=self.__author__)}"
|
f"{cf.bold('Author:')} {cf.humanize_list(self.__author__)}",
|
||||||
|
f"{cf.bold('Documentation:')} {self.__documentation__}",
|
||||||
]
|
]
|
||||||
return "\n".join(text)
|
return "\n".join(text)
|
||||||
|
|
||||||
|
|
||||||
def format_src(self, obj: Any) -> str:
|
def format_src(self, obj: Any) -> str:
|
||||||
"""A large portion of this code is repurposed from Zephyrkul's RTFS cog.
|
"""A large portion of this code is repurposed from Zephyrkul's RTFS cog.
|
||||||
https://github.com/Zephyrkul/FluffyCogs/blob/master/rtfs/rtfs.py"""
|
https://github.com/Zephyrkul/FluffyCogs/blob/master/rtfs/rtfs.py"""
|
||||||
|
|
Loading…
Add table
Reference in a new issue