Compare commits
No commits in common. "main" and "musicdownloader_async" have entirely different histories.
main
...
musicdownl
67 changed files with 937 additions and 4157 deletions
|
@ -1,16 +0,0 @@
|
|||
[MESSAGES CONTROL]
|
||||
disable=
|
||||
too-many-lines,
|
||||
missing-module-docstring,
|
||||
missing-function-docstring,
|
||||
missing-class-docstring,
|
||||
line-too-long,
|
||||
too-many-arguments,
|
||||
too-many-branches,
|
||||
superfluous-parens,
|
||||
invalid-name,
|
||||
too-many-locals,
|
||||
too-many-public-methods,
|
||||
too-many-statements,
|
||||
arguments-differ,
|
||||
too-many-return-statements
|
|
@ -1,20 +0,0 @@
|
|||
name: Actions
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
Lint Code (Pylint):
|
||||
runs-on: docker
|
||||
container: www.coastalcommits.com/cswimr/actions:galaxycogs
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: poetry install --with dev --no-root
|
||||
|
||||
- name: Analysing code with Pylint
|
||||
run: pylint --rcfile .forgejo/workflows/config/.pylintrc $(git ls-files '*.py')
|
|
@ -16,8 +16,5 @@ A clear and concise description of what you want to happen.
|
|||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
Add any other context or screenshots about the feature request here.
|
|
@ -7,9 +7,6 @@ assignees: ''
|
|||
|
||||
---
|
||||
|
||||
**What cog is your feature request for?**
|
||||
A cog in this repository.
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
@ -19,8 +16,5 @@ A clear and concise description of what you want to happen.
|
|||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
Add any other context or screenshots about the feature request here.
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,2 +1,7 @@
|
|||
galaxy/slashtag arguments.txt
|
||||
galaxy_server.yaml
|
||||
global.yaml
|
||||
galaxy_staff_server.yaml
|
||||
combat_welder.yaml
|
||||
OOUCogs
|
||||
.venv
|
||||
|
|
38
README.md
38
README.md
|
@ -1,9 +1,18 @@
|
|||
# GalaxyCogs
|
||||
|
||||
<p align="center">
|
||||
<a href="https://discord.com/invite/robloxgalaxy">
|
||||
<img src="https://discordapp.com/api/guilds/204965774618656769/widget.png?style=shield" alt="Galaxy Discord Server">
|
||||
</a>
|
||||
<a href="https://www.python.org/downloads/">
|
||||
<img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/Red-Discordbot">
|
||||
</a>
|
||||
<a href="https://github.com/Rapptz/discord.py/">
|
||||
<img src="https://img.shields.io/badge/discord-py-blue.svg" alt="discord.py">
|
||||
</a>
|
||||
</p>
|
||||
Repository for Redbot cogs developed by the Galaxy Discord Management team.
|
||||
|
||||
**This readme is heavily outdated.**
|
||||
|
||||
## ExportChannels **(WIP)**[^incomplete]
|
||||
|
||||
This cog allows you to easily export channels using Discord Chat Exporter.
|
||||
|
@ -30,7 +39,18 @@ Currently supports:
|
|||
|
||||
* Users
|
||||
* Servers/Guilds
|
||||
* Roles
|
||||
* Roles[^dpy_notice]
|
||||
[^dpy_notice]:
|
||||
Due to Red's use of Discord.py 1.7.3, the ``[p]roleinfo`` command in the Info cog does not show all permissions. This is due to the outdated Discord.py version not supporting checking for all permissions.
|
||||
|
||||
## MusicDownloader
|
||||
|
||||
Allows users to download **just the audio files** from any YouTube video. Currently only supports YouTube and does not work for video.
|
||||
|
||||
Currently partially broken on **just old versions of Red**, if you're on an old version (such as `3.4.18`) please download [this database file](https://drive.google.com/file/d/11Ena7bPrF7eVhR9ZVmdXb0hHCg-0I76d/view?usp=sharing) and put it in your cog data directory for this cog, which should be
|
||||
```
|
||||
<where-ever you've installed red>/Red-DiscordBot/data/<instance name>/cogs/MusicDownloader
|
||||
```
|
||||
|
||||
## Podcast **(WIP)**[^incomplete]
|
||||
|
||||
|
@ -40,7 +60,7 @@ Also features user blacklists, both configurable per-server and globally.
|
|||
[^incomplete]:
|
||||
This cog currently is non-functional. This notice will be removed once the Cog is completed.
|
||||
|
||||
## Shortmute
|
||||
## Shortmutes **(WIP)**[^incomplete]
|
||||
|
||||
Allows staff members to shortmute individuals for up to 30 minutes, using Discord's Timeouts feature.
|
||||
|
||||
|
@ -53,7 +73,7 @@ Features:
|
|||
* Separate approved and denied channels
|
||||
* Custom emoji support
|
||||
|
||||
## SugonCredit[^incomplete]
|
||||
## SugonCredit
|
||||
|
||||
Implements a way for moderators to give out social-credit like points, dubbed 'sugoncredits' by the community.
|
||||
|
||||
|
@ -62,11 +82,3 @@ Features:
|
|||
* Add Credit to people.
|
||||
* Remove Credit from people.
|
||||
* Supports custom currency names and bank names.
|
||||
|
||||
## YouTubeDownloader
|
||||
|
||||
Allows users to download any YouTube video.
|
||||
|
||||
`[p]download https://youtu.be/dQw4w9WgXcQ` - Downloads the audio from [this video](https://youtu.be/dQw4w9WgXcQ) in `.m4a` format.
|
||||
|
||||
`[p]download https://youtu.be/dQw4w9WgXcQ false` - Downloads [this video](https://youtu.be/dQw4w9WgXcQ) in `.mp4` format.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .exportchannels import ExportChannels
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(ExportChannels(bot))
|
||||
def setup(bot):
|
||||
bot.add_cog(ExportChannels(bot))
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,14 +6,14 @@
|
|||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v7.0": {
|
||||
"DiscordChatExporter.Cli/2.40.4": {
|
||||
"DiscordChatExporter.Cli/2.39.1": {
|
||||
"dependencies": {
|
||||
"CliFx": "2.3.4",
|
||||
"Deorcify": "1.0.2",
|
||||
"DiscordChatExporter.Core": "2.40.4",
|
||||
"DotnetRuntimeBootstrapper": "2.5.1",
|
||||
"Gress": "2.1.1",
|
||||
"Spectre.Console": "0.47.0"
|
||||
"CliFx": "2.3.1",
|
||||
"DiscordChatExporter.Core": "2.39.1",
|
||||
"DotnetRuntimeBootstrapper": "2.4.0",
|
||||
"FuckRussia": "1.0.1",
|
||||
"Gress": "2.0.1",
|
||||
"Spectre.Console": "0.46.0"
|
||||
},
|
||||
"runtime": {
|
||||
"DiscordChatExporter.Cli.dll": {}
|
||||
|
@ -27,40 +27,29 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"AngleSharp/1.0.4": {
|
||||
"dependencies": {
|
||||
"System.Text.Encoding.CodePages": "7.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net7.0/AngleSharp.dll": {
|
||||
"assemblyVersion": "1.0.4.0",
|
||||
"fileVersion": "1.0.4.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AsyncKeyedLock/6.2.1": {
|
||||
"AsyncKeyedLock/6.2.0": {
|
||||
"runtime": {
|
||||
"lib/net5.0/AsyncKeyedLock.dll": {
|
||||
"assemblyVersion": "6.2.1.0",
|
||||
"fileVersion": "6.2.1.0"
|
||||
"assemblyVersion": "6.2.0.0",
|
||||
"fileVersion": "6.2.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CliFx/2.3.4": {
|
||||
"CliFx/2.3.1": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.1/CliFx.dll": {
|
||||
"assemblyVersion": "2.3.4.0",
|
||||
"fileVersion": "2.3.4.0"
|
||||
"assemblyVersion": "2.3.1.0",
|
||||
"fileVersion": "2.3.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Deorcify/1.0.2": {},
|
||||
"DotnetRuntimeBootstrapper/2.5.1": {},
|
||||
"Gress/2.1.1": {
|
||||
"DotnetRuntimeBootstrapper/2.4.0": {},
|
||||
"FuckRussia/1.0.1": {},
|
||||
"Gress/2.0.1": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Gress.dll": {
|
||||
"assemblyVersion": "2.1.1.0",
|
||||
"fileVersion": "2.1.1.0"
|
||||
"assemblyVersion": "2.0.1.0",
|
||||
"fileVersion": "2.0.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -72,30 +61,30 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Polly/7.2.4": {
|
||||
"Polly/7.2.3": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Polly.dll": {
|
||||
"assemblyVersion": "7.0.0.0",
|
||||
"fileVersion": "7.2.4.982"
|
||||
"fileVersion": "7.2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RazorBlade/0.4.3": {
|
||||
"RazorBlade/0.4.2": {
|
||||
"runtime": {
|
||||
"lib/net6.0/RazorBlade.dll": {
|
||||
"assemblyVersion": "0.4.3.0",
|
||||
"fileVersion": "0.4.3.0"
|
||||
"assemblyVersion": "0.4.2.0",
|
||||
"fileVersion": "0.4.2.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spectre.Console/0.47.0": {
|
||||
"Spectre.Console/0.46.0": {
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.5"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net7.0/Spectre.Console.dll": {
|
||||
"assemblyVersion": "0.0.0.0",
|
||||
"fileVersion": "0.47.0.0"
|
||||
"fileVersion": "0.46.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -108,39 +97,26 @@
|
|||
}
|
||||
},
|
||||
"System.Memory/4.5.5": {},
|
||||
"System.Text.Encoding.CodePages/7.0.0": {},
|
||||
"WebMarkupMin.Core/2.14.0": {
|
||||
"WebMarkupMin.Core/2.13.8": {
|
||||
"dependencies": {
|
||||
"AdvancedStringBuilder": "0.1.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.1/WebMarkupMin.Core.dll": {
|
||||
"assemblyVersion": "2.14.0.0",
|
||||
"fileVersion": "2.14.0.0"
|
||||
"assemblyVersion": "2.13.8.0",
|
||||
"fileVersion": "2.13.8.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"YoutubeExplode/6.3.1": {
|
||||
"DiscordChatExporter.Core/2.39.1": {
|
||||
"dependencies": {
|
||||
"AngleSharp": "1.0.4"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net5.0/YoutubeExplode.dll": {
|
||||
"assemblyVersion": "6.3.1.0",
|
||||
"fileVersion": "6.3.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DiscordChatExporter.Core/2.40.4": {
|
||||
"dependencies": {
|
||||
"AsyncKeyedLock": "6.2.1",
|
||||
"Gress": "2.1.1",
|
||||
"AsyncKeyedLock": "6.2.0",
|
||||
"Gress": "2.0.1",
|
||||
"JsonExtensions": "1.2.0",
|
||||
"Polly": "7.2.4",
|
||||
"RazorBlade": "0.4.3",
|
||||
"Polly": "7.2.3",
|
||||
"RazorBlade": "0.4.2",
|
||||
"Superpower": "3.0.0",
|
||||
"WebMarkupMin.Core": "2.14.0",
|
||||
"YoutubeExplode": "6.3.1"
|
||||
"WebMarkupMin.Core": "2.13.8"
|
||||
},
|
||||
"runtime": {
|
||||
"DiscordChatExporter.Core.dll": {}
|
||||
|
@ -149,7 +125,7 @@
|
|||
}
|
||||
},
|
||||
"libraries": {
|
||||
"DiscordChatExporter.Cli/2.40.4": {
|
||||
"DiscordChatExporter.Cli/2.39.1": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
|
@ -161,47 +137,40 @@
|
|||
"path": "advancedstringbuilder/0.1.0",
|
||||
"hashPath": "advancedstringbuilder.0.1.0.nupkg.sha512"
|
||||
},
|
||||
"AngleSharp/1.0.4": {
|
||||
"AsyncKeyedLock/6.2.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-G8R4C2MEDFQvjUbYz1QIcGfibjsTJnzP0aWy8iQgWWk7eKacYydCNGD3JMhVL0Q5pZQ9RYlqpKNESEU5NpqsRw==",
|
||||
"path": "anglesharp/1.0.4",
|
||||
"hashPath": "anglesharp.1.0.4.nupkg.sha512"
|
||||
"sha512": "sha512-FJqEME0eyM1EHVymnG32vmqKEFZ9X7/LX0NOuHyAeQqpncJFt+ODvzfx0SAlmF6m5NtL6xyPfvt9uTckbSWLhQ==",
|
||||
"path": "asynckeyedlock/6.2.0",
|
||||
"hashPath": "asynckeyedlock.6.2.0.nupkg.sha512"
|
||||
},
|
||||
"AsyncKeyedLock/6.2.1": {
|
||||
"CliFx/2.3.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-WUvN3Q7aL3wARMcVi/KYTHjOC+0Ld+/ikKU6UGr4aOl7TuK1I2MNKeUkmfr7y4S3UNjQbpzNQCV2OcJq1S/yXg==",
|
||||
"path": "asynckeyedlock/6.2.1",
|
||||
"hashPath": "asynckeyedlock.6.2.1.nupkg.sha512"
|
||||
"sha512": "sha512-erOn8SLCHt3p+pj8nIKmzqDyXV6hwQr1wHmyVuFujnLJRKP1ovuswPBNKUNXv9Le7/HhNawln/Upc5v6Nhn3zA==",
|
||||
"path": "clifx/2.3.1",
|
||||
"hashPath": "clifx.2.3.1.nupkg.sha512"
|
||||
},
|
||||
"CliFx/2.3.4": {
|
||||
"DotnetRuntimeBootstrapper/2.4.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-CFD8yL9McQrKa2dnGdXx/e1ob3QXcVICHpE9XbPXnFw1yPdqAgij3pdS4Xfii3U0+D1HJRW1rOPtNnuR7lI1cQ==",
|
||||
"path": "clifx/2.3.4",
|
||||
"hashPath": "clifx.2.3.4.nupkg.sha512"
|
||||
"sha512": "sha512-CuNwd8O1trpAaPlhcP4ourZH4onf6FqBDWQzzmKqqgF3TZCKkFuL5xDZNvbWWdDQI2dx3z/XBRxwBQghVKSD7Q==",
|
||||
"path": "dotnetruntimebootstrapper/2.4.0",
|
||||
"hashPath": "dotnetruntimebootstrapper.2.4.0.nupkg.sha512"
|
||||
},
|
||||
"Deorcify/1.0.2": {
|
||||
"FuckRussia/1.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-khu8MahgfbXc8eQxfZgRX38Jgu1ZLg+6aK906XtIkUpzMovhaPs4ZOWcUiDLtPz0a9m7TcQwtGtoeRgghO8mVw==",
|
||||
"path": "deorcify/1.0.2",
|
||||
"hashPath": "deorcify.1.0.2.nupkg.sha512"
|
||||
"sha512": "sha512-mdo3UfOIrRfi05m6qN+rJt3yKkM5Bq9hC3b/NYe5c0orhF6TGsIoJBo+OqX6P2LLouSeFNE01UjR9bbvXbT9+A==",
|
||||
"path": "fuckrussia/1.0.1",
|
||||
"hashPath": "fuckrussia.1.0.1.nupkg.sha512"
|
||||
},
|
||||
"DotnetRuntimeBootstrapper/2.5.1": {
|
||||
"Gress/2.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Ufbf8Qe9hwY/KojfimWX4Kqka3QFH4IoGiQj8LPRlyk68/ApHxsG+p6GeTwukY0sIgxA1VXUAFkbvnn9rbZyaw==",
|
||||
"path": "dotnetruntimebootstrapper/2.5.1",
|
||||
"hashPath": "dotnetruntimebootstrapper.2.5.1.nupkg.sha512"
|
||||
},
|
||||
"Gress/2.1.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-n/YK1XYM2xUhoPFRRJpoTk5qtWBwTkSZlWtxqb4JNX3pDfwoMiu6G+zs65y8Z8SfIUxhOhU5yO/v58WkZynECg==",
|
||||
"path": "gress/2.1.1",
|
||||
"hashPath": "gress.2.1.1.nupkg.sha512"
|
||||
"sha512": "sha512-Ky68Wbb3WUkFw5g92tcTRNf5IVMV5AyLB8+uw4pCpXmfS2gkIzWV9BnTGNu+aTaklYqvSZlQL6BnH+9I7HG5tg==",
|
||||
"path": "gress/2.0.1",
|
||||
"hashPath": "gress.2.0.1.nupkg.sha512"
|
||||
},
|
||||
"JsonExtensions/1.2.0": {
|
||||
"type": "package",
|
||||
|
@ -210,26 +179,26 @@
|
|||
"path": "jsonextensions/1.2.0",
|
||||
"hashPath": "jsonextensions.1.2.0.nupkg.sha512"
|
||||
},
|
||||
"Polly/7.2.4": {
|
||||
"Polly/7.2.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-bw00Ck5sh6ekduDE3mnCo1ohzuad946uslCDEENu3091+6UKnBuKLo4e+yaNcCzXxOZCXWY2gV4a35+K1d4LDA==",
|
||||
"path": "polly/7.2.4",
|
||||
"hashPath": "polly.7.2.4.nupkg.sha512"
|
||||
"sha512": "sha512-DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==",
|
||||
"path": "polly/7.2.3",
|
||||
"hashPath": "polly.7.2.3.nupkg.sha512"
|
||||
},
|
||||
"RazorBlade/0.4.3": {
|
||||
"RazorBlade/0.4.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-EwoDxG2aw5UuKsxKbg9W8PPlmkvPvp9y7yLjRhfIQvnDRZEfr3n+RCs46VHlIvALbCGDCX3S0B/zKMEmkFLHLQ==",
|
||||
"path": "razorblade/0.4.3",
|
||||
"hashPath": "razorblade.0.4.3.nupkg.sha512"
|
||||
"sha512": "sha512-uLNIoO35t+gOEWT7EdofyN0tFsbrntoHX5a19CgjwheDhrDSgGY3ubF9zsp4hQnkGhBLAQaQ4K2yyxDHbuC3GQ==",
|
||||
"path": "razorblade/0.4.2",
|
||||
"hashPath": "razorblade.0.4.2.nupkg.sha512"
|
||||
},
|
||||
"Spectre.Console/0.47.0": {
|
||||
"Spectre.Console/0.46.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-wz8mszcZr0cSOo8GyoG9e2DFW0SkMT8/n78Q/lIXX7EbCtHNXOoOKWpJ9Str+rCYtmQOGGyDutZzubrUHK/XkA==",
|
||||
"path": "spectre.console/0.47.0",
|
||||
"hashPath": "spectre.console.0.47.0.nupkg.sha512"
|
||||
"sha512": "sha512-qd2OMEGxfQW1KLuQj56KKHkIK6eB/IUF/AET5CRl/efROHGsvBehx9shfkL0HgJEHLCwRiR7foWi4/LkdPk18g==",
|
||||
"path": "spectre.console/0.46.0",
|
||||
"hashPath": "spectre.console.0.46.0.nupkg.sha512"
|
||||
},
|
||||
"Superpower/3.0.0": {
|
||||
"type": "package",
|
||||
|
@ -245,28 +214,14 @@
|
|||
"path": "system.memory/4.5.5",
|
||||
"hashPath": "system.memory.4.5.5.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Encoding.CodePages/7.0.0": {
|
||||
"WebMarkupMin.Core/2.13.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==",
|
||||
"path": "system.text.encoding.codepages/7.0.0",
|
||||
"hashPath": "system.text.encoding.codepages.7.0.0.nupkg.sha512"
|
||||
"sha512": "sha512-dXAyg/mMmkOBGSzhjOv5dJIvT+u8mBhEPG+QGp+UW4et/lGucSas055tb1793UT+gV5BOAp+v0FqufLyC7Urjw==",
|
||||
"path": "webmarkupmin.core/2.13.8",
|
||||
"hashPath": "webmarkupmin.core.2.13.8.nupkg.sha512"
|
||||
},
|
||||
"WebMarkupMin.Core/2.14.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-fRydp8dkCWkC3RDL+7ABWLLTgAnFEb5k7flhWrTlyGatg0rpT4HArKnGmfgfQ27FVMa1lCOAzFE1ORF5y5Pzbw==",
|
||||
"path": "webmarkupmin.core/2.14.0",
|
||||
"hashPath": "webmarkupmin.core.2.14.0.nupkg.sha512"
|
||||
},
|
||||
"YoutubeExplode/6.3.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-+k/RYu3UCI2TwSo/QuIHnhSQ74GRy1gNSuthb/o2ZRNSLidzmbyWaNtBABOxqQJj6ohG/fVScin8cuFVg3Q5vA==",
|
||||
"path": "youtubeexplode/6.3.1",
|
||||
"hashPath": "youtubeexplode.6.3.1.nupkg.sha512"
|
||||
},
|
||||
"DiscordChatExporter.Core/2.40.4": {
|
||||
"DiscordChatExporter.Core/2.39.1": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,2 +1,2 @@
|
|||
This bundled version of Discord Chat Exporter can be found here: https://github.com/Tyrrrz/DiscordChatExporter/releases/tag/2.40.4
|
||||
This bundled version of Discord Chat Exporter can be found here: https://github.com/Tyrrrz/DiscordChatExporter/releases/tag/2.39.1
|
||||
I DID NOT MAKE THIS PROGRAM. This program is developed by Tyrrrz, NOT me.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,35 +1,24 @@
|
|||
import asyncio
|
||||
import os
|
||||
|
||||
import discord
|
||||
from redbot.core import Config, checks, commands, data_manager
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
from redbot.core import Config, checks, commands, bot, data_manager
|
||||
|
||||
class ExportChannels(commands.Cog):
|
||||
"""Custom cog to export channels to JSON and HTML formats using Discord Chat Exporter.
|
||||
Developed by cswimr and yname."""
|
||||
Developed by SeaswimmerTheFsh and yname."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=48258471944753312)
|
||||
self.config.register_global(
|
||||
bot_token = None
|
||||
bot_token = "0"
|
||||
)
|
||||
|
||||
|
||||
def export(self, ctx, channel, token):
|
||||
self.data_path = data_manager.cog_data_path(self)
|
||||
|
||||
class ConfigException(Exception):
|
||||
pass
|
||||
|
||||
async def export(self, channels: list):
|
||||
token = await self.config.bot_token()
|
||||
if token is None:
|
||||
raise(self.ConfigException("Bot token not set!"))
|
||||
data_path = data_manager.cog_data_path(self)
|
||||
bundled_data_path = data_manager.bundled_data_path(self)
|
||||
out = f'{data_path}{os.sep}Exported Channels'
|
||||
channel = channels[0]
|
||||
file_location_html = f'{os.sep}{out}{os.sep}%g{os.sep}%c{os.sep}Export.html'
|
||||
file_location_json = f'{os.sep}{out}{os.sep}%g{os.sep}%c{os.sep}DCE-f.json'
|
||||
self.bundled_data_path = data_manager.bundled_data_path(self)
|
||||
out = f'{self.data_path}/Exported Channels'
|
||||
try:
|
||||
os.mkdir(out)
|
||||
except FileExistsError:
|
||||
|
@ -39,47 +28,41 @@ class ExportChannels(commands.Cog):
|
|||
'DiscordChatExporter.Cli.dll',
|
||||
'export',
|
||||
'--format', 'HtmlDark',
|
||||
'--output', file_location_html,
|
||||
'--token', token,
|
||||
'--channel', channel,
|
||||
'--reuse-media',
|
||||
'--output', f'/{out}/%G (%g)/%C (%c)/Export.html',
|
||||
'--token', f'{token}',
|
||||
'--channel', {channel},
|
||||
'--media',
|
||||
'--fuck_russia', 'true',
|
||||
]
|
||||
os.chdir(bundled_data_path)
|
||||
process_1 = await asyncio.create_subprocess_exec(*args)
|
||||
while True:
|
||||
return_code_1 = process_1.poll()
|
||||
if return_code_1 is not None:
|
||||
break
|
||||
if bot:
|
||||
args += '--bot'
|
||||
os.chdir(self.bundled_data_path)
|
||||
subprocess.call(args)
|
||||
args = [
|
||||
'dotnet',
|
||||
'DiscordChatExporter.Cli.dll',
|
||||
'export',
|
||||
'--format', 'Json',
|
||||
'--output', file_location_json,
|
||||
'--token', token,
|
||||
'--channel', channels,
|
||||
'--output', f'/{out}/%G (%g)/%C (%c)/DCE-f.json',
|
||||
'--token', f'{token}',
|
||||
'--channel', {channel},
|
||||
'--reuse_media',
|
||||
'--media',
|
||||
'--markdown', 'false',
|
||||
'--fuck_russia', 'true',
|
||||
]
|
||||
os.chdir(bundled_data_path)
|
||||
process_2 = await asyncio.create_subprocess_exec(*args)
|
||||
while True:
|
||||
return_code_2 = process_2.poll()
|
||||
if return_code_2 is not None:
|
||||
break
|
||||
if bot:
|
||||
args += '--bot'
|
||||
os.chdir(self.bundled_data_path)
|
||||
subprocess.call(args)
|
||||
|
||||
@commands.group()
|
||||
@checks.is_owner()
|
||||
async def exportset(self, ctx: commands.Context):
|
||||
async def exportset(self, ctx):
|
||||
"""Configuration options for the ExportChannels cog."""
|
||||
|
||||
@exportset.command()
|
||||
@checks.is_owner()
|
||||
async def token(self, ctx: commands.Context, token: str):
|
||||
async def token(self, ctx, token: str):
|
||||
"""Sets the bot token used for Discord Chat Exporter."""
|
||||
await self.config.bot_token.set({token})
|
||||
await ctx.send(content="Token set!")
|
||||
|
@ -87,18 +70,19 @@ class ExportChannels(commands.Cog):
|
|||
|
||||
@exportset.command()
|
||||
@checks.is_owner()
|
||||
async def checkoutputpath(self, ctx: commands.Context):
|
||||
async def checkoutputpath(self, ctx):
|
||||
"""Checks what file path DCE is outputting to."""
|
||||
self.data_path = data_manager.cog_data_path(self)
|
||||
await ctx.send(content=f"{self.data_path}")
|
||||
|
||||
@commands.command()
|
||||
@commands.admin()
|
||||
async def exportchannel(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||
async def exportchannel(self, ctx, channel: discord.Channel):
|
||||
"""Exports a channel using Discord Chat Exporter."""
|
||||
token = await self.config.bot_token()
|
||||
if token is None:
|
||||
await ctx.send(content=f"Please set your token with the ``{ctx.prefix}exportset token`` command!")
|
||||
token = await self.config.bot_token
|
||||
dce_install = data_manager.bundled_data_path(self)
|
||||
if token == 0:
|
||||
await ctx.send(content="Please set your token with the ``exportset token`` command!")
|
||||
return
|
||||
id_list = [thread.id for thread in channel.threads]
|
||||
id_list.insert(0, channel.id)
|
||||
await self.export(id_list)
|
||||
else:
|
||||
await self.export(channel.id, token)
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
from .forums import Forums
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Forums(bot))
|
208
forums/forums.py
208
forums/forums.py
|
@ -1,208 +0,0 @@
|
|||
import discord
|
||||
from discord import ui
|
||||
from redbot.core import Config, commands
|
||||
|
||||
|
||||
class Forums(commands.Cog):
|
||||
"""Custom cog intended for use on the Galaxy discord server.
|
||||
Developed by cswimr."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=2352711325)
|
||||
self.config.register_guild(
|
||||
request_roles=[],
|
||||
forum_channel=None,
|
||||
forum_tag=None
|
||||
)
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
async def resolved(self, ctx: commands.Context, *, reason: str = None):
|
||||
"""Marks a thread as resolved."""
|
||||
channel_id = await self.config.guild(ctx.guild).forum_channel()
|
||||
tag = await self.config.guild(ctx.guild).forum_tag()
|
||||
request_role_ids = await self.config.guild(ctx.guild).request_roles()
|
||||
if channel_id is None or tag is None or request_role_ids is None:
|
||||
await ctx.reply(f"Configuration not set properly! Please set configuration options with `{ctx.prefix}resolvedset`.")
|
||||
return
|
||||
if isinstance(ctx.channel, discord.Thread) and ctx.channel.parent_id == channel_id:
|
||||
request_roles = [ctx.guild.get_role(role_id) for role_id in request_role_ids]
|
||||
match = any(role in ctx.author.roles for role in request_roles)
|
||||
passed_info = {
|
||||
"ctx": ctx
|
||||
}
|
||||
if match and reason:
|
||||
passed_info.update({"reason": reason})
|
||||
if match or ctx.author.id == ctx.channel.owner.id:
|
||||
msg = await ctx.send("Are you sure you'd like to mark this thread as resolved?")
|
||||
passed_info.update({"msg": msg})
|
||||
await msg.edit(view=self.ResolvedButtons(timeout=180, passed_info=passed_info))
|
||||
else:
|
||||
await ctx.message.add_reaction("❌")
|
||||
await ctx.message.delete(delay=5)
|
||||
|
||||
class ResolvedButtons(ui.View):
|
||||
def __init__(self, timeout, passed_info: dict):
|
||||
super().__init__()
|
||||
self.timeout = timeout
|
||||
self.ctx: commands.Context = passed_info['ctx']
|
||||
self.msg: discord.Message = passed_info['msg']
|
||||
if 'reason' in passed_info:
|
||||
self.reason: str = passed_info['reason']
|
||||
else:
|
||||
self.reason = False
|
||||
self.config = Config.get_conf(None, cog_name='Forums', identifier=2352711325)
|
||||
|
||||
@ui.button(label="Yes", style=discord.ButtonStyle.success, emoji="✅")
|
||||
async def resolved_button_yes(self, interaction: discord.Interaction, button: ui.Button): # pylint: disable=unused-argument
|
||||
request_role_ids = await self.config.guild(interaction.guild).request_roles()
|
||||
request_roles = [interaction.guild.get_role(role_id) for role_id in request_role_ids]
|
||||
match = any(role in interaction.user.roles for role in request_roles)
|
||||
if match or interaction.user.id == interaction.channel.owner.id:
|
||||
await interaction.response.defer()
|
||||
if self.reason:
|
||||
response_reason = f"Thread closed by {interaction.user.mention} with reason: {self.reason}"
|
||||
reason = f"Thread closed by {interaction.user.name} ({interaction.user.id}) with reason: {self.reason}"
|
||||
else:
|
||||
response_reason = f"Thread closed by {interaction.user.mention}"
|
||||
reason = f"Thread closed by {interaction.user.name} ({interaction.user.id})"
|
||||
await self.msg.edit(content=response_reason, view=None)
|
||||
await self.ctx.message.delete()
|
||||
tag = interaction.channel.parent.get_tag(await self.config.guild(interaction.guild).forum_tag())
|
||||
if tag in interaction.channel.applied_tags:
|
||||
await interaction.channel.edit(locked=True, archived=True, reason=reason)
|
||||
else:
|
||||
await interaction.channel.edit(locked=True, archived=True, applied_tags=interaction.channel.applied_tags + [tag], reason=reason)
|
||||
else:
|
||||
await interaction.response.send_message(content="You cannot close this thread!", ephemeral=True)
|
||||
|
||||
@ui.button(label="No", style=discord.ButtonStyle.danger, emoji="✖️")
|
||||
async def resolved_button_no(self, interaction: discord.Interaction, button: ui.Button): # pylint: disable=unused-argument
|
||||
request_role_ids = await self.config.guild(interaction.guild).request_roles()
|
||||
request_roles = [interaction.guild.get_role(role_id) for role_id in request_role_ids]
|
||||
match = any(role in interaction.user.roles for role in request_roles)
|
||||
if match or interaction.user.id == interaction.channel.owner.id:
|
||||
await interaction.response.defer()
|
||||
await self.msg.delete()
|
||||
await self.ctx.message.delete()
|
||||
else:
|
||||
await interaction.response.send_message(content="You cannot close this thread!", ephemeral=True)
|
||||
|
||||
@commands.group(name='resolvedset', autohelp=True, aliases=['resolvedconfig'])
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def resolvedset(self, ctx: commands.Context):
|
||||
"""Manages the configuration for the [p]resolved command."""
|
||||
|
||||
@resolvedset.command(name='show', aliases=['settings'])
|
||||
async def resolvedset_show(self, ctx: commands.Context):
|
||||
"""Shows the current cog configuration."""
|
||||
channel_id = await self.config.guild(ctx.guild).forum_channel()
|
||||
tag = await self.config.guild(ctx.guild).forum_tag()
|
||||
request_role_ids = await self.config.guild(ctx.guild).request_roles()
|
||||
split_content = ctx.message.content.split()
|
||||
command = ' '.join(split_content[:1])
|
||||
already_in_list = []
|
||||
for role_id in request_role_ids:
|
||||
role_obj = ctx.guild.get_role(role_id)
|
||||
if role_obj:
|
||||
already_in_list.append(role_obj.mention)
|
||||
if already_in_list:
|
||||
roles_list = "**Allowed Roles**:\n" + "\n".join(already_in_list)
|
||||
else:
|
||||
roles_list = f"No roles are currently in the allowed roles list.\n- Use `{command} add` to add some."
|
||||
tag_str = None
|
||||
if channel_id is not None:
|
||||
channel_obj = ctx.guild.get_channel(channel_id)
|
||||
if channel_obj is None:
|
||||
channel = f"**Channel**: {channel_id}\n- ⚠️ This channel cannot be found in this guild. Is this the correct ID?\n\n"
|
||||
else:
|
||||
channel = f"**Channel**: {channel_obj.mention}\n\n"
|
||||
if tag is not None:
|
||||
tag_obj = channel_obj.get_tag(tag)
|
||||
if tag_obj is None:
|
||||
tag_str = f"**Tag**: {tag}\n- ⚠️ This tag cannot be found in the set forums channel. Is this the correct ID?\n\n"
|
||||
else:
|
||||
tag_str = f"**Tag**: {tag_obj.emoji} {tag_obj.name}\n\n"
|
||||
else:
|
||||
channel = f"**Channel**: Not set!\n- Use `{command} channel` to set the forums channel.\n\n"
|
||||
if tag_str is None:
|
||||
tag_str = f"**Tag**: Not set!\n- Use `{command} tag` to set the tag.\n\n"
|
||||
embed = discord.Embed(title="Cog Settings", color=await self.bot.get_embed_color(None), description=channel + tag_str + roles_list)
|
||||
await ctx.reply(embed=embed)
|
||||
|
||||
@resolvedset.group(name='role', autohelp=True, aliases=['roles'])
|
||||
async def resolvedset_role(self, ctx: commands.Context):
|
||||
"""Manages the allowed roles list."""
|
||||
|
||||
@resolvedset_role.command(name='add')
|
||||
async def resolvedset_role_add(self, ctx: commands.Context, role: discord.Role = None):
|
||||
"""Adds roles to the allowed roles list."""
|
||||
current_list = await self.config.guild(ctx.guild).request_roles()
|
||||
if role:
|
||||
if role.id in current_list:
|
||||
await ctx.send("This role is already in the allowed roles list!")
|
||||
else:
|
||||
current_list.append(role.id)
|
||||
await self.config.guild(ctx.guild).request_roles.set(current_list)
|
||||
await ctx.send(f"{role.mention} has been added to the allowed roles list.", allowed_mentions = discord.AllowedMentions(roles=False))
|
||||
else:
|
||||
await ctx.send("Please provide a valid role.")
|
||||
|
||||
@resolvedset_role.command(name='remove')
|
||||
async def resolvedset_role_remove(self, ctx: commands.Context, role: discord.Role = None):
|
||||
"""Removes roles from the allowed roles list."""
|
||||
current_list = await self.config.guild(ctx.guild).request_roles()
|
||||
if role.id in current_list:
|
||||
current_list.remove(role.id)
|
||||
await self.config.guild(ctx.guild).request_roles.set(current_list)
|
||||
await ctx.send(f"{role.mention} has been removed from the allowed roles list.", allowed_mentions = discord.AllowedMentions(roles=False))
|
||||
else:
|
||||
await ctx.send("Please provide a valid role that exists in the allowed roles list.")
|
||||
|
||||
def create_select_options(self, ctx: commands.Context, data):
|
||||
options = []
|
||||
for tag in data:
|
||||
emoji = ctx.guild.get_emoji(tag.emoji.id) if tag.emoji.id else str(tag.emoji.name)
|
||||
options.append(discord.SelectOption(label=tag.name, emoji=emoji, description="", value=tag.id))
|
||||
return options
|
||||
|
||||
@resolvedset.command(name="channel")
|
||||
async def resolvedset_channel(self, ctx: commands.Context, channel: discord.abc.GuildChannel):
|
||||
"""Sets the channel used by the [p]resolved command."""
|
||||
if isinstance(channel, discord.ForumChannel):
|
||||
await self.config.guild(ctx.guild).forum_channel.set(channel.id)
|
||||
await ctx.reply(f"Forum channel has been set to {channel.mention}.")
|
||||
else:
|
||||
await ctx.reply(f"{channel.mention} is not a forums channel!")
|
||||
|
||||
@resolvedset.command(name="tag")
|
||||
async def resolvedset_tag(self, ctx: commands.Context):
|
||||
"""Sets the tag used by the [p]resolved command."""
|
||||
channel: discord.ForumChannel = ctx.guild.get_channel(await self.config.guild(ctx.guild).forum_channel())
|
||||
if channel is not None:
|
||||
options = self.create_select_options(ctx, channel.available_tags)
|
||||
msg = await ctx.reply("Select a forum tag below.")
|
||||
await msg.edit(view=SelectView(msg, options))
|
||||
else:
|
||||
await ctx.reply("Configuration error! Channel does not exist.")
|
||||
|
||||
class Select(ui.Select):
|
||||
def __init__(self, message, options):
|
||||
self.message = message
|
||||
super().__init__(placeholder="Select an option", max_values=1, min_values=1, options=options)
|
||||
|
||||
async def callback(self, interaction: discord.Interaction):
|
||||
msg: discord.Message = self.message
|
||||
config = Config.get_conf(None, cog_name='Forums', identifier=2352711325)
|
||||
await config.guild(msg.guild).forum_tag.set(int(self.values[0]))
|
||||
channel: discord.ForumChannel = msg.guild.get_channel(await config.guild(msg.guild).forum_channel())
|
||||
tag = channel.get_tag(int(self.values[0]))
|
||||
await msg.edit(content=f"Set resolved tag to {tag.emoji} {tag.name}", view=None)
|
||||
await interaction.response.defer()
|
||||
|
||||
class SelectView(ui.View):
|
||||
def __init__(self, message, options, *, timeout=180):
|
||||
super().__init__(timeout=timeout)
|
||||
self.add_item(Select(message, options))
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing Forums!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"name" : "Forums",
|
||||
"short" : "Custom cog intended for use on the Galaxy discord server.",
|
||||
"description" : "Custom cog intended for use on the Galaxy discord server.",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"hidden": false,
|
||||
"disabled": false
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
from .galaxy import Galaxy
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Galaxy(bot))
|
||||
def setup(bot):
|
||||
bot.add_cog(Galaxy(bot))
|
356
galaxy/galaxy.py
356
galaxy/galaxy.py
|
@ -1,155 +1,112 @@
|
|||
import re
|
||||
from datetime import datetime
|
||||
from random import randint
|
||||
|
||||
from redbot.core import commands, checks, Config
|
||||
import discord
|
||||
from redbot.core import Config, app_commands, commands
|
||||
from redbot.core.app_commands import Choice
|
||||
|
||||
from datetime import datetime
|
||||
import re
|
||||
|
||||
class Galaxy(commands.Cog):
|
||||
"""Custom cog intended for use on the Galaxy discord server.
|
||||
Developed by cswimr."""
|
||||
Developed by SeaswimmerTheFsh."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=6621962)
|
||||
self.config.register_guild(
|
||||
autoreact_target = 0,
|
||||
autoreact_emoji = '💀'
|
||||
cocotarget = 0,
|
||||
cocoemoji = 1028535684757209118
|
||||
)
|
||||
|
||||
@commands.command(aliases=["pxc", "pc", "polarisconvert", "tatsutopolaris", "ttp"])
|
||||
@commands.guild_only()
|
||||
async def polarisxpconvert(self, ctx, *, tatsu_studs: str):
|
||||
"""Converts Tatsu Studs to Polaris XP."""
|
||||
try:
|
||||
tatsu_studs_int = int(f"{tatsu_studs}".replace(",", ""))
|
||||
except ValueError:
|
||||
await ctx.send(content="Please input a number!")
|
||||
return
|
||||
math = round((tatsu_studs_int/25)*10)
|
||||
output_from = f'{tatsu_studs_int:,}'
|
||||
output_to = f'{math:,}'
|
||||
embed = discord.Embed(color=await self.bot.get_embed_color(None))
|
||||
embed.add_field(name="Tatsu Studs", value=f"{output_from}", inline=False)
|
||||
embed.add_field(name="Polaris XP", value=f"{output_to}", inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command()
|
||||
async def carnagerefund(self, ctx: commands.Context, message_id: str):
|
||||
async def galaxyissues(self, ctx, target: discord.Member = None):
|
||||
if ctx.me.id == 1070819799254438039:
|
||||
embed = discord.Embed(title="Issue Reporting & Suggestions", color=await self.bot.get_embed_color(None), description="Have a problem or a suggestion for the Galaxy bot or GalaxyCogs? Read this!")
|
||||
embed.add_field(name="Bot Issues & Suggestions", value="If you'd like to submit a suggestion or a bug report to the developers of the Galaxy bot, please do so [here](https://github.com/SeaswimmerTheFsh/GalaxyCogs/issues/new/choose).\n**Please make sure whatever you're suggesting or reporting doesn't have an existing issue! If it does, you can comment on that issue with additional details if necessary.**")
|
||||
else:
|
||||
embed = discord.Embed(title="Issue Reporting & Suggestions", color=await self.bot.get_embed_color(None), description="Have a problem or a suggestion for GalaxyCogs? Read this!")
|
||||
embed.add_field(name="Cog Issues & Suggestions", value="If you'd like to submit a suggestion or a bug report to the developers of GalaxyCogs, please do so [here](https://github.com/SeaswimmerTheFsh/GalaxyCogs/issues/new/choose).\n**Please make sure whatever you're suggesting or reporting doesn't have an existing issue! If it does, you can comment on that issue with additional details if necessary.**")
|
||||
if target:
|
||||
await ctx.send(embed=embed, content=f"{target.mention}")
|
||||
else:
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command()
|
||||
async def carnagerefund(self, ctx, message_id: str):
|
||||
"""This command generates a link to refund carnage of killed ships."""
|
||||
output = f"https://info.galaxy.casa/kills/{message_id}"
|
||||
await ctx.send(f"Output link: {output}")
|
||||
|
||||
@commands.Cog.listener('on_message')
|
||||
async def gank_won_let_us_flee(self, message: discord.Message):
|
||||
if message.author.id == 745790085789909033 and message.guild.id == 204965774618656769 and message.channel.id == 753714180900519937:
|
||||
gank_media_list = [
|
||||
"https://cdn.discordapp.com/attachments/1070838419212738621/1155174309711577138/gank_won_let_us_flee.gif",
|
||||
"https://cdn.discordapp.com/attachments/1070838419212738621/1155174310093262951/spin_back_I_dare_you.jpg",
|
||||
"https://cdn.discordapp.com/attachments/1070838419212738621/1155174310466572328/spin_back_mutt.gif",
|
||||
"https://cdn.discordapp.com/attachments/1070838419212738621/1155174310806290533/We_dont_run_1s_around_here.gif"
|
||||
]
|
||||
if len(message.embeds) == 1 and str(message.embeds[0].color) == '#57f287':
|
||||
await message.reply(gank_media_list[randint(0,3)])
|
||||
|
||||
@commands.Cog.listener('on_message')
|
||||
async def autoreact(self, message: discord.Message):
|
||||
if message.guild is not None:
|
||||
emoji_id = await self.config.guild(message.guild).autoreact_emoji()
|
||||
if self.check_if_discord_unicode_emoji(emoji_id) is False:
|
||||
emoji = self.bot.get_emoji(emoji_id)
|
||||
elif self.check_if_discord_unicode_emoji(emoji_id) is True:
|
||||
emoji = emoji_id
|
||||
autoreact_target = await self.config.guild(message.guild).autoreact_target()
|
||||
if autoreact_target == 0:
|
||||
async def cocoreact(self, message):
|
||||
emoji = self.bot.get_emoji(await self.config.guild(message.guild).cocoemoji())
|
||||
cocotarget = await self.config.guild(message.guild).cocotarget()
|
||||
if cocotarget == 0:
|
||||
return
|
||||
if not message.author.id == autoreact_target:
|
||||
if not message.author.id == cocotarget:
|
||||
return
|
||||
await message.add_reaction(emoji)
|
||||
|
||||
@commands.command(name='autoreact')
|
||||
@commands.group(autohelp=False, invoke_without_command=True)
|
||||
@commands.guild_only()
|
||||
async def autoreact_list(self, ctx: commands.Context):
|
||||
"""Checks Autoreact's settings."""
|
||||
emoji_id = await self.config.guild(ctx.guild).autoreact_emoji()
|
||||
autoreact_target = await self.config.guild(ctx.guild).autoreact_target()
|
||||
if self.check_if_discord_unicode_emoji(emoji_id) is True:
|
||||
emoji = emoji_id
|
||||
embed = discord.Embed(color=await self.bot.get_embed_color(None), description=f"Autoreact is currently set to target <@{autoreact_target}> ({autoreact_target}).\nAutoreact's emoji is currently set to {emoji}.")
|
||||
else:
|
||||
emoji = self.bot.get_emoji(emoji_id)
|
||||
embed = discord.Embed(color=await self.bot.get_embed_color(None), description=f"Autoreact is currently set to target <@{autoreact_target}> ({autoreact_target}).\nAutoreact's emoji is currently set to {emoji} ({await self.config.guild(ctx.guild).autoreact_emoji()}).")
|
||||
async def coco(self, ctx):
|
||||
"""Checks who Coco is currently set to."""
|
||||
emoji = self.bot.get_emoji(await self.config.guild(ctx.guild).cocoemoji())
|
||||
cocotarget = await self.config.guild(ctx.guild).cocotarget()
|
||||
embed = discord.Embed(color=await self.bot.get_embed_color(None), description=f"Coco is currently set to <@{cocotarget}> ({cocotarget}).\nCoco's emoji is currently set to {emoji} ({await self.config.guild(ctx.guild).cocoemoji()}).")
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
autoreact = app_commands.Group(name='autoreact', guild_only=True, description="This group handles the autoreact functionality.")
|
||||
|
||||
def check_if_discord_unicode_emoji(self, emoji: str):
|
||||
emoji_ranges = [
|
||||
(0x1F600, 0x1F64F), # Emoticons
|
||||
(0x1F300, 0x1F5FF), # Miscellaneous symbols and pictographs
|
||||
(0x1F680, 0x1F6FF), # Transport and map symbols
|
||||
(0x1F700, 0x1F77F), # Alchemical symbols
|
||||
]
|
||||
try:
|
||||
for char in emoji:
|
||||
code_point = ord(char)
|
||||
for start, end in emoji_ranges:
|
||||
if start <= code_point <= end:
|
||||
return True
|
||||
except TypeError:
|
||||
return False
|
||||
return False
|
||||
|
||||
def extract_id(self, input_string):
|
||||
match = re.search(r'(?<=:)\d+(?=>)', input_string)
|
||||
if match:
|
||||
return int(match.group())
|
||||
return input_string
|
||||
|
||||
@autoreact.command(name="emoji")
|
||||
@app_commands.describe(emoji="Which emoji are you setting Autoreact to use?")
|
||||
async def autoreact_emoji(self, interaction: discord.Interaction, emoji: str = None):
|
||||
"""Sets Autoreact's emoji."""
|
||||
@coco.command(name="emoji")
|
||||
async def coco_emoji(self, ctx, emoji: discord.Emoji = None):
|
||||
"""Sets Coco's emoji."""
|
||||
if emoji:
|
||||
if self.check_if_discord_unicode_emoji(emoji) is True:
|
||||
await self.config.guild(interaction.guild).autoreactemoji.set(emoji)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"Autoreact's emoji has been set to {emoji}.")
|
||||
await interaction.response.send_message(embed=embed)
|
||||
await self.config.guild(ctx.guild).cocoemoji.set(emoji.id)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"Coco's emoji has been set to {emoji} ({emoji.id}).")
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
emoji_id = self.extract_id(input_string=emoji)
|
||||
for guild in self.bot.guilds:
|
||||
emoji_to_find = discord.utils.get(guild.emojis, id=emoji_id)
|
||||
if emoji_to_find:
|
||||
emoji_obj = emoji_to_find
|
||||
break
|
||||
else:
|
||||
await interaction.response.send_message(content="You're trying to set the autoreact emoji to an emoji I don't have access to!", ephemeral=True)
|
||||
return
|
||||
await self.config.guild(interaction.guild).autoreact_emoji.set(emoji_obj.id)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"Autoreact's emoji has been set to {emoji_obj} ({emoji_obj.id}).")
|
||||
await interaction.response.send_message(embed=embed)
|
||||
else:
|
||||
await self.config.guild(interaction.guild).autoreact_emoji.set('💀')
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description="Autoreact's emoji has been set to 💀.")
|
||||
await interaction.response.send_message(embed=embed)
|
||||
await self.config.guild(ctx.guild).cocoemoji.set(1028535684757209118)
|
||||
emoji = self.bot.get_emoji(1028535684757209118)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"Coco's emoji has been set to {emoji} (1028535684757209118).")
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@autoreact.command(name="set")
|
||||
@app_commands.describe(member="Who are you targetting?")
|
||||
async def autoreact_set(self, interaction: discord.Interaction, member: discord.Member):
|
||||
"""Sets Autoreact's target."""
|
||||
@coco.command(name="set")
|
||||
async def coco_set(self, ctx, member: discord.Member):
|
||||
"""Sets Coco's target."""
|
||||
if member:
|
||||
await self.config.guild(interaction.guild).autoreact_target.set(member.id)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"Autoreact has been set to automatically react to {member.mention} ({member.id})'s messages.")
|
||||
await interaction.response.send_message(embed=embed)
|
||||
await self.config.guild(ctx.guild).cocotarget.set(member.id)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"Coco has been set to {member.mention} ({member.id}).")
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
await interaction.response.send_message(content="That is not a valid argument!", ephemeral=True)
|
||||
await ctx.send(content="That is not a valid argument!")
|
||||
|
||||
@autoreact.command(name="reset")
|
||||
async def autoreact_reset(self, interaction: discord.Interaction):
|
||||
"""Resets Autoreact's target."""
|
||||
await self.config.guild(interaction.guild).autoreact_target.set(0)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description="Autoreact's target has been reset.")
|
||||
await interaction.response.send_message(embed=embed)
|
||||
@coco.command(name="reset")
|
||||
async def coco_reset(self, ctx):
|
||||
"""Resets Coco's target."""
|
||||
await self.config.guild(ctx.guild).cocotarget.set(0)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"Coco has been reset.")
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command()
|
||||
async def unix(self, ctx: commands.Context):
|
||||
async def unix(self, ctx):
|
||||
"""Posts the current Unix timestamp."""
|
||||
timestamp = int(datetime.timestamp(datetime.now()))
|
||||
embed=discord.Embed(title="Current Time", url="https://www.unixtimestamp.com/", color=await self.bot.get_embed_color(None))
|
||||
embed.add_field(name="Default", value=f"<t:{timestamp}>\n`<t:{timestamp}>`")
|
||||
embed.add_field(name="Short Time", value=f"<t:{timestamp}:t>\n`<t:{timestamp}:t>`")
|
||||
embed.add_field(name="Long Time", value=f"<t:{timestamp}:T>\n`<t:{timestamp}:T>`")
|
||||
embed.add_field(name="Short Date/Time", value=f"<t:{timestamp}:f>\n`<t:{timestamp}:f>`")
|
||||
embed.add_field(name="Short Date", value=f"<t:{timestamp}:d>\n`<t:{timestamp}:d>`")
|
||||
embed.add_field(name="Long Date", value=f"<t:{timestamp}:D>\n`<t:{timestamp}:D>`")
|
||||
embed.add_field(name="Long Date/Time", value=f"<t:{timestamp}:F>\n`<t:{timestamp}:F>`")
|
||||
embed.add_field(name="Relative Time", value=f"<t:{timestamp}:R>\n`<t:{timestamp}:R>`")
|
||||
embed=discord.Embed(title="Current Time", url="https://www.unixtimestamp.com/", color=await self.bot.get_embed_color(None), description=f"<t:{timestamp}>")
|
||||
embed.set_footer(text=f"{timestamp}")
|
||||
embed.set_image(url="https://cdn.discordapp.com/attachments/1047347377348030494/1080048421127335956/image.png")
|
||||
await ctx.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
|
@ -183,8 +140,8 @@ class Galaxy(commands.Cog):
|
|||
}
|
||||
try:
|
||||
insurance_dict[f'{ship_class}']
|
||||
except KeyError as error:
|
||||
raise ValueError("Received value is not a valid ship class!") from error
|
||||
except KeyError:
|
||||
raise ValueError("Received value is not a valid ship class!")
|
||||
if ship_class == "super_capital":
|
||||
humanized_class = ship_class.replace("_", " ").title()
|
||||
else:
|
||||
|
@ -271,28 +228,86 @@ class Galaxy(commands.Cog):
|
|||
await ctx.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
@app_commands.command()
|
||||
@app_commands.describe(answer='Which answer are you trying to post?')
|
||||
@app_commands.choices(answer=[
|
||||
Choice(name='Important Links', value='links'),
|
||||
Choice(name='DPS Calculations', value='dps'),
|
||||
Choice(name='Reward Roles', value='reward_roles'),
|
||||
Choice(name='NPC Intervals', value='npc_intervals'),
|
||||
Choice(name='Linked Role', value='linked_role'),
|
||||
Choice(name='RoPro', value='ropro')
|
||||
])
|
||||
async def faq(self, interaction: discord.Interaction, answer: Choice[str], member: discord.Member = None):
|
||||
@commands.group(autohelp=True)
|
||||
async def faq(self, ctx):
|
||||
"""Posts answers to frequently asked questions."""
|
||||
embed = None
|
||||
embed_secondary = None
|
||||
if answer.value == 'dps':
|
||||
embed = discord.Embed(title="DPS Calculations", color=await self.bot.get_embed_color(None), description="The ``/info`` command (and by extention ``/shipinfo`` from Odin) misreports DPS, due to it calculating DPS disregarding the turret's type (kinetic, laser), causing it to assume the target ship is both hulled and has shield simultaneously. It also ignores turret overrides, custom reloads, and custom damage values. If you'd like to check ship stats accurately, you can either use the ``/ship`` command in this channel or you can use the [Galaxy Info Website](https://info.galaxy.casa/ships). Alternatively, to check turret stats, you can use the [Galaxy Info Turrets Page](https://info.galaxy.casa/turrets).")
|
||||
elif answer.value == 'links':
|
||||
embed = discord.Embed(title="Important Links", color=await self.bot.get_embed_color(None))
|
||||
|
||||
@faq.command(name="test")
|
||||
@checks.admin()
|
||||
async def faq_test(self, ctx, member: discord.Member = None):
|
||||
"""Testing FAQ"""
|
||||
embed=discord.Embed(title="Test Embed", color=await self.bot.get_embed_color(None), description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer in faucibus odio, at mollis metus.")
|
||||
embed.set_footer(text=ctx.author, icon_url=ctx.author.avatar_url_as(format="png", size=512))
|
||||
if member:
|
||||
await ctx.channel.send(embed=embed, content=member.mention)
|
||||
else:
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
@faq.command(name="dps")
|
||||
async def faq_dps(self, ctx, member: discord.Member = None):
|
||||
"""DPS Calculations/Inaccuracy"""
|
||||
embed=discord.Embed(title="DPS Calculations", color=await self.bot.get_embed_color(None), description="The ``/info`` command (and by extention ``/shipinfo`` from Odin) misreports DPS, due to it calculating DPS disregarding the turret's type (kinetic, laser), causing it to assume the target ship is both hulled and has shield simultaneously. It also ignores turret overrides, custom reloads, and custom damage values. If you'd like to check ship stats accurately, you can either use the ``/ship`` command in this channel or you can use the [Galaxy Info Website](https://info.galaxy.casa/ships). Alternatively, to check turret stats, you can use the [Galaxy Info Turrets Page](https://info.galaxy.casa/turrets).")
|
||||
if member:
|
||||
await ctx.channel.send(embed=embed, content=member.mention)
|
||||
else:
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
@faq.command(name="links")
|
||||
async def faq_links(self, ctx, member: discord.Member = None):
|
||||
"""Posts important links, primarily invite links."""
|
||||
embed=discord.Embed(title="Important Links", color=await self.bot.get_embed_color(None))
|
||||
embed.add_field(name="Galaxy", value="[Galaxy Discord](https://discord.com/invite/robloxgalaxy)\n[Galaxy Support](https://discord.com/invite/ShWshkhYhZ)")
|
||||
embed.add_field(name="Galaxypedia", value="[Galaxypedia Website](https://robloxgalaxy.wiki/wiki/Main_Page)\n[Galaxypedia Discord](https://discord.robloxgalaxy.wiki/)")
|
||||
elif answer.value == 'npc_intervals':
|
||||
embed = discord.Embed(title="NPC Spawn Intervals", color=await self.bot.get_embed_color(None), description="*Disclaimer: Spawn times may be different if EventID is active!*")
|
||||
if member:
|
||||
await ctx.channel.send(embed=embed, content=member.mention)
|
||||
else:
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
@faq.command(name="ropro")
|
||||
async def faq_ropro(self, ctx, member: discord.Member = None):
|
||||
"""Posts a link to RoPro"""
|
||||
embed=discord.Embed(title="RoPro", url="https://ropro.io", color=await self.bot.get_embed_color(None), description="""[RoPro](https://ropro.io) is a browser extension that tracks ROBLOX playtime, enhances your profile, and provides other useful utilities. **Please keep in mind that RoPro only tracks playtime from AFTER you install the extension.**""")
|
||||
if member:
|
||||
await ctx.channel.send(embed=embed, content=member.mention)
|
||||
else:
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
@faq.command(name="polaris_ranks")
|
||||
async def faq_polaris_ranks(self, ctx, member: discord.Member = None):
|
||||
"""Lists required levels for certain roles."""
|
||||
embed=discord.Embed(title="Polaris Ranks", color=await self.bot.get_embed_color(None))
|
||||
embed.add_field(name="Picture Perms", value="Level 7", inline=False)
|
||||
embed.add_field(name="Suggestions", value="Level 9", inline=False)
|
||||
embed.add_field(name="DJ", value="Level 11", inline=False)
|
||||
embed.add_field(name="Reaction Perms", value="Level 30", inline=False)
|
||||
if member:
|
||||
await ctx.channel.send(embed=embed, content=member.mention)
|
||||
else:
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
@faq.command(name="polaris_switch")
|
||||
@checks.admin()
|
||||
async def faq_polaris_switch(self, ctx, member: discord.Member = None):
|
||||
"""Posts an embed on the switch to the Polaris bot."""
|
||||
embed=discord.Embed(title="Polaris FAQ", color=await self.bot.get_embed_color(None), description="As you probably know, we've decided to switch to the Polaris bot for leveling/xp, as opposed to Tatsu.\nThere are many reasons for this, which will be explained below.")
|
||||
embed.add_field(name="Problems with Tatsu", value="1: Tatsu does not provide nearly as much configuration potential as Polaris does. An example of this is Polaris' customizable Level Curve.\n\n2: Tatsu does not have channel/role modifiers.\n\n3: Tatsu does not have actual levels, instead it has unconfigurable \"Global XP\", which gives \"Global Levels\". You cannot do anything with Global XP aside from blacklisting channels where people can gain it, like a bot-commands channel or something like that.\n\n4: Tatsu's leaderboard sucks, and only shows the top 10 on the web version.\n\n5: Tatsu has no XP management commands.\n\n6: Tatsu has TONS of bloat/useless commands, making the bot harder to configure.", inline=False)
|
||||
embed.add_field(name="Polaris' Features", value="1: Polaris allows you to customize the level curve of your server, and provides presets to make the transition easier.\n\n2: Polaris has XP management commands.\n\n3: Polaris has way more configuration in terms of Reward Roles.\n\n4: Polaris allows you to customize the level-up message shown whenever people achieve the next level.\n\n5: Polaris has both role and channel modifiers.\n\n6: Polaris' leaderboard is excellent, showing the top 1,000 ranked users on the same webpage, and allowing you to see your own stats, progress towards your next reward role, and all 350 levels and your progress towards them.\n\n7: Polaris is **just** a leveling bot. You don't have to deal with any of the bloat of multi-purpose bots like Tatsu or MEE6, you only get what you actually need.", inline=False)
|
||||
embed.add_field(name="Conclusion",value="With all of that said, you're probably wondering why we're putting so much effort into transferring peoples' data to the new bot.\n\nWell, Tatsu has been going since 2020, and I don't particularly favor the idea of clearing everyone's XP, especially when people have built up reward roles from Tatsu already, like Picture Perms, Suggestions access, and DJ.\n\nWith all this in mind, I hope this isn't too much of an inconvenience for you all, as I tried to make the process as seamless as possible without having to update all 10,000 people in the server.", inline=False)
|
||||
if member:
|
||||
await ctx.channel.send(embed=embed, content=member.mention)
|
||||
else:
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
@faq.command(name="npc_intervals")
|
||||
async def faq_npc_intervals(self, ctx, member: discord.Member = None):
|
||||
"""Posts an embed containing NPC spawn intervals."""
|
||||
embed=discord.Embed(title="NPC Spawn Intervals", color=await self.bot.get_embed_color(None), description="*Disclaimer: Spawn times may be different if EventID is active!*")
|
||||
embed.add_field(name="Every 6.7 Minutes", value="[Dragoon](https://robloxgalaxy.wiki/wiki/Dragoon) *(80% Chance)*")
|
||||
embed.add_field(name="Every 8.3 Minutes", value="[Swarmer](https://robloxgalaxy.wiki/wiki/Swarmer) *(33% Chance)*")
|
||||
embed.add_field(name="Every 10 Minutes", value="[Jormungand](https://robloxgalaxy.wiki/wiki/Jormungand) *(75% Chance)*")
|
||||
|
@ -302,47 +317,40 @@ class Galaxy(commands.Cog):
|
|||
embed.add_field(name="Every 60 Minutes", value="[X-0](https://robloxgalaxy.wiki/wiki/X-0) *(45% Chance)*\n[Decimator](https://robloxgalaxy.wiki/wiki/Decimator)")
|
||||
embed.add_field(name="Every 70 Minutes", value="[Galleon](https://robloxgalaxy.wiki/wiki/Galleon)")
|
||||
embed.add_field(name="Every 120 Minutes", value="[Kodiak](https://robloxgalaxy.wiki/wiki/Kodiak)")
|
||||
elif answer.value == 'linked_role':
|
||||
embed = discord.Embed(title="Desktop / Web", color=await self.bot.get_embed_color(None), description="**Step 1:** Open the Server Dropdown menu in the top-left by clicking on the server's name.\n\n**Step 2:** Click the \"*Linked Roles*\" button.\n\n**Step 3:** Click on \"*Linked*.\"\n\n**Step 4:** Click \"*Finish*.\" You're done!\n*Note: You should already be Verified on Bloxlink. If you are not, go to the verification channel to verify.*")
|
||||
embed.set_thumbnail(url="https://cdn.discordapp.com/attachments/1070838419212738621/1079927564421836930/image.png")
|
||||
embed_secondary = discord.Embed(title="Mobile", color=await self.bot.get_embed_color(None), description="**Step 1:** Open the Server menu on the top of the channel list by tapping the server's name.\n\n**Step 2:** Scroll down and tap the \"*Linked Roles*\" button.\n\n**Step 3:** Tap on \"*Linked*.\"\n\n**Step 4:** Tap \"*Finish*.\" You're done!\n*Note: You should already be Verified on Bloxlink. If you are not, go to the verification channel to verify.*")
|
||||
embed_secondary.set_thumbnail(url="https://cdn.discordapp.com/attachments/1047347377348030494/1079930169562771576/Screenshot_20230227_195338_Discord.jpg")
|
||||
elif answer.value == 'reward_roles':
|
||||
embed = discord.Embed(title="Reward Roles", color=await self.bot.get_embed_color(None))
|
||||
embed.add_field(name="Picture Perms", value="Level 6")
|
||||
embed.add_field(name="Suggestions", value="Level 8")
|
||||
embed.add_field(name="DJ", value="Level 10")
|
||||
embed.add_field(name="Reaction Perms", value="Level 20")
|
||||
embed.add_field(name="External Emoji Perms", value="Level 30")
|
||||
embed.set_footer(text="Use `-profile` to get your current level.")
|
||||
elif answer.value == 'ropro':
|
||||
embed = discord.Embed(title="RoPro", url="https://ropro.io", color=await self.bot.get_embed_color(None), description="""[RoPro](https://ropro.io) is a browser extension that tracks ROBLOX playtime, enhances your profile, and provides other useful utilities. **Please keep in mind that RoPro only tracks playtime from AFTER you install the extension.**""")
|
||||
content = member.mention if member else None
|
||||
await interaction.response.send_message(content="> The rigid requirement for bots to compulsorily respond to interactions in Discord, such as slash commands or application commands, is an irksome limitation that curtails the flexibility and natural flow of interactions. This forced response paradigm undermines the very essence of automation and intelligent design that bots were intended to offer. There are instances where silence or lack of response is not only acceptable but also desired, aligning with the nuanced dynamics of human communication. Discord's insistence on a response, even when it serves no purpose, imposes unnecessary complexity and verbosity, creating an environment where superfluous replies dilute the efficiency and elegance of bot-driven interactions. This constraint highlights the importance of granting bot developers the autonomy to determine the most suitable course of action based on context, contributing to a more seamless and user-centric experience within the Discord ecosystem.\n - ChatGPT", ephemeral=True)
|
||||
response: discord.InteractionMessage = await interaction.original_response()
|
||||
await response.delete()
|
||||
if embed_secondary:
|
||||
await interaction.channel.send(content=content, embeds=[embed, embed_secondary])
|
||||
if member:
|
||||
await ctx.channel.send(embed=embed, content=member.mention)
|
||||
else:
|
||||
await interaction.channel.send(content=content, embed=embed)
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.message.delete()
|
||||
|
||||
# @faq.command(name="polaris_switch")
|
||||
# @checks.admin()
|
||||
# async def faq_polaris_switch(self, ctx, member: discord.Member = None):
|
||||
# """Posts an embed on the switch to the Polaris bot."""
|
||||
# embed=discord.Embed(title="Polaris FAQ", color=await self.bot.get_embed_color(None), description="As you probably know, we've decided to switch to the Polaris bot for leveling/xp, as opposed to Tatsu.\nThere are many reasons for this, which will be explained below.")
|
||||
# embed.add_field(name="Problems with Tatsu", value="1: Tatsu does not provide nearly as much configuration potential as Polaris does. An example of this is Polaris' customizable Level Curve.\n\n2: Tatsu does not have channel/role modifiers.\n\n3: Tatsu does not have actual levels, instead it has unconfigurable \"Global XP\", which gives \"Global Levels\". You cannot do anything with Global XP aside from blacklisting channels where people can gain it, like a bot-commands channel or something like that.\n\n4: Tatsu's leaderboard sucks, and only shows the top 10 on the web version.\n\n5: Tatsu has no XP management commands.\n\n6: Tatsu has TONS of bloat/useless commands, making the bot harder to configure.", inline=False)
|
||||
# embed.add_field(name="Polaris' Features", value="1: Polaris allows you to customize the level curve of your server, and provides presets to make the transition easier.\n\n2: Polaris has XP management commands.\n\n3: Polaris has way more configuration in terms of Reward Roles.\n\n4: Polaris allows you to customize the level-up message shown whenever people achieve the next level.\n\n5: Polaris has both role and channel modifiers.\n\n6: Polaris' leaderboard is excellent, showing the top 1,000 ranked users on the same webpage, and allowing you to see your own stats, progress towards your next reward role, and all 350 levels and your progress towards them.\n\n7: Polaris is **just** a leveling bot. You don't have to deal with any of the bloat of multi-purpose bots like Tatsu or MEE6, you only get what you actually need.", inline=False)
|
||||
# embed.add_field(name="Conclusion",value="With all of that said, you're probably wondering why we're putting so much effort into transferring peoples' data to the new bot.\n\nWell, Tatsu has been going since 2020, and I don't particularly favor the idea of clearing everyone's XP, especially when people have built up reward roles from Tatsu already, like Picture Perms, Suggestions access, and DJ.\n\nWith all this in mind, I hope this isn't too much of an inconvenience for you all, as I tried to make the process as seamless as possible without having to update all 10,000 people in the server.", inline=False)
|
||||
# if member:
|
||||
# await ctx.channel.send(embed=embed, content=member.mention)
|
||||
# else:
|
||||
# await ctx.channel.send(embed=embed)
|
||||
# await ctx.message.delete()
|
||||
@faq.command(name="linked_role")
|
||||
async def faq_linked_role(self, ctx, member: discord.Member = None):
|
||||
"""Posts an embed containing FAQ about Linked Role."""
|
||||
color=await self.bot.get_embed_color(None)
|
||||
embed=discord.Embed(title="Linked Role", color=color, description="**Before reading this, please make sure your Discord client is updated! On Mobile, you can do this by going to your app store of choice and updating Discord manually. On the desktop app you can do this by clicking the green update button in the top right.**")
|
||||
embed_desktop=discord.Embed(title="Desktop / Web", color=color, description="**Step 1:** Open the Server Dropdown menu in the top-left by clicking on the server's name.\n\n**Step 2:** Click the \"*Linked Roles*\" button.\n\n**Step 3:** Click on \"*Linked*.\"\n\n**Step 4:** Click \"*Finish*.\" You're done!\n*Note: You should already be Verified on Bloxlink. If you are not, go to the verification channel to verify.*")
|
||||
embed_desktop.set_thumbnail(url="https://cdn.discordapp.com/attachments/1070838419212738621/1079927564421836930/image.png")
|
||||
embed_mobile=discord.Embed(title="Mobile", color=color, description="**Step 1:** Open the Server menu on the top of the channel list by tapping the server's name.\n\n**Step 2:** Scroll down and tap the \"*Linked Roles*\" button.\n\n**Step 3:** Tap on \"*Linked*.\"\n\n**Step 4:** Tap \"*Finish*.\" You're done!\n*Note: You should already be Verified on Bloxlink. If you are not, go to the verification channel to verify.*")
|
||||
embed_mobile.set_thumbnail(url="https://cdn.discordapp.com/attachments/1047347377348030494/1079930169562771576/Screenshot_20230227_195338_Discord.jpg")
|
||||
if member:
|
||||
await ctx.channel.send(embed=embed, content=member.mention)
|
||||
else:
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.channel.send(embed=embed_desktop)
|
||||
await ctx.channel.send(embed=embed_mobile)
|
||||
await ctx.message.delete()
|
||||
|
||||
@warehouse.error
|
||||
@unix.error
|
||||
async def faq_handler(self, error):
|
||||
@faq_test.error
|
||||
@faq_linked_role.error
|
||||
@faq_npc_intervals.error
|
||||
@faq_links.error
|
||||
@faq_dps.error
|
||||
@faq_ropro.error
|
||||
@faq_polaris_ranks.error
|
||||
@faq_polaris_switch.error
|
||||
async def faq_handler(self, ctx, error):
|
||||
"""Error Handler for Galaxy."""
|
||||
if isinstance(error, discord.NotFound):
|
||||
return
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing Galaxy!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"author" : ["SeaswimmerTheFsh"],
|
||||
"install_msg" : "Thank you for installing Galaxy!\nYou can find the source code of this cog here: https://github.com/SeaswimmerTheFsh/GalaxyCogs",
|
||||
"name" : "Galaxy",
|
||||
"short" : "Custom cog intended for use on the Galaxy discord server.",
|
||||
"description" : "Custom cog intended for use on the Galaxy discord server.",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"hidden": true,
|
||||
"disabled": false
|
||||
"end_user_data_statement" : "This cog does not store any End User Data."
|
||||
}
|
||||
|
|
0
galaxy/temp.py
Normal file
0
galaxy/temp.py
Normal file
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"author": [
|
||||
"cswimr, yname, meelyman"
|
||||
"SeaswimmerTheFsh, yname, meelyman"
|
||||
],
|
||||
"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/GalaxyCogs/issues.",
|
||||
"install_msg": "Thanks for installing my repo!\n\nIf you have any issues with any of the cogs, please create an issue here: https://github.com/SeaswimmerTheFsh/GalaxyCogs/issues.",
|
||||
"name": "Galaxy",
|
||||
"short": "Cogs intended for use on the Galaxy discord server.",
|
||||
"description": "Custom cogs/cog modifications intended for the Galaxy discord server."
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from .info import Info
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Info(bot))
|
||||
def setup(bot):
|
||||
bot.add_cog(Info(bot))
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing Info!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"author" : ["SeaswimmerTheFsh"],
|
||||
"install_msg" : "Thank you for installing Info!\nYou can find the source code of this cog here: https://github.com/SeaswimmerTheFsh/GalaxyCogs",
|
||||
"name" : "Info",
|
||||
"short" : "Provides information on Discord objects.",
|
||||
"description" : "Provides information on Discord objects. Most of this code is shamelessly ripped from <https://github.com/Cog-Creators/Red-DiscordBot/tree/V3/develop/redbot/cogs>.",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"hidden": true,
|
||||
"disabled": false
|
||||
"end_user_data_statement" : "This cog does not store any End User Data."
|
||||
}
|
||||
|
108
info/info.py
108
info/info.py
|
@ -1,13 +1,19 @@
|
|||
import re
|
||||
from datetime import datetime
|
||||
import discord
|
||||
from redbot.core import Config, app_commands, commands
|
||||
from redbot.core import commands, checks, Config
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
from redbot.core.utils.chat_formatting import (bold, humanize_number,
|
||||
humanize_timedelta)
|
||||
import re
|
||||
from redbot.core.utils.chat_formatting import (
|
||||
bold,
|
||||
humanize_number,
|
||||
humanize_timedelta,
|
||||
)
|
||||
from redbot.core.utils.common_filters import (
|
||||
escape_spoilers_and_mass_mentions, filter_invites)
|
||||
filter_invites,
|
||||
escape_spoilers_and_mass_mentions
|
||||
)
|
||||
|
||||
|
||||
_ = T_ = Translator("General", __file__)
|
||||
|
||||
|
@ -28,14 +34,14 @@ class Info(commands.Cog):
|
|||
self.config.register_user(**self.default_user_settings)
|
||||
self.cache: dict = {}
|
||||
|
||||
async def red_delete_data_for_user(self):
|
||||
async def red_delete_data_for_user(self, **kwargs):
|
||||
"""Nothing to delete."""
|
||||
return
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@commands.bot_has_permissions(embed_links=True)
|
||||
async def serverinfo(self, ctx: commands.Context, details: bool = False):
|
||||
async def serverinfo(self, ctx, details: bool = False):
|
||||
"""
|
||||
Show server information.
|
||||
|
||||
|
@ -73,8 +79,8 @@ class Info(commands.Cog):
|
|||
)
|
||||
)
|
||||
if guild.icon:
|
||||
data.set_author(name=guild.name, icon_url=str(guild.icon.replace(format='png')))
|
||||
data.set_thumbnail(url=str(guild.icon.replace(format='png')))
|
||||
data.set_author(name=guild.name, icon_url=str(guild.icon_url_as(format='png')))
|
||||
data.set_thumbnail(url=str(guild.icon_url_as(format='png')))
|
||||
else:
|
||||
data.set_author(name=guild.name)
|
||||
else:
|
||||
|
@ -82,16 +88,16 @@ class Info(commands.Cog):
|
|||
def _size(num: int):
|
||||
for unit in ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
|
||||
if abs(num) < 1024.0:
|
||||
return f"{num:.1f}{unit}"
|
||||
return "{0:.1f}{1}".format(num, unit)
|
||||
num /= 1024.0
|
||||
return f"{num:.1f}YB"
|
||||
return "{0:.1f}{1}".format(num, "YB")
|
||||
|
||||
def _bitsize(num: int):
|
||||
for unit in ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
|
||||
if abs(num) < 1000.0:
|
||||
return f"{num:.1f}{unit}"
|
||||
return "{0:.1f}{1}".format(num, unit)
|
||||
num /= 1000.0
|
||||
return f"{num:.1f}YB"
|
||||
return "{0:.1f}{1}".format(num, "YB")
|
||||
|
||||
shard_info = (
|
||||
_("\nShard ID: **{shard_id}/{shard_count}**").format(
|
||||
|
@ -123,7 +129,7 @@ class Info(commands.Cog):
|
|||
for emoji, value in online_stats.items():
|
||||
try:
|
||||
num = len([m for m in guild.members if value(m)])
|
||||
except Exception as error: # pylint: disable=broad-exception-caught
|
||||
except Exception as error:
|
||||
print(error)
|
||||
continue
|
||||
else:
|
||||
|
@ -167,7 +173,7 @@ class Info(commands.Cog):
|
|||
name=guild.name
|
||||
)
|
||||
if guild.icon:
|
||||
data.set_thumbnail(url=str(guild.icon.url))
|
||||
data.set_thumbnail(url=str(guild.icon_url))
|
||||
data.add_field(name=_("Members:"), value=member_msg)
|
||||
data.add_field(
|
||||
name=_("Channels:"),
|
||||
|
@ -254,7 +260,7 @@ class Info(commands.Cog):
|
|||
)
|
||||
data.add_field(name=_("Nitro Boost:"), value=nitro_boost)
|
||||
if guild.splash:
|
||||
data.set_image(url=str(guild.splash.replace(format='png')))
|
||||
data.set_image(url=str(guild.splash_url_as(format='png')))
|
||||
data.set_footer(text=joined_on)
|
||||
|
||||
await ctx.send(embed=data)
|
||||
|
@ -267,7 +273,7 @@ class Info(commands.Cog):
|
|||
c_status = None
|
||||
if not a.name and not a.emoji:
|
||||
return None, discord.ActivityType.custom
|
||||
if a.name and a.emoji:
|
||||
elif a.name and a.emoji:
|
||||
c_status = _("Custom: {emoji} {name}").format(emoji=a.emoji, name=a.name)
|
||||
elif a.emoji:
|
||||
c_status = _("Custom: {emoji}").format(emoji=a.emoji)
|
||||
|
@ -341,7 +347,7 @@ class Info(commands.Cog):
|
|||
self.handle_watching(user),
|
||||
self.handle_competing(user),
|
||||
]:
|
||||
status_string = a
|
||||
status_string, status_type = a
|
||||
if status_string is None:
|
||||
continue
|
||||
string += f"{status_string}\n"
|
||||
|
@ -359,7 +365,7 @@ class Info(commands.Cog):
|
|||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@commands.bot_has_permissions(embed_links=True)
|
||||
async def userinfo(self, ctx: commands.Context, *, member: discord.Member = None):
|
||||
async def userinfo(self, ctx, *, member: discord.Member = None):
|
||||
"""Show information about a member.
|
||||
This includes fields for status, discord join date, server
|
||||
join date, voice state and previous names/nicknames.
|
||||
|
@ -471,10 +477,10 @@ class Info(commands.Cog):
|
|||
if voice_state and voice_state.channel:
|
||||
data.add_field(
|
||||
name=_("Current voice channel"),
|
||||
value=f"{voice_state.channel.mention} ID: {voice_state.channel.id}",
|
||||
value="{0.mention} ID: {0.id}".format(voice_state.channel),
|
||||
inline=False,
|
||||
)
|
||||
data.set_footer(text=_(f"Member #{member_number} | User ID: {member.id}"))
|
||||
data.set_footer(text=_("Member #{} | User ID: {}").format(member_number, member.id))
|
||||
|
||||
if member.discriminator == "0":
|
||||
name = str(member.name)
|
||||
|
@ -483,42 +489,17 @@ class Info(commands.Cog):
|
|||
name = " ~ ".join((name, member.nick)) if member.nick else name
|
||||
name = filter_invites(name)
|
||||
|
||||
avatar = member.avatar.replace(format='png')
|
||||
avatar = member.avatar_url_as(format='png')
|
||||
data.set_author(name=f"{statusemoji} {name}", url=avatar)
|
||||
data.set_thumbnail(url=avatar)
|
||||
|
||||
await ctx.send(embed=data)
|
||||
|
||||
async def fetch_twemoji(self, unicode_emoji):
|
||||
base_url = "https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/72x72/"
|
||||
emoji_codepoint = "-".join([hex(ord(char))[2:] for char in unicode_emoji])
|
||||
segments = emoji_codepoint.split("-")
|
||||
valid_segments = [seg for seg in segments if len(seg) >= 4]
|
||||
emoji_url = f"{base_url}{valid_segments[0]}.png"
|
||||
return emoji_url
|
||||
|
||||
@app_commands.command()
|
||||
@app_commands.guild_only()
|
||||
async def roleinfo(self, interaction: discord.Interaction, role: discord.Role, list_permissions: bool = False):
|
||||
"""Gives information on a given role.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
role: discord.Role
|
||||
The role you're checking
|
||||
list_permissions: bool
|
||||
Whether or not to list permissions. Ignored if the role has Administrator.
|
||||
"""
|
||||
try:
|
||||
icon = role.display_icon
|
||||
if isinstance(icon, discord.Asset):
|
||||
icon_url = icon.url
|
||||
elif isinstance(icon, str):
|
||||
icon_url = await self.fetch_twemoji(unicode_emoji=icon)
|
||||
else:
|
||||
icon_url = None
|
||||
except: # pylint: disable=bare-except
|
||||
icon_url = None
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
async def roleinfo(self, ctx, role: discord.Role, list_permissions: bool = False):
|
||||
"""Gives information on a specific role.
|
||||
`list_permissions` is ignored if the role you're checking has the `Administrator` permission."""
|
||||
permissions = role.permissions
|
||||
if role.color.value == 0:
|
||||
colorint = 10070709
|
||||
|
@ -528,22 +509,7 @@ class Info(commands.Cog):
|
|||
color = re.sub('#',"",str(role.color))
|
||||
colorcodelink = f"https://www.color-hex.com/color/{color}"
|
||||
timestamp = int(datetime.timestamp(role.created_at))
|
||||
if role.managed is True:
|
||||
managed_status = "True"
|
||||
if role.is_premium_subscriber() is True:
|
||||
managed_status += "\n**Management Type:** Nitro Booster"
|
||||
if role.is_bot_managed() is True:
|
||||
managed_status += "\n**Management Type:** Managed Bot Role"
|
||||
if role.tags.is_guild_connection() is True:
|
||||
managed_status +="\n**Management Type:** Guild Connection"
|
||||
if role.tags.is_available_for_purchase() is True:
|
||||
managed_status +="\n**Management Type:** Subscription"
|
||||
else:
|
||||
managed_status = "False"
|
||||
description = f"**ID:** {role.id}\n**Mention:** {role.mention}\n**Creation Date:** <t:{timestamp}>\n**Color:** [#{color}]({colorcodelink})\n**Hoisted:** {role.hoist}\n**Position:** {role.position}\n**Managed:** {managed_status}\n**Mentionable:** {role.mentionable}\n**Administrator:** {permissions.administrator}"
|
||||
embed = discord.Embed(title=f"{role.name}", color=colorint, description=description)
|
||||
if icon_url:
|
||||
embed.set_thumbnail(url=icon_url)
|
||||
if permissions.administrator is False and list_permissions is True:
|
||||
embed.add_field(name="Permissions", value=f"**Manage Server:** {permissions.manage_guild}\n**Manage Webhooks:** {permissions.manage_webhooks}\n**Manage Channels:** {permissions.manage_channels}\n**Manage Roles:** {permissions.manage_roles}\n**Create Expressions:** {permissions.create_expressions}\n**Manage Events:** {permissions.manage_events}\n**Manage Messages:** {permissions.manage_messages}\n**Manage Nicknames:** {permissions.manage_nicknames}\n**Mention @everyone**: {permissions.mention_everyone}\n**Ban Members:** {permissions.ban_members}\n**Kick Members:** {permissions.kick_members}\n**Timeout Members:** {permissions.moderate_members}\n**View Audit Log:** {permissions.view_audit_log}")
|
||||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||||
embed = discord.Embed(title=f"{role.name}", color=colorint, description=f"**ID:** {role.id}\n**Mention:** {role.mention}\n**Creation Date:** <t:{timestamp}>\n**Color:** [#{color}]({colorcodelink})\n**Hoisted:** {role.hoist}\n**Position:** {role.position}\n**Managed:** {role.managed}\n**Mentionable:** {role.mentionable}\n**Administrator:** {permissions.administrator}")
|
||||
if permissions.administrator == False and list_permissions == True:
|
||||
embed.add_field(name="Permissions", value=f"**Manage Server:** {permissions.manage_guild}\n**Manage Webhooks:** {permissions.manage_webhooks}\n**Manage Channels:** {permissions.manage_channels}\n**Manage Roles:** {permissions.manage_roles}\n**Manage Emojis:** {permissions.manage_emojis}\n**Manage Messages:** {permissions.manage_messages}\n**Manage Nicknames:** {permissions.manage_nicknames}\n**Mention @everyone**: {permissions.mention_everyone}\n**Ban Members:** {permissions.ban_members}\n**Kick Members:** {permissions.kick_members}")
|
||||
await ctx.send(embed=embed)
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
from .issues import Issues
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Issues(bot))
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing Issues!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"name" : "Issues",
|
||||
"short" : "This cog allows you to create Gitea issues through a Discord modal.",
|
||||
"description" : "This cog allows you to create Gitea issues through a Discord modal.",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"hidden": true,
|
||||
"disabled": false
|
||||
}
|
100
issues/issues.py
100
issues/issues.py
|
@ -1,100 +0,0 @@
|
|||
import discord
|
||||
from redbot.core import Config, app_commands, checks, commands
|
||||
|
||||
from . import modals
|
||||
|
||||
|
||||
class Issues(commands.Cog):
|
||||
"""This cog allows you to create Gitea issues through a Discord modal.
|
||||
Developed by cswimr."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=4285273314713, force_registration=True)
|
||||
self.config.register_global(
|
||||
request_channel = None,
|
||||
gitea_root_url = None,
|
||||
gitea_repository_owner = None,
|
||||
gitea_repository = None,
|
||||
gitea_token = None
|
||||
)
|
||||
|
||||
@commands.command()
|
||||
@checks.is_owner()
|
||||
async def issuesconfig(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||
if channel:
|
||||
await self.config.request_channel.set(channel.id)
|
||||
await ctx.send(content=f"Channel set to {channel.mention}.\nRun this command again without a channel argument to configure the other settings.")
|
||||
else:
|
||||
await ctx.channel.send(content="Click the button below to configure the cog.", view=self.IssueConfigurationButton(self.config, ctx))
|
||||
|
||||
@app_commands.command()
|
||||
async def issues(self, interaction: discord.Interaction):
|
||||
"""Found a bug or have a suggestion for the Galaxy bot? Use this command."""
|
||||
color = await self.bot.get_embed_color(None)
|
||||
embed = discord.Embed(title="Issue Reporting & Suggestions", color=await self.bot.get_embed_color(None), description="Have a problem or a suggestion for the Galaxy bot or GalaxyCogs? Read this!")
|
||||
embed.add_field(name="Bot Issues & Suggestions", value="If you'd like to submit a suggestion or a bug report to the developers of the Galaxy bot, please do so with the buttons below or by going [here](https://coastalcommits.com/cswimr/GalaxyCogs/issues/new/choose).\n**Please make sure whatever you're suggesting or reporting doesn't have an existing issue! If it does, you can comment on that issue with additional details if necessary.**")
|
||||
embed.add_field(name="Cog Issues & Suggestions", value="If you'd like to submit a suggestion or a bug report to the developers of GalaxyCogs, please do so with the buttons below or by going [here](https://coastalcommits.com/cswimr/GalaxyCogs/issues/new/choose).\n**Please make sure whatever you're suggesting or reporting doesn't have an existing issue! If it does, you can comment on that issue with additional details if necessary.**")
|
||||
await interaction.response.send_message(embed=embed, view=self.IssueButtons(color, self, interaction), ephemeral=True)
|
||||
|
||||
async def submit_issue_request(self, interaction: discord.Interaction, original_interaction: discord.Interaction, embed: discord.Embed):
|
||||
channel = self.bot.get_channel(await self.config.request_channel())
|
||||
if channel is None:
|
||||
await original_interaction.edit_original_response(content="Command cancelled.", view=None)
|
||||
await interaction.response.send_message(content="The cog is misconfigured, please report this error.", ephemeral=True)
|
||||
try:
|
||||
message = await channel.send(content=".")
|
||||
await message.edit(content="", embed=embed, view=self.IssueResponseButtons(channel, message.id, interaction.user))
|
||||
await original_interaction.edit_original_response(content="Issue request sent!", embed=embed, view=None)
|
||||
await interaction.response.defer()
|
||||
except (discord.HTTPException, discord.Forbidden) as error:
|
||||
await original_interaction.edit_original_response(content="Command cancelled.", view=None)
|
||||
await interaction.response.send_message(content=f"The cog is misconfigured, please report this error.\n```{error}```", ephemeral=True)
|
||||
|
||||
class IssueButtons(discord.ui.View):
|
||||
def __init__(self, color, cog_instance, original_interaction):
|
||||
super().__init__()
|
||||
self.color = color
|
||||
self.cog_instance = cog_instance
|
||||
self.original_interaction = original_interaction
|
||||
|
||||
@discord.ui.button(label="Bot Bug", style=discord.ButtonStyle.danger)
|
||||
async def issue_button_bot_bug(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
|
||||
await interaction.response.send_modal(modals.BotBugModal(self.color, self.cog_instance, self.original_interaction))
|
||||
|
||||
@discord.ui.button(label="Cog Bug", style=discord.ButtonStyle.danger)
|
||||
async def issue_button_cog_bug(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
|
||||
await interaction.response.send_modal(modals.CogBugModal(self.color, self.cog_instance, self.original_interaction))
|
||||
|
||||
@discord.ui.button(label="Bot Suggestion", style=discord.ButtonStyle.blurple)
|
||||
async def issue_button_bot_suggestion(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
|
||||
await interaction.response.send_modal(modals.BotSuggestionModal(self.color, self.cog_instance, self.original_interaction))
|
||||
|
||||
@discord.ui.button(label="Cog Suggestion", style=discord.ButtonStyle.blurple)
|
||||
async def issue_button_cog_suggestion(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
|
||||
await interaction.response.send_modal(modals.CogSuggestionModal(self.color, self.cog_instance, self.original_interaction))
|
||||
|
||||
class IssueConfigurationButton(discord.ui.View):
|
||||
def __init__(self, config, ctx):
|
||||
super().__init__()
|
||||
self.config = config
|
||||
self.ctx = ctx
|
||||
|
||||
@discord.ui.button(label="Change Configuration", style=discord.ButtonStyle.blurple, row=0)
|
||||
async def issue_configuration_button(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
|
||||
await interaction.response.send_modal(modals.IssuesConfigurationModal(self.config, self.ctx))
|
||||
|
||||
class IssueResponseButtons(discord.ui.View):
|
||||
def __init__(self, channel, message_id, user):
|
||||
super().__init__()
|
||||
self.channel = channel
|
||||
self.message_id = message_id
|
||||
self.user = user
|
||||
|
||||
@discord.ui.button(label="Approve", style=discord.ButtonStyle.green)
|
||||
async def issue_response_button_approve(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
|
||||
await interaction.response.send_modal(modals.IssueResponseModal(self.channel, self.message_id, self.user, True))
|
||||
|
||||
@discord.ui.button(label="Deny", style=discord.ButtonStyle.danger)
|
||||
async def issue_response_button_deny(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
|
||||
await interaction.response.send_modal(modals.IssueResponseModal(self.channel, self.message_id, self.user, False))
|
382
issues/modals.py
382
issues/modals.py
|
@ -1,382 +0,0 @@
|
|||
import aiohttp
|
||||
import discord
|
||||
from redbot.core import Config
|
||||
|
||||
#
|
||||
# Misc. functions
|
||||
#
|
||||
|
||||
def construct_embed(interaction: discord.Interaction, fields: list[discord.Embed.fields], color: str):
|
||||
embed = discord.Embed(title="Issue Request", color=color)
|
||||
for item in fields:
|
||||
title = item.label
|
||||
value = item.value
|
||||
if value is not None:
|
||||
if len(value) > 1024:
|
||||
words = value.split()
|
||||
split_value = []
|
||||
current_part = ""
|
||||
for word in words:
|
||||
if len(current_part) + len(word) + 1 <= 1024:
|
||||
current_part += word + " "
|
||||
else:
|
||||
split_value.append(current_part.strip())
|
||||
current_part = word + " "
|
||||
if current_part:
|
||||
split_value.append(current_part.strip())
|
||||
for i, part in enumerate(split_value):
|
||||
embed.add_field(
|
||||
name=title if i == 0 else "\u200b", value=part, inline=False
|
||||
)
|
||||
else:
|
||||
embed.add_field(name=title, value=value, inline=False)
|
||||
if interaction.user.discriminator == "0":
|
||||
username = interaction.user.name
|
||||
else:
|
||||
username = f"{interaction.user.name}#{interaction.user.discriminator}"
|
||||
embed.set_footer(
|
||||
text=f"Submitted by {username} ({interaction.user.id})",
|
||||
icon_url=interaction.user.display_avatar.url,
|
||||
)
|
||||
return embed
|
||||
|
||||
#
|
||||
# Modals for the core '/issues' command.
|
||||
#
|
||||
|
||||
class BotBugModal(discord.ui.Modal, title="Creating issue..."):
|
||||
def __init__(self, color, cog_instance, original_interaction):
|
||||
super().__init__()
|
||||
self.color = color
|
||||
self.cog_instance = cog_instance
|
||||
self.original_interaction = original_interaction
|
||||
|
||||
bug_description = discord.ui.TextInput(
|
||||
label="Describe the bug",
|
||||
placeholder="A clear and concise description of what the bug is.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
reproduction_steps = discord.ui.TextInput(
|
||||
label="To Reproduce",
|
||||
placeholder="What caused the bug?",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
expected_behavior = discord.ui.TextInput(
|
||||
label="Expected Behavior",
|
||||
placeholder="A clear and concise description of what you expected to happen.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
additional_context = discord.ui.TextInput(
|
||||
label="Additional Context",
|
||||
placeholder="Add any other context about the problem here.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=False,
|
||||
max_length=2048
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
fields = [self.bug_description, self.reproduction_steps, self.expected_behavior, self.additional_context]
|
||||
embed = construct_embed(interaction, fields, self.color)
|
||||
embed.set_author(name="Bot Bug Report")
|
||||
await self.cog_instance.submit_issue_request(interaction=interaction, original_interaction=self.original_interaction, embed=embed)
|
||||
|
||||
class CogBugModal(discord.ui.Modal, title="Creating issue..."):
|
||||
def __init__(self, color, cog_instance, original_interaction):
|
||||
super().__init__()
|
||||
self.color = color
|
||||
self.cog_instance = cog_instance
|
||||
self.original_interaction = original_interaction
|
||||
|
||||
cog_name = discord.ui.TextInput(
|
||||
label="What cog is causing this error?",
|
||||
placeholder="If unsure, put \"GalaxyCogs\".",
|
||||
style=discord.TextStyle.short,
|
||||
required=True,
|
||||
max_length=50
|
||||
)
|
||||
bug_description = discord.ui.TextInput(
|
||||
label="Describe the bug",
|
||||
placeholder="A clear and concise description of what the bug is.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
reproduction_steps = discord.ui.TextInput(
|
||||
label="To Reproduce",
|
||||
placeholder="What caused the bug?",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
expected_behavior = discord.ui.TextInput(
|
||||
label="Expected Behavior",
|
||||
placeholder="A clear and concise description of what you expected to happen.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
additional_context = discord.ui.TextInput(
|
||||
label="Additional Context",
|
||||
placeholder="Add any other context about the problem here.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=False,
|
||||
max_length=2048
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
fields = [self.cog_name, self.bug_description, self.reproduction_steps, self.expected_behavior, self.additional_context]
|
||||
embed = construct_embed(interaction, fields, self.color)
|
||||
embed.set_author(name="Cog Bug Report")
|
||||
await self.cog_instance.submit_issue_request(interaction=interaction, original_interaction=self.original_interaction, embed=embed)
|
||||
|
||||
class BotSuggestionModal(discord.ui.Modal, title="Creating issue..."):
|
||||
def __init__(self, color, cog_instance, original_interaction):
|
||||
super().__init__()
|
||||
self.color = color
|
||||
self.cog_instance = cog_instance
|
||||
self.original_interaction = original_interaction
|
||||
|
||||
suggestion_description = discord.ui.TextInput(
|
||||
label="Describe your suggestion.",
|
||||
placeholder="A clear and concise description of what your suggestion is.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
alternatives = discord.ui.TextInput(
|
||||
label="Describe alternatives you've considered.",
|
||||
placeholder="A clear and concise description of any alternative solutions or features you've cnosidered.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
additional_context = discord.ui.TextInput(
|
||||
label="Additional Context",
|
||||
placeholder="Add any other context about your suggestion here.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=False,
|
||||
max_length=2048
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
fields = [self.suggestion_description, self.alternatives, self.additional_context]
|
||||
embed = construct_embed(interaction, fields, self.color)
|
||||
embed.set_author(name="Cog Suggestion")
|
||||
await self.cog_instance.submit_issue_request(interaction=interaction, original_interaction=self.original_interaction, embed=embed)
|
||||
|
||||
class CogSuggestionModal(discord.ui.Modal, title="Creating issue..."):
|
||||
def __init__(self, color, cog_instance, original_interaction):
|
||||
super().__init__()
|
||||
self.color = color
|
||||
self.cog_instance = cog_instance
|
||||
self.original_interaction = original_interaction
|
||||
|
||||
cog_name = discord.ui.TextInput(
|
||||
label="What cog is your suggestion for?",
|
||||
placeholder="If unsure, put \"GalaxyCogs\".",
|
||||
style=discord.TextStyle.short,
|
||||
required=True,
|
||||
max_length=50
|
||||
)
|
||||
suggestion_description = discord.ui.TextInput(
|
||||
label="Describe your suggestion.",
|
||||
placeholder="A clear and concise description of what your suggestion is.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
alternatives = discord.ui.TextInput(
|
||||
label="Describe alternatives you've considered.",
|
||||
placeholder="A clear and concise description of any alternative solutions or features you've cnosidered.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=True,
|
||||
max_length=2048
|
||||
)
|
||||
additional_context = discord.ui.TextInput(
|
||||
label="Additional Context",
|
||||
placeholder="Add any other context about your suggestion here.",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=False,
|
||||
max_length=2048
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
fields = [self.cog_name, self.suggestion_description, self.alternatives, self.additional_context]
|
||||
embed = construct_embed(interaction, fields, self.color)
|
||||
embed.set_author(name="Cog Suggestion")
|
||||
await self.cog_instance.submit_issue_request(interaction=interaction, original_interaction=self.original_interaction, embed=embed)
|
||||
|
||||
#
|
||||
# Response modal
|
||||
#
|
||||
|
||||
class IssueResponseModal(discord.ui.Modal, title="Sending response message..."):
|
||||
def __init__(self, channel, message_id, user, approved):
|
||||
super().__init__()
|
||||
self.channel = channel
|
||||
self.message_id = message_id
|
||||
self.user = user
|
||||
self.approved = approved
|
||||
self.config = Config.get_conf(None, cog_name="Issues", identifier=4285273314713)
|
||||
|
||||
issue_title_input = discord.ui.TextInput(
|
||||
label="Title",
|
||||
placeholder="Only use this if you're accepting the issue request.",
|
||||
style=discord.TextStyle.short,
|
||||
required=False,
|
||||
max_length=200,
|
||||
)
|
||||
response = discord.ui.TextInput(
|
||||
label="Response",
|
||||
placeholder="",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=False,
|
||||
max_length=1024,
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
message: discord.Message = await self.channel.fetch_message(self.message_id)
|
||||
embed = message.embeds[0]
|
||||
field_values = []
|
||||
field_names = []
|
||||
|
||||
if self.approved:
|
||||
embed.color = 1226519
|
||||
embed.title = "Issue Request Approved"
|
||||
status = "accepted"
|
||||
else:
|
||||
embed.color = 15671552
|
||||
embed.title = "Issue Request Denied"
|
||||
status = "denied"
|
||||
await interaction.response.send_message(content=f"Issue request {status}.", ephemeral=True)
|
||||
|
||||
if self.response.value != "":
|
||||
embed.add_field(
|
||||
name=f"Response from {interaction.user.name}",
|
||||
value=self.response.value,
|
||||
inline=False,
|
||||
)
|
||||
|
||||
if self.approved:
|
||||
for field in embed.fields:
|
||||
field_names.append(f"**{field.name}**")
|
||||
field_values.append(field.value)
|
||||
|
||||
if self.issue_title_input.value != "":
|
||||
_issue_title = self.issue_title_input.value
|
||||
else:
|
||||
_issue_title = "Automatically generated issue"
|
||||
|
||||
if embed.author.name == "Bot Bug Report":
|
||||
issue_title = f"[BOT BUG] {_issue_title}"
|
||||
# desired_labels = ["bot", "bug"]
|
||||
elif embed.author.name == "Bot Suggestion":
|
||||
issue_title = f"[BOT SUGGESTION] {_issue_title}"
|
||||
# desired_labels = ["bot", "enhancement"]
|
||||
elif embed.author.name == "Cog Bug Report":
|
||||
issue_title = f"[{field_values[0]}] {_issue_title}"
|
||||
# desired_labels = ["cog", "bug"]
|
||||
elif embed.author.name == "Cog Suggestion":
|
||||
issue_title = f"[{field_values[0]}] {_issue_title}"
|
||||
# desired_labels = ["cog", "enhancement"]
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {await self.config.gitea_token()}",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
url = f"{await self.config.gitea_root_url()}/api/v1/repos/{await self.config.gitea_repository_owner()}/{await self.config.gitea_repository()}/issues"
|
||||
|
||||
issue_body = "\n".join(
|
||||
[f"{name}\n{value}" for name, value in zip(field_names, field_values)]
|
||||
)
|
||||
|
||||
# async def fetch_labels():
|
||||
# async with aiohttp.ClientSession(headers=headers) as session:
|
||||
# async with session.post(url=f"{await self.config.gitea_root_url()}/api/v1/repos/{await self.config.gitea_repository_owner()}/{await self.config.gitea_repository()}/labels") as response:
|
||||
# label_list = []
|
||||
# for label in response.json():
|
||||
# if label["name"] in desired_labels:
|
||||
# label_list.append(label["id"])
|
||||
# if label_list is None:
|
||||
# print("Error! Labels are not properly configured on the target repository.")
|
||||
# return await label_list
|
||||
|
||||
issue_labels = None
|
||||
|
||||
if issue_labels is None:
|
||||
issue_data = {"title": issue_title, "body": issue_body}
|
||||
else:
|
||||
issue_data = {"title": issue_title, "body": issue_body, "labels": issue_labels}
|
||||
|
||||
async def create_issue():
|
||||
async with aiohttp.ClientSession(headers=headers) as session:
|
||||
async with session.post(url, json=issue_data) as response:
|
||||
return await response.json(), response.status
|
||||
|
||||
response_json, status_code = await create_issue()
|
||||
if status_code == 201:
|
||||
await interaction.response.send_message(
|
||||
content=f"Issue request {status}.\n[Issue successfully created.]({response_json.get('html_url')})",
|
||||
ephemeral=True,
|
||||
)
|
||||
embed.url = response_json.get("html_url")
|
||||
|
||||
await message.edit(embed=embed, view=None)
|
||||
await self.user.send(embed=embed)
|
||||
|
||||
#
|
||||
# Configuration modal
|
||||
#
|
||||
|
||||
class IssuesConfigurationModal(discord.ui.Modal, title="Modifying configuration..."):
|
||||
def __init__(self, config, ctx):
|
||||
super().__init__()
|
||||
self.config = config
|
||||
self.ctx = ctx
|
||||
|
||||
gitea_root_url = discord.ui.TextInput(
|
||||
label="Gitea Root URL",
|
||||
placeholder="https://try.gitea.io",
|
||||
style=discord.TextStyle.short,
|
||||
required=False,
|
||||
max_length=200
|
||||
)
|
||||
gitea_repository_owner = discord.ui.TextInput(
|
||||
label="Gitea Repository Owner",
|
||||
placeholder="foo",
|
||||
style=discord.TextStyle.short,
|
||||
required=False,
|
||||
max_length=200
|
||||
)
|
||||
gitea_repository = discord.ui.TextInput(
|
||||
label="Gitea Repository Name",
|
||||
placeholder="bar",
|
||||
style=discord.TextStyle.short,
|
||||
required=False,
|
||||
max_length=200
|
||||
)
|
||||
gitea_token = discord.ui.TextInput(
|
||||
label="Gitea User Access Token",
|
||||
placeholder="Generate one from your user settings page.",
|
||||
style=discord.TextStyle.short,
|
||||
required=False,
|
||||
max_length=200
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
if self.gitea_token.value != "":
|
||||
await self.config.gitea_token.set(self.gitea_token.value)
|
||||
if self.gitea_root_url.value != "":
|
||||
await self.config.gitea_root_url.set(self.gitea_root_url.value)
|
||||
if self.gitea_repository_owner.value != "":
|
||||
await self.config.gitea_repository_owner.set(self.gitea_repository_owner.value)
|
||||
if self.gitea_repository.value != "":
|
||||
await self.config.gitea_repository.set(self.gitea_repository.value)
|
||||
await interaction.response.send_message(content="Configuration changed!")
|
5
musicdownloader/__init__.py
Normal file
5
musicdownloader/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from .musicdownloader import MusicDownloader
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(MusicDownloader(bot))
|
9
musicdownloader/info.json
Normal file
9
musicdownloader/info.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"author" : ["SeaswimmerTheFsh"],
|
||||
"install_msg" : "Thank you for installing MusicDownloader!\nYou can find the source code of this cog here: https://github.com/SeaswimmerTheFsh/GalaxyCogs",
|
||||
"name" : "MusicDownloader",
|
||||
"short" : "Custom cog intended for use on the Galaxy discord server.",
|
||||
"description" : "Custom cog intended for use on the Galaxy discord server.",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"requirements": "yt_dlp"
|
||||
}
|
237
musicdownloader/musicdownloader.py
Normal file
237
musicdownloader/musicdownloader.py
Normal file
|
@ -0,0 +1,237 @@
|
|||
import asyncio
|
||||
import re
|
||||
import discord
|
||||
import os
|
||||
import sqlite3
|
||||
import concurrent.futures
|
||||
from yt_dlp import YoutubeDL, utils
|
||||
from redbot.core import commands, checks, Config, data_manager
|
||||
|
||||
class MusicDownloader(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=475728338)
|
||||
self.config.register_global(
|
||||
save_directory = str(data_manager.cog_data_path()) + f"{os.sep}MusicDownloader"
|
||||
)
|
||||
|
||||
class UserBlacklisted(Exception):
|
||||
def __init__(self, message="The user is blacklisted from using this command."):
|
||||
super().__init__(message)
|
||||
|
||||
def create_table(self):
|
||||
data_path = str(data_manager.cog_data_path()) + f"{os.sep}MusicDownloader"
|
||||
db_path = os.path.join(data_path, "database.db")
|
||||
if not os.path.isfile(db_path):
|
||||
con = sqlite3.connect(db_path)
|
||||
cur = con.cursor()
|
||||
cur.execute('''
|
||||
CREATE TABLE [IF NOT EXISTS] "blacklist_log" (
|
||||
"user_id" INTEGER NOT NULL UNIQUE,
|
||||
"reason" TEXT DEFAULT NULL,
|
||||
PRIMARY KEY("user_id")
|
||||
);
|
||||
''')
|
||||
con.commit()
|
||||
con.close()
|
||||
|
||||
def blacklist_checker(self, user_id):
|
||||
data_path = str(data_manager.cog_data_path()) + f"{os.sep}MusicDownloader"
|
||||
db_path = os.path.join(data_path, "database.db")
|
||||
con = sqlite3.connect(db_path)
|
||||
cur = con.cursor()
|
||||
cur.execute(f"SELECT user_id, reason FROM blacklist_log WHERE user_id = ?;", (user_id,))
|
||||
result = cur.fetchone()
|
||||
con.close()
|
||||
if result:
|
||||
user_id, reason = result
|
||||
raise self.UserBlacklisted(reason)
|
||||
|
||||
async def cog_load(self):
|
||||
self.create_table()
|
||||
|
||||
@commands.command()
|
||||
@checks.is_owner()
|
||||
async def change_data_path(self, ctx: commands.Context, *, data_path: str = None):
|
||||
"""This command changes the data path the `[p]download` command outputs to."""
|
||||
old_path = await self.config.save_directory()
|
||||
if not data_path:
|
||||
await ctx.send(f"The current data path is `{old_path}`.")
|
||||
return
|
||||
if os.path.isdir(data_path):
|
||||
await self.config.save_directory.set(data_path)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"The save directory has been set to `{data_path}`.\n It was previously set to `{old_path}`.")
|
||||
await ctx.send(embed=embed)
|
||||
elif os.path.isfile(data_path):
|
||||
await ctx.send("The path you've provided leads to a file, not a directory!")
|
||||
elif os.path.exists(data_path) is False:
|
||||
await ctx.send("The path you've provided doesn't exist!")
|
||||
|
||||
def youtube_download(self, url: str, path: str):
|
||||
"""This method does the actual downloading of the YouTube Video."""
|
||||
class Logger:
|
||||
def debug(self, msg):
|
||||
if msg.startswith('[debug] '):
|
||||
pass
|
||||
else:
|
||||
self.info(msg)
|
||||
def info(self, msg):
|
||||
pass
|
||||
def warning(self, msg):
|
||||
pass
|
||||
def error(self, msg):
|
||||
print(msg)
|
||||
ydl_opts = {
|
||||
'logger': Logger(),
|
||||
'format': 'm4a/bestaudio/best',
|
||||
'postprocessors': [{'key': 'FFmpegExtractAudio', 'preferredcodec': 'm4a',}],
|
||||
'paths': {'home': path},
|
||||
'verbose': True
|
||||
}
|
||||
with YoutubeDL(ydl_opts) as ydl:
|
||||
info = ydl.extract_info(url=url, download=False)
|
||||
title = info['title']
|
||||
id = info['id']
|
||||
filename = title + f' [{id}].m4a'
|
||||
full_filename = os.path.join(path, filename)
|
||||
if os.path.isfile(full_filename):
|
||||
previously_existed = True
|
||||
else:
|
||||
with YoutubeDL(ydl_opts) as ydl:
|
||||
error_code = ydl.download(url)
|
||||
previously_existed = False
|
||||
return filename, previously_existed
|
||||
|
||||
async def download_file(self, url: str, path: str):
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
result = await self.bot.loop.run_in_executor(executor, self.youtube_download, url, path)
|
||||
return result
|
||||
|
||||
@commands.command(aliases=["dl"])
|
||||
async def download(self, ctx: commands.Context, url: str, delete: bool = False, subfolder: str = None):
|
||||
"""This command downloads a YouTube Video as an `m4a` and uploads the file to discord.
|
||||
|
||||
If you're considered a bot owner, you will be able to save downloaded files to the data path set in the `[p]change_data_path` command.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- The `url` argument is just the url of the YouTube Video you're downloading.
|
||||
|
||||
- The `delete` argument will automatically delete the audio file after uploading it to Discord. If set to False, it will only save the file if you are a bot owner.
|
||||
|
||||
- The `subfolder` argument only does anything if `delete` is set to False, but it allows you to save to a subfolder in the data path you've set previously without having to change said data path manually."""
|
||||
try:
|
||||
self.blacklist_checker(ctx.author.id)
|
||||
except self.UserBlacklisted as e:
|
||||
await ctx.send(f"You are blacklisted from running this command!\nReason: `{e}`")
|
||||
return
|
||||
data_path = await self.config.save_directory()
|
||||
if subfolder and await self.bot.is_owner(ctx.author):
|
||||
data_path = os.path.join(data_path, subfolder)
|
||||
illegal_chars = r'<>:"/\|?*'
|
||||
if any(char in illegal_chars for char in subfolder):
|
||||
pattern = "[" + re.escape(illegal_chars) + "]"
|
||||
modified_subfolder = re.sub(pattern, r'__**\g<0>**__', subfolder)
|
||||
await ctx.send(f"Your subfolder contains illegal characters: `{modified_subfolder}`")
|
||||
return
|
||||
elif os.path.isfile(data_path):
|
||||
await ctx.send("Your 'subfolder' is a file, not a directory!")
|
||||
return
|
||||
elif os.path.exists(data_path) is False:
|
||||
message = await ctx.send("Your subfolder does not exist yet, would you like to continue? It will be automatically created.")
|
||||
def check(message):
|
||||
return message.author == ctx.author and message.content.lower() in ['yes', 'ye', 'y']
|
||||
try:
|
||||
await self.bot.wait_for('message', check=check, timeout=60) # Timeout after 60 seconds
|
||||
except asyncio.TimeoutError:
|
||||
await message.edit("You took too long to respond.")
|
||||
else:
|
||||
await message.edit("Confirmed!")
|
||||
try:
|
||||
os.makedirs(data_path)
|
||||
except OSError as e:
|
||||
await message.edit(f"Encountered an error attempting to create the subfolder!\n`{e}`")
|
||||
msg = message.edit
|
||||
else:
|
||||
msg = ctx.send
|
||||
message = await msg("YouTube Downloader started!")
|
||||
try:
|
||||
ytdlp_output = await self.download_file(url, data_path)
|
||||
except utils.DownloadError or utils.ExtractorError:
|
||||
await message.edit(content="Please provide a link to YouTube and not another site.\nThe site you've linked to is known for using DRM protection, so MusicDownloader cannot download from it.")
|
||||
return
|
||||
full_filename = os.path.join(data_path, ytdlp_output[0])
|
||||
while not os.path.isfile(full_filename):
|
||||
await asyncio.sleep(0.2)
|
||||
if os.path.isfile(full_filename):
|
||||
with open(full_filename, 'rb') as file:
|
||||
try:
|
||||
complete_message = await ctx.send(content="YouTube Downloader completed!\nDownloaded file:", file=discord.File(file, ytdlp_output[0]))
|
||||
except ValueError:
|
||||
complete_message = await ctx.send(content="YouTube Downloader completed, but the audio file was too large to upload.")
|
||||
file.close()
|
||||
if delete is True or await self.bot.is_owner(ctx.author) is False:
|
||||
if ytdlp_output[1] is False:
|
||||
os.remove(full_filename)
|
||||
await complete_message.edit(content="YouTube Downloader completed!\nFile has been deleted.\nDownloaded file:")
|
||||
if ytdlp_output[1] is True:
|
||||
await complete_message.edit(content="YouTube Downloader completed!\nFile has not been deleted, as it was previously downloaded and saved.\nDownloaded file:")
|
||||
|
||||
@commands.group(name="dl-blacklist", invoke_without_command=True)
|
||||
async def blacklist(self, ctx: commands.Context, user: discord.User = None):
|
||||
"""Group command for managing the blacklist."""
|
||||
if not user:
|
||||
user = ctx.author
|
||||
data_path = str(data_manager.cog_data_path()) + f"{os.sep}MusicDownloader"
|
||||
db_path = os.path.join(data_path, "database.db")
|
||||
if user is None:
|
||||
await ctx.send("Please provide a user to check in the blacklist.")
|
||||
return
|
||||
con = sqlite3.connect(db_path)
|
||||
cur = con.cursor()
|
||||
cur.execute("SELECT user_id, reason FROM blacklist_log WHERE user_id = ?;", (user.id,))
|
||||
result = cur.fetchone()
|
||||
if result:
|
||||
user_id, reason = result
|
||||
await ctx.send(f"{user.mention} is in the blacklist for the following reason: `{reason}`", allowed_mentions = discord.AllowedMentions(users=False))
|
||||
else:
|
||||
await ctx.send(f"{user.mention} is not in the blacklist.", allowed_mentions = discord.AllowedMentions(users=False))
|
||||
con.close()
|
||||
|
||||
@blacklist.command(name='add')
|
||||
@checks.is_owner()
|
||||
async def blacklist_add(self, ctx: commands.Context, user: discord.User, *, reason: str = None):
|
||||
if not reason:
|
||||
reason = 'No reason provided.'
|
||||
data_path = str(data_manager.cog_data_path()) + f"{os.sep}MusicDownloader"
|
||||
db_path = os.path.join(data_path, "database.db")
|
||||
con = sqlite3.connect(db_path)
|
||||
cur = con.cursor()
|
||||
cur.execute("SELECT user_id FROM blacklist_log WHERE user_id = ?;", (user.id,))
|
||||
result = cur.fetchone()
|
||||
if result:
|
||||
await ctx.send("User is already in the blacklist.")
|
||||
con.close()
|
||||
return
|
||||
cur.execute("INSERT INTO blacklist_log (user_id, reason) VALUES (?, ?);", (user.id, reason))
|
||||
con.commit()
|
||||
con.close()
|
||||
await ctx.send(f"{user.mention} has been added to the blacklist with the reason: `{reason}`")
|
||||
|
||||
@blacklist.command(name='remove')
|
||||
@checks.is_owner()
|
||||
async def blacklist_remove(self, ctx: commands.Context, user: discord.User):
|
||||
data_path = str(data_manager.cog_data_path()) + f"{os.sep}MusicDownloader"
|
||||
db_path = os.path.join(data_path, "database.db")
|
||||
con = sqlite3.connect(db_path)
|
||||
cur = con.cursor()
|
||||
cur.execute("SELECT user_id FROM blacklist_log WHERE user_id = ?;", (user.id,))
|
||||
result = cur.fetchone()
|
||||
if not result:
|
||||
await ctx.send("User is not in the blacklist.")
|
||||
con.close()
|
||||
return
|
||||
cur.execute("DELETE FROM blacklist_log WHERE user_id = ?;", (user.id,))
|
||||
con.commit()
|
||||
con.close()
|
||||
await ctx.send(f"{user.mention} has been removed from the blacklist.")
|
|
@ -1,5 +1,5 @@
|
|||
from .podcast import Podcast
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Podcast(bot))
|
||||
def setup(bot):
|
||||
bot.add_cog(Podcast(bot))
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing Podcast!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"author" : ["SeaswimmerTheFsh"],
|
||||
"install_msg" : "Thank you for installing Podcast!\nYou can find the source code of this cog here: https://github.com/SeaswimmerTheFsh/GalaxyCogs",
|
||||
"name" : "Podcast",
|
||||
"short" : "Provides a questions submission system.",
|
||||
"description" : "Provies a questions submission system.",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"hidden": true,
|
||||
"disabled": true
|
||||
"end_user_data_statement" : "This cog does not store any End User Data."
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
from redbot.core import Config, checks, commands
|
||||
|
||||
import discord
|
||||
from datetime import datetime
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core import commands, checks, Config, bot
|
||||
|
||||
class Podcast(commands.Cog):
|
||||
"""Provides a questions submission system for podcasts.
|
||||
Developed by cswimr."""
|
||||
Developed by SeaswimmerTheFsh."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
@ -19,19 +21,20 @@ class Podcast(commands.Cog):
|
|||
)
|
||||
|
||||
@commands.command()
|
||||
async def podcast(self, ctx: commands.Context, question: str):
|
||||
async def podcast(self, ctx, question: str):
|
||||
"""Submits a question to the Podcast."""
|
||||
prefix = await self.bot.get_valid_prefixes()
|
||||
if await self.config.guild(ctx.guild).global_mode(True):
|
||||
submission_channel = ctx.bot.get_channel(await self.config.global_submission_channel())
|
||||
submission_channel = bot.get_channel(await self.config.global_submission_channel())
|
||||
blacklisted_users = await self.config.global_blacklisted_users()
|
||||
elif await self.config.guild(ctx.guild).global_mode(False):
|
||||
submission_channel = ctx.bot.get_channel(await self.config.guild(ctx.guild).submission_channel())
|
||||
submission_channel = bot.get_channel(await self.config.guild(ctx.guild).submission_channel())
|
||||
blacklisted_users = await self.config.guild(ctx.guild).blacklisted_users()
|
||||
else:
|
||||
return
|
||||
if ctx.author.id in blacklisted_users:
|
||||
await ctx.author.send(content=f"You are blacklisted from ``{prefix}podcast``!")
|
||||
return
|
||||
else:
|
||||
await submission_channel.send(content=f"{question}")
|
||||
await ctx.send(content="Question submitted!")
|
||||
|
@ -45,10 +48,10 @@ class Podcast(commands.Cog):
|
|||
@podcastset.command(name="global")
|
||||
async def set_global_mode(self, ctx, enabled: bool):
|
||||
"""Enables or disables global mode."""
|
||||
if enabled is True:
|
||||
if enabled == True:
|
||||
await self.config.guild(ctx.guild).global_mode.set(True)
|
||||
await ctx.send(content="``global_mode`` has been enabled.")
|
||||
elif enabled is False:
|
||||
elif enabled == False:
|
||||
await self.config.guild(ctx.guild).global_mode.set(False)
|
||||
await ctx.send(content="``global_mode`` has been disabled.")
|
||||
else:
|
||||
|
|
2023
poetry.lock
generated
2023
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,28 +0,0 @@
|
|||
[tool.poetry]
|
||||
name = "galaxycogs"
|
||||
version = "0.1.0"
|
||||
description = "Custom cogs/cog modifications intended for the Galaxy discord server."
|
||||
authors = ["Galaxy Discord Management Team"]
|
||||
license = "MPL 2"
|
||||
readme = "README.md"
|
||||
package-mode = false
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<3.12"
|
||||
Red-DiscordBot = "^3.5.5"
|
||||
pytimeparse2 = "^1.7.1"
|
||||
prisma = "^0.10.0"
|
||||
mysql-connector-python = "^8.1.0"
|
||||
humanize = "^4.8.0"
|
||||
pytube = "^15.0.0"
|
||||
ruff = "^0.3.6"
|
||||
|
||||
[tool.poetry.group.dev]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pylint = "^2.17.5"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
|
@ -1,5 +0,0 @@
|
|||
from .send import Send
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Send(bot))
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing Send!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"name" : "Send",
|
||||
"short" : "Allows you to send messages as the bot user!",
|
||||
"description" : "Allows you to send messages as the bot user!.",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"hidden": true,
|
||||
"disabled": false
|
||||
}
|
66
send/send.py
66
send/send.py
|
@ -1,66 +0,0 @@
|
|||
from typing import Union
|
||||
import discord
|
||||
from redbot.core import commands, app_commands
|
||||
|
||||
class Send(commands.Cog):
|
||||
"""Allows you to send messages as the bot account."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
async def send_to_target(self, target: Union[discord.Member, discord.TextChannel], interaction: discord.Interaction, message: str, secondary_message: str = None):
|
||||
if isinstance(target, discord.Member):
|
||||
target_type = "member"
|
||||
elif isinstance(target, discord.TextChannel):
|
||||
target_type = "textchannel"
|
||||
try:
|
||||
await target.send(message)
|
||||
if secondary_message is not None:
|
||||
await target.send(secondary_message)
|
||||
await interaction.response.send_message(content=f"Message sent to {target.mention}!\nMessage contents:\n```{message}```\n```{secondary_message}```", ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(content=f"Message sent to {target.mention}!\nMessage contents:\n```{message}```", ephemeral=True)
|
||||
except (discord.HTTPException, discord.Forbidden):
|
||||
if target_type == "member":
|
||||
await interaction.response.send_message(content="That user has their direct messages closed!", ephemeral=True)
|
||||
elif target_type == "textchannel":
|
||||
await interaction.response.send_message(content="I cannot access that channel!", ephemeral=True)
|
||||
|
||||
class MessageModal(discord.ui.Modal, title="Sending message..."):
|
||||
def __init__(self, target):
|
||||
super().__init__()
|
||||
self.target = target
|
||||
message = discord.ui.TextInput(
|
||||
label="Message Content",
|
||||
placeholder="I'm contacting you about your cars extended warranty...",
|
||||
style=discord.TextStyle.paragraph,
|
||||
max_length=1750
|
||||
)
|
||||
secondary_message = discord.ui.TextInput(
|
||||
label="Secondary Message Content",
|
||||
placeholder="Typically used for images/image links.",
|
||||
style=discord.TextStyle.short,
|
||||
required=False,
|
||||
max_length=200
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
await Send.send_to_target(self, self.target, interaction, self.message, self.secondary_message)
|
||||
|
||||
send = app_commands.Group(name="send", description="Send a message as the bot user!")
|
||||
|
||||
@send.command(name="user", description="Sends a direct message to a user.")
|
||||
async def user(self, interaction: discord.Interaction, member: discord.Member, message: str = None):
|
||||
"""Sends a direct message to a user."""
|
||||
if message:
|
||||
await Send.send_to_target(self, member, interaction, message)
|
||||
else:
|
||||
await interaction.response.send_modal(Send.MessageModal(member))
|
||||
|
||||
@send.command(name="channel", description="Sends a message to a channel.")
|
||||
async def channel(self, interaction: discord.Interaction, channel: discord.TextChannel, message: str = None):
|
||||
"""Sends a message to a channel."""
|
||||
if message:
|
||||
await Send.send_to_target(self, channel, interaction, message)
|
||||
else:
|
||||
await interaction.response.send_modal(Send.MessageModal(channel))
|
|
@ -1,5 +1,5 @@
|
|||
from .shortmute import Shortmute
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Shortmute(bot))
|
||||
def setup(bot):
|
||||
bot.add_cog(Shortmute(bot))
|
|
@ -1,12 +1,9 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing Shortmute!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"author" : ["SeaswimmerTheFsh"],
|
||||
"install_msg" : "Thank you for installing Shortmute!\nYou can find the source code of this cog here: https://github.com/SeaswimmerTheFsh/GalaxyCogs\nYou can find the original source code from sravan-cogs here: <https://github.com/sravan1946/sravan-cogs/tree/master/timeout>",
|
||||
"name" : "Shortmute",
|
||||
"short" : "Allows staff members to shortmute individuals for up to 30 minutes.",
|
||||
"description" : "Allows staff members to shortmute individuals for up to 30 minutes, using Discord's Timeouts feature.",
|
||||
"tags" : ["moderation, mutes, timeouts"],
|
||||
"end_user_data_statement": "This cog stores no end user data.",
|
||||
"requirements": ["pytimeparse2"],
|
||||
"hidden": true,
|
||||
"disabled": false
|
||||
"end_user_data_statement": "This cog stores no end user data."
|
||||
}
|
||||
|
|
|
@ -1,346 +1,237 @@
|
|||
import contextlib
|
||||
import datetime
|
||||
from typing import List, Literal, Optional, Union
|
||||
|
||||
import discord
|
||||
from discord import ui
|
||||
from discord.ext.commands import Bot
|
||||
from pytimeparse2 import disable_dateutil, parse
|
||||
from redbot.core import Config, app_commands, commands
|
||||
import humanize
|
||||
from discord.http import Route
|
||||
from redbot.core import Config, commands, modlog
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.commands.converter import TimedeltaConverter
|
||||
|
||||
RequestType = Literal["discord_deleted_user", "owner", "user", "user_strict"]
|
||||
|
||||
class Shortmute(commands.Cog):
|
||||
"""Allows staff members to shortmute individuals for up to 30 minutes, using Discord's Timeouts feature."""
|
||||
"""Allows staff members to shortmute individuals for up to 30 minutes, using Discord's Timeouts feature.
|
||||
Maintained by SeaswimmerTheFsh.
|
||||
Original source code by sravan1946."""
|
||||
|
||||
def __init__(self, bot) -> None:
|
||||
def __init__(self, bot: Red) -> None:
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=25781647388294, force_registration=True)
|
||||
self.config = Config.get_conf(self, identifier=2354731, force_registration=True)
|
||||
self.config.register_guild(
|
||||
dm = True,
|
||||
confirm = True,
|
||||
logging_channels = [],
|
||||
immune_roles = [],
|
||||
blacklisted_users = []
|
||||
dm = True
|
||||
)
|
||||
|
||||
@app_commands.command()
|
||||
@app_commands.rename(target='member')
|
||||
async def shortmute(self, interaction: discord.Interaction, target: discord.Member, duration: int, reason: str, evidence_link: str = None, evidence_image: discord.Attachment = None, skip_confirmation: bool = None):
|
||||
"""Shortmute someone for up to 30m.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
target: discord.Member
|
||||
The member to shortmute
|
||||
duration: int
|
||||
The duration of the shortmute
|
||||
reason: str
|
||||
The reason for the shortmute
|
||||
evidence_link: str = None
|
||||
An image link to evidence for the shortmute, do not use with evidence_image
|
||||
evidence_image: discord.Attachment = None
|
||||
An image file used as evidence for the shortmute, do not use with evidence_link
|
||||
skip_confirmation: bool = None
|
||||
This allows you skip the confirmation prompt and immediately shortmute the user.
|
||||
def format_help_for_context(self, ctx: commands.Context) -> str:
|
||||
"""
|
||||
disable_dateutil()
|
||||
timedelta = parse(f'{duration} minutes', as_timedelta=True)
|
||||
if evidence_image and evidence_link:
|
||||
await interaction.response.send_message(content="You've provided both the `evidence_image` and the `evidence_link` arguments! Please only use one or the other.", ephemeral=True)
|
||||
return
|
||||
if evidence_link:
|
||||
evidence = evidence_link
|
||||
elif evidence_image:
|
||||
evidence = str(evidence_image)
|
||||
else:
|
||||
evidence = None
|
||||
if skip_confirmation is None:
|
||||
skip_confirmation = not bool(await self.config.guild(interaction.guild).confirm())
|
||||
passed_info = {
|
||||
"target": target,
|
||||
"timedelta": timedelta,
|
||||
"reason": reason,
|
||||
"interaction": interaction,
|
||||
"color": await self.bot.get_embed_color(interaction.guild),
|
||||
"evidence": evidence
|
||||
Thanks Sinbad!
|
||||
"""
|
||||
pre_processed = super().format_help_for_context(ctx)
|
||||
return f"{pre_processed}\n\nAuthors: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
|
||||
|
||||
async def red_delete_data_for_user(
|
||||
self, *, requester: RequestType, user_id: int
|
||||
) -> None:
|
||||
# TODO: Replace this with the proper end user data removal handling.
|
||||
super().red_delete_data_for_user(requester=requester, user_id=user_id)
|
||||
|
||||
async def is_user_timed_out(self, member: discord.Member) -> bool:
|
||||
r = Route(
|
||||
"GET",
|
||||
"/guilds/{guild_id}/members/{user_id}",
|
||||
guild_id=member.guild.id,
|
||||
user_id=member.id,
|
||||
)
|
||||
try:
|
||||
data = await self.bot.http.request(r)
|
||||
except discord.NotFound:
|
||||
return False
|
||||
return data["communication_disabled_until"] is not None
|
||||
|
||||
async def pre_load(self):
|
||||
with contextlib.suppress(RuntimeError):
|
||||
await modlog.register_casetype(
|
||||
name="timeout",
|
||||
default_setting=True,
|
||||
image=":mute:",
|
||||
case_str="Timeout",
|
||||
)
|
||||
await modlog.register_casetype(
|
||||
name="untimeout",
|
||||
default_setting=True,
|
||||
image=":sound:",
|
||||
case_str="Untimeout",
|
||||
)
|
||||
|
||||
async def timeout_user(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
member: discord.Member,
|
||||
time: Optional[datetime.timedelta],
|
||||
reason: Optional[str] = None,
|
||||
) -> None:
|
||||
r = Route(
|
||||
"PATCH",
|
||||
"/guilds/{guild_id}/members/{user_id}",
|
||||
guild_id=ctx.guild.id,
|
||||
user_id=member.id,
|
||||
)
|
||||
|
||||
payload = {
|
||||
"communication_disabled_until": str(
|
||||
datetime.datetime.now(datetime.timezone.utc) + time
|
||||
)
|
||||
if time
|
||||
else None
|
||||
}
|
||||
blacklisted_users_list = await self.config.guild(interaction.guild).blacklisted_users()
|
||||
for user_id in blacklisted_users_list:
|
||||
if user_id == interaction.user.id:
|
||||
await interaction.response.send_message(content="You are blacklisted from `/shortmute`!", ephemeral=True)
|
||||
return
|
||||
if target.bot is True:
|
||||
await interaction.response.send_message(content="You cannot shortmute bots!", ephemeral=True)
|
||||
return
|
||||
if interaction.user.guild_permissions.administrator is False:
|
||||
immune_roles_list = await self.config.guild(interaction.guild).immune_roles()
|
||||
for role_id in immune_roles_list:
|
||||
role = interaction.guild.get_role(role_id)
|
||||
if role in target.roles:
|
||||
await interaction.response.send_message(content="You're trying to shortmute someone who is immune from shortmuting.", ephemeral=True)
|
||||
return
|
||||
if target.guild_permissions.administrator is True:
|
||||
await interaction.response.send_message(content="You cannot shortmute people with the Administrator permission!", ephemeral=True)
|
||||
return
|
||||
if duration in (1, -1):
|
||||
readable_duration = f"{duration} minute"
|
||||
else:
|
||||
readable_duration = f"{duration} minutes"
|
||||
passed_info.update({
|
||||
"readable_duration": readable_duration
|
||||
})
|
||||
if duration > 30:
|
||||
await interaction.response.send_message(content=f"{readable_duration} is longer than the 30 minutes you are allowed to shortmute users for.", ephemeral=True)
|
||||
return
|
||||
if duration < 1:
|
||||
await interaction.response.send_message(content=f"Please shortmute the user for longer than {readable_duration}! The maximum duration is 30 minutes.", ephemeral=True)
|
||||
return
|
||||
if skip_confirmation is False:
|
||||
embed = discord.Embed(title="Are you sure?", description=f"**Moderator:** {interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=await self.bot.get_embed_color(interaction.guild))
|
||||
embed.set_footer(text="/shortmute")
|
||||
if evidence:
|
||||
embed.set_image(url=evidence)
|
||||
await interaction.response.send_message(embed=embed, view=self.ShortmuteButtons(timeout=180, passed_info=passed_info), ephemeral=True)
|
||||
elif skip_confirmation is True:
|
||||
edit_embed = discord.Embed(title="Shortmute confirmed!", description=f"**Moderator:** {interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=await self.bot.get_embed_color(interaction.guild))
|
||||
edit_embed.set_footer(text="/shortmute")
|
||||
if evidence:
|
||||
edit_embed.set_image(url=evidence)
|
||||
message = await interaction.response.send_message(embed=edit_embed, ephemeral=True)
|
||||
await target.timeout(timedelta, reason=f"User shortmuted for {readable_duration} by {interaction.user.name} ({interaction.user.id}) for: {reason}")
|
||||
shortmute_msg = await interaction.channel.send(content=f"{target.mention} was shortmuted for {readable_duration} by {interaction.user.mention} for: `{reason}`")
|
||||
if await self.config.guild(interaction.guild).dm() is True:
|
||||
dm_embed = discord.Embed(title=f"You've been shortmuted in {interaction.guild.name}!", description=f"**Moderator:** {interaction.user.mention}\n**Target:** {target.mention}\n**Message:** {shortmute_msg.jump_url}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=await self.bot.get_embed_color(interaction.guild))
|
||||
dm_embed.set_footer(text="/shortmute")
|
||||
if evidence:
|
||||
dm_embed.set_image(url=evidence)
|
||||
|
||||
await ctx.bot.http.request(r, json=payload, reason=reason)
|
||||
await modlog.create_case(
|
||||
bot=ctx.bot,
|
||||
guild=ctx.guild,
|
||||
created_at=datetime.datetime.now(datetime.timezone.utc),
|
||||
action_type="timeout" if time else "untimeout",
|
||||
user=member,
|
||||
moderator=ctx.author,
|
||||
reason=reason,
|
||||
until=(datetime.datetime.now(datetime.timezone.utc) + time)
|
||||
if time
|
||||
else None,
|
||||
channel=ctx.channel,
|
||||
)
|
||||
if await self.config.guild(member.guild).dm():
|
||||
with contextlib.suppress(discord.HTTPException):
|
||||
message = "You have been"
|
||||
message += (
|
||||
f" timed out for {humanize.naturaldelta(time)}"
|
||||
if time
|
||||
else " untimedout"
|
||||
)
|
||||
message += f" in {ctx.guild.name}"
|
||||
message += f" for reason: {reason}" if reason else ""
|
||||
await member.send(message)
|
||||
|
||||
async def timeout_role(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
role: discord.Role,
|
||||
time: datetime.timedelta,
|
||||
reason: Optional[str] = None,
|
||||
) -> List[discord.Member]:
|
||||
failed = []
|
||||
members = list(role.members)
|
||||
for member in members:
|
||||
try:
|
||||
await target.send(embed=dm_embed)
|
||||
await self.timeout_user(ctx, member, time, reason)
|
||||
except discord.HTTPException:
|
||||
await message.edit(content="Could not message the target, user most likely has Direct Messages disabled.")
|
||||
logging_channels_list = await self.config.guild(interaction.guild).logging_channels()
|
||||
if logging_channels_list:
|
||||
logging_embed = discord.Embed(title="User Shortmuted", description=f"**Moderator:** {interaction.user.mention} ({interaction.user.id})\n**Target:** {target.mention} ({target.id})\n**Message:** {shortmute_msg.jump_url}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`\n**Confirmation Skipped:** True", color=await self.bot.get_embed_color(interaction.guild))
|
||||
logging_embed.set_footer(text="/shortmute")
|
||||
if evidence:
|
||||
logging_embed.set_image(url=evidence)
|
||||
for channel_id in logging_channels_list:
|
||||
channel_obj = interaction.guild.get_channel(channel_id)
|
||||
await channel_obj.send(embed=logging_embed)
|
||||
failed.append(member)
|
||||
return failed
|
||||
|
||||
class ShortmuteButtons(ui.View):
|
||||
def __init__(self, timeout, passed_info: dict):
|
||||
super().__init__()
|
||||
self.timeout = timeout
|
||||
self.passed_info = passed_info
|
||||
self.config = Config.get_conf(None, cog_name='Shortmute', identifier=25781647388294)
|
||||
|
||||
@ui.button(label="Yes", style=discord.ButtonStyle.success)
|
||||
async def shortmute_button_yes(self, button: ui.Button, interaction: discord.Interaction): # pylint: disable=unused-argument
|
||||
disable_dateutil()
|
||||
target: discord.Member = self.passed_info['target']
|
||||
readable_duration = self.passed_info['readable_duration']
|
||||
reason: str = self.passed_info['reason']
|
||||
old_interaction: discord.Interaction = self.passed_info['interaction']
|
||||
color = self.passed_info['color']
|
||||
timedelta = self.passed_info['timedelta']
|
||||
evidence = self.passed_info['evidence']
|
||||
edit_embed = discord.Embed(title="Shortmute confirmed!", description=f"**Moderator:** {old_interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=color)
|
||||
if evidence:
|
||||
edit_embed.set_image(url=evidence)
|
||||
old_message = await old_interaction.edit_original_response(embed=edit_embed, view=None)
|
||||
await target.timeout(timedelta, reason=f"User shortmuted for {readable_duration} by {old_interaction.user.name} ({old_interaction.user.id}) for: {reason}")
|
||||
await old_interaction.channel.send(content=f"{target.mention} was shortmuted for {readable_duration} by {old_interaction.user.mention} for: `{reason}`")
|
||||
if await self.config.guild(old_interaction.guild).dm() is True:
|
||||
dm_embed = discord.Embed(title=f"You've been shortmuted in {old_interaction.guild.name}!", description=f"**Moderator:** {old_interaction.user.mention}\n**Target:** {target.mention}\n**Duration:** {readable_duration}\n**Reason:** `{reason}`", color=color)
|
||||
dm_embed.set_footer(text="/shortmute")
|
||||
if evidence:
|
||||
dm_embed.set_image(url=evidence)
|
||||
try:
|
||||
await target.send(embed=dm_embed)
|
||||
except discord.HTTPException:
|
||||
await old_message.edit(content="Could not message the target, user most likely has Direct Messages disabled.")
|
||||
logging_channels_list = await self.config.guild(old_interaction.guild).logging_channels()
|
||||
if logging_channels_list:
|
||||
logging_embed = discord.Embed(title="User Shortmuted", description=f"**Moderator:** {old_interaction.user.mention} ({old_interaction.user.id})\n**Target:** {target.mention} ({target.id})\n**Duration:** {readable_duration}\n**Reason:** `{reason}`\n**Confirmation Skipped:** False", color=color)
|
||||
logging_embed.set_footer(text="/shortmute")
|
||||
if evidence:
|
||||
logging_embed.set_image(url=evidence)
|
||||
for channel_id in logging_channels_list:
|
||||
channel_obj = old_interaction.guild.get_channel(channel_id)
|
||||
await channel_obj.send(embed=logging_embed)
|
||||
|
||||
@ui.button(label="No", style=discord.ButtonStyle.danger)
|
||||
async def shortmute_button_no(self, button: ui.Button, interaction: discord.Interaction): # pylint: disable=unused-argument
|
||||
message: discord.Message = await self.passed_info['interaction'].edit_original_response(content="Command cancelled.", view=None, embed=None)
|
||||
await message.delete(delay=3)
|
||||
|
||||
@commands.group(name='shortmuteset', autohelp=True)
|
||||
@commands.command(aliases=["tt"])
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config(self, ctx: commands.Context):
|
||||
"""Used to configure the /shortmute command."""
|
||||
@commands.cooldown(1, 1, commands.BucketType.user)
|
||||
@commands.mod_or_permissions(administrator=True)
|
||||
async def timeout(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
member_or_role: Union[discord.Member, discord.Role],
|
||||
time: TimedeltaConverter(
|
||||
minimum=datetime.timedelta(minutes=1),
|
||||
maximum=datetime.timedelta(days=28),
|
||||
default_unit="minutes",
|
||||
allowed_units=["minutes", "seconds", "hours", "days"],
|
||||
) = None,
|
||||
*,
|
||||
reason: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Timeout users.
|
||||
`<member_or_role>` is the username/rolename, ID or mention. If provided a role,
|
||||
everyone with that role will be timedout.
|
||||
`[time]` is the time to mute for. Time is any valid time length such as `45 minutes`
|
||||
or `3 days`. If nothing is provided the timeout will be 60 seconds default.
|
||||
`[reason]` is the reason for the timeout. Defaults to `None` if nothing is provided.
|
||||
Examples:
|
||||
`[p]timeout @member 5m talks too much`
|
||||
`[p]timeout @member 10m`
|
||||
"""
|
||||
if not time:
|
||||
time = datetime.timedelta(seconds=60)
|
||||
timestamp = datetime.datetime.now(datetime.timezone.utc) + time
|
||||
timestamp = int(datetime.datetime.timestamp(timestamp))
|
||||
if isinstance(member_or_role, discord.Member):
|
||||
check = await is_allowed_by_hierarchy(ctx.bot, ctx.author, member_or_role)
|
||||
if not check:
|
||||
return await ctx.send("You cannot timeout this user due to hierarchy.")
|
||||
if member_or_role.permissions_in(ctx.channel).administrator:
|
||||
return await ctx.send("You can't timeout an administrator.")
|
||||
await self.timeout_user(ctx, member_or_role, time, reason)
|
||||
return await ctx.send(
|
||||
f"{member_or_role.mention} has been timed out till <t:{timestamp}:f>."
|
||||
)
|
||||
if isinstance(member_or_role, discord.Role):
|
||||
await ctx.send(
|
||||
f"Timeing out {len(member_or_role.members)} members till <t:{timestamp}:f>."
|
||||
)
|
||||
failed = await self.timeout_role(ctx, member_or_role, time, reason)
|
||||
return await ctx.send(f"Failed to timeout {len(failed)} members.")
|
||||
|
||||
@shortmute_config.group(name='channel', invoke_without_command=True, aliases=['channels'])
|
||||
@commands.command(aliases=["utt"])
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_channel(self, ctx: commands.Context):
|
||||
"""Manages /shortmute logging."""
|
||||
current_list = await self.config.guild(ctx.guild).logging_channels()
|
||||
already_in_list = []
|
||||
for channel_id in current_list:
|
||||
channel_obj = ctx.guild.get_channel(channel_id)
|
||||
if channel_obj:
|
||||
already_in_list.append(channel_obj.mention)
|
||||
if already_in_list:
|
||||
await ctx.send("Channels already in the list:\n" + "\n".join(already_in_list))
|
||||
else:
|
||||
await ctx.send("No channels are currently in the logging list.")
|
||||
@commands.cooldown(1, 1, commands.BucketType.user)
|
||||
@commands.mod_or_permissions(administrator=True)
|
||||
async def untimeout(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
member_or_role: Union[discord.Member, discord.Role],
|
||||
*,
|
||||
reason: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Untimeout users.
|
||||
`<member_or_role>` is the username/rolename, ID or mention. If
|
||||
provided a role, everyone with that role will be untimed.
|
||||
`[reason]` is the reason for the untimeout. Defaults to `None`
|
||||
if nothing is provided.
|
||||
"""
|
||||
if isinstance(member_or_role, discord.Member):
|
||||
is_timedout = await self.is_user_timed_out(member_or_role)
|
||||
if not is_timedout:
|
||||
return await ctx.send("This user is not timed out.")
|
||||
await self.timeout_user(ctx, member_or_role, None, reason)
|
||||
return await ctx.send(f"Removed timeout from {member_or_role.mention}")
|
||||
if isinstance(member_or_role, discord.Role):
|
||||
await ctx.send(
|
||||
f"Removing timeout from {len(member_or_role.members)} members."
|
||||
)
|
||||
members = list(member_or_role.members)
|
||||
for member in members:
|
||||
if await self.is_user_timed_out(member):
|
||||
await self.timeout_user(ctx, member, None, reason)
|
||||
return await ctx.send(f"Removed timeout from {len(members)} members.")
|
||||
|
||||
@shortmute_config_channel.command(name='add')
|
||||
@commands.group()
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_channel_add(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||
"""Adds a channel to the logging channels list."""
|
||||
current_list: list = await self.config.guild(ctx.guild).logging_channels()
|
||||
if channel:
|
||||
if channel.id in current_list:
|
||||
await ctx.send("This channel is already in the logging channel list!")
|
||||
else:
|
||||
current_list.append(channel.id)
|
||||
await self.config.guild(ctx.guild).logging_channels.set(current_list)
|
||||
await ctx.send(f"{channel.mention} has been added to the logging channels list.")
|
||||
else:
|
||||
await ctx.send("Please provide a valid channel!")
|
||||
@commands.admin_or_permissions(manage_guild=True)
|
||||
async def timeoutset(self, ctx: commands.Context):
|
||||
"""Manage timeout settings."""
|
||||
|
||||
@shortmute_config_channel.command(name='remove')
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_channel_remove(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||
"""Removes a channel from the logging channels list."""
|
||||
current_list = await self.config.guild(ctx.guild).logging_channels()
|
||||
if channel.id in current_list:
|
||||
current_list.remove(channel.id)
|
||||
await self.config.guild(ctx.guild).logging_channels.set(current_list)
|
||||
await ctx.send(f"{channel.mention} has been removed from the logging channels list.")
|
||||
else:
|
||||
await ctx.send("Please provide a valid channel that exists in the logging channels list.")
|
||||
@timeoutset.command(name="dm")
|
||||
async def timeoutset_dm(self, ctx: commands.Context):
|
||||
"""Change whether to DM the user when they are timed out."""
|
||||
current = await self.config.guild(ctx.guild).dm()
|
||||
await self.config.guild(ctx.guild).dm.set(not current)
|
||||
w = "Will not" if current else "Will"
|
||||
await ctx.send(f"I {w} DM the user when they are timed out.")
|
||||
|
||||
@shortmute_config.group(name='role', invoke_without_command=True, aliases=['roles'])
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_role(self, ctx: commands.Context):
|
||||
"""Manages the immune roles list."""
|
||||
current_list = await self.config.guild(ctx.guild).immune_roles()
|
||||
already_in_list = []
|
||||
for role_id in current_list:
|
||||
role_obj = ctx.guild.get_role(role_id)
|
||||
if role_obj:
|
||||
already_in_list.append(role_obj.mention)
|
||||
if already_in_list:
|
||||
await ctx.send("Roles already in the immune roles list:\n" + "\n".join(already_in_list), allowed_mentions = discord.AllowedMentions(roles=False))
|
||||
else:
|
||||
await ctx.send("No roles are currently in the immune roles list.")
|
||||
|
||||
@shortmute_config_role.command(name='add')
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_role_add(self, ctx: commands.Context, role: discord.Role = None):
|
||||
"""Adds roles to the immune roles list."""
|
||||
current_list = await self.config.guild(ctx.guild).immune_roles()
|
||||
if role:
|
||||
if role.id in current_list:
|
||||
await ctx.send("This role is already in the immune roles list!")
|
||||
else:
|
||||
current_list.append(role.id)
|
||||
await self.config.guild(ctx.guild).immune_roles.set(current_list)
|
||||
await ctx.send(f"{role.mention} has been added to the immune roles list.", allowed_mentions = discord.AllowedMentions(roles=False))
|
||||
else:
|
||||
await ctx.send("Please provide a valid role.")
|
||||
|
||||
@shortmute_config_role.command(name='remove')
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_role_remove(self, ctx: commands.Context, role: discord.Role = None):
|
||||
"""Removes roles from the immune roles list."""
|
||||
current_list = await self.config.guild(ctx.guild).immune_roles()
|
||||
if role.id in current_list:
|
||||
current_list.remove(role.id)
|
||||
await self.config.guild(ctx.guild).immune_roles.set(current_list)
|
||||
await ctx.send(f"{role.mention} has been removed from the immune roles list.", allowed_mentions = discord.AllowedMentions(roles=False))
|
||||
else:
|
||||
await ctx.send("Please provide a valid role that exists in the immune roles list.")
|
||||
|
||||
@shortmute_config.group(name='blacklist', invoke_without_command=True)
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_blacklist(self, ctx: commands.Context):
|
||||
"""Manages the blacklist."""
|
||||
current_list = await self.config.guild(ctx.guild).blacklisted_users()
|
||||
already_in_list = []
|
||||
for user_id in current_list:
|
||||
user_obj = await Bot.fetch_user(user_id) # pylint: disable=no-value-for-parameter
|
||||
if user_obj:
|
||||
already_in_list.append(user_obj.mention)
|
||||
if already_in_list:
|
||||
await ctx.send("Users already blacklisted:\n" + "\n".join(already_in_list), allowed_mentions = discord.AllowedMentions(users=False))
|
||||
else:
|
||||
await ctx.send("No users are currently blacklisted.")
|
||||
|
||||
@shortmute_config_blacklist.command(name='add')
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_blacklist_add(self, ctx: commands.Context, user: discord.User = None):
|
||||
"""Adds users to the /shortmute blacklist."""
|
||||
current_list = await self.config.guild(ctx.guild).blacklisted_users()
|
||||
if user:
|
||||
if user.id in current_list:
|
||||
await ctx.send("That user is already in the blacklisted users list!")
|
||||
else:
|
||||
current_list.append(user.id)
|
||||
await self.config.guild(ctx.guild).blacklisted_users.set(current_list)
|
||||
await ctx.send(f"{user.mention} has been blacklisted from using `/shortmute`.", allowed_mentions = discord.AllowedMentions(users=False))
|
||||
else:
|
||||
await ctx.send("Please provide a valid user.")
|
||||
|
||||
@shortmute_config_blacklist.command(name='remove')
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_blacklist_remove(self, ctx: commands.Context, user: discord.User = None):
|
||||
"""Removes users from the /shortmute blacklist."""
|
||||
current_list = await self.config.guild(ctx.guild).blacklisted_users()
|
||||
if user.id in current_list:
|
||||
current_list.remove(user.id)
|
||||
await self.config.guild(ctx.guild).blacklisted_users.set(current_list)
|
||||
await ctx.send(f"{user.mention} has been removed from the `/shortmute` blacklist.", allowed_mentions = discord.AllowedMentions(users=False))
|
||||
else:
|
||||
await ctx.send("Please provide a valid user who is blacklisted from `/shortmute`.")
|
||||
|
||||
@shortmute_config.command(name='message')
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_message(self, ctx: commands.Context, enabled: bool = None):
|
||||
"""Manages if /shortmute Direct Messages its target.
|
||||
|
||||
Parameters
|
||||
------------
|
||||
enabled: bool (optional)
|
||||
This parameter, if set, will toggle this setting to either True or False."""
|
||||
old_value = await self.config.guild(ctx.guild).dm()
|
||||
if enabled is None:
|
||||
await ctx.send(content=f"Shortmute Direct Messages are currently {'enabled' if old_value else 'disabled'}!")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).dm.set(enabled)
|
||||
await ctx.send(content=f"Shortmute Direct Message setting changed!\nOld value: `{old_value}`\nNew value: `{enabled}`")
|
||||
|
||||
@shortmute_config.command(name='confirmation', aliases=['confirm'])
|
||||
@commands.guild_only()
|
||||
@commands.admin()
|
||||
async def shortmute_config_confirmation(self, ctx: commands.Context, enabled: bool = None):
|
||||
"""Manages if /shortmute has a confirmation prompt by default.
|
||||
|
||||
Parameters
|
||||
------------
|
||||
enabled: bool (optional)
|
||||
This parameter, if set, will toggle this setting to either True or False."""
|
||||
old_value = await self.config.guild(ctx.guild).confirm()
|
||||
if enabled is None:
|
||||
await ctx.send(content=f"Shortmute Confirmations are currently {'enabled' if old_value else 'disabled'} by default!")
|
||||
else:
|
||||
await self.config.guild(ctx.guild).confirm.set(enabled)
|
||||
await ctx.send(content=f"Shortmute Confirmation setting changed!\nOld value: `{old_value}`\nNew value: `{enabled}`")
|
||||
# https://github.com/phenom4n4n/phen-cogs/blob/8727d6ee74b40709c7eb9300713dc22b88a17915/roleutils/utils.py#L34
|
||||
async def is_allowed_by_hierarchy(
|
||||
bot: Red, user: discord.Member, member: discord.Member
|
||||
) -> bool:
|
||||
return (
|
||||
user.guild.owner_id == user.id
|
||||
or user.top_role > member.top_role
|
||||
or await bot.is_owner(user)
|
||||
)
|
|
@ -1,12 +1,5 @@
|
|||
from .suggestions import Suggestions, approve_context, deny_context
|
||||
from .suggestions import Suggestions
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Suggestions(bot))
|
||||
bot.tree.add_command(approve_context)
|
||||
bot.tree.add_command(deny_context)
|
||||
|
||||
async def teardown(bot):
|
||||
# We're removing the commands here to ensure they get unloaded properly when the cog is unloaded.
|
||||
bot.tree.remove_command("approve_context", type=discord.AppCommandType.message) # pylint: disable=undefined-variable
|
||||
bot.tree.remove_command("deny_context", type=discord.AppCommandType.message) # pylint: disable=undefined-variable
|
||||
def setup(bot):
|
||||
bot.add_cog(Suggestions(bot))
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
{
|
||||
"author" : ["cswimr, meelyman"],
|
||||
"install_msg" : "Thank you for installing Suggestions!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs\nYou can find the original source code from SauriCogs here: <https://github.com/elijabesu/SauriCogs>",
|
||||
"author" : ["SeaswimmerTheFsh, meelyman"],
|
||||
"install_msg" : "Thank you for installing Suggestions!\nYou can find the source code of this cog here: https://github.com/SeaswimmerTheFsh/GalaxyCogs\nYou can find the original source code from SauriCogs here: <https://github.com/elijabesu/SauriCogs>",
|
||||
"name" : "Suggestions",
|
||||
"short" : "Simple suggestions system.",
|
||||
"description" : "Per guild suggestions system.",
|
||||
"tags" : ["suggestions, suggestion, voting"],
|
||||
"end_user_data_statement": "This cog stores user names, discriminators, and IDs upon sending a suggestion. This data is used solely to display user information in suggestions.",
|
||||
"hidden": true,
|
||||
"disabled": false
|
||||
"end_user_data_statement": "This cog stores user names, discriminators, and IDs upon sending a suggestion. This data is used solely to display user information in suggestions."
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import re
|
||||
import typing
|
||||
import discord
|
||||
from redbot.core import Config, app_commands, checks, commands
|
||||
import datetime
|
||||
import typing
|
||||
|
||||
from redbot.core import Config, checks, commands
|
||||
from redbot.core.utils.chat_formatting import humanize_list
|
||||
|
||||
from redbot.core.bot import Red
|
||||
|
||||
|
||||
|
@ -44,9 +47,10 @@ class Suggestions(commands.Cog):
|
|||
def check_discrim(self, user: discord.User):
|
||||
if user.discriminator == "0":
|
||||
return user.name
|
||||
else:
|
||||
return f"{user.name}#{user.discriminator}"
|
||||
|
||||
async def red_delete_data_for_user(self, *, requester, user_id: discord.User.id):
|
||||
async def red_delete_data_for_user(self, *, requester, user_id):
|
||||
# per guild suggestions
|
||||
for guild in self.bot.guilds:
|
||||
for suggestion_id in range(1, await self.config.guild(guild).next_id()):
|
||||
|
@ -62,13 +66,11 @@ class Suggestions(commands.Cog):
|
|||
context = super().format_help_for_context(ctx)
|
||||
return f"{context}"
|
||||
|
||||
@commands.command(name='suggest')
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@checks.bot_has_permissions(add_reactions=True)
|
||||
async def suggest(self, ctx: commands.Context, *, suggestion: str):
|
||||
"""Suggest something."""
|
||||
if ctx.interaction is True:
|
||||
await ctx.defer()
|
||||
suggest_id = await self.config.guild(ctx.guild).suggest_id()
|
||||
if not suggest_id:
|
||||
if not await self.config.toggle():
|
||||
|
@ -82,9 +84,9 @@ class Suggestions(commands.Cog):
|
|||
"Uh oh, looks like the Admins haven't added the required channel."
|
||||
)
|
||||
embed = discord.Embed(color=await ctx.embed_colour(), description=suggestion)
|
||||
footer = [f"Suggested by {self.check_discrim(ctx.author)} • ({ctx.author.id})",
|
||||
ctx.author.display_avatar.url]
|
||||
author = [f"{ctx.author.display_name}", ctx.author.display_avatar.url]
|
||||
footer = [f"Suggested by {self.check_discrim(ctx.author)} ({ctx.author.id})",
|
||||
ctx.author.avatar_url]
|
||||
author = [f"{ctx.author.display_name}", ctx.author.avatar_url]
|
||||
embed.set_footer(
|
||||
text=footer[0],
|
||||
icon_url=footer[1]
|
||||
|
@ -135,7 +137,7 @@ class Suggestions(commands.Cog):
|
|||
reason: typing.Optional[str],
|
||||
):
|
||||
"""Approve a suggestion."""
|
||||
await self._finish_suggestion(ctx, suggestion_id, True, reason)
|
||||
await self._finish_suggestion(ctx, suggestion_id, True, reason, ctx.author)
|
||||
|
||||
@checks.admin()
|
||||
@commands.command()
|
||||
|
@ -149,7 +151,7 @@ class Suggestions(commands.Cog):
|
|||
reason: typing.Optional[str],
|
||||
):
|
||||
"""Deny a suggestion. Reason is optional."""
|
||||
await self._finish_suggestion(ctx, suggestion_id, False, reason)
|
||||
await self._finish_suggestion(ctx, suggestion_id, False, reason, ctx.author)
|
||||
|
||||
@checks.admin()
|
||||
@commands.command()
|
||||
|
@ -187,8 +189,8 @@ class Suggestions(commands.Cog):
|
|||
content = old_msg.content
|
||||
approved = "Approved" if approve else "Denied"
|
||||
embed.title = f"Suggestion {approved} (#{suggestion_id})"
|
||||
footer = [f"{approved} by {self.check_discrim(author)} • ({author.id})",
|
||||
author.display_avatar.replace(format="png", size=512)]
|
||||
footer = [f"{approved} by {self.check_discrim(author)} ({author.id}",
|
||||
author.avatar_url_as(format="png", size=512)]
|
||||
embed.set_footer(
|
||||
text=footer[0],
|
||||
icon_url=footer[1]
|
||||
|
@ -370,7 +372,7 @@ class Suggestions(commands.Cog):
|
|||
embed = discord.Embed(
|
||||
colour=await ctx.embed_colour()
|
||||
)
|
||||
embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon.url)
|
||||
embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon_url)
|
||||
embed.title = "**Suggestion settings:**"
|
||||
|
||||
embed.add_field(name="Suggestions channel:", value=suggest_channel)
|
||||
|
@ -393,7 +395,7 @@ class Suggestions(commands.Cog):
|
|||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_reaction_add(self, reaction: discord.Reaction, user: discord.Message):
|
||||
async def on_reaction_add(self, reaction, user):
|
||||
message = reaction.message
|
||||
if user.id == self.bot.user.id:
|
||||
return
|
||||
|
@ -408,7 +410,7 @@ class Suggestions(commands.Cog):
|
|||
):
|
||||
await message_reaction.remove(user)
|
||||
|
||||
async def _get_results(self, ctx: commands.Context, message: discord.Message):
|
||||
async def _get_results(self, ctx, message):
|
||||
up_emoji, down_emoji = await self._get_emojis(ctx)
|
||||
up_count = 0
|
||||
down_count = 0
|
||||
|
@ -421,7 +423,7 @@ class Suggestions(commands.Cog):
|
|||
|
||||
return f"{up_count}x {up_emoji}\n{down_count}x {down_emoji}"
|
||||
|
||||
async def _get_emojis(self, ctx: typing.Union[commands.Context, discord.Interaction]):
|
||||
async def _get_emojis(self, ctx):
|
||||
up_emoji = self.bot.get_emoji(await self.config.guild(ctx.guild).up_emoji())
|
||||
if not up_emoji:
|
||||
up_emoji = "✅"
|
||||
|
@ -430,9 +432,8 @@ class Suggestions(commands.Cog):
|
|||
down_emoji = "âŽ"
|
||||
return up_emoji, down_emoji
|
||||
|
||||
async def _finish_suggestion(self, ctx: commands.Context, suggestion_id: int, approve: bool, reason: str):
|
||||
async def _finish_suggestion(self, ctx, suggestion_id, approve, reason, author):
|
||||
server = ctx.guild.id
|
||||
author = ctx.author
|
||||
old_channel = ctx.guild.get_channel(
|
||||
await self.config.guild(ctx.guild).suggest_id()
|
||||
)
|
||||
|
@ -462,8 +463,8 @@ class Suggestions(commands.Cog):
|
|||
approved = "Approved" if approve else "Denied"
|
||||
|
||||
embed.title = f"Suggestion {approved} (#{suggestion_id})"
|
||||
footer = [f"{approved} by {self.check_discrim(author)} • ({author.id})",
|
||||
author.display_avatar.replace(format="png", size=512)]
|
||||
footer = [f"{approved} by {self.check_discrim(author)} ({author.id}",
|
||||
author.avatar_url_as(format="png", size=512)]
|
||||
embed.set_footer(
|
||||
text=footer[0],
|
||||
icon_url=footer[1]
|
||||
|
@ -509,162 +510,3 @@ class Suggestions(commands.Cog):
|
|||
True
|
||||
)
|
||||
await ctx.tick()
|
||||
|
||||
async def _interaction_get_results(self, interaction: discord.Interaction, message: discord.Message):
|
||||
up_emoji, down_emoji = await self._get_emojis(interaction)
|
||||
up_count = 0
|
||||
down_count = 0
|
||||
|
||||
for reaction in message.reactions:
|
||||
if reaction.emoji == up_emoji:
|
||||
up_count = reaction.count - 1 # minus the bot
|
||||
if reaction.emoji == down_emoji:
|
||||
down_count = reaction.count - 1 # minus the bot
|
||||
|
||||
return f"{up_count}x {up_emoji}\n{down_count}x {down_emoji}"
|
||||
|
||||
async def _interaction_finish_suggestion(self, interaction: discord.Interaction, message: discord.Message, approve: bool, reason: str = None):
|
||||
embed = message.embeds
|
||||
title = embed[0].title
|
||||
if not title.startswith("Suggestion #"):
|
||||
await interaction.response.send_message(content="This message is not a suggestion!", ephemeral=True)
|
||||
return
|
||||
numbers = re.findall(r'\d+', title)
|
||||
suggestion_id = ''.join(numbers)
|
||||
author = interaction.user
|
||||
server = interaction.guild.id
|
||||
old_channel = interaction.guild.get_channel(
|
||||
await self.config.guild(interaction.guild).suggest_id()
|
||||
)
|
||||
if approve:
|
||||
channel = interaction.guild.get_channel(
|
||||
await self.config.guild(interaction.guild).approve_id()
|
||||
)
|
||||
else:
|
||||
channel = interaction.guild.get_channel(
|
||||
await self.config.guild(interaction.guild).denied_id()
|
||||
)
|
||||
msg_id = await self.config.custom("SUGGESTION", server, suggestion_id).msg_id()
|
||||
if (
|
||||
msg_id != 0
|
||||
and await self.config.custom("SUGGESTION", server, suggestion_id).finished()
|
||||
):
|
||||
return await interaction.response.send_message(content="This suggestion has been finished already.", ephemeral=True)
|
||||
try:
|
||||
old_msg = await old_channel.fetch_message(msg_id)
|
||||
except discord.NotFound:
|
||||
return await interaction.response.send_message(content="Uh oh, message with this ID doesn't exist.", ephemeral=True)
|
||||
if not old_msg:
|
||||
return await interaction.response.send_message(content="Uh oh, message with this ID doesn't exist.", ephemeral=True)
|
||||
embed = old_msg.embeds[0]
|
||||
content = old_msg.content
|
||||
|
||||
approved = "Approved" if approve else "Denied"
|
||||
|
||||
embed.title = f"Suggestion {approved} (#{suggestion_id})"
|
||||
footer = [f"{approved} by {self.check_discrim(author)} • ({author.id})",
|
||||
author.display_avatar.replace(format="png", size=512)]
|
||||
embed.set_footer(
|
||||
text=footer[0],
|
||||
icon_url=footer[1]
|
||||
)
|
||||
embed.add_field(
|
||||
name="Results", value=await self._interaction_get_results(interaction, old_msg), inline=False
|
||||
)
|
||||
if reason:
|
||||
embed.add_field(name="Reason", value=reason, inline=False)
|
||||
await self.config.custom("SUGGESTION", server, suggestion_id).reason.set(
|
||||
True
|
||||
)
|
||||
await self.config.custom("SUGGESTION", server, suggestion_id).rtext.set(
|
||||
reason
|
||||
)
|
||||
|
||||
if channel:
|
||||
if not await self.config.guild(interaction.guild).same():
|
||||
if await self.config.guild(interaction.guild).delete_suggestion():
|
||||
await old_msg.delete()
|
||||
nmsg = await channel.send(content=content, embed=embed)
|
||||
await self.config.custom(
|
||||
"SUGGESTION", server, suggestion_id
|
||||
).msg_id.set(nmsg.id)
|
||||
else:
|
||||
await old_msg.edit(content=content, embed=embed)
|
||||
else:
|
||||
if not await self.config.guild(interaction.guild).same():
|
||||
if await self.config.guild(interaction.guild).delete_suggestion():
|
||||
await old_msg.delete()
|
||||
await self.config.custom(
|
||||
"SUGGESTION", server, suggestion_id
|
||||
).msg_id.set(1)
|
||||
else:
|
||||
await old_msg.edit(content=content, embed=embed)
|
||||
await self.config.custom("SUGGESTION", server, suggestion_id).finished.set(True)
|
||||
if approve:
|
||||
await self.config.custom("SUGGESTION", server, suggestion_id).approved.set(
|
||||
True
|
||||
)
|
||||
else:
|
||||
await self.config.custom("SUGGESTION", server, suggestion_id).denied.set(
|
||||
True
|
||||
)
|
||||
return nmsg
|
||||
|
||||
# pylint: disable=protected-access
|
||||
class SuggestionApproveModal(discord.ui.Modal, title="Approving suggestion..."):
|
||||
def __init__(self, message):
|
||||
super().__init__()
|
||||
self.message: discord.Message = message
|
||||
|
||||
reason = discord.ui.TextInput(
|
||||
label="Approval Reason",
|
||||
placeholder="Why are you approving this suggestion?",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=False,
|
||||
max_length=1024
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
cog = interaction.client.get_cog('Suggestions')
|
||||
if self.reason.value != "":
|
||||
nmsg = await Suggestions._interaction_finish_suggestion(cog, interaction, self.message, True, self.reason.value)
|
||||
else:
|
||||
nmsg = await Suggestions._interaction_finish_suggestion(cog, interaction, self.message, True, None)
|
||||
msg = await interaction.response.send_message(content=f"Suggestion approved!\nJump Link: {nmsg.jump_url}", ephemeral=True)
|
||||
await msg.delete(10)
|
||||
|
||||
class SuggestionDenyModal(discord.ui.Modal, title="Denying suggestion..."):
|
||||
def __init__(self, message):
|
||||
super().__init__()
|
||||
self.message: discord.Message = message
|
||||
|
||||
reason = discord.ui.TextInput(
|
||||
label="Denial Reason",
|
||||
placeholder="Why are you denying this suggestion?",
|
||||
style=discord.TextStyle.paragraph,
|
||||
required=False,
|
||||
max_length=1024
|
||||
)
|
||||
|
||||
async def on_submit(self, interaction: discord.Interaction):
|
||||
cog = interaction.client.get_cog('Suggestions')
|
||||
if self.reason.value != "":
|
||||
nmsg = await Suggestions._interaction_finish_suggestion(cog, interaction, self.message, False, self.reason.value)
|
||||
else:
|
||||
nmsg = await Suggestions._interaction_finish_suggestion(cog, interaction, self.message, False, None)
|
||||
msg: discord.Message = await interaction.response.send_message(content=f"Suggestion denied!\nJump Link: {nmsg.jump_url}", ephemeral=True)
|
||||
await msg.delete(10)
|
||||
|
||||
@app_commands.context_menu(name="Approve Suggestion")
|
||||
async def approve_context(interaction: discord.Interaction, message: discord.Message):
|
||||
if message.author.id == interaction.client.user.id and len(message.embeds) == 1 and message.embeds[0].title.startswith('Suggestion #'):
|
||||
await interaction.response.send_modal(SuggestionApproveModal(message))
|
||||
else:
|
||||
await interaction.response.send_message(content="This is not a suggestion!", ephemeral=True)
|
||||
|
||||
@app_commands.context_menu(name="Deny Suggestion")
|
||||
async def deny_context(interaction: discord.Interaction, message: discord.Message):
|
||||
if message.author.id == interaction.client.user.id and len(message.embeds) == 1 and message.embeds[0].title.startswith('Suggestion #'):
|
||||
await interaction.response.send_modal(SuggestionDenyModal(message))
|
||||
else:
|
||||
await interaction.response.send_message(content="This is not a suggestion!", ephemeral=True)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from .sugoncredit import SugonCredit
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(SugonCredit(bot))
|
||||
def setup(bot):
|
||||
bot.add_cog(SugonCredit(bot))
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing SugonCredit!\n**Please load the Economy cog before loading this cog.**\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs.",
|
||||
"author" : ["SeaswimmerTheFsh"],
|
||||
"install_msg" : "Thank you for installing SugonCredit!\n**Please load the Bank cog before loading this cog.**\nYou can find the source code of this cog here: https://github.com/SeaswimmerTheFsh/GalaxyCogs.",
|
||||
"name" : "SugonCredit",
|
||||
"short" : "Simple points system.",
|
||||
"description" : "Implements a way for moderators to give out social-credit like points, dubbed 'sugoncredits' by the community.",
|
||||
"tags" : ["economy"],
|
||||
"end_user_data_statement": "This cog stores no end user data.",
|
||||
"hidden": true,
|
||||
"disabled": false
|
||||
"tags" : ["bank"],
|
||||
"end_user_data_statement": "This cog stores no end user data."
|
||||
}
|
||||
|
|
|
@ -1,37 +1,39 @@
|
|||
import discord
|
||||
from redbot.core import commands, bank, data_manager
|
||||
from redbot.core import commands, bank, checks, data_manager
|
||||
|
||||
class SugonCredit(commands.Cog):
|
||||
"""Implements a way for moderators to give out social-credit like points, dubbed 'sugoncredits' by the community."""
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.hybrid_group(autohelp=True, aliases=["sugoncredit"])
|
||||
@commands.group(autohelp=True, aliases=["sugoncredit"])
|
||||
@commands.guild_only()
|
||||
async def credit(self, ctx: commands.Context):
|
||||
async def credit(self, ctx):
|
||||
"""Simple points system."""
|
||||
|
||||
@credit.command()
|
||||
async def balance(self, ctx: commands.Context, user: discord.Member = None):
|
||||
@commands.guild_only()
|
||||
async def balance(self, ctx, user: discord.Member = None):
|
||||
"""Checks an account's balance."""
|
||||
bank_name = await bank.get_bank_name(ctx.guild)
|
||||
currency_name = await bank.get_currency_name(ctx.guild)
|
||||
if user is None:
|
||||
if user == None:
|
||||
bal = await bank.get_balance(ctx.author)
|
||||
target = ctx.author
|
||||
else:
|
||||
bal = await bank.get_balance(user)
|
||||
target = user
|
||||
output_bal = (f'{bal:,}')
|
||||
if bal in (1, -1):
|
||||
if bal == 1 or bal == -1:
|
||||
embed=discord.Embed(title=f"{bank_name} - Balance", color=await self.bot.get_embed_color(None), description=f"{target.mention} has {output_bal} {currency_name}.")
|
||||
else:
|
||||
embed=discord.Embed(title=f"{bank_name} - Balance", color=await self.bot.get_embed_color(None), description=f"{target.mention} has {output_bal} {currency_name}s.")
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@credit.command()
|
||||
@commands.guild_only()
|
||||
@commands.mod()
|
||||
async def add(self, ctx: commands.Context, target: discord.Member, amount: int):
|
||||
async def add(self, ctx, target: discord.Member, amount: int):
|
||||
"""Adds credits to an account."""
|
||||
try:
|
||||
val = int(amount)
|
||||
|
@ -49,13 +51,15 @@ class SugonCredit(commands.Cog):
|
|||
output_max_bal = (f'{max_bal:,}')
|
||||
if new_bal > max_bal:
|
||||
await ctx.send(content=f"You are attempting to set {target.mention}'s balance to above {output_max_bal}. Please try again!")
|
||||
return
|
||||
elif new_bal < 0:
|
||||
await ctx.send(content=f"You are attempting to set {target.mention}'s balance to below 0. Please try again!")
|
||||
return
|
||||
elif ctx.guild.id == 204965774618656769:
|
||||
logging_channel: discord.TextChannel = self.bot.get_channel(1082495815878189076)
|
||||
logging_channel = self.bot.get_channel(1082495815878189076)
|
||||
await bank.deposit_credits(target, amount=amount)
|
||||
await ctx.send(content=f"{target.mention} now has {output_amount} more SugonCredit, with a total of {output_new_bal}!")
|
||||
if amount in (1, -1):
|
||||
if amount == 1 or amount == -1:
|
||||
await target.send(content=f"You gained {output_amount} SugonCredit! Good work community member! You now have {output_new_bal} SugonCredits.", file=image)
|
||||
else:
|
||||
await target.send(content=f"You gained {output_amount} SugonCredits! Good work community member! You now have {output_new_bal} SugonCredits.", file=image)
|
||||
|
@ -67,8 +71,9 @@ class SugonCredit(commands.Cog):
|
|||
await ctx.send(embed=embed)
|
||||
|
||||
@credit.command()
|
||||
@commands.guild_only()
|
||||
@commands.mod()
|
||||
async def remove(self, ctx: commands.Context, target: discord.Member, amount: int):
|
||||
async def remove(self, ctx, target: discord.Member, amount: int):
|
||||
"""Removes credits from an account."""
|
||||
try:
|
||||
val = int(amount)
|
||||
|
@ -84,11 +89,12 @@ class SugonCredit(commands.Cog):
|
|||
output_new_bal = (f'{new_bal:,}')
|
||||
if new_bal < 0:
|
||||
await ctx.send(content=f"You are attempting to set {target.mention}'s balance to below 0. Please try again!")
|
||||
return
|
||||
elif ctx.guild.id == 204965774618656769:
|
||||
await bank.withdraw_credits(target, amount=amount)
|
||||
logging_channel: discord.TextChannel = self.bot.get_channel(1082495815878189076)
|
||||
logging_channel = self.bot.get_channel(1082495815878189076)
|
||||
await ctx.send(content=f"{target.mention} now has {output_amount} less SugonCredit, with a total of {output_new_bal}!\nIf this is a punishment, do better Galaxy Player! Re-education mods will be sent to your DM's if your SugonCredit drops to a substantially low amount!")
|
||||
if amount in (1, -1):
|
||||
if amount == 1 or amount == -1:
|
||||
await target.send(content=f"__MESSAGE FROM THE MINISTRY OF THE MEGA BASE__\n\n(我们的) {output_amount} SugonCredit has been taken from your account. Citizen, do not continue to preform bad actions! Glory to the Galaxy Communist Party!", file=image)
|
||||
else:
|
||||
await target.send(content=f"__MESSAGE FROM THE MINISTRY OF THE MEGA BASE__\n\n(我们的) {output_amount} SugonCredits have been taken from your account. Citizen, do not continue to preform bad actions! Glory to the Galaxy Communist Party!", file=image)
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
from .worldzero import WorldZero
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(WorldZero(bot))
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing World Zero!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"name" : "World Zero",
|
||||
"short" : "This cog is meant to provide random functions for my crippling World Zero addiction!",
|
||||
"description" : "This cog is meant to provide random functions for my crippling World Zero addiction!",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"hidden": true,
|
||||
"disabled": false
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
import discord
|
||||
from redbot.core import commands
|
||||
|
||||
|
||||
class WorldZero(commands.Cog):
|
||||
"""This cog is meant to provide random functions for my crippling World Zero addiction!
|
||||
Developed by cswimr."""
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.group(name="worldzero", invoke_without_command=True, aliases=['wz'])
|
||||
async def worldzero(self, ctx: commands.Context):
|
||||
"""Tells the user that this command doesn't do anything currently."""
|
||||
await ctx.send("This command doesn't do anything currently, have you tried a subcommand?\nCurrent subcommands:\n- `-worldzero upgrade` - Checks what the Attack Power/Health of an item will be after upgrading it.\n - See `-help worldzero upgrade` for more information.")
|
||||
|
||||
@worldzero.command(name="upgrade")
|
||||
async def worldzero_upgrade(self, ctx: commands.Context, power_amount: str, upgrade_amount: str):
|
||||
"""Checks what the Attack Power/Health of an item will be after upgrading it.
|
||||
|
||||
|
||||
**Arguments**
|
||||
- The `power_amount` argument is the Attack Power/Health of the item you're looking to upgrade.
|
||||
- The `upgrade_amount` argument is the number of times your item can be upgraded."""
|
||||
try:
|
||||
stat_int = int(f"{power_amount}".replace(",", ""))
|
||||
upgrade_int = int(f"{upgrade_amount}".replace(",", ""))
|
||||
except ValueError:
|
||||
await ctx.send(content="Please input a number!")
|
||||
return
|
||||
math = round(stat_int + ((stat_int/15)*upgrade_int))
|
||||
output_from = f'{stat_int:,}'
|
||||
output_to = f'{math:,}'
|
||||
embed = discord.Embed(color=await self.bot.get_embed_color(None))
|
||||
embed.add_field(name="Default Power", value=f"{output_from}", inline=False)
|
||||
embed.add_field(name="Upgraded Power", value=f"{output_to}", inline=False)
|
||||
await ctx.send(embed=embed)
|
|
@ -1,5 +0,0 @@
|
|||
from .youtubedownloader import YouTubeDownloader
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(YouTubeDownloader(bot))
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"author" : ["cswimr"],
|
||||
"install_msg" : "Thank you for installing YouTubeDownloader!\nYou can find the source code of this cog here: https://coastalcommits.com/cswimr/GalaxyCogs",
|
||||
"name" : "YouTubeDownloader",
|
||||
"short" : "Custom cog intended for use on the Galaxy discord server.",
|
||||
"description" : "Custom cog intended for use on the Galaxy discord server.",
|
||||
"end_user_data_statement" : "This cog does not store any End User Data.",
|
||||
"requirements": ["pytube"],
|
||||
"hidden": false,
|
||||
"disabled": false
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
import asyncio
|
||||
import os
|
||||
import re
|
||||
import discord
|
||||
from redbot.core import Config, checks, commands, data_manager
|
||||
from pytube import YouTube, exceptions
|
||||
|
||||
|
||||
class YouTubeDownloader(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=475728338)
|
||||
self.config.register_global(
|
||||
save_directory = str(data_manager.cog_data_path()) + f"{os.sep}YouTubeDownloader",
|
||||
blacklisted_users = []
|
||||
)
|
||||
|
||||
class UserBlacklisted(Exception):
|
||||
def __init__(self, message="The user is blacklisted from using this command."):
|
||||
super().__init__(message)
|
||||
|
||||
async def blacklist_checker(self, user_id):
|
||||
blacklisted_users = await self.config.blacklisted_users()
|
||||
for blacklisted_user_id in blacklisted_users:
|
||||
if blacklisted_user_id == user_id:
|
||||
raise self.UserBlacklisted
|
||||
|
||||
@commands.command()
|
||||
@checks.is_owner()
|
||||
async def change_data_path(self, ctx: commands.Context, *, data_path: str = None):
|
||||
"""This command changes the data path the `[p]download` command outputs to."""
|
||||
old_path = await self.config.save_directory()
|
||||
if not data_path:
|
||||
await ctx.send(f"The current data path is `{old_path}`.")
|
||||
return
|
||||
if os.path.isdir(data_path):
|
||||
await self.config.save_directory.set(data_path)
|
||||
embed=discord.Embed(color=await self.bot.get_embed_color(None), description=f"The save directory has been set to `{data_path}`.\n It was previously set to `{old_path}`.")
|
||||
await ctx.send(embed=embed)
|
||||
elif os.path.isfile(data_path):
|
||||
await ctx.send("The path you've provided leads to a file, not a directory!")
|
||||
elif os.path.exists(data_path) is False:
|
||||
await ctx.send("The path you've provided doesn't exist!")
|
||||
|
||||
@commands.command(aliases=["dl"])
|
||||
async def download(self, ctx: commands.Context, url: str, audio_only: bool = True, delete: bool = True, *, subfolder: str = None):
|
||||
"""This command downloads a YouTube Video as an `m4a` (or `mp4`) and uploads the file to discord.
|
||||
|
||||
If you're considered a bot owner, you will be able to save downloaded files to the data path set in the `[p]change_data_path` command.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- The `url` argument is just the url of the YouTube Video you're downloading.
|
||||
|
||||
- The `audio_only` argument determines if the video will be an `m4a` file or an `mp4` file.
|
||||
|
||||
- The `delete` argument will automatically delete the file after uploading it to Discord. If set to False, it will only save the file if you are a bot owner.
|
||||
|
||||
- The `subfolder` argument only does anything if `delete` is set to False, but it allows you to save to a subfolder in the data path you've set previously without having to change said data path manually."""
|
||||
try:
|
||||
await self.blacklist_checker(ctx.author.id)
|
||||
except self.UserBlacklisted:
|
||||
await ctx.send("You are blacklisted from running this command!")
|
||||
return
|
||||
def youtube_download(url: str, path: str):
|
||||
"""This function does the actual downloading of the YouTube Video."""
|
||||
yt = YouTube(url=url)
|
||||
translation_table = dict.fromkeys(map(ord, r'<>:"/\|?*'), None)
|
||||
filename = f"{yt.title.translate(translation_table)} ({yt.video_id})"
|
||||
if audio_only:
|
||||
stream = yt.streams.filter(only_audio=True, mime_type='audio/mp4')
|
||||
stream = stream.order_by('abr')
|
||||
filename_format = filename + ".m4a"
|
||||
else:
|
||||
stream = yt.streams.filter(progressive=True, mime_type='video/mp4')
|
||||
stream = stream.order_by('resolution')
|
||||
filename_format = filename + ".mp4"
|
||||
previously_existed = bool(os.path.isfile(path + f"{os.sep}{filename_format}"))
|
||||
return stream.desc().first().download(filename=filename, output_path=path), previously_existed, filename_format
|
||||
data_path = await self.config.save_directory()
|
||||
if subfolder and await self.bot.is_owner(ctx.author):
|
||||
data_path = os.path.join(data_path, subfolder)
|
||||
illegal_chars = r'<>:"/\|?*'
|
||||
if any(char in illegal_chars for char in subfolder):
|
||||
pattern = "[" + re.escape(illegal_chars) + "]"
|
||||
modified_subfolder = re.sub(pattern, r'__**\g<0>**__', subfolder)
|
||||
await ctx.send(f"Your subfolder contains illegal characters: `{modified_subfolder}`")
|
||||
elif os.path.isfile(data_path):
|
||||
await ctx.send("Your 'subfolder' is a file, not a directory!")
|
||||
elif os.path.exists(data_path) is False:
|
||||
message = await ctx.send("Your subfolder does not exist yet, would you like to continue? It will be automatically created.")
|
||||
def check(message):
|
||||
return message.author == ctx.author and message.content.lower() in ['yes', 'ye', 'y', '1']
|
||||
try:
|
||||
await self.bot.wait_for('message', check=check, timeout=60)
|
||||
except asyncio.TimeoutError:
|
||||
await message.edit("You took too long to respond.")
|
||||
else:
|
||||
await message.edit("Confirmed!")
|
||||
try:
|
||||
os.makedirs(data_path)
|
||||
except OSError as e:
|
||||
await message.edit(f"Encountered an error attempting to create the subfolder!\n`{e}`")
|
||||
msg = message.edit
|
||||
else:
|
||||
msg = ctx.send
|
||||
message = await msg("YouTube Downloader started!")
|
||||
try:
|
||||
output = youtube_download(url, data_path)
|
||||
except exceptions.RegexMatchError:
|
||||
await message.edit(content="Please provide a link to YouTube and not another site.")
|
||||
return
|
||||
while not os.path.isfile(output[0]):
|
||||
await asyncio.sleep(0.2)
|
||||
if os.path.isfile(output[0]):
|
||||
with open(output[0], 'rb') as file:
|
||||
try:
|
||||
complete_message = await ctx.send(content="YouTube Downloader completed!\nDownloaded file:", file=discord.File(file, output[2]))
|
||||
except ValueError:
|
||||
complete_message = await ctx.send(content="YouTube Downloader completed, but the file was too large to upload.")
|
||||
file.close()
|
||||
if delete is True or await self.bot.is_owner(ctx.author) is False:
|
||||
if output[1] is False:
|
||||
os.remove(output[0])
|
||||
await complete_message.edit(content="YouTube Downloader completed!\nFile has been deleted.\nDownloaded file:")
|
||||
if output[1] is True:
|
||||
await complete_message.edit(content="YouTube Downloader completed!\nFile has not been deleted, as it was previously downloaded and saved.\nDownloaded file:")
|
||||
|
||||
@commands.group(name="dl-blacklist", invoke_without_command=True)
|
||||
async def blacklist(self, ctx: commands.Context, user: discord.User):
|
||||
"""Group command for managing the blacklist."""
|
||||
for user_id in await self.config.blacklisted_users():
|
||||
if user_id == user.id:
|
||||
await ctx.send(f"{user.mention} is in the blacklist.", allowed_mentions = discord.AllowedMentions(users=False))
|
||||
return
|
||||
await ctx.send(f"{user.mention} is not in the blacklist.", allowed_mentions = discord.AllowedMentions(users=False))
|
||||
|
||||
@blacklist.command(name='add')
|
||||
@checks.is_owner()
|
||||
async def blacklist_add(self, ctx: commands.Context, user: discord.User):
|
||||
blacklisted_users: list = await self.config.blacklisted_users()
|
||||
for user_id in blacklisted_users:
|
||||
if user_id == user.id:
|
||||
await ctx.send(f"{user.mention} is already in the blacklist.")
|
||||
return
|
||||
blacklisted_users.append(user.id)
|
||||
await self.config.blacklisted_users.set(blacklisted_users)
|
||||
await ctx.send(f"{user.mention} has been added to the blacklist.")
|
||||
|
||||
@blacklist.command(name='remove')
|
||||
@checks.is_owner()
|
||||
async def blacklist_remove(self, ctx: commands.Context, user: discord.User):
|
||||
blacklisted_users: list = await self.config.blacklisted_users()
|
||||
for user_id in blacklisted_users:
|
||||
if user_id == user.id:
|
||||
blacklisted_users.remove(user_id)
|
||||
await self.config.blacklisted_users.set(blacklisted_users)
|
||||
await ctx.send(f"{user.mention} has been removed from the blacklist.")
|
||||
return
|
||||
await ctx.send(f"{user.mention} is not in the blacklist.")
|
Loading…
Reference in a new issue