Compare commits

..

347 commits

Author SHA1 Message Date
73d3449894
fix(aurora): fixed two pydantic validation errors
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 1m3s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 1m11s
Signed-off-by: cswimr <seaswimmerthefsh@gmail.com>
2024-09-25 22:57:57 -04:00
24005800fa
fix(aurora): actually fixed the thing i just tried to fix
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 1m2s
Signed-off-by: cswimr <seaswimmerthefsh@gmail.com>
2024-09-25 22:55:33 -04:00
d92a0d27da
fix(aurora): fix /case breaking if a case is resolved but the resolving user doesn't exist anymore
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 3s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 55s
Signed-off-by: cswimr <seaswimmerthefsh@gmail.com>
2024-09-25 22:51:45 -04:00
97406e7bac
feat(aurora): updated phx-class-registry to ^5.0.0 and fixed some typos
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 54s
2024-09-20 14:49:28 -04:00
405729f37d
force downgrade of phx-class-registry
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 35s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m6s
Signed-off-by: seaswimmer <seaswimmerthefsh@gmail.com>
2024-09-20 10:38:49 -04:00
1c941462df
feat(aurora): log how many errors occured in the handle_expiry task
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 39s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 48s
2024-09-08 12:47:01 -04:00
b91946abeb
fix(aurora): catch exceptions thrown inside of the expiry handler instead of just stopping the task
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 49s
2024-09-08 12:40:38 -04:00
d4fe97f247
fix(aurora): fixed retrieving a User object instead of a Member object
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 49s
2024-09-08 12:33:16 -04:00
9e21879a49
fix(aurora): fixed note's handler using cls.string where it should have been using cls.verb
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 50s
2024-08-24 19:21:49 -04:00
d600a10729
fix(aurora): don't try and use the attribute of a Member object when the object is a User object
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 51s
2024-08-24 19:13:01 -04:00
5934506c8a
fix(aurora): make silent an optional argument in Aurora.moderate()
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 55s
2024-08-23 14:54:06 -04:00
5459392d7e
fix(aurora): update the autologger method to use the new type registry
All checks were successful
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 54s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
2024-08-23 14:43:43 -04:00
6560f98aef
feat(aurora): add [p]aurora info command
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 50s
2024-08-22 18:47:17 -04:00
fa8036291c
fix(aurora): bump version and change author key
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 49s
2024-08-22 17:54:36 -04:00
fadb3e1a9d
fix(aurora): make the moderation_id key in the moderation tables not nullable (NOT NULL)
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 49s
2024-08-22 17:52:50 -04:00
bc3ea67e1e
fix(aurora): pylint fixes
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 53s
2024-08-22 16:04:37 -04:00
43e82c9eb5
feat(aurora): minify export json to reduce file sizes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 52s
2024-08-22 15:59:23 -04:00
320db1b692
fix(aurora): make sure that bans with durations are imported as tempbans
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 51s
2024-08-22 15:44:22 -04:00
fa27d12de5
feat(aurora): make all models compatible with repr
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 51s
2024-08-22 15:33:36 -04:00
794ce56040
misc(aurora): changed a docstring
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 51s
2024-08-22 12:33:30 -04:00
064784c9d7
fix(aurora): fixed the Note moderation type handler using cls.string instead of cls.verb
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 54s
2024-08-22 12:26:42 -04:00
101d364241
misc(aurora): remove a useless logging call
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 52s
2024-08-21 17:21:54 -04:00
cd3d3c7733
fix(aurora): make sure the bot key does not exist in the Change.from_dict() data dictionary
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 54s
2024-08-21 15:16:40 -04:00
4d2004ed93
fix(aurora): fix changes not being imported 2024-08-21 15:16:10 -04:00
a3ad38f338
misc(aurora): improved Change.from_dict() 2024-08-21 15:15:50 -04:00
64758686bb
misc(aurora): simplified AuroraGuildModel a bit
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 52s
2024-08-21 14:49:47 -04:00
fce4001152
fix(aurora): hopefully fixed changes not being imported 2024-08-21 14:48:56 -04:00
90be42769c
Merge branch 'main' into aurora-pydantic
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 52s
2024-08-21 14:37:16 -04:00
8479dcdd48
fix(aurora): fixed a bug in the Aurora importer that prevented new imports from being imported
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 4s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
2024-08-21 14:15:25 -04:00
797793b970
feat(aurora): add _obj to the partial models
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 17:57:26 -04:00
5d22e67864
fix(aurora): don't mutate a list while iterating through it
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 17:53:15 -04:00
7c86d862cf
fix(aurora): fixed a TypeError
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 17:52:18 -04:00
edbc950741
fix(aurora): remove empty lines in changes_factory and fix timestamp formatting
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 17:50:52 -04:00
709042f057
fix(aurora): fixed an AttributeError in changes_factory
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-08-19 17:46:05 -04:00
1c3b9377b5
fix(aurora): catch a ValueError in changes_factory
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 17:45:02 -04:00
891b36ccaa
fix(aurora): fixed another syntax error in changes_factory
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 17:42:25 -04:00
9be187e4aa
fix(aurora): fixed a SyntaxError in changes_factory
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-08-19 17:41:41 -04:00
ec082b58ad
fix(aurora): make changes_factory support changes properly
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-08-19 17:40:52 -04:00
b0509d748c
fix(aurora): fixed a 403 forbidden error in some moderation type handlers when moderating someone with the administrator permission
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-08-19 17:23:53 -04:00
29f393fa89
fix(aurora): fix an attributeerror when editing a case type that doesn't use durations
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 17:21:33 -04:00
0c2cde1a78
fix(aurora): changed how moderation changes are added. only log the information that is actually changed, and not everything
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 17:19:01 -04:00
dc407c1125
fix(aurora): fixed the jsonencoder converting timedeltas to strings using str()
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 14:49:04 -04:00
3d3036f9b6
fix(aurora): convert timedeltas to strings before creating changes from them
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-08-19 14:46:08 -04:00
3e484e1ae5
fix(aurora): fixed /edit command not setting the moderation's duration properly
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-08-19 14:37:47 -04:00
5cd0ef61cb
fix(aurora): fixed an error in the /edit command
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-19 14:33:11 -04:00
46c1cf53bf
fix(aurora): fixed most of the moderation handlers having improper error handling for incorrect durations
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 44s
2024-08-19 14:24:25 -04:00
7b3608e264
fix(aurora): fixed a keyerror in the message_factory function and cleaned up some other problems
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 37s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 56s
2024-08-19 03:55:08 -04:00
ac7d950aaa
fix(aurora): added temporary debug logging
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-14 16:05:20 -04:00
8cbe9d94cf
fix(aurora): pass interaction instead of interaction.client to the duration_edit_handler
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-08-14 15:59:09 -04:00
ec508a92e3
fix(aurora): fixed a typeerror in /edit
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-08-14 15:57:53 -04:00
4a4f24bb8f
fix(aurora): don't continue banning someone if they're already banned
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-08-14 15:46:27 -04:00
9ae7607015
fix(aurora): fixed the Moderation.update() method again
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-14 15:26:55 -04:00
b0c9656758
fix(aurora): awaited a coroutine
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-08-14 15:23:40 -04:00
7854af8ec0
fix(aurora): fixed the /edit command failing again
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-08-14 15:23:05 -04:00
de4c789fea
fix(aurora): don't use deepcopy
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-14 15:21:07 -04:00
51f165c480
fix(aurora): fixed /edit command not properly storing the data from the old moderation
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 43s
2024-08-14 15:14:22 -04:00
5fc6d0eb6a
fix(aurora): fixed Moderation.update() not converting the duration timedelta to the correct kind of string
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-14 15:08:37 -04:00
48d2f8b416
fix(aurora): fixed debug logging
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 43s
2024-08-14 14:37:32 -04:00
c5472d25c1
Merge branch 'main' into aurora-pydantic
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 49s
2024-08-14 01:06:13 -04:00
001fccfe34
fix(aurora): pylint fixes
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 51s
2024-08-14 00:06:48 -04:00
fb92cdc08c
fix(aurora): fixed a bug in removerole
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 47s
2024-08-14 00:04:25 -04:00
123cd188dc
fix(aurora): fixed addrole and removerole *again*
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-08-14 00:02:48 -04:00
871cabfb71
fix(aurora): fixed addrole and removerole again
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-08-14 00:01:13 -04:00
6aaef7d3b2
fix(aurora): fixed removerole and addrole being broken if used without a duration set
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 33s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 49s
2024-08-13 23:59:36 -04:00
86c0cadd82
fix(aurora): don't pass key
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-13 16:39:36 -04:00
6c7cdd4e5e
fix(aurora): fixed all type
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-08-13 16:38:20 -04:00
1275b8e99a
fix(aurora): revert a stupid change
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-08-13 16:36:15 -04:00
71840cc148
misc(aurora): added a temporary logging call
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-13 16:31:00 -04:00
f5aa9f2b20
fix(aurora): fixed a programmingError when using all
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-08-13 16:26:14 -04:00
1a20ae30de
feat(aurora): add autocomplete entry for all
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-08-13 16:23:35 -04:00
3d8050d3b9
fix(aurora): fixed a minor bug with autocomplete
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-08-13 16:08:16 -04:00
a5e3a11479
fix(aurora): set a default argument for the resolve command's string parameter
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-08-13 15:11:39 -04:00
48259df18f
misc(aurora): move moderate() into the Aurora cog class
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-13 15:09:49 -04:00
233e6ac908
feat(aurora): added expired argument to the /history command
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-08-13 14:25:03 -04:00
448ac6792e
fix(aurora): fixed some expiry_handler() methods fetching the user object but not putting it into a variable
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-08-13 14:18:25 -04:00
009b406ff6
fix(aurora): fixed some broken expiry handlers
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-08-13 01:30:47 -04:00
a6124dd9ca
fix(aurora): fixed a TypeError whenever an expiry_handler function is called
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 33s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-08-13 01:26:50 -04:00
ad063062fb
feat(aurora): add autocomplete to /history's types argument
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 45s
2024-08-13 01:22:18 -04:00
830237938a
fix(aurora): pylint fix
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 47s
2024-08-13 00:41:25 -04:00
e559db9f10
feat(aurora): moved the converter commands to their own convert command group
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-08-12 20:46:40 -04:00
9e2bcf9b00
feat(aurora): added datetime command 2024-08-12 20:44:12 -04:00
4ec065e438
fix(aurora): fixed a NotFound error being thrown to the console 2024-08-12 20:40:10 -04:00
7c8aaba309
misc(aurora): version bump
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-08-12 20:33:47 -04:00
c82aa6a0f5
fix(aurora): catch an attribute error 2024-08-12 20:33:37 -04:00
10cfeeefcd
fix(aurora): awaited a coroutine 2024-08-12 20:32:14 -04:00
818ff810ea
misc(aurora): removed an old kwarg from the moderate function docstring
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-08-12 19:52:35 -04:00
85dbb6cc88
fix(aurora): export all moderation types
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-08-12 19:45:14 -04:00
8bb9223054
misc(aurora): updated my own name
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-08-12 18:57:48 -04:00
1bc6412b07
misc(aurora): version bump to 3.0.0-indev1
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-08-12 18:56:57 -04:00
cbd82f8572
fix(aurora): minor changes to the aurora importer and also fixed a bug in the Moderation.get_latest method
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-08-12 18:50:07 -04:00
8822d1714e
misc(aurora): make show_in_history default to True
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-12 18:18:34 -04:00
d546fb3371
misc(aurora): minor formatting fix
All checks were successful
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
2024-08-12 18:15:15 -04:00
4d408ff616
feat(aurora): implemented the per-type configuration options so they actually do something
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-08-12 18:13:04 -04:00
08278c2de4
fix(aurora): fixed some formatting
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-12 17:49:51 -04:00
bf36e99224
fix(aurora): fixed a registry error
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-12 17:48:01 -04:00
7c69a89442
feat(aurora): added reset button to the types menu
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 45s
2024-08-12 17:44:48 -04:00
290ac5947f
fix(aurora): actually fixed the previous TypeError 2024-08-12 17:44:39 -04:00
c134a3ed18
fix(aurora): maybe fixed a TypeError?
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-12 17:42:46 -04:00
9726b1423c
fix(aurora): fixed a ModuleNotFoundError being caused by a typo
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-08-12 17:40:17 -04:00
9c345ed96b
feat(aurora): add per-type configuration options and a menu to configure them
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
none of the options do anything yet, this is just creating the configuration keys and the menu to modify them
2024-08-12 17:39:13 -04:00
8aaa918b6e
Merge branch 'main' into aurora-pydantic
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 33s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 50s
2024-08-12 03:19:05 -04:00
de06490050
fix(aurora): typo
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-10 13:51:17 -04:00
2e3640b738
fix(aurora): make on more accurate
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-10 13:50:47 -04:00
cb424ed722
fix(aurora): fixed another OperationalError
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-08-10 13:44:18 -04:00
f3246366ff
fix(aurora): fixed Moderation.get_latest() breaking when using only before and after, and no other kwargs
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-08-10 13:42:15 -04:00
14750787b2
fix(aurora): cast timestamps to integers
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-08-10 13:34:15 -04:00
14dc256919
fix(aurora); log when Modereation.execute() fails with an operationalerror
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-08-10 13:32:37 -04:00
49cf7c9005
feat(aurora): switch from dateparser to dateutil and add on parameter to history command
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-08-10 13:27:49 -04:00
1a3af342df
feat(aurora): allow for querying moderation history by date (before/after)
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-08-10 13:17:16 -04:00
7f0bd8c1a8
fix(aurora): fixed the Moderation.update() method being broken
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 43s
2024-07-12 16:33:00 -04:00
6edda87baa
fix(aurora): add a resolve_handler to AddRole (oops!)
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 44s
2024-07-12 16:30:34 -04:00
b379584251
fix(aurora): fixed a TypeError in the note and warn handlers
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-07-12 16:05:05 -04:00
f1e763673b
fix(aurora): legitimately the final pylint fixes for now
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-07-12 16:02:19 -04:00
c4c5b323a3
fix(aurora): ACTUALLY final pylint fixes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 45s
2024-07-12 15:58:15 -04:00
ea12d362df
fix(aurora): final pylint fixes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 44s
2024-07-12 15:55:16 -04:00
8d03022453
fix(aurora): more pylint fixes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-07-12 15:52:27 -04:00
63e6f4b552
fix(aurora): pylint fixes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 49s
2024-07-12 15:49:54 -04:00
47c8116ee0
misc(aurora): removed useless whitespace from the top of a file
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
2024-07-12 15:42:58 -04:00
ad0d981888
fix(aurora): hopefully fixed a pydantic schema error
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-07-12 15:28:39 -04:00
e2b0fc999a
feat(aurora): finishing up moderation handlers
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 46s
2024-07-12 15:22:24 -04:00
9d0f2e3887
misc(aurora): send evidenceformat to dms if send_evidenceformat is called outside of an Interaction context, as epheremal messages would be disabled in this case 2024-07-06 18:58:04 -04:00
461fbf83ee
fix(aurora): use phx-class-registry instead of class-registry 2024-07-06 14:23:56 -04:00
9faf1b027c
misc(aurora): typehints
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-07-06 13:39:01 -04:00
ec32e19b8b
fix(aurora): pass a Type instead of a string
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-07-06 13:24:44 -04:00
9f747a77ca
fix(aurora): fixed a history call to Moderation.type that was causing a TypeError
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-07-06 13:16:53 -04:00
6d0d79c6c7
fix(aurora): properly skip over case 0
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 37s
2024-07-06 13:15:06 -04:00
13874dd4f0
fix(aurora): add a debug logging statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 25s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 38s
2024-07-06 13:12:47 -04:00
aae4370868
misc(aurora): fixed an incorrect string
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 25s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-07-06 13:10:47 -04:00
37bae2eeb3
fix(aurora): add a softban type
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-07-06 13:10:09 -04:00
8f0425456c
feat(aurora): boilerplate for all currently added moderation types
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-07-06 13:03:59 -04:00
a05e957dde
fix(aurora): fixed a json encoder issue that was causing an attributeerror
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-07-06 12:29:43 -04:00
0c628cf2a2
fix(aurora): fixed a TypeError
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-07-06 12:27:59 -04:00
bf945ec9f1
fix(aurora): fixed some stuff up
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-07-06 12:23:06 -04:00
54ac77ceb9
fix(aurora): fix `__str__() method of the Type` class
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-07-06 12:16:15 -04:00
b85932c338
feat(aurora): use resolve handlers 2024-07-06 12:15:39 -04:00
08512d0dad
feat(aurora): use the type_registry in the Moderation object
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-07-06 12:03:49 -04:00
134e787c42
fix(aurora): fixed the Moderation.get_target method trying to get a channel object from a user id
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-07-06 11:58:46 -04:00
127dd564f5
fix(aurora): use the verb for message embed titles
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 24s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-07-06 11:55:05 -04:00
88cc7b4a3f
fix(aurora): fixed another attributeerror
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 37s
2024-07-06 11:53:39 -04:00
dd89bfaf34
fix(aurora): checking against the wrong bool value for silent
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-07-06 11:52:19 -04:00
ee49d5b10c
misc(aurora): make target_type lowercase 2024-07-06 11:50:59 -04:00
fac00feeef
fix(aurora): fixed another AttributeError
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-07-06 11:47:56 -04:00
f2a88cbf94
fix(aurora): fixed an AttributeError
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-07-06 11:46:13 -04:00
aeec616be5
fix(aurora): assume that ctx is a commands.Context object before trying to use it in moderate
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 25s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 38s
2024-07-06 11:32:33 -04:00
9f068bba6f
feat(aurora): starting on the updated moderation type system
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-07-06 11:30:37 -04:00
fcecfe8e88
fix(aurora): fixed an issue in the galacticbot importer
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-07-05 19:14:03 -04:00
2aadf5134d
fix(aurora): use f-strings
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
Actions / Build Documentation (MkDocs) (pull_request) Failing after 26s
2024-07-05 19:03:44 -04:00
3b102debac
fix(aurora): fixed a PermissionError 2024-06-30 05:04:21 -04:00
c28b089edb
fix(aurora): fixed an issue with the /case command preventing the cog from loading 2024-06-30 05:02:32 -04:00
987fc0dbf8
feat(aurora): add scoped export functionality to aurora's /history command 2024-06-30 04:56:42 -04:00
87942213a5
fix(aurora): typehints 2024-06-11 03:17:38 -04:00
60d4afc6f3
fix(aurora): fixed AuroraBaseModel.to_json() not using AuroraBaseModel.dump() 2024-06-11 03:11:45 -04:00
0bcbcd6c0c
feat(aurora): bunch of changes 2024-06-08 20:12:22 -04:00
f6a42b97d9
misc(aurora): various model changes 2024-06-05 23:13:23 -04:00
5cb61ecd65
fix(aurora): awaited a coroutine 2024-06-05 01:39:34 -04:00
9622e037c9
fix(aurora): fixed another sql syntax error 2024-06-05 01:38:27 -04:00
e9a64e5a39
fix(aurora): fixed an sql syntax error 2024-06-05 01:37:41 -04:00
afac274978
fix(aurora): removed a useless try/except block 2024-06-05 01:36:45 -04:00
e988917319
feat(aurora): added a return_obj parameter to Moderation.execute() 2024-06-05 01:31:40 -04:00
42f7f9f69b
feat(aurora): migrated Aurora.handle_expiry() to use Moderation.execute() instead of opening its own connections 2024-06-05 01:29:47 -04:00
d07e5ed804
misc(aurora): changing around some logging levels 2024-06-05 01:12:49 -04:00
df465e5ba6
fix(aurora): awaited another coroutine 2024-06-05 01:11:18 -04:00
76572e2281
fix(aurora): awaited a coroutine 2024-06-05 01:05:11 -04:00
d629f1a5a2
fix(aurora): awaited two coroutines 2024-06-05 01:02:09 -04:00
ca4510d3a5
fix(aurora): why was I CLOSING THE CONNECTION THERE 😭 2024-06-05 01:01:17 -04:00
3247e6fb82
fix(aurora): lmao i'm dumb 2024-06-05 00:59:33 -04:00
67e3abf5ce
fix(aurora): use interaction.channel.send instead 2024-06-05 00:54:05 -04:00
d1b5346396
fix(aurora): fixed failed_cases in the aurora importer 2024-06-05 00:51:13 -04:00
fe5823b637
fix(aurora): awaited a coroutine 2024-06-05 00:41:41 -04:00
3383e84221
fix(aurora): fixed some issues with aiosqlite 2024-06-05 00:39:56 -04:00
56a2f96a2d
feat(aurora): made database.connect() into an async context manager 2024-06-05 00:36:12 -04:00
eebddd6e89
fix(aurora): override aiosqlite's logging level to warning 2024-06-05 00:25:19 -04:00
5cbf4e7e47
feat(aurora): migrated to aiosqlite 2024-06-05 00:14:43 -04:00
027144f35d
fix(aurora): pylint fixes
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 49s
2024-06-04 23:55:55 -04:00
0d64e3f652
misc(aurora): convert Change.from_dict() to use utils.timedelta_from_string()
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-06-04 23:52:35 -04:00
8591649465
fix(aurora): fixed aurora.utilities.utils.timedelta_from_string not including days in its calculations
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 46s
2024-06-04 23:43:53 -04:00
78630dc317
feat(aurora): added timedelta_from_string() function
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 50s
2024-06-04 23:31:52 -04:00
74d122a2e7
fix(aurora): catch importer errors instead of letting the entire import process die 2024-06-04 23:31:36 -04:00
ce48c1e889
fix(aurora): fixed a valueerror
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 47s
2024-06-04 16:55:29 -04:00
ff34310113
fix(aurora): fixed a valueerror
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 47s
2024-06-04 16:52:19 -04:00
3d2dabae08
fix(aurora): fixed a typeerror
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 47s
2024-06-04 16:50:33 -04:00
3fdc54b7cb
fix(aurora): get_next_case_number now only retrieves the first entry in the sql database (using LIMIT 1)
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
2024-06-04 16:37:28 -04:00
b252343dc0
misc(aurora): allow Moderation.log() to skip returning the class object
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 44s
2024-06-04 16:35:18 -04:00
720e100a20
fix(aurora): removed some logging statements
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 47s
2024-06-04 15:52:23 -04:00
ff66006b8a
misc(aurora): add more logging
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 50s
2024-06-04 15:43:15 -04:00
fdb96539c3
fix(aurora): removed a useless debug statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 44s
2024-06-04 15:39:16 -04:00
7a664ce9c3
fix(aurora): make Change.reason optional
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 44s
2024-06-04 15:37:58 -04:00
3168c42787
fix(aurora): fixed a pydantic ValidationError
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 44s
2024-06-04 15:36:38 -04:00
5d53eec2f1
fix(aurora): fixed a valueerror in the Change.from_dict() method
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
2024-06-04 15:32:59 -04:00
166421b6ba
fix(aurora): removed a character causing a valueerror
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
2024-06-04 15:23:45 -04:00
0a207b66e4
fix(aurora): fixed an error with timedelta formatting
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
2024-06-04 15:22:50 -04:00
5151f65317
fix(aurora): added more to the debugging statement
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 43s
2024-06-04 14:59:57 -04:00
0089625ef3
fix(aurora): add a debug statement
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 43s
2024-06-04 14:56:51 -04:00
8ac735dafe
misc(aurora): change the JSONEncoder subclass to use match/case instead of if statements
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 40s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m4s
2024-06-04 14:20:01 -04:00
21fa3d9eb0
feat(aurora): added an __int__ dunder method to the moderation model
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 46s
2024-06-04 12:44:58 -04:00
38180f5ccd
fix(aurora): fixed an sql operation error
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 46s
2024-06-04 12:29:24 -04:00
19e50c25e3
Merge branch 'aurora-pydantic' of https://www.coastalcommits.com/Seaswimmer/SeaCogs into aurora-pydantic
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 49s
2024-06-04 12:23:13 -04:00
a10af37f14
feat(aurora): add support for OFFSET in Moderation.get_latest() 2024-06-04 12:23:12 -04:00
a5b344a323
Merge branch 'main' into aurora-pydantic
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 46s
2024-06-04 16:16:26 +00:00
cb420d2fc4
fix(aurora): pylint/ruff fix
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 37s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 54s
2024-06-04 11:52:34 -04:00
04223c3c55
Merge branch 'main' into aurora-pydantic
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 46s
2024-06-04 11:48:15 -04:00
1c6d2456ed
misc(aurora): changed aurora.utilities.utils.get_bool_emoji to use match/case instead of if/else
All checks were successful
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 43s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
2024-06-04 00:04:46 -04:00
9b0f977016
feat(aurora): moved get_next_case_number() into the Moderation class, along with removing from_sql and get_all_cases. also added some other classmethods to replace those.
also modified message_factory and its calls to add a new kwarg
2024-06-04 00:04:11 -04:00
460d5a31fc
feat(aurora): allow the Moderation.execute() classmethod to accept a cursor, to prevent opening a new database connection on every call 2024-06-03 23:48:21 -04:00
bbe8b281d1
misc(aurora): changed two typehints in aurora.utilities.utils and added other typehints 2024-06-03 23:46:22 -04:00
be253b668b
feat(aurora): converted /history to use the new Moderation sql queries
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 45s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m9s
2024-06-03 01:07:00 -04:00
22f9ce52d1
fix(aurora): fixed a typeerror
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 42s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m11s
2024-06-03 00:55:56 -04:00
499cfbe8a9
misc(aurora): use tuples instead of lists
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 44s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m14s
2024-06-03 00:55:09 -04:00
99d95afe07
fix(aurora): fixed another TypeError
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 45s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m10s
2024-06-03 00:51:51 -04:00
028e22ebec
fix(aurora): happy now?
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 1m9s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 43s
2024-06-03 00:50:17 -04:00
4c28453173
fix(aurora): fixed a TypeError
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 1m9s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 42s
2024-06-03 00:49:08 -04:00
d646754466
feat(aurora): added a bunch of functionality to the Moderation model
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 44s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 1m11s
2024-06-03 00:47:56 -04:00
76f176d4cc
feat(aurora): change history to use Moderation.from_sql_all()
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 45s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m10s
2024-06-03 00:22:32 -04:00
c35580c576
misc(aurora): change to aurora importer log arguments
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 47s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m15s
2024-06-03 00:17:33 -04:00
7fc6235abe
fix(aurora): remove bot keys from the change import when importing from aurora
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 46s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m12s
2024-06-03 00:15:17 -04:00
21e51dc320
misc(aurora): minor syntax change
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 46s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m15s
2024-06-03 00:09:39 -04:00
db477c4744
fix(aurora): fixed an issue with json encoding
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 47s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m10s
2024-06-03 00:07:52 -04:00
1990b97518
fix(aurora): ignore moderation cases where the moderation_id is 0
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 49s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m13s
2024-06-02 23:59:17 -04:00
641f45d126
feat(aurora): add the from_sql_all classmethod to the Moderation model
All checks were successful
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 1m9s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 47s
2024-06-02 23:53:19 -04:00
73c9104882
fix(aurora): cast to string before checking length to avoid typeerrors when reason is None
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-05-24 04:26:58 -04:00
5b64ee9578
feat(aurora): add metadata to evidenceformat
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-05-24 04:22:42 -04:00
bfb4d8768d
fix(aurora): add moderation metadata to the log factory
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 38s
2024-05-24 04:18:17 -04:00
7f71ca3d6d
fix(aurora): change interval metadata
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-05-24 04:16:48 -04:00
3dcc637920
fix(aurora): avoid keyerrors
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-05-24 04:15:27 -04:00
9a4f19f4a1
fix(aurora): show metadata key/value pairs in /case
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-05-24 04:12:28 -04:00
7a9c9846de
fix(aurora): fixed a broken from_id method
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-05-24 04:09:27 -04:00
0cc7d6079d
fix(aurora): removed useless debug statement
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-05-24 04:06:05 -04:00
ed923f1d9b
fix(aurora): finally actually maybe fixed the pydantic validation error
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 25s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 38s
2024-05-24 04:04:21 -04:00
39cb5feb50
misc(aurora): adding temporary debug logging
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 44s
2024-05-24 04:02:15 -04:00
c90796f6b3
fix(aurora): hopefully actually fixed the pydantic validation error
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 38s
2024-05-24 04:01:15 -04:00
0b1d1d29e6
fix(aurora): hopefully fixed a pydantic validation error
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-05-24 03:57:54 -04:00
599ab8c51d
fix(aurora): fixed a non-async function being awaited
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-05-24 03:56:32 -04:00
797fd561c9
fix(aurora): fixed another typeerror
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-05-24 03:53:38 -04:00
51d3245703
fix(aurora): fixed a typeerror in Moderation.log()
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 38s
2024-05-24 03:51:43 -04:00
0411e3dab7
fix(aurora): fixed a typeerror in the check_moddable function
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-05-24 03:49:55 -04:00
67b33a2eb8
feat(aurora): added a slowmode command
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 39s
2024-05-24 03:46:20 -04:00
dc51aa7bdc
fix(aurora): import fixes
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 45s
2024-05-09 21:27:26 -04:00
904fd1c914
fix(aurora): fixed more import errors
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-05-06 21:46:01 -04:00
92d221ff70
fix(aurora): forgot a file!
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-05-06 21:43:08 -04:00
d91a4f49f9
fix(aurora): fixed import errors
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-05-06 21:39:43 -04:00
ab878739c4
misc(aurora): pep 604 compliance
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 43s
2024-05-06 21:04:08 -04:00
946e14ee3c
fix(aurora): something idk
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-05-06 20:45:22 -04:00
bcc4aa384f
fix(aurora): hopefully fixed the module not found issue?
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-05-06 17:41:14 -04:00
260dd3ef4c
fix(aurora): fixing a ModuleNotFound error
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-05-06 17:35:43 -04:00
c69b3cd032
misc(aurora): import change
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 42s
2024-05-06 17:33:28 -04:00
2b79e3b6a8
fix(aurora): added an __init__.py to a directory that was missing one
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 40s
2024-05-06 17:29:57 -04:00
7dfe94869c
misc(aurora): codebase cleanup
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 41s
2024-05-06 17:23:59 -04:00
ac8cefd779
fix(aurora): pylint fixes
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 43s
2024-05-06 16:47:21 -04:00
09471a4027
misc(aurora): change the logging severity of Change.from_dict()'s logs to trace
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 16:39:13 -04:00
15ccd5530a
fix(aurora): fixed a few unboundlocalerrors
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 16:37:52 -04:00
bb5ab8e61b
fix(aurora): make Moderration.from_result() return a Moderation object instead of a dictionary
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-05-06 16:36:27 -04:00
c2e017339e
fix(aurora): fixed a broken classmethod
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 44s
2024-05-06 16:35:06 -04:00
d7ca5cab46
feat(aurora): cleaned up the codebase and fixed a whole bunch of bugs
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-05-06 16:34:08 -04:00
53b67e1c95
fix(aurora): convert results into a dictionary before making a Moderation instance out of them
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
2024-05-06 16:07:35 -04:00
d7a8fbe367
fix(aurora): ignore moderations with the id 0 in the history command
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-06 16:02:00 -04:00
f8968e8e9e
feat(aurora): updated /history command
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-05-06 15:59:43 -04:00
834d116b20
fix(aurora): fixed a syntax error
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 15:32:01 -04:00
d375716fbf
feat(aurora): added a resolve function to the Moderation model
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-06 15:29:48 -04:00
dcda128f11
fix(aurora): edit command will now edit reasons as intended
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 15:15:54 -04:00
16af26f93e
fix(aurora): fixed a logging statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
2024-05-06 15:11:11 -04:00
5be5a39c54
fix(aurora): check type of dictionary in debug log
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 15:06:10 -04:00
e14845ef85
fix(aurora): make sure the user_id in changes is always an integer
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-05-06 15:03:41 -04:00
8d1a57165d
fix(aurora): added debug logging
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 15:00:54 -04:00
a4e11fd828
fix(aurora): fixing some model stuff
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 14:55:36 -04:00
9c7e0b0b89
fix(aurora): fixed a missing parameter
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-06 14:48:16 -04:00
1e865643a0
fix(aurora): fixed duration being a timedelta in case_safe
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-05-06 14:45:04 -04:00
b7b6dc2f2e
fix(aurora): don't convert end_timestamp to an integer
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
2024-05-06 14:44:06 -04:00
84235d6504
fix(aurora): call from_dict properly
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-05-06 14:41:16 -04:00
70c00a59d6
fix(aurora): fixed the broken logging statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 14:40:14 -04:00
0386cb346e
fix(aurora): finally fixed the debug logging statement (maybe)
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
2024-05-06 14:35:02 -04:00
335d1a2a4c
fix(aurora): actually actually ACTUALLY fixed the broken logging statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 14:29:57 -04:00
1b322dfe50
fix(aurora): actually actually fixed the broken logging statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-06 14:29:09 -04:00
5b6b04dfe0
fix(aurora): actually fixed a broken logging statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-06 14:25:42 -04:00
158e7560f8
fix(aurora): fixed a broken logging statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-05-06 14:22:40 -04:00
52fcdcc96a
fix(aurora): fixed a logging issue
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 38s
2024-05-06 14:21:57 -04:00
65bb9af7a6
fix(aurora): fixed an issue with dictionary values not being converted to a tuple
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-06 14:20:49 -04:00
e591b2c4a5
fix(aurora): convert floats to ints
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-05-06 14:18:54 -04:00
a86348fae3
fix(aurora): fixed a programming error
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 36s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 50s
2024-05-06 14:16:51 -04:00
37e471fbaa
fix(aurora): fixing a whole bunch of stuff
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-05-06 14:15:05 -04:00
0553856aa9
fix(aurora): fixed utils.generate_dict() using literal_eval still even when it's unnecessary
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-06 13:51:23 -04:00
6745f0a486
fix(aurora): fixed an issue with importing changes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
2024-05-06 13:44:48 -04:00
85a935f9b3
fix(aurora): fixed the json issue from yesterday
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-06 13:27:09 -04:00
4c8cd7bd16
Revert "fix(aurora): trying ast"
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
This reverts commit 278bd98349.
2024-05-04 23:02:57 -04:00
278bd98349
fix(aurora): trying ast
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-04 23:01:39 -04:00
cad24d852c
fix(aurora): ok fine bro 😭
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-05-04 22:58:29 -04:00
de90f6a8b7
fix(aurora): troubleshooting this annoying json issue
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 49s
2024-05-04 22:57:34 -04:00
293f77c228
fix(aurora): changed the logging statement again
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 54s
2024-05-04 22:55:50 -04:00
a6371fd367
fix(aurora): changed the logging statement slightly
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-05-04 22:55:17 -04:00
8433c946fd
fix(aurora): added a debug logging statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 47s
2024-05-04 22:52:54 -04:00
300d26dc7e
fix(aurora): fixed a bunch of json issues
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 30s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 49s
2024-05-04 22:50:30 -04:00
e8d210df2a
fix(aurora): removed a %s
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 45s
2024-05-04 22:19:29 -04:00
acf3b0c68f
fix(aurora): added logging for the Moderation.update() method
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 45s
2024-05-04 22:17:52 -04:00
04d10d2cf8
fix(aurora): strip and replace the json dumps before inserting them into the db
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-04 22:12:00 -04:00
0b697f9f50
fix(aurora): change how Moderation.update() works
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-04 22:07:51 -04:00
557ac45fcc
fix(aurora): fixed a circular import
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-04 22:05:14 -04:00
3f8cdf2012
fix(aurora): fixed an sqlite error
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 26s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 40s
2024-05-04 22:04:29 -04:00
b03ce04245
fix(aurora): fixed an unboundlocalerror in Change.from_dict()
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 25s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-05-04 22:02:31 -04:00
dc44e8c6de
feat(aurora): migrated /edit to the new model system
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 39s
2024-05-04 22:01:32 -04:00
fc15b434c7
feat(aurora): finished the factory migrations
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 23s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 32s
2024-05-04 21:24:37 -04:00
e89db3de5a
misc(aurora): added some markdown formatting
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 21:20:05 -04:00
a8414a7918
fix(aurora): fixed some subscripting errors in changes_factory
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 21:19:06 -04:00
6eeab9ed96
fix(aurora): fixed a keyerror
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 21:17:03 -04:00
94f6d6c3b5
fix(aurora): fixed the changes_factory
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 22s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 21:15:39 -04:00
ee331b7544
fix(aurora): fixed an incorrect conditional statement resulting in unintended behavior
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 32s
2024-05-04 21:07:37 -04:00
50c2db80d9
fix(aurora): convert datetimes to unix timestamps
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 21:06:29 -04:00
23eba46948
fix(aurora): fix a few classmethods returning None instead of their constructed classes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 30s
2024-05-04 21:02:18 -04:00
0b31e70089
fix(aurora): fixed a conditional statement and a pydantic issue
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 24s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 33s
2024-05-04 20:59:23 -04:00
3f6aec0a82
fix(aurora): don't JSON serialize the Red class
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 22s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 20:55:11 -04:00
a22de1d2c2
fix(aurora): fixed another pydantic error
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 22s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 18:28:03 -04:00
6a7758e8f9
fix(aurora): reverted previous change, instead using pydantic's ConfigDicts to resolve the error I was encountering previously
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 20s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 30s
2024-05-04 18:27:12 -04:00
a7d8f452d1
fix(aurora): updated utilities.json.JSONEncoder to match the model change I just made
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 18:23:24 -04:00
6520f4f2b9
fix(aurora): don't inherit from AuroraBaseModel
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 22s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 18:22:42 -04:00
ea280c2d62
fix(aurora): fixed a syntax error
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 30s
2024-05-04 18:18:57 -04:00
d70f2bf5f1
fix(aurora): fixed guild ids appearing in changes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 18:17:21 -04:00
356d58f9d7
fix(aurora): changed the jsonencoder
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 18:09:06 -04:00
74a0983419
fix(aurora): fixed a missing kwarg in a model initialization function 2024-05-04 18:08:27 -04:00
1313834ea5
fix(aurora): fixed a pydantic error
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 18:07:29 -04:00
b752181781
fix(aurora): oops lol
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 20s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 30s
2024-05-04 18:06:23 -04:00
39479eb216
fix(aurora): fixing up some model stuff
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 30s
2024-05-04 18:05:20 -04:00
e46612dc08
fix(aurora): fixed a circular import
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 22s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 17:49:01 -04:00
e7e8d60f3b
feat(aurora): added a new object for Changes
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 21s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 31s
2024-05-04 17:48:08 -04:00
eea66607d9
feat(aurora): finished migrating case_factory to the new model 2024-05-04 17:31:16 -04:00
2c336ff70c
fix(aurora): fixed a faulty expiration check
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 41s
2024-05-04 17:15:21 -04:00
a3a208b38e
fix(aurora): fixed an incorrect function call in Moderation.from_sql()
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-05-04 17:12:35 -04:00
26bf5920c6
fix(aurora): mark the bot as a classvar in the moderation model
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-05-04 17:10:26 -04:00
d13ad88f16
fix(aurora): fixed a whole bunch of pydantic errors
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 28s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 42s
2024-05-04 17:09:22 -04:00
6147c8c6d5
feat(aurora): whole bunch of changes to the models and various other things
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 39s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 58s
2024-05-04 16:54:12 -04:00
2dfc9d9824
fix(aurora): use AuroraBaseModels for the JSONEncoder class instead of just pydantic ones to prevent issues with other data types
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-05-04 15:37:07 -04:00
ca7c0d8d7c
fix(aurora): use pydantic basemodels instead of the Moderation model
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 34s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 51s
2024-05-04 15:33:49 -04:00
98f3e5943b
fix(aurora): fixed incorrect kwarg name
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 34s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 48s
2024-05-04 15:29:01 -04:00
7dfd9c607a
misc(aurora): moved some stuff around
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 34s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 50s
2024-05-04 15:25:05 -04:00
df87612055
feat(aurora): added imported_timestamp to metadata on new imports
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 36s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 51s
2024-05-04 15:13:31 -04:00
58303b8e9c
feat(aurora): added a to_json method to the moderation model
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 33s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 49s
2024-05-04 15:08:08 -04:00
ca1722fee3
feat(aurora): migrated to a custom json encoder
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 35s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 51s
2024-05-04 15:05:50 -04:00
69805b276f
fix(aurora): fixed an issue with json decoding
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 43s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
2024-05-04 14:58:24 -04:00
25d7101cb5
fix(aurora): testing a potential bugfix
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 33s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 49s
2024-05-04 14:55:17 -04:00
92f9619cea
fix(aurora): added two conditional statements to fix an error
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 45s
2024-05-04 14:54:14 -04:00
1d825625f3
fix(aurora): fixed some broken conditional statements
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 29s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 44s
2024-05-04 14:53:04 -04:00
ea65816c32
fix(aurora): fixed an incorrect if statement
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 46s
2024-05-04 14:51:22 -04:00
afed1d6a37
feat(aurora): changed a lot of stuff. THIS IS A BREAKING CHANGE! VERY BREAKING! TAKE DATABASE BACKUPS BEFORE UPDATING TO THIS
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 45s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 33s
2024-05-04 14:49:07 -04:00
e8ca0aeb1c
fix(aurora): convert float timestamps to integers
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 33s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 48s
2024-05-04 14:19:48 -04:00
2da76eb51a
feat(aurora): subclassed jsonencoder to allow for custom behavior
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 46s
2024-05-04 14:18:19 -04:00
e5cdd3893f
fix(aurora): cleaned up the Moderation model
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 46s
2024-05-04 14:08:53 -04:00
14a04cff59
fix(aurora): fixed an error
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 47s
2024-05-04 13:48:57 -04:00
f3d6244a17
fix(aurora): optimizing the from_sql method
All checks were successful
Actions / Build Documentation (MkDocs) (pull_request) Successful in 32s
Actions / Lint Code (Ruff & Pylint) (pull_request) Successful in 46s
2024-05-04 13:47:07 -04:00
b6d1510698
fix(aurora): fixed a broken import
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 31s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 45s
2024-05-04 13:42:58 -04:00
c0969ea947
feat(aurora): added a Moderation model
Some checks failed
Actions / Build Documentation (MkDocs) (pull_request) Successful in 33s
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 49s
2024-05-04 13:41:11 -04:00
59 changed files with 6442 additions and 4310 deletions

View file

@ -10,7 +10,7 @@ Aurora is a fully-featured moderation system. It is heavily inspired by Galactic
## Installation ## Installation
```bash ```bash
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs [p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
[p]cog install seacogs aurora [p]cog install seacogs aurora
[p]cog load aurora [p]cog load aurora
``` ```

View file

@ -5,14 +5,14 @@ Backup allows you to export a JSON list of all of your installed repositories an
## Installation ## Installation
```bash ```bash
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs [p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
[p]cog install seacogs backup [p]cog install seacogs backup
[p]cog load backup [p]cog load backup
``` ```
## Version Compatibility ## Version Compatibility
As of commit [1edb08a](https://www.coastalcommits.com/cswimr/SeaCogs/commit/1edb08a1271f12098ca0bed11a735f7162cedd14), the Backup cog no longer supports Red versions older than 3.5.6. If you want to use the cog on an earlier version (3.5.0 - 3.5.5), install the cog pinned to this commit: `43464db6a7c51bc69282b1ae3dc507a4aae851de`. As of commit [1edb08a](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/commit/1edb08a1271f12098ca0bed11a735f7162cedd14), the Backup cog no longer supports Red versions older than 3.5.6. If you want to use the cog on an earlier version (3.5.0 - 3.5.5), install the cog pinned to this commit: `43464db6a7c51bc69282b1ae3dc507a4aae851de`.
```bash ```bash
[p]cog installversion sea-cogs 43464db6a7c51bc69282b1ae3dc507a4aae851de backup [p]cog installversion sea-cogs 43464db6a7c51bc69282b1ae3dc507a4aae851de backup

View file

@ -6,7 +6,7 @@ This cog does require an api key to work.
## Installation ## Installation
```bash ```bash
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs [p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
[p]cog install seacogs bible [p]cog install seacogs bible
[p]cog load bible [p]cog load bible
``` ```

View file

@ -5,7 +5,7 @@ EmojiInfo allows you to retrieve information about an emoji.
## Installation ## Installation
```bash ```bash
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs [p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
[p]cog install seacogs emojiinfo [p]cog install seacogs emojiinfo
[p]cog load emojiinfo [p]cog load emojiinfo
``` ```

View file

@ -5,7 +5,7 @@ Nerdify allows you to nerdify other people's text.
## Installation ## Installation
```bash ```bash
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs [p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
[p]cog install seacogs nerdify [p]cog install seacogs nerdify
[p]cog load nerdify [p]cog load nerdify
``` ```

View file

@ -28,7 +28,7 @@ The Downloader cog allows you to add Git repositories to your bot in order to do
Now, use Downloader to add my repository to your bot: Now, use Downloader to add my repository to your bot:
``` ```
[p]repo add sea-cogs https://www.coastalcommits.com/cswimr/SeaCogs [p]repo add sea-cogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
``` ```
Now, install the Pterodactyl cog: Now, install the Pterodactyl cog:

View file

@ -10,7 +10,7 @@ Pterodactyl allows for connecting to a Pterodactyl server through websockets. It
## Installation ## Installation
```bash ```bash
[p]repo add seacogs https://www.coastalcommits.com/cswimr/SeaCogs [p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
[p]cog install seacogs pterodactyl [p]cog install seacogs pterodactyl
[p]cog load aurora [p]cog load aurora
``` ```

View file

@ -10,7 +10,7 @@ There are a few caveats to running an instance of Red on Pterodactyl.
- You will not receive any support from the Red developers. - You will not receive any support from the Red developers.
- The built-in Audio cog will not work. - The built-in Audio cog will not work.
- Depending on your host, you might have to request a [`tmpfs` size increase](https://github.com/pelican-eggs/eggs/tree/master/bots/discord/redbot#additional-requirements). - Depending on your host, you might have to request a [`tmpfs` size increase](https://github.com/ign-gg/Pterodactyl-Eggs/tree/master/bots/discord/redbot#additional-requirements).
If these are unacceptable to you, you should [install Red normally](https://docs.discord.red/en/stable/install_guides/index.html). If these are unacceptable to you, you should [install Red normally](https://docs.discord.red/en/stable/install_guides/index.html).
/// ///
@ -64,7 +64,7 @@ Red is quite a large bot, so I'll focus on the specifics of getting the bot work
``` ```
2. Add my repository to the bot 2. Add my repository to the bot
```bash ```bash
[p]repo add sea-cogs https://www.coastalcommits.com/cswimr/SeaCogs [p]repo add sea-cogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs
``` ```
3. Install and load the Pterodactyl cog 3. Install and load the Pterodactyl cog
```bash ```bash

10
.envrc
View file

@ -1,10 +0,0 @@
if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
fi
watch_file flake.nix
watch_file flake.lock
if ! use flake . --no-pure-eval
then
echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2
fi

View file

@ -1,6 +1,8 @@
name: Bug Report name: Bug Report
about: File a bug report about: File a bug report
title: "[Cog Name] "
labels: [bug] labels: [bug]
ref: master
body: body:
- type: markdown - type: markdown
attributes: attributes:
@ -11,7 +13,7 @@ body:
attributes: attributes:
label: Please confirm that; label: Please confirm that;
options: options:
- label: I have checked that this bug does not already have an opened/closed [issue](https://www.coastalcommits.com/cswimr/SeaCogs/issues) or [pull request](https://www.coastalcommits.com/cswimr/SeaCogs/pulls) associated with it. - label: I have checked that this bug does not already have an opened/closed [issue](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/issues) or [pull request](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/pulls) associated with it.
required: true required: true
- label: I have checked that I am on the latest version of [Red-DiscordBot](https://github.com/CogCreators/Red-DiscordBot), and SeaCogs. - label: I have checked that I am on the latest version of [Red-DiscordBot](https://github.com/CogCreators/Red-DiscordBot), and SeaCogs.
required: true required: true

View file

@ -1,6 +1,8 @@
name: Suggestion name: Suggestion
about: Trying to suggest something for SeaCogs? Use this. about: Trying to suggest something for SeaCogs? Use this.
labels: [enhancement] title: "[Cog Name] "
labels: enhancement
ref: master
body: body:
- type: markdown - type: markdown
attributes: attributes:
@ -11,7 +13,7 @@ body:
attributes: attributes:
label: What cog is your feature request for? label: What cog is your feature request for?
description: Specify the cog within the repository. description: Specify the cog within the repository.
placeholder: E.g., Pterodactyl placeholder: E.g., ModerationCog
validations: validations:
required: true required: true
- type: textarea - type: textarea

View file

@ -2,5 +2,5 @@
<!-- Create a new issue, if it doesn't exist yet --> <!-- Create a new issue, if it doesn't exist yet -->
- [ ] By submitting this pull request, I permit cswimr to license my work under - [ ] By submitting this pull request, I permit SeaswimmerTheFsh to license my work under
the [Mozilla Public License Version 2.0](https://www.coastalcommits.com/cswimr/SeaCogs/src/branch/main/LICENSE). the [Mozilla Public License Version 2.0](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/src/branch/main/LICENSE).

View file

@ -1,46 +1,39 @@
name: Actions name: Actions
on: on:
push: push:
branches:
- 'main'
pull_request: pull_request:
jobs: jobs:
lint: Lint Code (Ruff & Pylint):
name: Lint Code (Ruff & Pylint)
runs-on: docker runs-on: docker
container: www.coastalcommits.com/cswimr/actions:uv container: www.coastalcommits.com/seaswimmerthefsh/actionscontainers-seacogs:latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install python
run: uv python install 3.11
- name: Install dependencies - name: Install dependencies
run: uv sync run: poetry install --with dev --no-root
- name: Analysing code with Ruff - name: Analysing code with Ruff
run: uv run ruff check $(git ls-files '*.py') run: ./.venv/bin/ruff check $(git ls-files '*.py')
continue-on-error: true continue-on-error: true
- name: Analysing code with Pylint - name: Analysing code with Pylint
run: uv run pylint --rcfile=.forgejo/workflows/config/.pylintrc $(git ls-files '*.py') run: ./.venv/bin/pylint --rcfile=.forgejo/workflows/config/.pylintrc $(git ls-files '*.py')
docs: Build Documentation (MkDocs):
name: Build Documentation (MkDocs)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: docker runs-on: docker
container: www.coastalcommits.com/cswimr/actions:docs container: www.coastalcommits.com/seaswimmerthefsh/actionscontainers-seacogs:latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install python
run: uv python install 3.11
- name: Install dependencies - name: Install dependencies
run: uv sync --no-dev --extra=documentation run: poetry install --with docs --no-root
- name: Set environment variables - name: Set environment variables
uses: actions/env@v2 uses: actions/env@v2
@ -49,7 +42,7 @@ jobs:
run: | run: |
export SITE_URL="https://$CI_ACTION_REF_NAME_SLUG.seacogs.coastalcommits.com" export SITE_URL="https://$CI_ACTION_REF_NAME_SLUG.seacogs.coastalcommits.com"
export EDIT_URI="src/branch/$CI_ACTION_REF_NAME/.docs" export EDIT_URI="src/branch/$CI_ACTION_REF_NAME/.docs"
uv run mkdocs build -v ./.venv/bin/mkdocs build -v
- name: Deploy documentation - name: Deploy documentation
run: | run: |
@ -65,7 +58,7 @@ jobs:
npx -p "@getmeli/cli" meli upload ./site \ npx -p "@getmeli/cli" meli upload ./site \
--url "https://pages.coastalcommits.com" \ --url "https://pages.coastalcommits.com" \
--site "${{ vars.MELI_SITE_ID }}" \ --site "${{ vars.MELI_SITE_ID }}" \
--token "${{ secrets.MELI_TOKEN }}" \ --token "${{ secrets.MELI_SECRET }}" \
--release "$CI_ACTION_REF_NAME_SLUG/${{ env.GITHUB_SHA }}" \ --release "$CI_ACTION_REF_NAME_SLUG/${{ env.GITHUB_SHA }}" \
--branch "$CI_ACTION_REF_NAME_SLUG" --branch "$CI_ACTION_REF_NAME_SLUG"

2
.gitignore vendored
View file

@ -3,5 +3,3 @@
site site
.venv .venv
__pycache__ __pycache__
.direnv
.devenv

View file

@ -11,7 +11,7 @@ My assorted cogs for Red-DiscordBot.
To get started with a development environment, first clone this repository. To get started with a development environment, first clone this repository.
```sh ```sh
git clone https://coastalcommits.com/cswimr/SeaCogs.git git clone https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs.git
``` ```
Then, install Poetry. Then, install Poetry.

View file

@ -9,15 +9,14 @@ import discord
from red_commons.logging import getLogger from red_commons.logging import getLogger
from redbot.core import commands from redbot.core import commands
from redbot.core.bot import Config, Red from redbot.core.bot import Config, Red
from redbot.core.utils.chat_formatting import bold, humanize_list from redbot.core.utils.chat_formatting import humanize_list
class AntiPolls(commands.Cog): class AntiPolls(commands.Cog):
"""AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages).""" """AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages)."""
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"] __author__ = ["SeaswimmerTheFsh"]
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs" __version__ = "1.0.0"
__version__ = "1.0.1"
__documentation__ = "https://seacogs.coastalcommits.com/antipolls/" __documentation__ = "https://seacogs.coastalcommits.com/antipolls/"
def __init__(self, bot: Red): def __init__(self, bot: Red):
@ -39,9 +38,9 @@ class AntiPolls(commands.Cog):
n = "\n" if "\n\n" not in pre_processed else "" n = "\n" if "\n\n" not in pre_processed else ""
text = [ text = [
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})", f"Cog Version: **{self.__version__}**",
f"{bold('Author:')} {humanize_list(self.__author__)}", f"Author: {humanize_list(self.__author__)}",
f"{bold('Documentation:')} {self.__documentation__}", f"Documentation: {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)

View file

@ -1,6 +1,6 @@
{ {
"author" : ["cswimr"], "author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing AntiPolls!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).", "install_msg" : "Thank you for installing AntiPolls!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
"name" : "AntiPolls", "name" : "AntiPolls",
"short" : "AntiPolls deletes messages that contain polls.", "short" : "AntiPolls deletes messages that contain polls.",
"description" : "AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages).", "description" : "AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages).",

File diff suppressed because it is too large Load diff

View file

@ -1,20 +1,25 @@
# pylint: disable=duplicate-code # pylint: disable=duplicate-code
import json import json
from datetime import timedelta import os
from typing import Dict from time import time
from typing import Dict, List
from discord import ButtonStyle, Interaction, Message, ui from discord import ButtonStyle, File, Interaction, Message, ui
from redbot.core import commands from redbot.core import commands, data_manager
from redbot.core.utils.chat_formatting import box, warning from redbot.core.utils.chat_formatting import warning
from ..utilities.database import connect, create_guild_table, mysql_log from ..models.moderation import Moderation
from ..models.type import Type, type_registry
from ..utilities.json import dump
from ..utilities.utils import create_guild_table, timedelta_from_string
class ImportAuroraView(ui.View): class ImportAuroraView(ui.View):
def __init__(self, timeout, ctx, message): def __init__(self, timeout, ctx, message, data: List[Dict[str, any]]):
super().__init__() super().__init__()
self.ctx: commands.Context = ctx self.ctx: commands.Context = ctx
self.message: Message = message self.message: Message = message
self.data: List[Dict[str, any]] = data
@ui.button(label="Yes", style=ButtonStyle.success) @ui.button(label="Yes", style=ButtonStyle.success)
async def import_button_y( async def import_button_y(
@ -25,14 +30,8 @@ class ImportAuroraView(ui.View):
"Deleting original table...", ephemeral=True "Deleting original table...", ephemeral=True
) )
database = connect()
cursor = database.cursor()
query = f"DROP TABLE IF EXISTS moderation_{self.ctx.guild.id};" query = f"DROP TABLE IF EXISTS moderation_{self.ctx.guild.id};"
cursor.execute(query) await Moderation.execute(query=query, return_obj=False)
cursor.close()
database.commit()
await interaction.edit_original_response(content="Creating new table...") await interaction.edit_original_response(content="Creating new table...")
@ -40,73 +39,102 @@ class ImportAuroraView(ui.View):
await interaction.edit_original_response(content="Importing moderations...") await interaction.edit_original_response(content="Importing moderations...")
file = await self.ctx.message.attachments[0].read()
data: list[dict] = sorted(json.loads(file), key=lambda x: x["moderation_id"])
user_mod_types = ["NOTE", "WARN", "ADDROLE", "REMOVEROLE", "MUTE", "UNMUTE", "KICK", "TEMPBAN", "BAN", "UNBAN"]
channel_mod_types = ["SLOWMODE", "LOCKDOWN"]
failed_cases = [] failed_cases = []
for case in data: for case in self.data:
if case["moderation_id"] == 0: if case["moderation_id"] == 0:
continue continue
moderation_type: Type = type_registry[case["moderation_type"].lower()]
if "target_type" not in case or not case["target_type"]: if "target_type" not in case or not case["target_type"]:
if case["moderation_type"] in user_mod_types: if moderation_type.channel:
case["target_type"] = "USER" case["target_type"] = "channel"
elif case["moderation_type"] in channel_mod_types:
case["target_type"] = "CHANNEL"
else: else:
case["target_type"] = "USER" case["target_type"] = "user"
if "role_id" not in case or not case["role_id"]: if "role_id" not in case or not case["role_id"]:
case["role_id"] = 0 case["role_id"] = None
else:
case["role_id"] = int(case["role_id"])
if "changes" not in case or not case["changes"]: case["target_id"] = int(case["target_id"])
case["changes"] = [] case["moderator_id"] = int(case["moderator_id"])
changes = case.get("changes", None)
if not changes:
changes = []
else:
if not isinstance(changes, list):
changes = json.loads(changes)
if isinstance(changes, str):
changes: list[dict] = json.loads(changes)
for change in changes:
if "bot" in change:
del change["bot"]
if "metadata" not in case: if "metadata" not in case:
metadata = {} metadata = {}
else: else:
metadata: Dict[str, any] = json.loads(case["metadata"]) if isinstance(case["metadata"], str):
metadata: Dict[str, any] = json.loads(case["metadata"])
else:
metadata = case["metadata"]
if not metadata.get("imported_from"): if not metadata.get("imported_from"):
metadata.update({"imported_from": "Aurora"}) metadata.update({"imported_from": "Aurora"})
metadata.update({"imported_timestamp": int(time())})
if case["duration"] != "NULL": if case["duration"] != "NULL" and case["duration"] is not None:
hours, minutes, seconds = map(int, case["duration"].split(":")) duration = timedelta_from_string(case["duration"])
duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) if moderation_type.key == "ban":
moderation_type = type_registry["tempban"]
else: else:
duration = "NULL" duration = None
await mysql_log( try:
self.ctx.guild.id, await Moderation.log(
case["moderator_id"], bot=interaction.client,
case["moderation_type"], guild_id=self.ctx.guild.id,
case["target_type"], moderator_id=case["moderator_id"],
case["target_id"], moderation_type=moderation_type,
case["role_id"], target_type=case["target_type"].lower(),
duration, target_id=case["target_id"],
case["reason"], role_id=case["role_id"],
timestamp=case["timestamp"], duration=duration,
resolved=case["resolved"], reason=case["reason"],
resolved_by=case["resolved_by"], timestamp=case["timestamp"],
resolved_reason=case["resolve_reason"], resolved=case["resolved"],
expired=case["expired"], resolved_by=case["resolved_by"],
changes=case["changes"], resolved_reason=case["resolve_reason"],
metadata=metadata, expired=case["expired"],
database=database, changes=changes,
) metadata=metadata,
return_obj=False
)
except Exception as e: # pylint: disable=broad-exception-caught
failed_cases.append(str(case["moderation_id"]) + f": {e}")
await interaction.edit_original_response(content="Import complete.") await interaction.edit_original_response(content="Import complete.")
if failed_cases: if failed_cases:
await interaction.edit_original_response( filename = (
content="Import complete.\n" str(data_manager.cog_data_path(cog_instance=self))
+ warning("Failed to import the following cases:\n") + str(os.sep)
+ box(failed_cases) + f"failed_cases_{interaction.guild.id}.json"
) )
with open(filename, "w", encoding="utf-8") as f:
dump(obj=failed_cases, fp=f, indent=2)
await interaction.channel.send(
content="Import complete.\n"
+ warning("Failed to import the following cases:\n"),
file=File(
filename, f"failed_cases_{interaction.guild.id}.json"
)
)
os.remove(filename)
@ui.button(label="No", style=ButtonStyle.danger) @ui.button(label="No", style=ButtonStyle.danger)
async def import_button_n( async def import_button_n(
self, interaction: Interaction, button: ui.Button self, interaction: Interaction, button: ui.Button

View file

@ -1,18 +1,21 @@
# pylint: disable=duplicate-code # pylint: disable=duplicate-code
import json import json
from datetime import timedelta from datetime import timedelta
from time import time
from discord import ButtonStyle, Interaction, Message, ui from discord import ButtonStyle, Interaction, Message, ui
from redbot.core import commands from redbot.core import commands
from redbot.core.utils.chat_formatting import box, warning from redbot.core.utils.chat_formatting import box, warning
from ..utilities.database import connect, create_guild_table, mysql_log from ..models.moderation import Change, Moderation
from ..utilities.utils import create_guild_table
class ImportGalacticBotView(ui.View): class ImportGalacticBotView(ui.View):
def __init__(self, timeout, ctx, message): def __init__(self, timeout, ctx, message):
super().__init__() super().__init__()
self.ctx: commands.Context = ctx self.ctx: commands.Context = ctx
self.timeout = timeout
self.message: Message = message self.message: Message = message
@ui.button(label="Yes", style=ButtonStyle.success) @ui.button(label="Yes", style=ButtonStyle.success)
@ -24,14 +27,14 @@ class ImportGalacticBotView(ui.View):
"Deleting original table...", ephemeral=True "Deleting original table...", ephemeral=True
) )
database = connect() database = await Moderation.connect()
cursor = database.cursor() cursor = await database.cursor()
query = f"DROP TABLE IF EXISTS moderation_{self.ctx.guild.id};" query = f"DROP TABLE IF EXISTS moderation_{self.ctx.guild.id};"
cursor.execute(query) await cursor.execute(query)
cursor.close() await cursor.close()
database.commit() await database.commit()
await interaction.edit_original_response(content="Creating new table...") await interaction.edit_original_response(content="Creating new table...")
@ -67,12 +70,12 @@ class ImportGalacticBotView(ui.View):
if case["duration"] is not None and float(case["duration"]) != 0: if case["duration"] is not None and float(case["duration"]) != 0:
duration = timedelta(seconds=round(float(case["duration"]) / 1000)) duration = timedelta(seconds=round(float(case["duration"]) / 1000))
else: else:
duration = "NULL" duration = None
except OverflowError: except OverflowError:
failed_cases.append(case["case"]) failed_cases.append(case["case"])
continue continue
metadata = {"imported_from": "GalacticBot"} metadata = {"imported_from": "GalacticBot", "imported_timestamp": int(time())}
if case["type"] == "SLOWMODE": if case["type"] == "SLOWMODE":
metadata["seconds"] = case["data"]["seconds"] metadata["seconds"] = case["data"]["seconds"]
@ -98,37 +101,37 @@ class ImportGalacticBotView(ui.View):
if resolved_timestamp is None: if resolved_timestamp is None:
resolved_timestamp = timestamp resolved_timestamp = timestamp
changes = [ changes = [
{ Change.from_dict(interaction.client, {
"type": "ORIGINAL", "type": "ORIGINAL",
"reason": case["reason"], "reason": case["reason"],
"user_id": case["executor"], "user_id": case["executor"],
"timestamp": timestamp, "timestamp": timestamp,
}, }),
{ Change.from_dict(interaction.client, {
"type": "RESOLVE", "type": "RESOLVE",
"reason": resolved_reason, "reason": resolved_reason,
"user_id": resolved_by, "user_id": resolved_by,
"timestamp": resolved_timestamp, "timestamp": resolved_timestamp,
}, }),
] ]
else: else:
resolved = 0 resolved = None
resolved_by = "NULL" resolved_by = None
resolved_reason = "NULL" resolved_reason = None
changes = [] changes = None
if case["reason"] and case["reason"] != "N/A": if case["reason"] and case["reason"] != "N/A":
reason = case["reason"] reason = case["reason"]
else: else:
reason = "NULL" reason = None
await mysql_log( await Moderation.log(
self.ctx.guild.id, self.ctx.guild.id,
case["executor"], case["executor"],
case["type"], case["type"],
case["targetType"], case["targetType"],
case["target"], case["target"],
0, None,
duration, duration,
reason, reason,
timestamp=timestamp, timestamp=timestamp,

View file

@ -1,6 +1,6 @@
{ {
"author" : ["cswimr"], "author" : ["Seaswimmer (cswimr)"],
"install_msg" : "Thank you for installing Aurora!\nMost of this cog's functionality requires enabling slash commands.\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).", "install_msg" : "Thank you for installing Aurora!\nMost of this cog's functionality requires enabling slash commands.\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
"name" : "Aurora", "name" : "Aurora",
"short" : "A full replacement for Red's core Mod cogs.", "short" : "A full replacement for Red's core Mod cogs.",
"description" : "Aurora is a fully-featured moderation system. It is heavily inspired by GalacticBot, and is designed to be a more user-friendly alternative to Red's core Mod cogs. This cog stores all of its data in an SQLite database.", "description" : "Aurora is a fully-featured moderation system. It is heavily inspired by GalacticBot, and is designed to be a more user-friendly alternative to Red's core Mod cogs. This cog stores all of its data in an SQLite database.",
@ -9,6 +9,7 @@
"disabled": false, "disabled": false,
"min_bot_version": "3.5.0", "min_bot_version": "3.5.0",
"min_python_version": [3, 10, 0], "min_python_version": [3, 10, 0],
"requirements": ["pydantic", "aiosqlite", "phx-class-registry==5.0.0"],
"tags": [ "tags": [
"mod", "mod",
"moderate", "moderate",

View file

@ -1,20 +1,24 @@
from discord import ButtonStyle, Interaction, Message, ui from discord import ButtonStyle, Interaction, Message, ui
from discord.errors import NotFound
from redbot.core import commands from redbot.core import commands
from redbot.core.utils.chat_formatting import error from redbot.core.utils.chat_formatting import error
from aurora.utilities.config import config from ..utilities.config import config
from aurora.utilities.factory import addrole_embed from ..utilities.factory import addrole_embed
class Addrole(ui.View): class Addrole(ui.View):
def __init__(self, ctx: commands.Context, message: Message, timeout: int = None): def __init__(self, ctx: commands.Context, message: Message, timeout: int | None = None):
super().__init__() super().__init__()
self.ctx = ctx self.ctx = ctx
self.message = message self.message = message
self.timeout = timeout self.timeout = timeout
async def on_timeout(self): async def on_timeout(self):
await self.message.edit(view=None) try:
await self.message.edit(view=None)
except NotFound:
pass
@ui.select(cls=ui.RoleSelect, placeholder="Select a role", min_values=0, max_values=25) @ui.select(cls=ui.RoleSelect, placeholder="Select a role", min_values=0, max_values=25)
async def addrole_select(self, interaction: Interaction, select: ui.RoleSelect): async def addrole_select(self, interaction: Interaction, select: ui.RoleSelect):

View file

@ -1,20 +1,24 @@
from discord import ButtonStyle, Interaction, Message, ui from discord import ButtonStyle, Interaction, Message, ui
from discord.errors import NotFound
from redbot.core import commands from redbot.core import commands
from aurora.utilities.config import config from ..utilities.config import config
from aurora.utilities.factory import guild_embed from ..utilities.factory import guild_embed
from aurora.utilities.utils import create_pagesize_options from ..utilities.utils import create_pagesize_options
class Guild(ui.View): class Guild(ui.View):
def __init__(self, ctx: commands.Context, message: Message, timeout: int = None): def __init__(self, ctx: commands.Context, message: Message, timeout: int | None = None):
super().__init__() super().__init__()
self.ctx = ctx self.ctx = ctx
self.message = message self.message = message
self.timeout = timeout self.timeout = timeout
async def on_timeout(self): async def on_timeout(self):
await self.message.edit(view=None) try:
await self.message.edit(view=None)
except NotFound:
pass
@ui.button(label="Show Moderator", style=ButtonStyle.green, row=0) @ui.button(label="Show Moderator", style=ButtonStyle.green, row=0)
async def show_moderator(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument async def show_moderator(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument

View file

@ -1,20 +1,24 @@
from discord import ButtonStyle, Interaction, Message, ui from discord import ButtonStyle, Interaction, Message, ui
from discord.errors import NotFound
from redbot.core import commands from redbot.core import commands
from redbot.core.utils.chat_formatting import error from redbot.core.utils.chat_formatting import error
from aurora.utilities.config import config from ..utilities.config import config
from aurora.utilities.factory import immune_embed from ..utilities.factory import immune_embed
class Immune(ui.View): class Immune(ui.View):
def __init__(self, ctx: commands.Context, message: Message, timeout: int = None): def __init__(self, ctx: commands.Context, message: Message, timeout: int | None = None):
super().__init__() super().__init__()
self.ctx = ctx self.ctx = ctx
self.message = message self.message = message
self.timeout = timeout self.timeout = timeout
async def on_timeout(self): async def on_timeout(self):
await self.message.edit(view=None) try:
await self.message.edit(view=None)
except NotFound:
pass
@ui.select(cls=ui.RoleSelect, placeholder="Select a role", min_values=0, max_values=25) @ui.select(cls=ui.RoleSelect, placeholder="Select a role", min_values=0, max_values=25)
async def immune_select(self, interaction: Interaction, select: ui.RoleSelect): async def immune_select(self, interaction: Interaction, select: ui.RoleSelect):

View file

@ -1,20 +1,24 @@
from discord import ButtonStyle, Interaction, Message, ui from discord import ButtonStyle, Interaction, Message, ui
from discord.errors import NotFound
from redbot.core import commands from redbot.core import commands
from aurora.utilities.config import config from ..utilities.config import config
from aurora.utilities.factory import overrides_embed from ..utilities.factory import overrides_embed
from aurora.utilities.utils import create_pagesize_options from ..utilities.utils import create_pagesize_options
class Overrides(ui.View): class Overrides(ui.View):
def __init__(self, ctx: commands.Context, message: Message, timeout: int = None): def __init__(self, ctx: commands.Context, message: Message, timeout: int | None = None):
super().__init__() super().__init__()
self.ctx = ctx self.ctx = ctx
self.message = message self.message = message
self.timeout = timeout self.timeout = timeout
async def on_timeout(self): async def on_timeout(self):
await self.message.edit(view=None) try:
await self.message.edit(view=None)
except NotFound:
pass
@ui.button(label="Auto Evidence Format", style=ButtonStyle.green, row=0) @ui.button(label="Auto Evidence Format", style=ButtonStyle.green, row=0)
async def auto_evidenceformat(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument async def auto_evidenceformat(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument

71
aurora/menus/types.py Normal file
View file

@ -0,0 +1,71 @@
from discord import ButtonStyle, Interaction, Message, ui
from discord.errors import NotFound
from redbot.core import commands
from ..models.type import Type
from ..utilities.config import config
from ..utilities.factory import type_embed
class Types(ui.View):
def __init__(self, ctx: commands.Context, message: Message, moderation_type: Type, timeout: int | None = None):
super().__init__()
self.ctx = ctx
self.message = message
self.type = moderation_type
self.timeout = timeout
async def on_timeout(self):
try:
await self.message.edit(view=None)
except NotFound:
pass
@ui.button(label="Show in History", style=ButtonStyle.green, row=0)
async def show_in_history(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
if not interaction.user.guild_permissions.manage_guild and not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("You must have the manage guild permission to change this setting.", ephemeral=True)
return
await interaction.response.defer()
current_setting = await config.custom("types", interaction.guild.id, self.type.key).show_in_history()
await config.custom("types", interaction.guild.id, self.type.key).show_in_history.set(not current_setting)
await interaction.message.edit(embed=await type_embed(self.ctx, self.type))
@ui.button(label="Show Moderator", style=ButtonStyle.green, row=0)
async def show_moderator(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
if not interaction.user.guild_permissions.manage_guild and not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("You must have the manage guild permission to change this setting.", ephemeral=True)
return
await interaction.response.defer()
current_setting = await config.custom("types", interaction.guild.id, self.type.key).show_moderator()
await config.custom("types", interaction.guild.id, self.type.key).show_moderator.set(not current_setting)
await interaction.message.edit(embed=await type_embed(self.ctx, self.type))
@ui.button(label="Use Discord Permissions", style=ButtonStyle.green, row=0)
async def use_discord_permissions(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
if not interaction.user.guild_permissions.manage_guild and not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("You must have the manage guild permission to change this setting.", ephemeral=True)
return
await interaction.response.defer()
current_setting = await config.custom("types", interaction.guild.id, self.type.key).use_discord_permissions()
await config.custom("types", interaction.guild.id, self.type.key).use_discord_permissions.set(not current_setting)
await interaction.message.edit(embed=await type_embed(self.ctx, self.type))
@ui.button(label="DM Users", style=ButtonStyle.green, row=0)
async def dm_users(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
if not interaction.user.guild_permissions.manage_guild and not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("You must have the manage guild permission to change this setting.", ephemeral=True)
return
await interaction.response.defer()
current_setting = await config.custom("types", interaction.guild.id, self.type.key).dm_users()
await config.custom("types", interaction.guild.id, self.type.key).dm_users.set(not current_setting)
await interaction.message.edit(embed=await type_embed(self.ctx, self.type))
@ui.button(label="Reset", style=ButtonStyle.red, row=1)
async def reset(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
if not interaction.user.guild_permissions.manage_guild and not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("You must have the manage guild permission to change this setting.", ephemeral=True)
return
await interaction.response.defer()
await config.custom("types", interaction.guild.id, self.type.key).clear()
await interaction.message.edit(embed=await type_embed(self.ctx, self.type))

View file

@ -0,0 +1,2 @@
from .moderation_types import * # noqa: F403
# This just imports all the built-in moderation types so they can be registered, as they aren't imported anywhere else.

30
aurora/models/base.py Normal file
View file

@ -0,0 +1,30 @@
from typing import Any, Optional
from discord import Guild
from pydantic import BaseModel, ConfigDict
from redbot.core.bot import Red
class AuroraBaseModel(BaseModel):
"""Base class for all models in Aurora."""
model_config = ConfigDict(ignored_types=(Red,), arbitrary_types_allowed=True)
bot: Red
def dump(self) -> dict:
return self.model_dump(exclude={"bot"})
def to_json(self, indent: int | None = None, file: Any | None = None, **kwargs) -> str:
from ..utilities.json import dump, dumps # pylint: disable=cyclic-import
return dump(self.dump(), file, indent=indent, **kwargs) if file else dumps(self.dump(), indent=indent, **kwargs)
class AuroraGuildModel(AuroraBaseModel):
"""Subclass of AuroraBaseModel that includes a guild_id attribute and a guild attribute."""
model_config = ConfigDict(ignored_types=(Red, Guild), arbitrary_types_allowed=True)
guild_id: int
guild: Optional[Guild] = None
def dump(self) -> dict:
return self.model_dump(exclude={"bot", "guild_id", "guild"})
def __repr__(self) -> str:
return f"<{self.__class__.__name__} guild_id={self.guild_id}>"

83
aurora/models/change.py Normal file
View file

@ -0,0 +1,83 @@
import json
from datetime import datetime, timedelta
from typing import Literal, Optional
from redbot.core.bot import Red
from ..utilities.utils import timedelta_from_string
from .base import AuroraBaseModel
from .partials import PartialUser
class Change(AuroraBaseModel):
type: Literal["ORIGINAL", "RESOLVE", "EDIT"]
timestamp: datetime
user_id: int
reason: Optional[str] = None
duration: Optional[timedelta] = None
end_timestamp: Optional[datetime] = None
@property
def unix_timestamp(self) -> int:
return int(self.timestamp.timestamp())
@property
def unix_end_timestamp(self) -> Optional[int]:
if self.end_timestamp:
return int(self.end_timestamp.timestamp())
return None
def __str__(self):
return f"{self.type} {self.user_id} {self.reason}"
def __repr__(self) -> str:
attrs = [
('type', self.type),
('timestamp', self.timestamp),
('user_id', self.user_id),
('reason', self.reason),
('duration', self.duration),
('end_timestamp', self.end_timestamp),
]
joined = ' '.join(f'{key}={value!r}' for key, value in attrs)
return f"<{self.__class__.__name__} {joined}>"
async def get_user(self) -> "PartialUser":
return await PartialUser.from_id(self.bot, self.user_id)
@classmethod
def from_dict(cls, bot: Red, data: dict) -> "Change":
if isinstance(data, str):
data = json.loads(data)
if data.get('duration') and not isinstance(data["duration"], timedelta) and not data["duration"] == "NULL":
duration = timedelta_from_string(data["duration"])
elif data.get('duration') and isinstance(data["duration"], timedelta):
duration = data["duration"]
else:
duration = None
if data.get('end_timestamp') and not isinstance(data["end_timestamp"], datetime):
end_timestamp = datetime.fromtimestamp(data["end_timestamp"])
elif data.get('end_timestamp') and isinstance(data["end_timestamp"], datetime):
end_timestamp = data["end_timestamp"]
else:
end_timestamp = None
if not isinstance(data["timestamp"], datetime):
timestamp = datetime.fromtimestamp(data["timestamp"])
else:
timestamp = data["timestamp"]
try:
data["user_id"] = int(data["user_id"])
except ValueError:
data["user_id"] = 0
data.update({
"timestamp": timestamp,
"end_timestamp": end_timestamp,
"duration": duration
})
if "bot" in data:
del data["bot"]
return cls(bot=bot, **data)

565
aurora/models/moderation.py Normal file
View file

@ -0,0 +1,565 @@
import json
import sqlite3
from datetime import datetime, timedelta
from time import time
from typing import Dict, Iterable, List, Optional, Tuple, Union
import discord
from aiosqlite import Connection, Cursor, OperationalError, Row
from aiosqlite import connect as aiosqlite_connect
from redbot.core import data_manager
from redbot.core.bot import Red
from ..utilities.logger import logger
from ..utilities.utils import timedelta_to_string
from .base import AuroraGuildModel
from .change import Change
from .partials import PartialChannel, PartialRole, PartialUser
from .type import Type, type_registry
class Moderation(AuroraGuildModel):
"""This class represents a moderation case in the database.
Attributes:
bot (Red): The bot instance.
guild (discord.Guild): The guild the case belongs to.
moderation_id (int): The ID of the moderation case.
timestamp (datetime): The timestamp of the case.
moderation_type (Type): The type of moderation case.
target_type (str): The type of target. Should be either `user` or `channel`.
target_id (int): The ID of the target.
moderator_id (int): The ID of the moderator who issued the case.
role_id (int): The ID of the role, if applicable.
duration (timedelta): The duration of the case, if applicable.
end_timestamp (datetime): The end timestamp of the case, if applicable.
reason (str): The reason for the case.
resolved (bool): Whether the case is resolved.
resolved_by (int): The ID of the user who resolved the case.
resolve_reason (str): The reason the case was resolved.
expired (bool): Whether the case is expired.
changes (List[Change]): A list of changes to the case.
metadata (Dict): A dictionary of metadata stored with the case.
Properties:
id (int): The ID of the case.
type (Type): The type of the case.
unix_timestamp (int): The timestamp of the case as a Unix timestamp.
Methods:
get_moderator: Gets the moderator who issued the case.
get_target: Gets the target of the case.
get_resolved_by: Gets the user who resolved the case.
get_role: Gets the role, if applicable.
resolve: Resolves the case.
update: Updates the case in the database.
Class Methods:
from_dict: Creates a `Moderation` object from a dictionary.
from_result: Creates a `Moderation` object from a database result.
execute: Executes a query on the database.
get_latest: Gets the latest cases from the database.
get_next_case_number: Gets the next case number to use.
find_by_id: Finds a case by its ID.
find_by_target: Finds cases by the target.
find_by_moderator: Finds cases by the moderator.
log: Logs a moderation case in the database.
Static Methods:
connect: Connects to the SQLite database.
"""
moderation_id: int
timestamp: datetime
moderation_type: Type
target_type: str
target_id: int
moderator_id: int
role_id: Optional[int] = None
duration: Optional[timedelta] = None
end_timestamp: Optional[datetime] = None
reason: Optional[str] = None
resolved: bool
resolved_by: Optional[int] = None
resolve_reason: Optional[str] = None
expired: bool
changes: List["Change"]
metadata: Dict
@property
def id(self) -> int:
return self.moderation_id
@property
def type(self) -> Type:
return self.moderation_type
@property
def unix_timestamp(self) -> int:
return int(self.timestamp.timestamp())
async def get_moderator(self) -> "PartialUser":
return await PartialUser.from_id(self.bot, self.moderator_id)
async def get_target(self) -> Union["PartialUser", "PartialChannel"]:
if self.target_type.lower() == "user":
return await PartialUser.from_id(self.bot, self.target_id)
return await PartialChannel.from_id(self.bot, self.target_id, self.guild)
async def get_resolved_by(self) -> Optional["PartialUser"]:
if self.resolved_by:
return await PartialUser.from_id(self.bot, self.resolved_by)
return None
async def get_role(self) -> Optional["PartialRole"]:
if self.role_id:
return await PartialRole.from_id(self.bot, self.guild, self.role_id)
return None
def __str__(self) -> str:
return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}"
def __int__(self) -> int:
return self.moderation_id
def __repr__(self) -> str:
attrs = [
('guild_id', self.guild_id),
('moderation_id', self.moderation_id),
('timestamp', self.timestamp),
('type', self.type),
('target_type', self.target_type),
('target_id', self.target_id),
('moderator_id', self.moderator_id),
('role_id', self.role_id),
('duration', self.duration),
('end_timestamp', self.end_timestamp),
('reason', self.reason),
('resolved', self.resolved),
('resolved_by', self.resolved_by),
('resolve_reason', self.resolve_reason),
('expired', self.expired),
('changes', self.changes),
('metadata', self.metadata),
]
joined = ' '.join(f'{key}={value!r}' for key, value in attrs)
return f"<{self.__class__.__name__} {joined}>"
async def resolve(self, resolved_by: int, reason: str) -> Tuple[bool, str]:
if self.resolved:
raise ValueError("Case is already resolved!")
self.resolved = True
self.resolved_by = resolved_by
self.resolve_reason = reason
success, msg = await self.type.resolve_handler(moderation=self, reason=reason)
if not self.changes:
self.changes.append(Change.from_dict(self.bot, {
"type": "ORIGINAL",
"timestamp": self.timestamp,
"reason": self.reason,
"user_id": self.moderator_id,
"duration": self.duration,
"end_timestamp": self.end_timestamp,
}))
self.changes.append(Change.from_dict(self.bot, {
"type": "RESOLVE",
"timestamp": datetime.now(),
"reason": reason,
"user_id": resolved_by,
}))
await self.update()
return success, msg
async def update(self) -> None:
from ..utilities.json import dumps
query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;"
await self.execute(query, (
self.timestamp.timestamp(),
self.moderation_type.key,
self.target_type,
self.moderator_id,
self.role_id,
timedelta_to_string(self.duration) if self.duration else None,
self.end_timestamp.timestamp() if self.end_timestamp else None,
self.reason,
self.resolved,
self.resolved_by,
self.resolve_reason,
self.expired,
dumps(self.changes),
dumps(self.metadata),
self.moderation_id,
))
logger.verbose("Row updated in moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s",
self.moderation_id,
self.guild_id,
self.timestamp.timestamp(),
self.moderation_type.key,
self.target_type,
self.moderator_id,
self.role_id,
timedelta_to_string(self.duration) if self.duration else None,
self.end_timestamp.timestamp() if self.end_timestamp else None,
self.reason,
self.resolved,
self.resolved_by,
self.resolve_reason,
self.expired,
dumps(self.changes),
dumps(self.metadata),
)
@classmethod
async def from_dict(cls, bot: Red, data: dict) -> "Moderation":
if data.get("guild_id"):
try:
guild = bot.get_guild(data["guild_id"])
if not guild:
guild = await bot.fetch_guild(data["guild_id"])
except (discord.Forbidden, discord.HTTPException):
guild = None
data.update({"guild": guild})
return cls(bot=bot, **data)
@classmethod
async def from_result(cls, bot: Red, result: Iterable, guild_id: int) -> "Moderation":
if result[7] is not None and result[7] != "NULL":
try:
hours, minutes, seconds = map(int, result[7].split(':'))
duration = timedelta(hours=hours, minutes=minutes, seconds=seconds)
except ValueError as e:
logger.error("Error parsing duration for case %s: %s", result[0], result[7])
raise e
else:
duration = None
if result[14] is not None:
changes = json.loads(result[14])
change_obj_list = []
if changes:
for change in changes:
change_obj_list.append(Change.from_dict(bot=bot, data=change))
if result[15] is not None:
metadata = json.loads(result[15])
else:
metadata = {}
moderation_type = str.lower(result[2])
if moderation_type in type_registry:
moderation_type = type_registry[moderation_type]
else:
logger.error("Unknown moderation type in case %s: %s", result[0], result[2])
case = {
"moderation_id": int(result[0]),
"guild_id": int(guild_id),
"timestamp": datetime.fromtimestamp(result[1]),
"moderation_type": moderation_type,
"target_type": str(result[3]),
"target_id": int(result[4]),
"moderator_id": int(result[5]),
"role_id": int(result[6]) if result[6] is not None else None,
"duration": duration,
"end_timestamp": datetime.fromtimestamp(result[8]) if result[8] is not None else None,
"reason": result[9],
"resolved": bool(result[10]),
"resolved_by": result[11],
"resolve_reason": result[12],
"expired": bool(result[13]),
"changes": change_obj_list,
"metadata": metadata if metadata else {},
}
return await cls.from_dict(bot=bot, data=case)
@staticmethod
async def connect() -> Connection:
"""Connects to the SQLite database, and returns a connection object."""
try:
connection = await aiosqlite_connect(
database=data_manager.cog_data_path(raw_name="Aurora") / "aurora.db"
)
return connection
except OperationalError as e:
logger.error("Unable to access the SQLite database!\nError:\n%s", e.msg)
raise ConnectionRefusedError(
f"Unable to access the SQLite Database!\n{e.msg}"
) from e
@classmethod
async def execute(cls, query: str, parameters: tuple | None = None, bot: Red | None = None, guild_id: int | None = None, cursor: Cursor | None = None, return_obj: bool = True) -> Union[Tuple["Moderation"], Iterable[Row]]:
"""Executes a query on the database.
Arguments:
query (str): The query to execute.
parameters (tuple): The parameters to pass to the query.
bot (Red): The bot instance.
guild_id (int): The ID of the guild to execute the query on.
cursor (Cursor): The cursor to use for the query.
return_obj (bool): Whether to return the case object(s). Defaults to `True`. If `False`, returns a `Iterable` of `aiosqlite.Row` objects.
Returns: The result of the query, either as a `Tuple` of `Moderation` objects or an `Iterable` of `aiosqlite.Row` objects.
"""
logger.trace("Executing query: \"%s\" with parameters \"%s\"", query, parameters)
if not parameters:
parameters = ()
if not cursor:
no_cursor = True
database = await cls.connect()
cursor = await database.cursor()
else:
no_cursor = False
try:
await cursor.execute(query, parameters)
except OperationalError as e:
logger.error("Error executing query: \"%s\" with parameters \"%s\"\nError:\n%s",
query, parameters, e)
raise OperationalError(f"Error executing query: \"{query}\" with parameters \"{parameters}\"") from e
results = await cursor.fetchall()
await database.commit()
if no_cursor:
await cursor.close()
await database.close()
if results and return_obj and bot and guild_id:
cases = []
for result in results:
if result[0] == 0:
continue
case = await cls.from_result(bot=bot, result=result, guild_id=guild_id)
cases.append(case)
return tuple(cases)
return results
@classmethod
async def get_latest(cls, bot: Red, guild_id: int, before: datetime | None = None, after: datetime | None = None, limit: int | None = None, offset: int = 0, types: Iterable[Type] | None = None, expired: bool | None = None, cursor: Cursor | None = None) -> Tuple["Moderation"]:
params = []
query = f"SELECT * FROM moderation_{guild_id}"
conditions = []
if types:
conditions.append(f"moderation_type IN ({', '.join(['?' for _ in types])})")
params.extend([t.key for t in types])
if before:
conditions.append("timestamp < ?")
params.append(int(before.timestamp()))
if after:
conditions.append("timestamp > ?")
params.append(int(after.timestamp()))
if expired is not None:
conditions.append("expired = ?")
params.append(int(expired))
if conditions:
query += " WHERE " + " AND ".join(conditions)
query += " ORDER BY moderation_id DESC"
if limit:
query += " LIMIT ? OFFSET ?"
params.extend((limit, offset))
query += ";"
return await cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=tuple(params) if params else (), cursor=cursor)
@classmethod
async def get_next_case_number(cls, bot: Red, guild_id: int, cursor: Cursor | None = None) -> int:
result = await cls.get_latest(bot=bot, guild_id=guild_id, cursor=cursor, limit=1)
return (result[0].moderation_id + 1) if result else 1
@classmethod
async def find_by_id(cls, bot: Red, moderation_id: int, guild_id: int, cursor: Cursor | None = None) -> "Moderation":
query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;"
case = await cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(moderation_id,), cursor=cursor)
if case:
return case[0]
raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!")
@classmethod
async def find_by_target(cls, bot: Red, guild_id: int, target: int, before: datetime = None, after: datetime = None, types: Iterable[Type] | None = None, expired: bool | None = None, cursor: Cursor | None = None) -> Tuple["Moderation"]:
query = f"SELECT * FROM moderation_{guild_id} WHERE target_id = ?"
params = [target]
if types:
query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})"
for t in types:
params.append(t.key)
if before:
query += " AND timestamp < ?"
params.append(int(before.timestamp()))
if after:
query += " AND timestamp > ?"
params.append(int(after.timestamp()))
if expired is not None:
query += " AND expired = ?"
params.append(int(expired))
query += " ORDER BY moderation_id DESC;"
return await cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=params, cursor=cursor)
@classmethod
async def find_by_moderator(cls, bot: Red, guild_id: int, moderator: int, before: datetime = None, after: datetime = None, types: Iterable[Type] | None = None, expired: bool | None = None, cursor: Cursor | None = None) -> Tuple["Moderation"]:
query = f"SELECT * FROM moderation_{guild_id} WHERE moderator_id = ?"
params = [moderator]
if types:
query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})"
for t in types:
params.append(t.key)
if before:
query += " AND timestamp < ?"
params.append(int(before.timestamp()))
if after:
query += " AND timestamp > ?"
params.append(int(after.timestamp()))
if expired is not None:
query += " AND expired = ?"
params.append(int(expired))
query += " ORDER BY moderation_id DESC;"
return await cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=params, cursor=cursor)
@classmethod
async def log(
cls,
bot: Red,
guild_id: int,
moderator_id: int,
moderation_type: Type,
target_type: str,
target_id: int,
role_id: int | None = None,
duration: timedelta | None = None,
reason: str | None = None,
database: sqlite3.Connection | None = None,
timestamp: datetime | None = None,
resolved: bool = False,
resolved_by: int | None = None,
resolved_reason: str | None = None,
expired: bool | None = None,
changes: list | None = None,
metadata: dict | None = None,
return_obj: bool = True,
) -> Union["Moderation", int]:
"""Logs a moderation case in the database.
Args:
bot (Red): The bot instance.
guild_id (int): The ID of the guild to log the case in.
moderator_id (int): The ID of the moderator who issued the case.
moderation_type (Type): The type of moderation case. See `aurora.models.moderation_types` for the built-in options.
target_type (str): The type of target. Should be either `user` or `channel`.
target_id (int): The ID of the target.
role_id (int): The ID of the role, if applicable.
duration (timedelta): The duration of the case, if applicable.
reason (str): The reason for the case.
database (sqlite3.Connection): The database connection to use to log the case. A connection will be automatically created if not provided.
timestamp (datetime): The timestamp of the case. Will be automatically generated if not provided.
resolved (bool): Whether the case is resolved.
resolved_by (int): The ID of the user who resolved the case.
resolved_reason (str): The reason the case was resolved.
expired (bool): Whether the case is expired.
changes (list): A list of changes to log. You usually shouldn't pass this, as it's automatically generated by the `/edit` and `/resolve` commands.
metadata (dict): A dictionary of metadata to store with the case.
return_obj (bool): Whether to return the case object. Defaults to `True`. If `False`, returns the case ID.
Returns:
Union[Moderation, int]: The `Moderation` object if `return_obj` is `True`, otherwise the case ID.
"""
from ..utilities.json import dumps
if not timestamp:
timestamp = datetime.fromtimestamp(time())
elif not isinstance(timestamp, datetime):
timestamp = datetime.fromtimestamp(timestamp)
if duration == "NULL":
duration = None
if duration is not None:
end_timestamp = timestamp + duration
else:
duration = None
end_timestamp = None
if not expired:
if end_timestamp:
expired = bool(timestamp > end_timestamp)
else:
expired = False
if reason == "NULL":
reason = None
if resolved_by in ["NULL", "?"]:
resolved_by = None
if resolved_reason == "NULL":
resolved_reason = None
if role_id == 0:
role_id = None
if not database:
database = await cls.connect()
close_db = True
else:
close_db = False
moderation_id = await cls.get_next_case_number(bot=bot, guild_id=guild_id)
case = {
"moderation_id": moderation_id,
"timestamp": timestamp.timestamp(),
"moderation_type": moderation_type.key,
"target_type": target_type,
"target_id": target_id,
"moderator_id": moderator_id,
"role_id": role_id,
"duration": timedelta_to_string(duration) if duration else None,
"end_timestamp": end_timestamp.timestamp() if end_timestamp else None,
"reason": reason,
"resolved": resolved,
"resolved_by": resolved_by,
"resolve_reason": resolved_reason,
"expired": expired,
"changes": dumps(changes),
"metadata": dumps(metadata)
}
sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
await database.execute(sql, tuple(case.values()))
await database.commit()
if close_db:
await database.close()
logger.verbose(
"Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s",
guild_id,
case["moderation_id"],
case["timestamp"],
case["moderation_type"],
case["target_type"],
case["target_id"],
case["moderator_id"],
case["role_id"],
case["duration"],
case["end_timestamp"],
case["reason"],
case["resolved"],
case["resolved_by"],
case["resolve_reason"],
case["expired"],
case["changes"],
case["metadata"],
)
if return_obj:
return await cls.find_by_id(bot=bot, moderation_id=moderation_id, guild_id=guild_id)
return moderation_id

File diff suppressed because it is too large Load diff

89
aurora/models/partials.py Normal file
View file

@ -0,0 +1,89 @@
from discord import ChannelType, Forbidden, Guild, HTTPException, InvalidData, NotFound, Role, User
from discord.abc import Messageable
from redbot.core.bot import Red
from .base import AuroraBaseModel, AuroraGuildModel
class PartialUser(AuroraBaseModel):
id: int
username: str
discriminator: int
_obj: User | None
@property
def name(self):
return f"{self.username}#{self.discriminator}" if self.discriminator != 0 else self.username
def __str__(self):
return self.name
def __repr__(self):
return f"<{self.__class__.__name__} id={self.id}>"
@classmethod
async def from_id(cls, bot: Red, user_id: int) -> "PartialUser":
user = bot.get_user(user_id)
if not user:
try:
user = await bot.fetch_user(user_id)
return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator, _obj=user)
except NotFound:
return cls(bot=bot, id=user_id, username="Deleted User", discriminator=0, _obj=None)
return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator, _obj=user)
class PartialChannel(AuroraGuildModel):
id: int
name: str
type: ChannelType
_obj: Messageable | None
@property
def mention(self):
if self.name in ["Deleted Channel", "Forbidden Channel"]:
return self.name
return f"<#{self.id}>"
def __str__(self):
return self.mention
def __repr__(self):
return f"<{self.__class__.__name__} id={self.id} guild_id={self.guild_id}>"
@classmethod
async def from_id(cls, bot: Red, channel_id: int, guild: Guild) -> "PartialChannel":
channel = bot.get_channel(channel_id)
if not channel:
try:
channel = await bot.fetch_channel(channel_id)
return cls(bot=bot, guild_id=channel.guild.id, guild=guild, id=channel.id, name=channel.name, type=channel.type, _obj=channel)
except (NotFound, InvalidData, HTTPException, Forbidden) as e:
if e == Forbidden:
return cls(bot=bot, guild_id=0, id=channel_id, name="Forbidden Channel")
return cls(bot=bot, guild_id=0, id=channel_id, name="Deleted Channel", type=ChannelType.text, _obj=None)
return cls(bot=bot, guild_id=channel.guild.id, guild=guild, id=channel.id, name=channel.name, type=channel.type, _obj=channel)
class PartialRole(AuroraGuildModel):
id: int
name: str
_obj: Role | None
@property
def mention(self):
if self.name in ["Deleted Role", "Forbidden Role"]:
return self.name
return f"<@&{self.id}>"
def __str__(self):
return self.mention
def __repr__(self) -> str:
return f"<{self.__class__.__name__} id={self.id} guild_id={self.guild_id}>"
@classmethod
async def from_id(cls, bot: Red, guild: Guild, role_id: int) -> "PartialRole":
role = guild.get_role(role_id)
if not role:
return cls(bot=bot, guild_id=guild.id, id=role_id, name="Deleted Role", _obj=None)
return cls(bot=bot, guild_id=guild.id, id=role.id, name=role.name, _obj=role)

97
aurora/models/type.py Normal file
View file

@ -0,0 +1,97 @@
from abc import ABC, abstractmethod
from typing import Any, Dict, Tuple
from class_registry import ClassRegistry
from class_registry.base import AutoRegister
from discord import Interaction, Member, User
from discord.abc import Messageable
from redbot.core import commands
type_registry: Dict['str', 'Type'] = ClassRegistry(attr_name='key', unique=True)
class Type(AutoRegister(type_registry), ABC):
"""This is a base class for moderation types.
Attributes:
key (str): The key to use for this type. This should be unique, as this is how the type is registered internally. Changing this key will break existing cases with this type.
string (str): The string to display for this type.
verb (str): The verb to use for this type.
embed_desc (str): The string to use for embed descriptions.
channel (bool): Whether this type targets channels or users. If this is `true` in a subclass, its overridden handler methods should be typed with `discord.abc.Messageable` instead of `discord.Member | discord.User`.
removes_from_guild (bool): Whether this type's handler removes the target from the guild, or if the moderation is expected to occur whenever the user is not in the guild. This does not actually remove the target from the guild, the handler method is responsible for that.
Properties:
name (str): The string to display for this type. This is the same as the `string` attribute.
"""
key = "type"
string = "type"
verb = "typed"
embed_desc = "been "
channel = False
removes_from_guild = False
@abstractmethod
def void(self) -> Any:
"""This method should be overridden by any child classes. This is a placeholder to allow for automatic class registration."""
raise NotImplementedError
@property
def name(self) -> str:
"""Alias for the `string` attribute."""
return self.string
def __str__(self) -> str:
return self.string
def __repr__(self) -> str:
attrs = [
('key', self.key),
('channel', self.channel),
]
joined = ' '.join(f'{key}={value!r}' for key, value in attrs)
return f"<{self.__class__.__name__} {joined}>"
@classmethod
async def handler(cls, ctx: commands.Context, target: Member | User | Messageable, silent: bool, **kwargs) -> 'Type': # pylint: disable=unused-argument
"""This method should be overridden by any child classes, but should retain the same starting keyword arguments.
Arguments:
ctx (commands.Context): The context of the command.
target (discord.Member | discord.User | discord.abc.Messageable): The target of the moderation.
silent (bool): Whether details about the moderation should be DM'ed to the target of the moderation.
"""
raise NotImplementedError
@classmethod
async def resolve_handler(cls, moderation, reason: str) -> Tuple[bool, str]: # pylint: disable=unused-argument
"""This method should be overridden by any resolvable child classes, but should retain the same keyword arguments.
If your moderation type should not be resolvable, do not override this.
Arguments:
moderation (aurora.models.Moderation): The moderation to resolve.
reason (str): The reason for resolving the moderation.
"""
raise NotImplementedError
@classmethod
async def expiry_handler(cls, moderation) -> int: # pylint: disable=unused-argument
"""This method should be overridden by any expirable child classes, but should retain the same keyword arguments and return an integer.
If your moderation type should not expire, do not override this, but also do not set an `end_timestamp` when you log your moderation.
Arguments:
moderation (aurora.models.Moderation): The moderation that is expiring.
"""
raise NotImplementedError
@classmethod
async def duration_edit_handler(cls, interaction: Interaction, old_moderation, new_moderation) -> bool: # pylint: disable=unused-argument
"""This method should be overridden by any child classes with editable durations, but should retain the same keyword arguments and should return True if the duration was successfully modified, or False if it was not.
If your moderation type's duration should not be editable, do not override this.
Arguments:
interaction (discord.Interaction): The interaction that triggered the duration edit.
old_moderation (aurora.models.Moderation): The old moderation, from before the `/edit` command was invoked.
new_moderation (aurora.models.Moderation): The current state of the moderation.
"""
raise NotImplementedError

View file

@ -27,3 +27,13 @@ def register_config(config_obj: Config):
history_inline_pagesize=None, history_inline_pagesize=None,
auto_evidenceformat=None, auto_evidenceformat=None,
) )
moderation_type = {
"show_in_history": True,
"show_moderator": None,
"use_discord_permissions": None,
"dm_users": None,
}
config_obj.init_custom("types", 2)
config_obj.register_custom("types", **moderation_type)

View file

@ -1,219 +0,0 @@
# pylint: disable=cyclic-import
import json
import sqlite3
import time
from datetime import datetime, timedelta
from discord import Guild
from redbot.core import data_manager
from .logger import logger
from .utils import (convert_timedelta_to_str, generate_dict,
get_next_case_number)
def connect() -> sqlite3.Connection:
"""Connects to the SQLite database, and returns a connection object."""
try:
connection = sqlite3.connect(
database=data_manager.cog_data_path(raw_name="Aurora") / "aurora.db"
)
return connection
except sqlite3.OperationalError as e:
logger.error("Unable to access the SQLite database!\nError:\n%s", e.msg)
raise ConnectionRefusedError(
f"Unable to access the SQLite Database!\n{e.msg}"
) from e
async def create_guild_table(guild: Guild):
database = connect()
cursor = database.cursor()
try:
cursor.execute(f"SELECT * FROM `moderation_{guild.id}`")
logger.debug("SQLite Table exists for server %s (%s)", guild.name, guild.id)
except sqlite3.OperationalError:
query = f"""
CREATE TABLE `moderation_{guild.id}` (
moderation_id INTEGER PRIMARY KEY,
timestamp INTEGER NOT NULL,
moderation_type TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT NOT NULL,
moderator_id TEXT NOT NULL,
role_id TEXT,
duration TEXT,
end_timestamp INTEGER,
reason TEXT,
resolved INTEGER NOT NULL,
resolved_by TEXT,
resolve_reason TEXT,
expired INTEGER NOT NULL,
changes TEXT NOT NULL,
metadata TEXT NOT NULL
)
"""
cursor.execute(query)
index_query_1 = f"CREATE INDEX IF NOT EXISTS idx_target_id ON moderation_{guild.id}(target_id);"
cursor.execute(index_query_1)
index_query_2 = f"CREATE INDEX IF NOT EXISTS idx_moderator_id ON moderation_{guild.id}(moderator_id);"
cursor.execute(index_query_2)
index_query_3 = f"CREATE INDEX IF NOT EXISTS idx_moderation_id ON moderation_{guild.id}(moderation_id);"
cursor.execute(index_query_3)
insert_query = f"""
INSERT INTO `moderation_{guild.id}`
(moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
insert_values = (
0,
0,
"NULL",
"NULL",
0,
0,
0,
"NULL",
0,
"NULL",
0,
"NULL",
"NULL",
0,
json.dumps([]),
json.dumps({}),
)
cursor.execute(insert_query, insert_values)
database.commit()
logger.debug(
"SQLite Table (moderation_%s) created for %s (%s)",
guild.id,
guild.name,
guild.id,
)
database.close()
async def mysql_log(
guild_id: str,
author_id: str,
moderation_type: str,
target_type: str,
target_id: int,
role_id: int,
duration: timedelta,
reason: str,
database: sqlite3.Connection = None,
timestamp: int = None,
resolved: bool = False,
resolved_by: str = None,
resolved_reason: str = None,
expired: bool = None,
changes: list = None,
metadata: dict = None,
) -> int:
if not timestamp:
timestamp = int(time.time())
if duration != "NULL":
end_timedelta = datetime.fromtimestamp(timestamp) + duration
end_timestamp = int(end_timedelta.timestamp())
duration = convert_timedelta_to_str(duration)
else:
end_timestamp = 0
if not expired:
if int(time.time()) > end_timestamp:
expired = 1
else:
expired = 0
if resolved_by is None:
resolved_by = "NULL"
if resolved_reason is None:
resolved_reason = "NULL"
if not database:
database = connect()
close_db = True
else:
close_db = False
cursor = database.cursor()
moderation_id = await get_next_case_number(guild_id=guild_id, cursor=cursor)
sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
val = (
moderation_id,
timestamp,
moderation_type,
target_type,
target_id,
author_id,
role_id,
duration,
end_timestamp,
reason,
int(resolved),
resolved_by,
resolved_reason,
expired,
json.dumps(changes if changes else []),
json.dumps(metadata if metadata else {}),
)
cursor.execute(sql, val)
cursor.close()
database.commit()
if close_db:
database.close()
logger.debug(
"Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s",
guild_id,
moderation_id,
timestamp,
moderation_type,
target_type,
target_id,
author_id,
role_id,
duration,
end_timestamp,
reason,
int(resolved),
resolved_by,
resolved_reason,
expired,
changes,
metadata,
)
return moderation_id
async def fetch_case(moderation_id: int, guild_id: str) -> dict:
"""This method fetches a case from the database and returns the case's dictionary."""
database = connect()
cursor = database.cursor()
query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;"
cursor.execute(query, (moderation_id,))
result = cursor.fetchone()
cursor.close()
database.close()
return generate_dict(result)

View file

@ -2,78 +2,68 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Union from typing import Union
from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User from discord import Color, Embed, Guild, Interaction, Member, Message, Role, User
from redbot.core import commands from redbot.core import commands
from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, inline, warning
from aurora.utilities.config import config from ..models.moderation import Moderation
from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str from ..models.partials import PartialUser
from ..models.type import Type
from .config import config
from .utils import get_bool_emoji, get_pagesize_str
async def message_factory( async def message_factory(
bot: Red,
color: Color, color: Color,
guild: Guild, guild: Guild,
reason: str, reason: str,
moderation_type: str, moderation_type: Type,
moderator: Union[Member, User] = None, moderator: Union[Member, User] | None = None,
duration: timedelta = None, duration: timedelta | None = None,
response: InteractionMessage = None, response: Message | None = None,
role: Role = None, case: bool = True,
) -> Embed: ) -> Embed:
"""This function creates a message from set parameters, meant for contacting the moderated user. """This function creates a message from set parameters, meant for contacting the moderated user.
Args: Args:
bot (Red): The bot instance.
color (Color): The color of the embed. color (Color): The color of the embed.
guild (Guild): The guild the moderation occurred in. guild (Guild): The guild the moderation occurred in.
reason (str): The reason for the moderation. reason (str): The reason for the moderation.
moderation_type (str): The type of moderation. moderation_type (Type): The type of moderation.
moderator (Union[Member, User], optional): The moderator who performed the moderation. Defaults to None. moderator (Union[Member, User], optional): The moderator who performed the moderation. Defaults to None.
duration (timedelta, optional): The duration of the moderation. Defaults to None. duration (timedelta, optional): The duration of the moderation. Defaults to None.
response (InteractionMessage, optional): The response message. Defaults to None. response (Message, optional): The response message. Defaults to None.
role (Role, optional): The role that was added or removed. Defaults to None. case (bool, optional): Whether the message is for a moderation case. Defaults to True.
Returns: Returns:
embed: The message embed. embed: The message embed.
""" """
if response is not None and moderation_type not in [ if response is not None and not moderation_type.removes_from_guild:
"kicked",
"banned",
"tempbanned",
"unbanned",
]:
guild_name = f"[{guild.name}]({response.jump_url})" guild_name = f"[{guild.name}]({response.jump_url})"
else: else:
guild_name = guild.name guild_name = guild.name
title = moderation_type if duration:
if moderation_type in ["tempbanned", "muted"] and duration:
embed_duration = f" for {humanize_timedelta(timedelta=duration)}" embed_duration = f" for {humanize_timedelta(timedelta=duration)}"
else: else:
embed_duration = "" embed_duration = ""
if moderation_type == "note":
embed_desc = "received a"
elif moderation_type == "addrole":
embed_desc = f"received the {role.name} role"
title = "Role Added"
moderation_type = ""
elif moderation_type == "removerole":
embed_desc = f"lost the {role.name} role"
title = "Role Removed"
moderation_type = ""
else:
embed_desc = "been"
embed = Embed( embed = Embed(
title=str.title(title), title=str.title(moderation_type.verb),
description=f"You have {embed_desc} {moderation_type}{embed_duration} in {guild_name}.", description=f"You have {moderation_type.embed_desc}{moderation_type.verb}{embed_duration} in {guild_name}.",
color=color, color=color,
timestamp=datetime.now(), timestamp=datetime.now(),
) )
if await config.guild(guild).show_moderator() and moderator is not None: show_moderator = await config.custom("types", guild.id, moderation_type.key).show_moderator()
if show_moderator is None:
show_moderator = await config.guild(guild).show_moderator()
if show_moderator and moderator is not None:
embed.add_field( embed.add_field(
name="Moderator", value=f"`{moderator.name} ({moderator.id})`", inline=False name="Moderator", value=f"`{moderator.name} ({moderator.id})`", inline=False
) )
@ -85,312 +75,243 @@ async def message_factory(
else: else:
embed.set_author(name=guild.name) embed.set_author(name=guild.name)
if case:
embed.set_footer(
text=f"Case #{await Moderation.get_next_case_number(bot=bot, guild_id=guild.id):,}",
icon_url="attachment://arrow.png",
)
return embed
async def resolve_factory(moderation: Moderation, reason: str) -> Embed:
"""This function creates a resolved embed from set parameters, meant for contacting the moderated user.
Args:
moderation (aurora.models.Moderation): The moderation object.
reason (str): The reason for resolving the moderation.
Returns: `discord.Embed`
"""
embed = Embed(
title=str.title(moderation.type.name) + " Resolved",
description=f"Your {moderation.type.name} in {moderation.guild.name} has been resolved.",
color=await moderation.bot.get_embed_color(moderation.guild.channels[0]),
timestamp=datetime.now(),
)
embed.add_field(name="Reason", value=f"`{reason}`", inline=False)
if moderation.guild.icon.url is not None:
embed.set_author(name=moderation.guild.name, icon_url=moderation.guild.icon.url)
else:
embed.set_author(name=moderation.guild.name)
embed.set_footer( embed.set_footer(
text=f"Case #{await get_next_case_number(guild.id):,}", text=f"Case #{moderation.id:,}",
icon_url="attachment://arrow.png", icon_url="attachment://arrow.png",
) )
return embed return embed
async def log_factory( async def log_factory(
interaction: Interaction, case_dict: dict, resolved: bool = False ctx: commands.Context, moderation: Moderation, resolved: bool = False
) -> Embed: ) -> Embed:
"""This function creates a log embed from set parameters, meant for moderation logging. """This function creates a log embed from set parameters, meant for moderation logging.
Args: Args:
interaction (Interaction): The interaction object. ctx (commands.Context): The ctx object.
case_dict (dict): The case dictionary. moderation (aurora.models.Moderation): The moderation object.
resolved (bool, optional): Whether the case is resolved or not. Defaults to False. resolved (bool, optional): Whether the case is resolved or not. Defaults to False.
""" """
target = await moderation.get_target()
moderator = await moderation.get_moderator()
if resolved: if resolved:
if case_dict["target_type"] == "USER":
target_user = await fetch_user_dict(interaction.client, case_dict["target_id"])
target_name = (
f"`{target_user['name']}`"
if target_user["discriminator"] == "0"
else f"`{target_user['name']}#{target_user['discriminator']}`"
)
elif case_dict["target_type"] == "CHANNEL":
target_user = await fetch_channel_dict(interaction.guild, case_dict["target_id"])
if target_user["mention"]:
target_name = f"{target_user['mention']}"
else:
target_name = f"`{target_user['name']}`"
moderator_user = await fetch_user_dict(interaction.client, case_dict["moderator_id"])
moderator_name = (
f"`{moderator_user['name']}`"
if moderator_user["discriminator"] == "0"
else f"`{moderator_user['name']}#{moderator_user['discriminator']}`"
)
embed = Embed( embed = Embed(
title=f"📕 Case #{case_dict['moderation_id']:,} Resolved", title=f"📕 Case #{moderation.id:,} Resolved",
color=await interaction.client.get_embed_color(interaction.channel), color=await ctx.bot.get_embed_color(ctx.channel),
) )
embed.description = f"**Type:** {str.title(case_dict['moderation_type'])}\n**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})\n**Timestamp:** <t:{case_dict['timestamp']}> | <t:{case_dict['timestamp']}:R>" resolved_by = await moderation.get_resolved_by()
embed.description = f"**Type:** {str.title(moderation.type.string)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** <t:{moderation.unix_timestamp}> | <t:{moderation.unix_timestamp}:R>"
if case_dict["duration"] != "NULL": if moderation.duration is not None:
td = timedelta(
**{
unit: int(val)
for unit, val in zip(
["hours", "minutes", "seconds"],
case_dict["duration"].split(":"),
)
}
)
duration_embed = ( duration_embed = (
f"{humanize_timedelta(timedelta=td)} | <t:{case_dict['end_timestamp']}:R>" f"{humanize_timedelta(timedelta=moderation.duration)} | <t:{moderation.end_timestamp}:R>"
if case_dict["expired"] == "0" if not moderation.expired
else str(humanize_timedelta(timedelta=td)) else str(humanize_timedelta(timedelta=moderation.duration))
) )
embed.description = ( embed.description = (
embed.description embed.description
+ f"\n**Duration:** {duration_embed}\n**Expired:** {bool(case_dict['expired'])}" + f"\n**Duration:** {duration_embed}\n**Expired:** {moderation.expired}"
) )
embed.add_field(name="Reason", value=box(case_dict["reason"]), inline=False) if moderation.metadata.items():
for key, value in moderation.metadata.items():
embed.description += f"\n**{key.title()}:** {value}"
embed.add_field(name="Reason", value=box(moderation.reason), inline=False)
resolved_user = await fetch_user_dict(interaction.client, case_dict["resolved_by"])
resolved_name = (
resolved_user["name"]
if resolved_user["discriminator"] == "0"
else f"{resolved_user['name']}#{resolved_user['discriminator']}"
)
embed.add_field( embed.add_field(
name="Resolve Reason", name="Resolve Reason",
value=f"Resolved by `{resolved_name}` ({resolved_user['id']}) for:\n" value=f"Resolved by `{resolved_by.name}` ({resolved_by.id}) for:\n"
+ box(case_dict["resolve_reason"]), + box(moderation.resolve_reason),
inline=False, inline=False,
) )
else: else:
if case_dict["target_type"] == "USER":
target_user = await fetch_user_dict(interaction.client, case_dict["target_id"])
target_name = (
f"`{target_user['name']}`"
if target_user["discriminator"] == "0"
else f"`{target_user['name']}#{target_user['discriminator']}`"
)
elif case_dict["target_type"] == "CHANNEL":
target_user = await fetch_channel_dict(interaction.guild, case_dict["target_id"])
if target_user["mention"]:
target_name = target_user["mention"]
else:
target_name = f"`{target_user['name']}`"
moderator_user = await fetch_user_dict(interaction.client, case_dict["moderator_id"])
moderator_name = (
f"`{moderator_user['name']}`"
if moderator_user["discriminator"] == "0"
else f"`{moderator_user['name']}#{moderator_user['discriminator']}`"
)
embed = Embed( embed = Embed(
title=f"📕 Case #{case_dict['moderation_id']:,}", title=f"📕 Case #{moderation.id:,}",
color=await interaction.client.get_embed_color(interaction.channel), color=await ctx.bot.get_embed_color(ctx.channel),
) )
embed.description = f"**Type:** {str.title(case_dict['moderation_type'])}\n**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})\n**Timestamp:** <t:{case_dict['timestamp']}> | <t:{case_dict['timestamp']}:R>" embed.description = f"**Type:** {str.title(moderation.type.string)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** <t:{moderation.unix_timestamp}> | <t:{moderation.unix_timestamp}:R>"
if case_dict["duration"] != "NULL": if moderation.duration:
td = timedelta(
**{
unit: int(val)
for unit, val in zip(
["hours", "minutes", "seconds"],
case_dict["duration"].split(":"),
)
}
)
embed.description = ( embed.description = (
embed.description embed.description
+ f"\n**Duration:** {humanize_timedelta(timedelta=td)} | <t:{case_dict['end_timestamp']}:R>" + f"\n**Duration:** {humanize_timedelta(timedelta=moderation.duration)} | <t:{moderation.unix_timestamp}:R>"
) )
embed.add_field(name="Reason", value=box(case_dict["reason"]), inline=False) if moderation.metadata.items():
for key, value in moderation.metadata.items():
embed.description += f"\n**{key.title()}:** {value}"
embed.add_field(name="Reason", value=box(moderation.reason), inline=False)
return embed return embed
async def case_factory(interaction: Interaction, case_dict: dict) -> Embed: async def case_factory(interaction: Interaction, moderation: Moderation) -> Embed:
"""This function creates a case embed from set parameters. """This function creates a case embed from set parameters.
Args: Args:
interaction (Interaction): The interaction object. interaction (discord.Interaction): The interaction object.
case_dict (dict): The case dictionary. moderation (aurora.models.Moderation): The moderation object.
""" """
if case_dict["target_type"] == "USER": target = await moderation.get_target()
target_user = await fetch_user_dict(interaction.client, case_dict["target_id"]) moderator = await moderation.get_moderator()
target_name = (
f"`{target_user['name']}`"
if target_user["discriminator"] == "0"
else f"`{target_user['name']}#{target_user['discriminator']}`"
)
elif case_dict["target_type"] == "CHANNEL":
target_user = await fetch_channel_dict(interaction.guild, case_dict["target_id"])
if target_user["mention"]:
target_name = f"{target_user['mention']}"
else:
target_name = f"`{target_user['name']}`"
moderator_user = await fetch_user_dict(interaction.client, case_dict["moderator_id"])
moderator_name = (
f"`{moderator_user['name']}`"
if moderator_user["discriminator"] == "0"
else f"`{moderator_user['name']}#{moderator_user['discriminator']}`"
)
embed = Embed( embed = Embed(
title=f"📕 Case #{case_dict['moderation_id']:,}", title=f"📕 Case #{moderation.id:,}",
color=await interaction.client.get_embed_color(interaction.channel), color=await interaction.client.get_embed_color(interaction.channel),
) )
embed.description = f"**Type:** {str.title(case_dict['moderation_type'])}\n**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})\n**Resolved:** {bool(case_dict['resolved'])}\n**Timestamp:** <t:{case_dict['timestamp']}> | <t:{case_dict['timestamp']}:R>" embed.description = f"**Type:** {str.title(moderation.type.string)}\n**Target:** `{target.name}` ({target.id})\n**Moderator:** `{moderator.name}` ({moderator.id})\n**Resolved:** {moderation.resolved}\n**Timestamp:** <t:{moderation.unix_timestamp}> | <t:{moderation.unix_timestamp}:R>"
if case_dict["duration"] != "NULL": if moderation.duration:
td = timedelta(
**{
unit: int(val)
for unit, val in zip(
["hours", "minutes", "seconds"], case_dict["duration"].split(":")
)
}
)
duration_embed = ( duration_embed = (
f"{humanize_timedelta(timedelta=td)} | <t:{case_dict['end_timestamp']}:R>" f"{humanize_timedelta(timedelta=moderation.duration)} | <t:{moderation.unix_timestamp}:R>"
if bool(case_dict["expired"]) is False if moderation.expired is False
else str(humanize_timedelta(timedelta=td)) else str(humanize_timedelta(timedelta=moderation.duration))
) )
embed.description += f"\n**Duration:** {duration_embed}\n**Expired:** {bool(case_dict['expired'])}" embed.description += f"\n**Duration:** {duration_embed}\n**Expired:** {moderation.expired}"
embed.description += ( embed.description += (
f"\n**Changes:** {len(case_dict['changes']) - 1}" f"\n**Changes:** {len(moderation.changes) - 1}"
if case_dict["changes"] if moderation.changes
else "\n**Changes:** 0" else "\n**Changes:** 0"
) )
if case_dict["role_id"]: if moderation.role_id:
embed.description += f"\n**Role:** <@&{case_dict['role_id']}>" role = await moderation.get_role()
embed.description += f"\n**Role:** {role.name}"
if case_dict["metadata"]: if moderation.metadata:
if case_dict["metadata"]["imported_from"]: if moderation.metadata.get("imported_from"):
embed.description += ( embed.description += (
f"\n**Imported From:** {case_dict['metadata']['imported_from']}" f"\n**Imported From:** {moderation.metadata['imported_from']}"
) )
moderation.metadata.pop("imported_from")
if moderation.metadata.get("imported_timestamp"):
embed.description += (
f"\n**Imported Timestamp:** <t:{moderation.metadata['imported_timestamp']}> | <t:{moderation.metadata['imported_timestamp']}:R>"
)
moderation.metadata.pop("imported_timestamp")
if moderation.metadata.items():
for key, value in moderation.metadata.items():
embed.description += f"\n**{key.title()}:** {value}"
embed.add_field(name="Reason", value=box(case_dict["reason"]), inline=False) embed.add_field(name="Reason", value=box(moderation.reason), inline=False)
if case_dict["resolved"] == 1: if moderation.resolved:
resolved_user = await fetch_user_dict(interaction.client, case_dict["resolved_by"]) resolved_user = await moderation.get_resolved_by()
resolved_name = ( if not resolved_user:
f"`{resolved_user['name']}`" resolved_user = PartialUser(bot=interaction.client, id=0, username="Deleted User", discriminator="0")
if resolved_user["discriminator"] == "0"
else f"`{resolved_user['name']}#{resolved_user['discriminator']}`"
)
embed.add_field( embed.add_field(
name="Resolve Reason", name="Resolve Reason",
value=f"Resolved by {resolved_name} ({resolved_user['id']}) for:\n{box(case_dict['resolve_reason'])}", value=f"Resolved by `{resolved_user.name or 'Deleted User'}` ({resolved_user.id or '0'}) for:\n{box(moderation.resolve_reason)}",
inline=False, inline=False,
) )
return embed return embed
async def changes_factory(interaction: Interaction, case_dict: dict) -> Embed: async def changes_factory(interaction: Interaction, moderation: Moderation) -> Embed:
"""This function creates a changes embed from set parameters. """This function creates a changes embed from set parameters.
Args: Args:
interaction (Interaction): The interaction object. interaction (discord.Interaction): The interaction object.
case_dict (dict): The case dictionary. moderation (aurora.models.Moderation): The moderation object.
""" """
embed = Embed( embed = Embed(
title=f"📕 Case #{case_dict['moderation_id']:,} Changes", title=f"📕 Case #{moderation.id:,} Changes",
color=await interaction.client.get_embed_color(interaction.channel), color=await interaction.client.get_embed_color(interaction.channel),
) )
memory_dict = {} memory_dict = {}
if case_dict["changes"]: if moderation.changes:
for change in case_dict["changes"]: for change in moderation.changes:
if change["user_id"] not in memory_dict: if change.user_id not in memory_dict:
memory_dict[str(change["user_id"])] = await fetch_user_dict( memory_dict[str(change.user_id)] = await change.get_user()
interaction.client, change["user_id"]
)
user = memory_dict[str(change["user_id"])] user: PartialUser = memory_dict[str(change.user_id)]
name = (
user["name"] timestamp = f"<t:{change.unix_timestamp}> | <t:{change.unix_timestamp}:R>"
if user["discriminator"] == "0" end_timestamp = f"<t:{change.unix_end_timestamp}> | <t:{change.unix_end_timestamp}:R>" if change.end_timestamp else None
else f"{user['name']}#{user['discriminator']}"
change_str = [
f"{bold('User:')} {inline(user.name)} ({user.id})",
f"{bold('Reason:')} {change.reason}" if change.reason else "",
f"{bold('Duration:')} {humanize_timedelta(timedelta=change.duration)}" if change.duration else "",
f"{bold('End Timestamp:')} {end_timestamp}" if end_timestamp else "",
f"{bold('Timestamp')} {timestamp}",
]
copy = change_str.copy()
for string in change_str:
if string == "":
copy.remove(string)
embed.add_field(
name=change.type.title(),
value="\n".join(copy),
inline=False,
) )
timestamp = f"<t:{change['timestamp']}> | <t:{change['timestamp']}:R>"
if change["type"] == "ORIGINAL":
embed.add_field(
name="Original",
value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change['reason']}\n**Timestamp:** {timestamp}",
inline=False,
)
elif change["type"] == "EDIT":
embed.add_field(
name="Edit",
value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change['reason']}\n**Timestamp:** {timestamp}",
inline=False,
)
elif change["type"] == "RESOLVE":
embed.add_field(
name="Resolve",
value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change['reason']}\n**Timestamp:** {timestamp}",
inline=False,
)
else: else:
embed.description = "*No changes have been made to this case.* 🙁" embed.description = "*No changes have been made to this case.* 🙁"
return embed return embed
async def evidenceformat_factory(interaction: Interaction, case_dict: dict) -> str: async def evidenceformat_factory(moderation: Moderation) -> str:
"""This function creates a codeblock in evidence format from set parameters. """This function creates a codeblock in evidence format from set parameters.
Args: Args:
interaction (Interaction): The interaction object. interaction (discord.Interaction): The interaction object.
case_dict (dict): The case dictionary. moderation (aurora.models.Moderation): The moderation object.
""" """
if case_dict["target_type"] == "USER": target = await moderation.get_target()
target_user = await fetch_user_dict(interaction.client, case_dict["target_id"]) moderator = await moderation.get_moderator()
target_name = (
target_user["name"]
if target_user["discriminator"] == "0"
else f"{target_user['name']}#{target_user['discriminator']}"
)
elif case_dict["target_type"] == "CHANNEL": content = f"Case: {moderation.id:,} ({str.title(moderation.type.string)})\nTarget: {target.name} ({target.id})\nModerator: {moderator.name} ({moderator.id})"
target_user = await fetch_channel_dict(interaction.guild, case_dict["target_id"])
target_name = target_user["name"]
moderator_user = await fetch_user_dict(interaction.client, case_dict["moderator_id"]) if moderation.duration is not None:
moderator_name = ( content += f"\nDuration: {humanize_timedelta(timedelta=moderation.duration)}"
moderator_user["name"]
if moderator_user["discriminator"] == "0"
else f"{moderator_user['name']}#{moderator_user['discriminator']}"
)
content = f"Case: {case_dict['moderation_id']:,} ({str.title(case_dict['moderation_type'])})\nTarget: {target_name} ({target_user['id']})\nModerator: {moderator_name} ({moderator_user['id']})" if moderation.role_id:
role = await moderation.get_role()
content += "\nRole: " + (role.name if role is not None else moderation.role_id)
if case_dict["role_id"] != "0": content += f"\nReason: {moderation.reason}"
role = interaction.guild.get_role(int(case_dict["role_id"]))
content += "\nRole: " + (role.name if role is not None else case_dict["role_id"])
if case_dict["duration"] != "NULL": for key, value in moderation.metadata.items():
hours, minutes, seconds = map(int, case_dict["duration"].split(":")) content += f"\n{key.title()}: {value}"
td = timedelta(hours=hours, minutes=minutes, seconds=seconds)
content += f"\nDuration: {humanize_timedelta(timedelta=td)}"
content += f"\nReason: {case_dict['reason']}"
return box(content, "prolog") return box(content, "prolog")
@ -622,3 +543,41 @@ async def immune_embed(ctx: commands.Context) -> Embed:
e.description += "\n\n" + immune_str e.description += "\n\n" + immune_str
return e return e
async def type_embed(ctx: commands.Context, moderation_type = Type) -> Embed:
"""Generates a configuration menu field value for a guild's settings."""
type_settings = {
"show_in_history": await config.custom("types", ctx.guild.id, moderation_type.key).show_in_history(),
"show_moderator": await config.custom("types", ctx.guild.id, moderation_type.key).show_moderator(),
"use_discord_permissions": await config.custom("types", ctx.guild.id, moderation_type.key).use_discord_permissions(),
"dm_users": await config.custom("types", ctx.guild.id, moderation_type.key).dm_users(),
}
guild_str = [
"- "
+ bold("Show in History: ")
+ get_bool_emoji(type_settings["show_in_history"]),
"- "
+ bold("Show Moderator: ")
+ get_bool_emoji(type_settings["show_moderator"]),
"- "
+ bold("Use Discord Permissions: ")
+ get_bool_emoji(type_settings["use_discord_permissions"]),
"- "
+ bold("DM Users: ")
+ get_bool_emoji(type_settings["dm_users"]),
]
guild_str = "\n".join(guild_str)
e = await _config(ctx)
e.title += f": {moderation_type.string.title()} Configuration"
e.description = (
f"""
Use the buttons below to manage Aurora's configuration for the {bold(moderation_type.string)} moderation type.
If an option has a question mark (\N{BLACK QUESTION MARK ORNAMENT}\N{VARIATION SELECTOR-16}) next to it, Aurora will default to the guild level setting instead.
See `{ctx.prefix}aurora set guild` for more information.\n
"""
+ guild_str
)
return e

135
aurora/utilities/json.py Normal file
View file

@ -0,0 +1,135 @@
import json
from datetime import datetime, timedelta
from typing import Any
from redbot.core.bot import Red
from ..models.base import AuroraBaseModel
from ..models.type import Type
class JSONEncoder(json.JSONEncoder):
def default(self, o) -> Any:
match o:
case datetime():
return int(o.timestamp())
case timedelta():
from ..utilities.utils import timedelta_to_string
return timedelta_to_string(o)
case AuroraBaseModel():
return o.dump()
case Type():
return o.key
case Red():
return None
case _:
return super().default(o)
# This is a wrapper around the json module's dumps function that uses our custom JSONEncoder class
def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, indent=None, separators=None,
default=None, sort_keys=False, **kw) -> str:
"""Serialize ``obj`` to a JSON formatted ``str``.
If ``skipkeys`` is true then ``dict`` keys that are not basic types
(``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped
instead of raising a ``TypeError``.
If ``ensure_ascii`` is false, then the return value can contain non-ASCII
characters if they appear in strings contained in ``obj``. Otherwise, all
such characters are escaped in JSON strings.
If ``check_circular`` is false, then the circular reference check
for container types will be skipped and a circular reference will
result in an ``RecursionError`` (or worse).
If ``allow_nan`` is false, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
representation.
If specified, ``separators`` should be an ``(item_separator, key_separator)``
tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
``(',', ': ')`` otherwise. To get the most compact JSON representation,
you should specify ``(',', ':')`` to eliminate whitespace.
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
If *sort_keys* is true (default: ``False``), then the output of
dictionaries will be sorted by key.
"""
return json.dumps(
obj,
cls=JSONEncoder,
skipkeys=skipkeys,
ensure_ascii=ensure_ascii,
check_circular=check_circular,
allow_nan=allow_nan,
indent=indent,
separators=separators,
default=default,
sort_keys=sort_keys,
**kw
)
# This is a wrapper around the json module's dump function that uses our custom JSONEncoder class
def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, indent=None, separators=None,
default=None, sort_keys=False, **kw) -> str:
"""Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
``.write()``-supporting file-like object).
If ``skipkeys`` is true then ``dict`` keys that are not basic types
(``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped
instead of raising a ``TypeError``.
If ``ensure_ascii`` is false, then the strings written to ``fp`` can
contain non-ASCII characters if they appear in strings contained in
``obj``. Otherwise, all such characters are escaped in JSON strings.
If ``check_circular`` is false, then the circular reference check
for container types will be skipped and a circular reference will
result in an ``RecursionError`` (or worse).
If ``allow_nan`` is false, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
in strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
representation.
If specified, ``separators`` should be an ``(item_separator, key_separator)``
tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
``(',', ': ')`` otherwise. To get the most compact JSON representation,
you should specify ``(',', ':')`` to eliminate whitespace.
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
If *sort_keys* is true (default: ``False``), then the output of
dictionaries will be sorted by key.
"""
return json.dump(
obj,
fp,
cls=JSONEncoder,
skipkeys=skipkeys,
ensure_ascii=ensure_ascii,
check_circular=check_circular,
allow_nan=allow_nan,
indent=indent,
separators=separators,
default=default,
sort_keys=sort_keys,
**kw
)

View file

@ -1,23 +1,25 @@
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
import json from datetime import datetime, timedelta
from datetime import datetime from typing import Optional, Tuple, Union
from datetime import timedelta as td
from typing import Optional, Union
import aiosqlite
from dateutil.relativedelta import relativedelta as rd from dateutil.relativedelta import relativedelta as rd
from discord import File, Guild, Interaction, Member, SelectOption, User from discord import File, Guild, Interaction, Member, SelectOption, TextChannel, User
from discord.errors import Forbidden, NotFound from discord.errors import Forbidden
from redbot.core import commands, data_manager from redbot.core import commands, data_manager
from redbot.core.utils.chat_formatting import error from redbot.core.utils.chat_formatting import error
from .config import config from ..models.type import Type
from ..utilities.config import config
from ..utilities.json import dumps
from ..utilities.logger import logger
def check_permissions( def check_permissions(
user: User, user: User,
permissions: list, permissions: Tuple[str],
ctx: Union[commands.Context, Interaction] = None, ctx: commands.Context | Interaction | None = None,
guild: Guild = None, guild: Guild | None = None,
) -> Union[bool, str]: ) -> Union[bool, str]:
"""Checks if a user has a specific permission (or a list of permissions) in a channel.""" """Checks if a user has a specific permission (or a list of permissions) in a channel."""
if ctx: if ctx:
@ -42,11 +44,17 @@ def check_permissions(
async def check_moddable( async def check_moddable(
target: Union[User, Member], interaction: Interaction, permissions: list target: Union[User, Member, TextChannel], ctx: commands.Context, permissions: Tuple[str], moderation_type: Type,
) -> bool: ) -> bool:
"""Checks if a moderator can moderate a target.""" """Checks if a moderator can moderate a target."""
if check_permissions(interaction.client.user, permissions, guild=interaction.guild): is_channel = isinstance(target, TextChannel)
await interaction.response.send_message(
use_discord_permissions = await config.custom("types", ctx.guild.id, moderation_type.key).use_discord_permissions()
if use_discord_permissions is None:
use_discord_permissions = await config.guild(ctx.guild).use_discord_permissions()
if check_permissions(ctx.bot.user, permissions, guild=ctx.guild):
await ctx.send(
error( error(
f"I do not have the `{permissions}` permission, required for this action." f"I do not have the `{permissions}` permission, required for this action."
), ),
@ -54,9 +62,9 @@ async def check_moddable(
) )
return False return False
if await config.guild(interaction.guild).use_discord_permissions() is True: if use_discord_permissions is True:
if check_permissions(interaction.user, permissions, guild=interaction.guild): if check_permissions(ctx.author, permissions, guild=ctx.guild):
await interaction.response.send_message( await ctx.send(
error( error(
f"You do not have the `{permissions}` permission, required for this action." f"You do not have the `{permissions}` permission, required for this action."
), ),
@ -64,21 +72,21 @@ async def check_moddable(
) )
return False return False
if interaction.user.id == target.id: if ctx.author.id == target.id:
await interaction.response.send_message( await ctx.send(
content="You cannot moderate yourself!", ephemeral=True content="You cannot moderate yourself!", ephemeral=True
) )
return False return False
if target.bot: if not is_channel and target.bot:
await interaction.response.send_message( await ctx.send(
content="You cannot moderate bots!", ephemeral=True content="You cannot moderate bots!", ephemeral=True
) )
return False return False
if isinstance(target, Member): if isinstance(target, Member):
if interaction.user.top_role <= target.top_role and await config.guild(interaction.guild).respect_hierarchy() is True: if ctx.author.top_role <= target.top_role and await config.guild(ctx.guild).respect_hierarchy() is True:
await interaction.response.send_message( await ctx.send(
content=error( content=error(
"You cannot moderate members with a higher role than you!" "You cannot moderate members with a higher role than you!"
), ),
@ -86,11 +94,17 @@ async def check_moddable(
) )
return False return False
if target.guild_permissions.administrator:
await ctx.send(
content="You cannot moderate members with the Administrator permission!", ephemeral=True
)
return False
if ( if (
interaction.guild.get_member(interaction.client.user.id).top_role ctx.guild.get_member(ctx.bot.user.id).top_role
<= target.top_role <= target.top_role
): ):
await interaction.response.send_message( await ctx.send(
content=error( content=error(
"You cannot moderate members with a role higher than the bot!" "You cannot moderate members with a role higher than the bot!"
), ),
@ -102,7 +116,7 @@ async def check_moddable(
for role in target.roles: for role in target.roles:
if role.id in immune_roles: if role.id in immune_roles:
await interaction.response.send_message( await ctx.send(
content=error("You cannot moderate members with an immune role!"), content=error("You cannot moderate members with an immune role!"),
ephemeral=True, ephemeral=True,
) )
@ -111,152 +125,56 @@ async def check_moddable(
return True return True
async def get_next_case_number(guild_id: str, cursor=None) -> int: async def log(ctx: commands.Context, moderation_id: int, resolved: bool = False) -> None:
"""This function returns the next case number from the MySQL table for a specific guild."""
from .database import connect
if not cursor:
database = connect()
cursor = database.cursor()
cursor.execute(
f"SELECT moderation_id FROM `moderation_{guild_id}` ORDER BY moderation_id DESC LIMIT 1"
)
result = cursor.fetchone()
return (result[0] + 1) if result else 1
def generate_dict(result) -> dict:
case = {
"moderation_id": result[0],
"timestamp": result[1],
"moderation_type": result[2],
"target_type": result[3],
"target_id": result[4],
"moderator_id": result[5],
"role_id": result[6],
"duration": result[7],
"end_timestamp": result[8],
"reason": result[9],
"resolved": result[10],
"resolved_by": result[11],
"resolve_reason": result[12],
"expired": result[13],
"changes": json.loads(result[14]),
"metadata": json.loads(result[15]),
}
return case
async def fetch_user_dict(client: commands.Bot, user_id: str) -> dict:
"""This function returns a dictionary containing either user information or a standard deleted user template."""
if user_id == "?":
user_dict = {"id": "?", "name": "Unknown User", "discriminator": "0"}
else:
try:
user = client.get_user(int(user_id))
if user is None:
user = await client.fetch_user(int(user_id))
user_dict = {
"id": user.id,
"name": user.name,
"discriminator": user.discriminator,
}
except NotFound:
user_dict = {
"id": user_id,
"name": "Deleted User",
"discriminator": "0",
}
return user_dict
async def fetch_channel_dict(guild: Guild, channel_id: int) -> dict:
"""This function returns a dictionary containing either channel information or a standard deleted channel template."""
try:
channel = guild.get_channel(int(channel_id))
if not channel:
channel = await guild.fetch_channel(channel_id)
channel_dict = {
"id": channel.id,
"name": channel.name,
"mention": channel.mention,
}
except NotFound:
channel_dict = {"id": channel_id, "name": "Deleted Channel", "mention": None}
return channel_dict
async def fetch_role_dict(guild: Guild, role_id: int) -> dict:
"""This function returns a dictionary containing either role information or a standard deleted role template."""
role = guild.get_role(int(role_id))
if not role:
role_dict = {"id": role_id, "name": "Deleted Role"}
role_dict = {"id": role.id, "name": role.name}
return role_dict
async def log(interaction: Interaction, moderation_id: int, resolved: bool = False) -> None:
"""This function sends a message to the guild's configured logging channel when an infraction takes place.""" """This function sends a message to the guild's configured logging channel when an infraction takes place."""
from .database import fetch_case from ..models.moderation import Moderation
from .factory import log_factory from .factory import log_factory
logging_channel_id = await config.guild(interaction.guild).log_channel() logging_channel_id = await config.guild(ctx.guild).log_channel()
if logging_channel_id != " ": if logging_channel_id != " ":
logging_channel = interaction.guild.get_channel(logging_channel_id) logging_channel = ctx.guild.get_channel(logging_channel_id)
case = await fetch_case(moderation_id, interaction.guild.id) try:
if case: moderation = await Moderation.find_by_id(ctx.bot, moderation_id, ctx.guild.id)
embed = await log_factory( embed = await log_factory(
interaction=interaction, case_dict=case, resolved=resolved ctx=ctx, moderation=moderation, resolved=resolved
) )
try: try:
await logging_channel.send(embed=embed) await logging_channel.send(embed=embed)
except Forbidden: except Forbidden:
return return
except ValueError:
return
async def send_evidenceformat(interaction: Interaction, case_dict: dict) -> None: async def send_evidenceformat(ctx: commands.Context, moderation_id: int) -> None:
"""This function sends an ephemeral message to the moderator who took the moderation action, with a pre-made codeblock for use in the mod-evidence channel.""" """This function sends an ephemeral message to the moderator who took the moderation action, with a pre-made codeblock for use in the mod-evidence channel."""
from ..models.moderation import Moderation
from .factory import evidenceformat_factory from .factory import evidenceformat_factory
send_evidence_bool = ( send_evidence_bool = (
await config.user(interaction.user).auto_evidenceformat() await config.user(ctx.author).auto_evidenceformat()
or await config.guild(interaction.guild).auto_evidenceformat() or await config.guild(guild=ctx.guild).auto_evidenceformat()
or False or False
) )
if send_evidence_bool is False: if send_evidence_bool is True:
return moderation = await Moderation.find_by_id(ctx.bot, moderation_id, ctx.guild.id)
content = await evidenceformat_factory(moderation=moderation)
content = await evidenceformat_factory(interaction=interaction, case_dict=case_dict) if not ctx.interaction:
await interaction.followup.send(content=content, ephemeral=True) await ctx.author.send(content=content)
else:
await ctx.send(content=content, ephemeral=True)
def convert_timedelta_to_str(timedelta: td) -> str:
"""This function converts a timedelta object to a string."""
total_seconds = int(timedelta.total_seconds())
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
seconds = total_seconds % 60
return f"{hours}:{minutes}:{seconds}"
def get_bool_emoji(value: Optional[bool]) -> str: def get_bool_emoji(value: Optional[bool]) -> str:
"""Returns a unicode emoji based on a boolean value.""" """Returns a unicode emoji based on a boolean value."""
if value is True: match value:
return "\N{WHITE HEAVY CHECK MARK}" case True:
if value is False: return "\N{WHITE HEAVY CHECK MARK}"
return "\N{NO ENTRY SIGN}" case False:
return "\N{BLACK QUESTION MARK ORNAMENT}\N{VARIATION SELECTOR-16}" return "\N{NO ENTRY SIGN}"
case _:
return "\N{BLACK QUESTION MARK ORNAMENT}\N{VARIATION SELECTOR-16}"
def get_pagesize_str(value: Union[int, None]) -> str: def get_pagesize_str(value: Union[int, None]) -> str:
@ -286,13 +204,91 @@ def create_pagesize_options() -> list[SelectOption]:
) )
return options return options
def timedelta_from_relativedelta(relativedelta: rd) -> td: def timedelta_from_relativedelta(relativedelta: rd) -> timedelta:
"""Converts a relativedelta object to a timedelta object.""" """Converts a relativedelta object to a timedelta object."""
now = datetime.now() now = datetime.now()
then = now - relativedelta then = now - relativedelta
return now - then return now - then
def timedelta_from_string(string: str) -> timedelta:
"""Converts a string to a timedelta object."""
hours, minutes, seconds = map(int, string.split(":"))
return timedelta(hours=hours, minutes=minutes, seconds=seconds)
def timedelta_to_string(td: timedelta) -> str:
"""Converts a timedelta object to a string."""
days = td.days * 24
hours, remainder = divmod(td.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"{days + hours}:{minutes:02}:{seconds:02}"
def get_footer_image(coginstance: commands.Cog) -> File: def get_footer_image(coginstance: commands.Cog) -> File:
"""Returns the footer image for the embeds.""" """Returns the footer image for the embeds."""
image_path = data_manager.bundled_data_path(coginstance) / "arrow.png" image_path = data_manager.bundled_data_path(coginstance) / "arrow.png"
return File(image_path, filename="arrow.png", description="arrow") return File(image_path, filename="arrow.png", description="arrow")
async def create_guild_table(guild: Guild) -> None:
from ..models.moderation import Moderation
try:
await Moderation.execute(f"SELECT * FROM `moderation_{guild.id}`", return_obj=False)
logger.trace("SQLite Table exists for server %s (%s)", guild.name, guild.id)
except aiosqlite.OperationalError:
query = f"""
CREATE TABLE `moderation_{guild.id}` (
moderation_id INTEGER PRIMARY KEY NOT NULL,
timestamp INTEGER NOT NULL,
moderation_type TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id INTEGER NOT NULL,
moderator_id INTEGER NOT NULL,
role_id INTEGER,
duration TEXT,
end_timestamp INTEGER,
reason TEXT,
resolved INTEGER NOT NULL,
resolved_by TEXT,
resolve_reason TEXT,
expired INTEGER NOT NULL,
changes JSON NOT NULL,
metadata JSON NOT NULL
)
"""
await Moderation.execute(query=query, return_obj=False)
index_query_1 = f"CREATE INDEX IF NOT EXISTS idx_target_id ON moderation_{guild.id}(target_id);"
await Moderation.execute(query=index_query_1, return_obj=False)
index_query_2 = f"CREATE INDEX IF NOT EXISTS idx_moderator_id ON moderation_{guild.id}(moderator_id);"
await Moderation.execute(query=index_query_2, return_obj=False)
index_query_3 = f"CREATE INDEX IF NOT EXISTS idx_moderation_id ON moderation_{guild.id}(moderation_id);"
await Moderation.execute(query=index_query_3, return_obj=False)
insert_query = f"""
INSERT INTO `moderation_{guild.id}`
(moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
insert_values = (
0,
0,
"NULL",
"NULL",
0,
0,
None,
None,
None,
None,
0,
None,
None,
0,
dumps([]),
dumps({}),
)
await Moderation.execute(query=insert_query, parameters=insert_values, return_obj=False)
logger.trace("SQLite Table created for server %s (%s)", guild.name, guild.id)

View file

@ -14,16 +14,15 @@ from redbot.cogs.downloader import errors
from redbot.cogs.downloader.converters import InstalledCog from redbot.cogs.downloader.converters import InstalledCog
from redbot.core import commands from redbot.core import commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import bold, error, humanize_list, text_to_file from redbot.core.utils.chat_formatting import error, humanize_list, text_to_file
# pylint: disable=protected-access # pylint: disable=protected-access
class Backup(commands.Cog): class Backup(commands.Cog):
"""A utility to make reinstalling repositories and cogs after migrating the bot far easier.""" """A utility to make reinstalling repositories and cogs after migrating the bot far easier."""
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"] __author__ = ["SeaswimmerTheFsh"]
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs" __version__ = "1.1.0"
__version__ = "1.1.1"
__documentation__ = "https://seacogs.coastalcommits.com/backup/" __documentation__ = "https://seacogs.coastalcommits.com/backup/"
def __init__(self, bot: Red): def __init__(self, bot: Red):
@ -36,9 +35,9 @@ class Backup(commands.Cog):
n = "\n" if "\n\n" not in pre_processed else "" n = "\n" if "\n\n" not in pre_processed else ""
text = [ text = [
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})", f"Cog Version: **{self.__version__}**",
f"{bold('Author:')} {humanize_list(self.__author__)}", f"Author: {humanize_list(self.__author__)}",
f"{bold('Documentation:')} {self.__documentation__}", f"Documentation: {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)
@ -197,7 +196,7 @@ class Backup(commands.Cog):
cog_modules = [] cog_modules = []
for cog in cogs: for cog in cogs:
# If you're forking this cog, make sure to change these strings! # If you're forking this cog, make sure to change these strings!
if cog["name"] == "backup" and "cswimr/SeaCogs" in url: if cog["name"] == "backup" and "SeaswimmerTheFsh/SeaCogs" in url:
continue continue
try: try:
cog_module = await InstalledCog.convert(ctx, cog["name"]) cog_module = await InstalledCog.convert(ctx, cog["name"])
@ -233,7 +232,7 @@ class Backup(commands.Cog):
commit = None commit = None
# If you're forking this cog, make sure to change these strings! # If you're forking this cog, make sure to change these strings!
if cog_name == "backup" and "cswimr/SeaCogs" in url: if cog_name == "backup" and "SeaswimmerTheFsh/SeaCogs" in url:
continue continue
async with repository.checkout( async with repository.checkout(

View file

@ -1,6 +1,6 @@
{ {
"author" : ["cswimr"], "author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing Backup!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).", "install_msg" : "Thank you for installing Backup!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
"name" : "Backup", "name" : "Backup",
"short" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.", "short" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.",
"description" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.", "description" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.",
@ -8,7 +8,7 @@
"hidden": false, "hidden": false,
"disabled": false, "disabled": false,
"min_bot_version": "3.5.6", "min_bot_version": "3.5.6",
"max_bot_version": "3.5.13", "max_bot_version": "3.5.12",
"min_python_version": [3, 9, 0], "min_python_version": [3, 9, 0],
"tags": [ "tags": [
"utility", "utility",

View file

@ -15,7 +15,7 @@ from PIL import Image
from red_commons.logging import getLogger from red_commons.logging import getLogger
from redbot.core import Config, commands, data_manager from redbot.core import Config, commands, data_manager
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import bold, error, humanize_list from redbot.core.utils.chat_formatting import error, humanize_list
import bible.errors import bible.errors
from bible.models import Version from bible.models import Version
@ -24,10 +24,9 @@ from bible.models import Version
class Bible(commands.Cog): class Bible(commands.Cog):
"""Retrieve Bible verses from the API.bible API.""" """Retrieve Bible verses from the API.bible API."""
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"] __author__ = ["SeaswimmerTheFsh"]
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs" __version__ = "1.1.0"
__version__ = "1.1.1" __documentation__ = "https://seacogs.coastalcommits.com/bible/"
__documentation__ = "https://seacogs.coastalcommits.com/pterodactyl/"
def __init__(self, bot: Red): def __init__(self, bot: Red):
super().__init__() super().__init__()
@ -45,13 +44,12 @@ class Bible(commands.Cog):
n = "\n" if "\n\n" not in pre_processed else "" n = "\n" if "\n\n" not in pre_processed else ""
text = [ text = [
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})", f"Cog Version: **{self.__version__}**",
f"{bold('Author:')} {humanize_list(self.__author__)}", f"Author: {humanize_list(self.__author__)}",
f"{bold('Documentation:')} {self.__documentation__}", f"Documentation: {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)
def get_icon(self, color: Colour) -> File: def get_icon(self, color: Colour) -> File:
"""Get the docs.api.bible favicon with a given color.""" """Get the docs.api.bible favicon with a given color."""
image_path = data_manager.bundled_data_path(self) / "api.bible-logo.png" image_path = data_manager.bundled_data_path(self) / "api.bible-logo.png"

View file

@ -1,6 +1,6 @@
{ {
"author" : ["cswimr"], "author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing Bible!\nThis cog requires setting an API key for API.Bible. Please read the [documentation](https://seacogs.coastalcommits.com/bible/#setup) for more information.\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).", "install_msg" : "Thank you for installing Bible!\nThis cog requires setting an API key for API.Bible. Please read the [documentation](https://seacogs.coastalcommits.com/bible/#setup) for more information.\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
"name" : "Bible", "name" : "Bible",
"short" : "Retrieve Bible verses from API.Bible.", "short" : "Retrieve Bible verses from API.Bible.",
"description" : "Retrieve Bible verses from the API.Bible API. This cog requires an API.Bible api key.", "description" : "Retrieve Bible verses from the API.Bible API. This cog requires an API.Bible api key.",

View file

@ -1,4 +1,5 @@
import io import io
from typing import Any, Literal
import aiohttp import aiohttp
import discord import discord
@ -14,10 +15,9 @@ from .model import PartialEmoji
class EmojiInfo(commands.Cog): class EmojiInfo(commands.Cog):
"""Retrieve information about emojis.""" """Retrieve information about emojis."""
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"] __author__: list[str] = ["SeaswimmerTheFsh"]
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs" __version__: str = "1.0.0"
__version__ = "1.0.1" __documentation__: str = "https://seacogs.coastalcommits.com/emojiinfo/"
__documentation__ = "https://seacogs.coastalcommits.com/emojiinfo/"
def __init__(self, bot: Red) -> None: def __init__(self, bot: Red) -> None:
super().__init__() super().__init__()
@ -25,17 +25,16 @@ class EmojiInfo(commands.Cog):
self.logger: RedTraceLogger = getLogger(name="red.SeaCogs.Emoji") self.logger: RedTraceLogger = getLogger(name="red.SeaCogs.Emoji")
def format_help_for_context(self, ctx: commands.Context) -> str: def format_help_for_context(self, ctx: commands.Context) -> str:
pre_processed = super().format_help_for_context(ctx) or "" pre_processed: Any | Literal[''] = super().format_help_for_context(ctx) or ""
n = "\n" if "\n\n" not in pre_processed else "" n: Literal['\n'] | Literal[''] = "\n" if "\n\n" not in pre_processed else ""
text = [ text: list[str] = [
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})", f"Cog Version: **{self.__version__}**",
f"{bold('Author:')} {humanize_list(self.__author__)}", f"Author: {humanize_list(items=self.__author__)}",
f"{bold('Documentation:')} {self.__documentation__}", f"Documentation: {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)
async def fetch_twemoji(self, unicode_emoji) -> str: async def fetch_twemoji(self, unicode_emoji) -> str:
base_url = "https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/72x72/" base_url = "https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/72x72/"
emoji_codepoint = "-".join([hex(ord(char))[2:] for char in unicode_emoji]) emoji_codepoint = "-".join([hex(ord(char))[2:] for char in unicode_emoji])

View file

@ -1,5 +1,5 @@
{ {
"author" : ["cswimr"], "author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing Emoji!", "install_msg" : "Thank you for installing Emoji!",
"name" : "Emoji", "name" : "Emoji",
"short" : "Retrieve information about emojis.", "short" : "Retrieve information about emojis.",

View file

@ -1,303 +0,0 @@
{
"nodes": {
"cachix": {
"inputs": {
"devenv": [
"devenv"
],
"flake-compat": [
"devenv"
],
"git-hooks": [
"devenv"
],
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1728672398,
"narHash": "sha256-KxuGSoVUFnQLB2ZcYODW7AVPAh9JqRlD5BrfsC/Q4qs=",
"owner": "cachix",
"repo": "cachix",
"rev": "aac51f698309fd0f381149214b7eee213c66ef0a",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "latest",
"repo": "cachix",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nix": "nix",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1732121232,
"narHash": "sha256-CmJt7aeSCJnJYGtYpyslRI+pC28RPVD43PD/7kkIVuM=",
"owner": "cachix",
"repo": "devenv",
"rev": "6ff1e5f92c0d74bbb12f7454a239ca2f02e05ea1",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"devenv"
],
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": [
"devenv"
]
},
"locked": {
"lastModified": 1730302582,
"narHash": "sha256-W1MIJpADXQCgosJZT8qBYLRuZls2KSiKdpnTVdKBuvU=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": [
"devenv"
],
"flake-parts": "flake-parts",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs_2",
"nixpkgs-23-11": [
"devenv"
],
"nixpkgs-regression": [
"devenv"
],
"pre-commit-hooks": [
"devenv"
]
},
"locked": {
"lastModified": 1727438425,
"narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=",
"owner": "domenkozar",
"repo": "nix",
"rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1730531603,
"narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-python": {
"inputs": {
"flake-compat": "flake-compat_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1730716553,
"narHash": "sha256-n4cibCp/ggDlSacCTnP8dVnywclQKYcHy6PRfe35Hk0=",
"owner": "cachix",
"repo": "nixpkgs-python",
"rev": "8fcdb8ec34a1c2bae3f5326873a41b310e948ccc",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "nixpkgs-python",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1717432640,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1731676054,
"narHash": "sha256-OZiZ3m8SCMfh3B6bfGC/Bm4x3qc1m2SVEAlkV6iY7Yg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5e4fbfb6b3de1aa2872b76d49fafc942626e2add",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"nixpkgs": "nixpkgs_3",
"nixpkgs-python": "nixpkgs-python",
"systems": "systems"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,46 +0,0 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-python.url = "github:cachix/nixpkgs-python";
nixpkgs-python.inputs = { nixpkgs.follows = "nixpkgs"; };
systems.url = "github:nix-systems/default";
devenv.url = "github:cachix/devenv";
devenv.inputs.nixpkgs.follows = "nixpkgs";
};
nixConfig = {
extra-trusted-public-keys =
"devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=";
extra-substituters = "https://devenv.cachix.org";
};
outputs = { self, nixpkgs, devenv, systems, ... }@inputs:
let forEachSystem = nixpkgs.lib.genAttrs (import systems);
in {
packages = forEachSystem (system: {
devenv-up = self.devShells.${system}.default.config.procfileScript;
devenv-test = self.devShells.${system}.default.config.test;
});
devShells = forEachSystem (system:
let pkgs = nixpkgs.legacyPackages.${system};
in {
default = devenv.lib.mkShell {
inherit inputs pkgs;
modules = [{
languages.python = {
enable = true;
version = "3.11";
uv = {
enable = true;
sync = {
enable = true;
allExtras = true;
};
};
};
}];
};
});
};
}

View file

@ -1,9 +1,9 @@
{ {
"author": [ "author": [
"cswimr" "SeaswimmerTheFsh (seasw.)"
], ],
"install_msg": "Thanks for installing my repo!\n\nIf you have any issues with any of the cogs, please create an issue [here](https://coastalcommits.com/cswimr/SeaCogs/issues) or join my [Discord Server](https://discord.gg/eMUMe77Yb8 ).", "install_msg": "Thanks for installing my repo!\n\nIf you have any issues with any of the cogs, please create an issue [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs/issues) or join my [Discord Server](https://discord.gg/eMUMe77Yb8 ).",
"index_name": "sea-cogs", "index_name": "sea-cogs",
"short": "Various cogs for Red, by cswimr", "short": "Various cogs for Red, by SeaswimmerTheFsh (seasw.)",
"description": "Various cogs for Red, by cswimr" "description": "Various cogs for Red, by SeaswimmerTheFsh (seasw.)"
} }

View file

@ -1,12 +1,12 @@
site_name: SeaCogs Documentation site_name: SeaCogs Documentation
site_url: !ENV [SITE_URL, 'https://seacogs.coastalcommits.com'] site_url: !ENV [SITE_URL, 'https://seacogs.coastalcommits.com']
repo_name: CoastalCommits repo_name: CoastalCommits
repo_url: https://coastalcommits.com/cswimr/SeaCogs repo_url: https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs
edit_uri: !ENV [EDIT_URI, 'src/branch/main/.docs'] edit_uri: !ENV [EDIT_URI, 'src/branch/main/.docs']
copyright: Copyright &copy; 2023-2024, cswimr copyright: Copyright &copy; 2023-2024, SeaswimmerTheFsh
docs_dir: .docs docs_dir: .docs
site_author: cswimr site_author: SeaswimmerTheFsh
site_description: Documentation for my Red-DiscordBot Cogs. site_description: Documentation for my Red-DiscordBot Cogs.
nav: nav:
@ -30,7 +30,7 @@ nav:
plugins: plugins:
- git-authors - git-authors
- search - search
- social #- social
- git-revision-date-localized: - git-revision-date-localized:
enable_creation_date: true enable_creation_date: true
type: timeago type: timeago
@ -113,5 +113,3 @@ watch:
- ./bible - ./bible
- ./nerdify - ./nerdify
- ./pterodactyl - ./pterodactyl
- ./emojiinfo
- ./antipolls

View file

@ -1,6 +1,6 @@
{ {
"author" : ["cswimr"], "author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing Nerdify!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs). Based off of PhasecoreX's [UwU](<https://github.com/PhasecoreX/PCXCogs/tree/master/uwu>) cog.", "install_msg" : "Thank you for installing Nerdify!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs). Based off of PhasecoreX's [UwU](<https://github.com/PhasecoreX/PCXCogs/tree/master/uwu>) cog.",
"name" : "Nerdify", "name" : "Nerdify",
"short" : "Nerdify your text!", "short" : "Nerdify your text!",
"description" : "Nerdify your text!", "description" : "Nerdify your text!",

View file

@ -12,15 +12,13 @@ from typing import Any, Optional, Union
import discord import discord
from redbot.core import commands from redbot.core import commands
from redbot.core.utils import chat_formatting, common_filters from redbot.core.utils import chat_formatting, common_filters
from redbot.core.utils.chat_formatting import bold, humanize_list
class Nerdify(commands.Cog): class Nerdify(commands.Cog):
"""Nerdify your text.""" """Nerdify your text."""
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"] __author__ = ["SeaswimmerTheFsh"]
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs" __version__ = "1.3.4"
__version__ = "1.3.5"
__documentation__ = "https://seacogs.coastalcommits.com/nerdify/" __documentation__ = "https://seacogs.coastalcommits.com/nerdify/"
def __init__(self, bot): def __init__(self, bot):
@ -31,13 +29,12 @@ class Nerdify(commands.Cog):
n = "\n" if "\n\n" not in pre_processed else "" n = "\n" if "\n\n" not in pre_processed else ""
text = [ text = [
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})", f"Cog Version: **{self.__version__}**",
f"{bold('Author:')} {humanize_list(self.__author__)}", f"Author: {chat_formatting.humanize_list(self.__author__)}",
f"{bold('Documentation:')} {self.__documentation__}", f"Documentation: {self.__documentation__}"
] ]
return "\n".join(text) return "\n".join(text)
@commands.command(aliases=["nerd"]) @commands.command(aliases=["nerd"])
async def nerdify( async def nerdify(
self, ctx: commands.Context, *, text: Optional[str] = None self, ctx: commands.Context, *, text: Optional[str] = None

2850
poetry.lock generated Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

View file

@ -1,6 +1,6 @@
{ {
"author" : ["cswimr"], "author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing Pterodactyl!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).\nDocumentation can be found [here](https://seacogs.coastalcommits.com/pterodactyl ).", "install_msg" : "Thank you for installing Pterodactyl!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).\nDocumentation can be found [here](https://seacogs.coastalcommits.com/pterodactyl ).",
"name" : "Pterodactyl", "name" : "Pterodactyl",
"short" : "Interface with Pterodactyl through websockets.", "short" : "Interface with Pterodactyl through websockets.",
"description" : "Interface with Pterodactyl through websockets.", "description" : "Interface with Pterodactyl through websockets.",
@ -9,7 +9,7 @@
"disabled": false, "disabled": false,
"min_bot_version": "3.5.0", "min_bot_version": "3.5.0",
"min_python_version": [3, 8, 0], "min_python_version": [3, 8, 0],
"requirements": ["git+https://github.com/cswimr/pydactyl", "websockets"], "requirements": ["git+https://github.com/SeaswimmerTheFsh/pydactyl", "websockets"],
"tags": [ "tags": [
"pterodactyl", "pterodactyl",
"minecraft", "minecraft",

View file

@ -9,7 +9,7 @@ from pydactyl import PterodactylClient
from redbot.core import app_commands, commands from redbot.core import app_commands, commands
from redbot.core.app_commands import Choice from redbot.core.app_commands import Choice
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import bold, box, error, humanize_list from redbot.core.utils.chat_formatting import box, error, humanize_list
from redbot.core.utils.views import ConfirmView from redbot.core.utils.views import ConfirmView
from pterodactyl import mcsrvstatus from pterodactyl import mcsrvstatus
@ -20,9 +20,8 @@ from pterodactyl.logger import logger
class Pterodactyl(commands.Cog): class Pterodactyl(commands.Cog):
"""Pterodactyl allows you to manage your Pterodactyl Panel from Discord.""" """Pterodactyl allows you to manage your Pterodactyl Panel from Discord."""
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"] __author__ = ["SeaswimmerTheFsh"]
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs" __version__ = "2.0.0"
__version__ = "2.0.4"
__documentation__ = "https://seacogs.coastalcommits.com/pterodactyl/" __documentation__ = "https://seacogs.coastalcommits.com/pterodactyl/"
def __init__(self, bot: Red): def __init__(self, bot: Red):
@ -40,29 +39,12 @@ class Pterodactyl(commands.Cog):
n = "\n" if "\n\n" not in pre_processed else "" n = "\n" if "\n\n" not in pre_processed else ""
text = [ text = [
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"{bold('Cog Version:')} [{self.__version__}]({self.__git__})", f"Cog Version: **{self.__version__}**",
f"{bold('Author:')} {humanize_list(self.__author__)}", f"Author: {humanize_list(self.__author__)}",
f"{bold('Documentation:')} {self.__documentation__}", f"Documentation: {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)
async def cog_load(self) -> None:
pterodactyl_keys = await self.bot.get_shared_api_tokens("pterodactyl")
api_key = pterodactyl_keys.get("api_key")
if api_key is None:
self.task.cancel()
raise ValueError("Pterodactyl API key not set. Please set it using `[p]set api`.")
base_url = await config.base_url()
if base_url is None:
self.task.cancel()
raise ValueError("Pterodactyl base URL not set. Please set it using `[p]pterodactyl config url`.")
server_id = await config.server_id()
if server_id is None:
self.task.cancel()
raise ValueError("Pterodactyl server ID not set. Please set it using `[p]pterodactyl config serverid`.")
self.client = PterodactylClient(base_url, api_key).client
async def cog_unload(self) -> None: async def cog_unload(self) -> None:
self.update_topic.cancel() self.update_topic.cancel()
self.task.cancel() self.task.cancel()
@ -193,47 +175,76 @@ class Pterodactyl(commands.Cog):
async def power(self, ctx: Union[discord.Interaction, commands.Context], action: str, action_ing: str, warning: str = '') -> None: async def power(self, ctx: Union[discord.Interaction, commands.Context], action: str, action_ing: str, warning: str = '') -> None:
if isinstance(ctx, discord.Interaction): if isinstance(ctx, discord.Interaction):
ctx = await self.bot.get_context(ctx) author = ctx.user
else:
author = ctx.author
current_status = await config.current_status() current_status = await config.current_status()
if current_status == action_ing: if current_status == action_ing:
return await ctx.send(f"Server is already {action_ing}.", ephemeral=True) if isinstance(ctx, discord.Interaction):
return await ctx.response.send_message(f"Server is already {action_ing}.", ephemeral=True)
return await ctx.send(f"Server is already {action_ing}.")
if current_status in ["starting", "stopping"] and action != "kill": if current_status in ["starting", "stopping"] and action != "kill":
return await ctx.send("Another power action is already in progress.", ephemeral=True) if isinstance(ctx, discord.Interaction):
return await ctx.response.send_message("Another power action is already in progress.", ephemeral=True)
return await ctx.send("Another power action is already in progress.")
view = ConfirmView(ctx.author, disable_buttons=True) view = ConfirmView(author, disable_buttons=True)
message = await ctx.send(f"{warning}Are you sure you want to {action} the server?", view=view) if isinstance(ctx, discord.Interaction):
await ctx.response.send_message(f"{warning}Are you sure you want to {action} the server?", view=view)
else:
message = await ctx.send(f"{warning}Are you sure you want to {action} the server?", view=view)
await view.wait() await view.wait()
if view.result is True: if view.result is True:
await message.edit(content=f"Sending websocket command to {action} server...", view=None) if isinstance(ctx, discord.Interaction):
await ctx.edit_original_response(content=f"Sending websocket command to {action} server...", view=None)
else:
await message.edit(content=f"Sending websocket command to {action} server...", view=None)
await self.websocket.send(json.dumps({"event": "set state", "args": [action]})) await self.websocket.send(json.dumps({"event": "set state", "args": [action]}))
await message.edit(content=f"Server {action_ing}", view=None) if isinstance(ctx, discord.Interaction):
await ctx.edit_original_response(content=f"Server {action_ing}", view=None)
else:
await message.edit(content=f"Server {action_ing}", view=None)
else: else:
await message.edit(content="Cancelled.", view=None) if isinstance(ctx, discord.Interaction):
await ctx.edit_original_response(content="Cancelled.", view=None)
else:
await message.edit(content="Cancelled.", view=None)
async def send_command(self, ctx: Union[discord.Interaction, commands.Context], command: str): async def send_command(self, ctx: Union[discord.Interaction, commands.Context], command: str):
channel = self.bot.get_channel(await config.console_channel()) channel = self.bot.get_channel(await config.console_channel())
if isinstance(ctx, discord.Interaction): if isinstance(ctx, discord.Interaction):
ctx = await self.bot.get_context(ctx) if channel:
if channel: await channel.send(f"Received console command from {ctx.user.id}: {command[:1900]}", allowed_mentions=discord.AllowedMentions.none())
await channel.send(f"Received console command from {ctx.author.id}: {command[:1900]}", allowed_mentions=discord.AllowedMentions.none()) try:
try: await self.websocket.send(json.dumps({"event": "send command", "args": [command]}))
await self.websocket.send(json.dumps({"event": "send command", "args": [command]})) await ctx.response.send_message(f"Command sent to server. {box(command, 'json')}", ephemeral=True)
await ctx.send(f"Command sent to server. {box(command, 'json')}") except websockets.exceptions.ConnectionClosed as e:
except websockets.exceptions.ConnectionClosed as e: logger.error("WebSocket connection closed: %s", e)
logger.error("WebSocket connection closed: %s", e) await ctx.response.send_message(error("WebSocket connection closed."))
await ctx.send(error("WebSocket connection closed.")) self.task.cancel()
self.task.cancel() self.retry_counter = 0
self.retry_counter = 0 self.task = self.get_task()
self.task = self.get_task() else:
if channel:
await channel.send(f"Received console command from {ctx.author.id}: {command[:1900]}", allowed_mentions=discord.AllowedMentions.none())
try:
await self.websocket.send(json.dumps({"event": "send command", "args": [command]}))
await ctx.send(f"Command sent to server. {box(command, 'json')}")
except websockets.exceptions.ConnectionClosed as e:
logger.error("WebSocket connection closed: %s", e)
await ctx.send(error("WebSocket connection closed."))
self.task.cancel()
self.retry_counter = 0
self.task = self.get_task()
@commands.Cog.listener() @commands.Cog.listener()
async def on_red_api_tokens_update(self, service_name: str, api_tokens: Mapping[str,str]): # pylint: disable=unused-argument async def on_red_api_tokens_update(self, service_name: str, api_tokens: Mapping[str,str]): # pylint: disable=unused-argument

View file

@ -1,14 +1,12 @@
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
import json import json
import re import re
from pathlib import Path from typing import Optional, Union
from typing import Optional, Tuple, Union
import aiohttp import aiohttp
import discord import discord
import websockets import websockets
from pydactyl import PterodactylClient from pydactyl import PterodactylClient
from redbot.core.data_manager import bundled_data_path
from redbot.core.utils.chat_formatting import bold, pagify from redbot.core.utils.chat_formatting import bold, pagify
from pterodactyl.config import config from pterodactyl.config import config
@ -80,12 +78,7 @@ async def establish_websocket_connection(coginstance: Pterodactyl) -> None:
if join_message: if join_message:
if chat_channel is not None: if chat_channel is not None:
if coginstance.bot.embed_requested(chat_channel): if coginstance.bot.embed_requested(chat_channel):
embed, img = await generate_join_leave_embed(coginstance=coginstance, username=join_message,join=True) await chat_channel.send(embed=await generate_join_leave_embed(join_message, True))
if img:
with open(img, 'rb') as file:
await chat_channel.send(embed=embed, file=file)
else:
await chat_channel.send(embed=embed)
else: else:
await chat_channel.send(f"{join_message} joined the game", allowed_mentions=discord.AllowedMentions.none()) await chat_channel.send(f"{join_message} joined the game", allowed_mentions=discord.AllowedMentions.none())
@ -93,12 +86,7 @@ async def establish_websocket_connection(coginstance: Pterodactyl) -> None:
if leave_message: if leave_message:
if chat_channel is not None: if chat_channel is not None:
if coginstance.bot.embed_requested(chat_channel): if coginstance.bot.embed_requested(chat_channel):
embed, img = await generate_join_leave_embed(coginstance=coginstance, username=leave_message,join=False) await chat_channel.send(embed=await generate_join_leave_embed(leave_message, False))
if img:
with open(img, 'rb') as file:
await chat_channel.send(embed=embed, file=file)
else:
await chat_channel.send(embed=embed)
else: else:
await chat_channel.send(f"{leave_message} left the game", allowed_mentions=discord.AllowedMentions.none()) await chat_channel.send(f"{leave_message} left the game", allowed_mentions=discord.AllowedMentions.none())
@ -106,7 +94,7 @@ async def establish_websocket_connection(coginstance: Pterodactyl) -> None:
if achievement_message: if achievement_message:
if chat_channel is not None: if chat_channel is not None:
if coginstance.bot.embed_requested(chat_channel): if coginstance.bot.embed_requested(chat_channel):
await chat_channel.send(embed=await generate_achievement_embed(coginstance, achievement_message['username'], achievement_message['achievement'], achievement_message['challenge'])) await chat_channel.send(embed=await generate_achievement_embed(achievement_message['username'], achievement_message['achievement'], achievement_message['challenge']))
else: else:
await chat_channel.send(f"{achievement_message['username']} has {'completed the challenge' if achievement_message['challenge'] else 'made the advancement'} {achievement_message['achievement']}") await chat_channel.send(f"{achievement_message['username']} has {'completed the challenge' if achievement_message['challenge'] else 'made the advancement'} {achievement_message['achievement']}")
@ -165,7 +153,7 @@ async def check_if_server_message(text: str) -> Union[bool, str]:
regex = await config.server_regex() regex = await config.server_regex()
match: Optional[re.Match[str]] = re.match(regex, text) match: Optional[re.Match[str]] = re.match(regex, text)
if match: if match:
logger.trace("Message is a server message") logger.debug("Message is a server message")
return match.group(1) return match.group(1)
return False return False
@ -174,7 +162,7 @@ async def check_if_chat_message(text: str) -> Union[bool, dict]:
match: Optional[re.Match[str]] = re.match(regex, text) match: Optional[re.Match[str]] = re.match(regex, text)
if match: if match:
groups = {"username": match.group(1), "message": match.group(2)} groups = {"username": match.group(1), "message": match.group(2)}
logger.trace("Message is a chat message\n%s", json.dumps(groups)) logger.debug("Message is a chat message\n%s", json.dumps(groups))
return groups return groups
return False return False
@ -182,7 +170,7 @@ async def check_if_join_message(text: str) -> Union[bool, str]:
regex = await config.join_regex() regex = await config.join_regex()
match: Optional[re.Match[str]] = re.match(regex, text) match: Optional[re.Match[str]] = re.match(regex, text)
if match: if match:
logger.trace("Message is a join message") logger.debug("Message is a join message")
return match.group(1) return match.group(1)
return False return False
@ -190,7 +178,7 @@ async def check_if_leave_message(text: str) -> Union[bool, str]:
regex = await config.leave_regex() regex = await config.leave_regex()
match: Optional[re.Match[str]] = re.match(regex, text) match: Optional[re.Match[str]] = re.match(regex, text)
if match: if match:
logger.trace("Message is a leave message") logger.debug("Message is a leave message")
return match.group(1) return match.group(1)
return False return False
@ -203,23 +191,23 @@ async def check_if_achievement_message(text: str) -> Union[bool, dict]:
groups["challenge"] = True groups["challenge"] = True
else: else:
groups["challenge"] = False groups["challenge"] = False
logger.trace("Message is an achievement message") logger.debug("Message is an achievement message")
return groups return groups
return False return False
async def get_info(username: str) -> Optional[dict]: async def get_info(username: str) -> Optional[dict]:
logger.verbose("Retrieving player info for %s", username) logger.debug("Retrieving player info for %s", username)
endpoint = await config.api_endpoint() endpoint = await config.api_endpoint()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(f"https://playerdb.co/api/player/{endpoint}/{username}") as response: async with session.get(f"https://playerdb.co/api/player/{endpoint}/{username}") as response:
if response.status == 200: if response.status == 200:
logger.verbose("Player info retrieved for %s", username) logger.debug("Player info retrieved for %s", username)
return await response.json() return await response.json()
logger.warning("Failed to retrieve player info for %s: %s", username, response.status) logger.error("Failed to retrieve player info for %s: %s", username, response.status)
return None return None
async def send_chat_discord(coginstance: Pterodactyl, username: str, message: str, avatar_url: str) -> None: async def send_chat_discord(coginstance: Pterodactyl, username: str, message: str, avatar_url: str) -> None:
logger.trace("Sending chat message to Discord") logger.debug("Sending chat message to Discord")
channel = coginstance.bot.get_channel(await config.chat_channel()) channel = coginstance.bot.get_channel(await config.chat_channel())
if channel is not None: if channel is not None:
webhooks = await channel.webhooks() webhooks = await channel.webhooks()
@ -227,37 +215,33 @@ async def send_chat_discord(coginstance: Pterodactyl, username: str, message: st
if webhook is None: if webhook is None:
webhook = await channel.create_webhook(name="Pterodactyl Chat") webhook = await channel.create_webhook(name="Pterodactyl Chat")
await webhook.send(content=message, username=username, avatar_url=avatar_url, allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=True)) await webhook.send(content=message, username=username, avatar_url=avatar_url, allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=True))
logger.trace("Chat message sent to Discord") logger.debug("Chat message sent to Discord")
else: else:
logger.warning("Chat channel not set. Skipping sending chat message to Discord") logger.warning("Chat channel not set. Skipping sending chat message to Discord")
async def generate_join_leave_embed(coginstance: Pterodactyl, username: str, join: bool) -> Tuple[discord.Embed, Optional[Union[str, Path]]]: async def generate_join_leave_embed(username: str, join: bool) -> discord.Embed:
embed = discord.Embed() embed = discord.Embed()
embed.color = discord.Color.green() if join else discord.Color.red() embed.color = discord.Color.green() if join else discord.Color.red()
embed.description = await config.join_msg() if join else await config.leave_msg() embed.description = await config.join_msg() if join else await config.leave_msg()
info = await get_info(username) info = await get_info(username)
if info: if info:
img = None
embed.set_author(name=username, icon_url=info['data']['player']['avatar']) embed.set_author(name=username, icon_url=info['data']['player']['avatar'])
else: else:
img = bundled_data_path(coginstance) / "unknown.png" embed.set_author(name=username, icon_url='https://seafsh.cc/u/j3AzqQ.png')
embed.set_author(name=username, icon_url='attachment://unknown.png')
embed.timestamp = discord.utils.utcnow() embed.timestamp = discord.utils.utcnow()
return embed, img return embed
async def generate_achievement_embed(coginstance: Pterodactyl, username: str, achievement: str, challenge: bool) -> Tuple[discord.Embed, Optional[Union[str, Path]]]: async def generate_achievement_embed(username: str, achievement: str, challenge: bool) -> discord.Embed:
embed = discord.Embed() embed = discord.Embed()
embed.color = discord.Color.from_str('#a800a7') if challenge else discord.Color.from_str('#54fb54') embed.color = discord.Color.from_str('#a800a7') if challenge else discord.Color.from_str('#54fb54')
embed.description = f"{bold(username)} has {'completed the challenge' if challenge else 'made the advancement'} {bold(achievement)}" embed.description = f"{bold(username)} has {'completed the challenge' if challenge else 'made the advancement'} {bold(achievement)}"
info = await get_info(username) info = await get_info(username)
if info: if info:
img = None
embed.set_author(name=username, icon_url=info['data']['player']['avatar']) embed.set_author(name=username, icon_url=info['data']['player']['avatar'])
else: else:
img = bundled_data_path(coginstance) / "unknown.png" embed.set_author(name=username, icon_url='https://seafsh.cc/u/j3AzqQ.png')
embed.set_author(name=username, icon_url='attachment://unknown.png')
embed.timestamp = discord.utils.utcnow() embed.timestamp = discord.utils.utcnow()
return embed, img return embed
def mask_ip(string: str) -> str: def mask_ip(string: str) -> str:
def check(match: re.Match[str]): def check(match: re.Match[str]):

View file

@ -1,44 +1,49 @@
[project] [tool.poetry]
name = "seacogs" name = "seacogs"
version = "0.1.0" version = "0.1.0"
description = "My assorted cogs for Red-DiscordBot." description = "My assorted cogs for Red-DiscordBot."
authors = [{name = "cswimr", email = "seaswimmerthefsh@gmail.com"}] authors = ["SeaswimmerTheFsh"]
license = {file="LICENSE"} license = "MPL 2"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" package-mode = false
dependencies = [
"aiosqlite>=0.20.0",
"beautifulsoup4>=4.12.3",
"colorthief>=0.2.1",
"markdownify>=0.13.1",
"numpy>=2.1.2",
"phx-class-registry>=5.0.0",
"pillow>=10.4.0",
"py-dactyl",
"pydantic>=2.9.2",
"red-discordbot>=3.5.13",
"websockets>=13.1",
]
[project.optional-dependencies] [tool.poetry.dependencies]
documentation = [ python = ">=3.11,<3.12"
"mkdocs>=1.6.1", Red-DiscordBot = "^3.5.9"
"mkdocs-git-authors-plugin>=0.9.0", py-dactyl = "^2.0.4"
"mkdocs-git-revision-date-localized-plugin>=1.2.9", websockets = "^12.0"
"mkdocs-material[imaging]>=9.5.40", pillow = "^10.3.0"
"mkdocstrings[python]>=0.26.1", numpy = "^1.26.4"
"mkdocs-redirects>=1.2.1", pydantic = "^2.7.1"
] colorthief = "^0.2.1"
beautifulsoup4 = "^4.12.3"
markdownify = "^0.12.1"
aiosqlite = "^0.20.0"
phx-class-registry = "^5.0.0"
[tool.uv] [tool.poetry.group.dev]
dev-dependencies = [ optional = true
"pylint>=3.3.1",
"ruff>=0.6.9",
"sqlite-web>=0.6.4",
]
[tool.uv.sources] [tool.poetry.group.dev.dependencies]
py-dactyl = { git = "https://github.com/cswimr/pydactyl" } ruff = "^0.3.1"
pylint = "^3.1.0"
pipx = "^1.5.0"
sqlite-web = "^0.6.4"
[tool.poetry.group.docs]
optional = true
[tool.poetry.group.docs.dependencies]
mkdocs = "1.5.3"
mkdocstrings = {extras = ["python"], version = "0.24.0"}
mkdocs-git-authors-plugin = "0.7.2"
mkdocs-git-revision-date-localized-plugin = "1.2.2"
mkdocs-material = {extras = ["imaging"], version = "^9.5.2"}
mkdocs-redirects = "^1.2.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.ruff] [tool.ruff]
# Exclude a variety of commonly ignored directories. # Exclude a variety of commonly ignored directories.

View file

@ -1,6 +1,6 @@
{ {
"author" : ["cswimr"], "author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing SeaUtils!\nYou can find the source code of this cog [here](https://coastalcommits.com/cswimr/SeaCogs).", "install_msg" : "Thank you for installing SeaUtils!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
"name" : "SeaUtils", "name" : "SeaUtils",
"short" : "A collection of useful utilities.", "short" : "A collection of useful utilities.",
"description" : "A collection of useful utilities.", "description" : "A collection of useful utilities.",

View file

@ -38,26 +38,22 @@ def format_rfc_text(text: str, number: int) -> str:
class SeaUtils(commands.Cog): class SeaUtils(commands.Cog):
"""A collection of random utilities.""" """A collection of random utilities."""
__author__ = ["[cswimr](https://www.coastalcommits.com/cswimr)"] __author__ = ["SeaswimmerTheFsh"]
__git__ = "https://www.coastalcommits.com/cswimr/SeaCogs" __version__ = "1.0.0"
__version__ = "1.0.1"
__documentation__ = "https://seacogs.coastalcommits.com/seautils/"
def __init__(self, bot: Red) -> None: def __init__(self, bot: Red) -> None:
self.bot = bot self.bot = bot
def format_help_for_context(self, ctx: commands.Context) -> str: def format_help_for_context(self, ctx: commands.Context) -> str:
pre_processed = super().format_help_for_context(ctx) or "" pre_processed = super().format_help_for_context(ctx=ctx) or ""
n = "\n" if "\n\n" not in pre_processed else "" n = "\n" if "\n\n" not in pre_processed else ""
text = [ text = [
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"{cf.bold('Cog Version:')} [{self.__version__}]({self.__git__})", f"Cog Version: **{self.__version__}**",
f"{cf.bold('Author:')} {cf.humanize_list(self.__author__)}", f"Author: {cf.humanize_list(items=self.__author__)}"
f"{cf.bold('Documentation:')} {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)
def format_src(self, obj: Any) -> str: def format_src(self, obj: Any) -> str:
"""A large portion of this code is repurposed from Zephyrkul's RTFS cog. """A large portion of this code is repurposed from Zephyrkul's RTFS cog.
https://github.com/Zephyrkul/FluffyCogs/blob/master/rtfs/rtfs.py""" https://github.com/Zephyrkul/FluffyCogs/blob/master/rtfs/rtfs.py"""

1826
uv.lock

File diff suppressed because it is too large Load diff