Skip to content

Commit 24553bd

Browse files
committed
fix stuff
1 parent a20f87a commit 24553bd

13 files changed

Lines changed: 352 additions & 124 deletions

File tree

.github/workflows/lint.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ jobs:
3535
3636
- name: Run Linter
3737
run: pylint src
38+
39+
- name: Run mypy
40+
run: mypy src/SideBot

README.md

Whitespace-only changes.

pyproject.toml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
name = "SideBot"
33
version = "0.0.1"
44
authors = [
5-
{ name="nythepegasus", email="[email protected]" },
5+
{ name="nythepegasus", email="[email protected]" },
66
]
77
description = "SideBot is a multipurpose bot for the SideStore community."
88
readme = "README.md"
9-
requires-python = ">=3.8"
9+
requires-python = ">=3.11"
1010
dependencies = [
11-
"discord.py>=2.3.2",
11+
"discord.py>=2.3.2",
12+
"psycopg2==2.9.9",
1213
]
1314
classifiers = [
1415
"Programming Language :: Python :: 3",
@@ -18,5 +19,8 @@ classifiers = [
1819
[project.optional-dependencies]
1920
dev = [
2021
"build",
21-
"pylint"
22+
"pylint",
23+
"mypy",
24+
"ruff",
25+
"types-psycopg2",
2226
]

requirements.txt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
aiohttp==3.9.3
22
aiosignal==1.3.1
3+
astroid==3.1.0
4+
async-timeout==4.0.3
5+
asyncpg==0.29.0
36
attrs==23.2.0
47
build==1.2.1
5-
certifi==2024.2.2
6-
charset-normalizer==3.3.2
8+
dill==0.3.8
79
discord.py==2.3.2
810
frozenlist==1.4.1
911
idna==3.6
12+
isort==5.13.2
13+
mccabe==0.7.0
1014
multidict==6.0.5
15+
mypy==1.9.0
16+
mypy-extensions==1.0.0
1117
packaging==24.0
18+
platformdirs==4.2.0
19+
pylint==3.1.0
1220
pyproject_hooks==1.0.0
13-
requests==2.31.0
14-
urllib3==2.2.1
21+
ruff==0.3.5
22+
-e git+ssh://[email protected]/nythepegasus/SideBot.git@a20f87ae8e52939f3c050f9bb09b2326f179815e#egg=SideBot
23+
tomlkit==0.12.4
24+
typing_extensions==4.11.0
1525
yarl==1.9.4

ruff.toml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Exclude a variety of commonly ignored directories.
2+
exclude = [
3+
".bzr",
4+
".direnv",
5+
".eggs",
6+
".git",
7+
".git-rewrite",
8+
".hg",
9+
".ipynb_checkpoints",
10+
".mypy_cache",
11+
".nox",
12+
".pants.d",
13+
".pyenv",
14+
".pytest_cache",
15+
".pytype",
16+
".ruff_cache",
17+
".svn",
18+
".tox",
19+
".venv",
20+
".vscode",
21+
"__pypackages__",
22+
"_build",
23+
"buck-out",
24+
"build",
25+
"dist",
26+
"node_modules",
27+
"site-packages",
28+
"venv",
29+
]
30+
31+
# Same as Black.
32+
line-length = 88
33+
indent-width = 4
34+
35+
# Assume Python 3.8
36+
target-version = "py38"
37+
38+
[lint]
39+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
40+
select = ["E4", "E7", "E9", "F"]
41+
ignore = []
42+
43+
# Allow fix for all enabled rules (when `--fix`) is provided.
44+
fixable = ["ALL"]
45+
unfixable = []
46+
47+
# Allow unused variables when underscore-prefixed.
48+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
49+
50+
[format]
51+
# Like Black, use double quotes for strings.
52+
quote-style = "double"
53+
54+
# Like Black, indent with spaces, rather than tabs.
55+
indent-style = "space"
56+
57+
# Like Black, respect magic trailing commas.
58+
skip-magic-trailing-comma = false
59+
60+
# Like Black, automatically detect the appropriate line ending.
61+
line-ending = "lf"

src/SideBot/__init__.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,64 @@
11
# pylint: disable=C0103,C0114
22
import discord
3+
import logging
34
from discord.ext.commands import Bot, when_mentioned_or
45

56

67
class SideBot(Bot):
78
"Custom SideBot class to simplify start up"
9+
810
def __init__(self, config: dict):
911
self.__tok = config.pop("DTOKEN")
1012
self.config = config
13+
self.logger = logging.getLogger(__name__)
1114

1215
intents = discord.Intents.all()
1316

14-
super().__init__(command_prefix=when_mentioned_or('##'),
15-
intents=intents)
17+
super().__init__(command_prefix=when_mentioned_or("##"), intents=intents)
1618

17-
self.owner_id = self.config['OWNER']
18-
self.conf_cogs = self.config['COGS'].split(',')
19+
self.owner_id = self.config["OWNER"]
20+
self.conf_cogs = self.config["COGS"].split(",")
1921

2022
async def setup_hook(self) -> None:
2123
"Set up cogs and app commands"
2224
for cog in self.conf_cogs:
2325
await self.load_extension(f"SideBot.cogs.{cog}")
24-
print(self.extensions)
25-
26-
guild = discord.Object(856315760224894986)
26+
self.logger.debug(self.extensions)
27+
try:
28+
guild = discord.Object(self.config["GUILD"])
29+
except KeyError:
30+
guild = discord.Object(856315760224894986)
2731
self.tree.clear_commands(guild=guild)
2832
await self.tree.sync(guild=guild)
29-
print("Set up hook done!")
33+
self.logger.info("Set up hook done!")
3034

3135
async def on_ready(self):
3236
"Handle bot ready status"
3337
if self.user:
34-
print(f'Logged in as {self.user} (ID: {self.user.id})')
35-
print('------')
38+
self.logger.info(f"Logged in as {self.user} (ID: {self.user.id})")
3639
else:
37-
print("Error getting user")
40+
self.logger.error("Error getting user")
3841

3942
# pylint: disable=W0221
4043
async def on_command_error(self, ctx, error):
4144
"Handle unhandled command errors"
42-
print(ctx)
43-
print(error)
45+
self.logger.error(ctx)
46+
self.logger.error(error)
4447

45-
def run(self, token: str | None = None):
48+
def run(self, token: str | None = None, *args, **kwargs):
4649
if token:
47-
return super().run(token)
48-
return super().run(self.__tok)
50+
return super().run(token, *args, **kwargs)
51+
return super().run(self.__tok, *args, **kwargs)
4952

5053
@classmethod
5154
def from_env(cls, path: str = ".env"):
5255
"Loads the bot from a .env file with the proper configuration"
5356
with open(path, "r", encoding="utf-8") as env:
54-
conf = {k: v for line in env
55-
if (k := line.strip().split("=", 1)[0]) and \
56-
(v := line.strip().split("=", 1)[1])}
57+
conf = {
58+
k: v
59+
for line in env
60+
if (k := line.strip().split("=", 1)[0])
61+
and (v := line.strip().split("=", 1)[1])
62+
}
5763

5864
return cls(conf)

src/SideBot/__main__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# pylint: disable=C0114
22
from . import SideBot
33

4-
5-
SideBot.from_env().run()
4+
SideBot.from_env().run(root_logger=True)

src/SideBot/cogs/admin.py

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
"Admin cog with commands for moderation"
2+
23
import asyncio
34
from datetime import timedelta
45

6+
import discord
57
from discord.ext import tasks
68
from discord import Interaction, Member, Message, TextChannel
79
from discord.app_commands import command, describe, checks, errors
10+
import typing
811

912
from .basecog import BaseCog, Bot
1013

1114

1215
class SpamMessage:
1316
"A message representation"
14-
__slots__ = ('i',)
17+
18+
__slots__ = ("i",)
19+
1520
def __init__(self, i: int):
1621
self.i = i
1722

@@ -21,7 +26,9 @@ def __repr__(self):
2126

2227
class SpamChannel:
2328
"A channel representation that holds messages"
24-
__slots__ = ('i', 'messages')
29+
30+
__slots__ = ("i", "messages")
31+
2532
def __init__(self, i: int, messages: list[SpamMessage]):
2633
self.i = i
2734
self.messages = messages
@@ -37,7 +44,9 @@ def get_message(self, i: int) -> SpamMessage | None:
3744

3845
class SpamUser:
3946
"A user representation that holds channels"
40-
__slots__ = ('i', 'channels')
47+
48+
__slots__ = ("i", "channels")
49+
4150
def __init__(self, i: int, channels: list[SpamChannel]):
4251
self.i = i
4352
self.channels = channels
@@ -53,11 +62,12 @@ def get_channel(self, i: int) -> SpamChannel | None:
5362

5463
class Admin(BaseCog):
5564
"Admin cog with commands for moderation"
65+
5666
def __init__(self, bot: Bot):
57-
super().__init__(bot)
58-
self.spammers = []
59-
self.clear_spammers.start() # pylint: disable=E1101
67+
self.spammers: typing.List[SpamUser] = []
68+
self.clear_spammers.start() # pylint: disable=E1101
6069
self.description = "This is the moderation cog"
70+
super().__init__(bot)
6171

6272
@command(name="clean", description="Clean messages from channel")
6373
@describe(count="Amount of messages to delete")
@@ -66,10 +76,12 @@ def __init__(self, bot: Bot):
6676
async def clean(self, inter: Interaction, count: int, member: Member | None = None):
6777
"Cleans `count` messages from optional `member` in the channel it's used"
6878
if not isinstance(inter.channel, TextChannel):
69-
return await inter.response.send_message("Not a text channel, doing nothing.", ephemeral=True)
79+
return await inter.response.send_message(
80+
"Not a text channel, doing nothing.", ephemeral=True
81+
)
7082
await inter.response.defer(ephemeral=True)
7183
if member:
72-
del_messages = []
84+
del_messages: typing.List[Message] = []
7385
async for message in inter.channel.history(limit=200):
7486
if len(del_messages) >= count:
7587
break
@@ -78,59 +90,81 @@ async def clean(self, inter: Interaction, count: int, member: Member | None = No
7890
await inter.channel.delete_messages(del_messages)
7991
else:
8092
del_messages = await inter.channel.purge(limit=count)
81-
await inter.followup.send(f"Attempted to delete {count} messages,"
82-
f"actually deleted {len(del_messages)}!", ephemeral=True)
93+
await inter.followup.send(
94+
f"Attempted to delete {count} messages,"
95+
f"actually deleted {len(del_messages)}!",
96+
ephemeral=True,
97+
)
8398

8499
@clean.error
85100
async def app_command_error(self, inter: Interaction, err: errors.AppCommandError):
86101
"Handle app command errors"
87102
if isinstance(err, errors.MissingPermissions):
88-
return await inter.response.send_message("You do not have permission to run this command.",
89-
ephemeral=True)
103+
return await inter.response.send_message(
104+
"You do not have permission to run this command.", ephemeral=True
105+
)
90106
return await inter.response.send_message(f"{err}", ephemeral=True)
91107

92108
@tasks.loop(minutes=30)
93109
async def clear_spammers(self):
94110
"Clears the current 'spammer' list"
95-
print(self.spammers)
111+
self.logger.debug(self.spammers)
96112
self.spammers = []
97-
print("Cleared spammers")
113+
self.logger.debug("Cleared spammers")
98114

99115
@BaseCog.listener()
100116
async def on_message(self, message: Message):
101117
"Handle messages to detect for spam"
102-
if self.bot.user is None or message.guild is None or message.author.id == self.bot.user.id:
118+
if (
119+
self.bot.user is None
120+
or message.guild is None
121+
or message.author.id == self.bot.user.id
122+
):
103123
return
104124
if message.author.id not in [su.i for su in self.spammers]:
105-
self.spammers.append(SpamUser(message.author.id,
106-
[SpamChannel(message.channel.id, [SpamMessage(message.id)])]))
125+
self.spammers.append(
126+
SpamUser(
127+
message.author.id,
128+
[SpamChannel(message.channel.id, [SpamMessage(message.id)])],
129+
)
130+
)
107131
return
108132
cur = [su for su in self.spammers if su.i == message.author.id][0]
109133
curch = cur.get_channel(message.channel.id)
110134
if curch:
111135
curch.messages.append(SpamMessage(message.id))
112136
else:
113-
cur.channels.append(SpamChannel(message.channel.id, [SpamMessage(message.id)]))
137+
cur.channels.append(
138+
SpamChannel(message.channel.id, [SpamMessage(message.id)])
139+
)
114140

115141
if len(cur.channels) >= 4:
116-
print(f"Spammer alert! {message.author.name} has sent "
117-
f"messages to {len(cur.channels)} different channels recently!")
118-
await message.author.timeout(timedelta(seconds=30),
119-
reason=f"For spamming {len(cur.channels)} channels")
142+
self.logger.info(
143+
f"Spammer alert! {message.author.name} has sent "
144+
f"messages to {len(cur.channels)} different channels recently!"
145+
)
146+
if not isinstance(message.author, discord.User):
147+
await message.author.timeout(
148+
timedelta(seconds=30),
149+
reason=f"For spamming {len(cur.channels)} channels",
150+
)
120151
del_chans = []
121152
for channel in cur.channels:
122153
chan = self.bot.get_channel(channel.i)
123154
if isinstance(chan, TextChannel):
124155
del_chans.append(
125156
chan.delete_messages(
126-
[chan.get_partial_message(msg.i) for msg in channel.messages]
157+
[
158+
chan.get_partial_message(msg.i)
159+
for msg in channel.messages
160+
]
127161
)
128162
)
129163
await asyncio.gather(*del_chans)
130-
#await message.channel.send(f"Spammer alert! {message.author.name}
131-
#has sent messages to {len(cur)} different channels recently!", delete_after=5)
164+
# await message.channel.send(f"Spammer alert! {message.author.name}
165+
# has sent messages to {len(cur)} different channels recently!", delete_after=5)
132166
self.spammers.pop(self.spammers.index(cur))
133-
print(self.spammers)
167+
self.logger.debug(self.spammers)
134168

135169

136170
setup = Admin.setup

0 commit comments

Comments
 (0)