Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/grace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ jobs:

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10

- name: Set up Python 3.12
uses: actions/setup-python@v3
with:
python-version: "3.11"
python-version: "3.12"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install .[dev]

- name: Lint with ruff
run: |
ruff check --preview
Expand All @@ -49,7 +49,7 @@ jobs:
adapter = sqlite
database = grace_test.db
EOF

- name: Test with pytest
run: |
pytest -v
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Installing Grace is fairly simple. You can do it in three short step.
2. [Start the bot](#2-start-the-bot)

### 0. Python and Dependencies
Install [Python](https://www.python.org/downloads/). Python 3.10 or higher is required.
Install [Python](https://www.python.org/downloads/). Python 3.12 or higher is required.

> [!NOTE]
> We highly recommend that you set up a virtual environment to work on Grace.
Expand Down
Empty file modified bot/__init__.py
100755 → 100644
Empty file.
Empty file modified bot/extensions/__init__.py
100755 → 100644
Empty file.
7 changes: 3 additions & 4 deletions bot/extensions/bookmark_cog.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from typing import List

from discord import Embed, File, Interaction, Message
from discord.app_commands import ContextMenu
Expand All @@ -17,7 +16,7 @@ def __init__(self, bot: Grace) -> None:

self.bot.tree.add_command(save_message_ctx_menu)

async def get_message_files(self, message: Message) -> List[File]:
async def get_message_files(self, message: Message) -> list[File]:
"""Fetch files from the message attachments

:param message: Message to fetch files from
Expand All @@ -26,7 +25,7 @@ async def get_message_files(self, message: Message) -> List[File]:
:return: List of files
:rtype: List[File]
"""
return list(map(lambda attachment: attachment.to_file(), message.attachments))
return [attachment.to_file() for attachment in message.attachments]

async def save_message(self, interaction: Interaction, message: Message) -> None:
"""Saves the message
Expand All @@ -37,7 +36,7 @@ async def save_message(self, interaction: Interaction, message: Message) -> None
:type message: Message
"""
sent_at: int = int(message.created_at.timestamp())
files: List[File] = await self.get_message_files(message)
files: list[File] = await self.get_message_files(message)

save_embed: Embed = Embed(title="Bookmark Info", color=self.bot.default_color)

Expand Down
13 changes: 4 additions & 9 deletions bot/extensions/color_cog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
from typing import Tuple, Union

from discord import Color, Embed, File
from discord.ext.commands import (
Expand All @@ -14,7 +13,7 @@
from bot.helpers.error_helper import send_command_error


def get_embed_color(color: Union[Tuple[int, int, int], str]) -> Color:
def get_embed_color(color: tuple[int, int, int] | str) -> Color:
"""Convert a color to an Embed Color object.

:param color: A tuple of 3 integers in the range 0-255 representing an RGB
Expand Down Expand Up @@ -60,7 +59,7 @@ async def show_group(self, ctx: Context) -> None:
await ctx.send_help(ctx.command)

async def display_color(
self, ctx: Context, color: Union[Tuple[int, int, int], str]
self, ctx: Context, color: tuple[int, int, int] | str
) -> None:
"""Display a color in an embed message.

Expand Down Expand Up @@ -114,9 +113,7 @@ async def rgb_command_error(self, ctx: Context, error: Exception) -> None:
:param error: The error that was raised during command execution.
:type error: Exception
"""
if isinstance(error, HybridCommandError) or isinstance(
error, CommandInvokeError
):
if isinstance(error, (HybridCommandError, CommandInvokeError)):
await send_command_error(
ctx, "Expected rgb color", ctx.command, "244 195 8"
)
Expand Down Expand Up @@ -149,9 +146,7 @@ async def hex_command_error(self, ctx: Context, error: Exception) -> None:
:param error: The error that was raised during command execution.
:type error: Exception
"""
if isinstance(error, HybridCommandError) or isinstance(
error, CommandInvokeError
):
if isinstance(error, (HybridCommandError, CommandInvokeError)):
await send_command_error(
ctx, "Expected hexadecimal color", ctx.command, "#F4C308"
)
Expand Down
11 changes: 7 additions & 4 deletions bot/extensions/command_error_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import logging
from collections.abc import Coroutine
from datetime import timedelta
from logging import warning
from typing import Any, Coroutine, Optional
from typing import Any

logger = logging.getLogger(__name__)

from discord import Interaction
from discord.ext.commands import (
Expand Down Expand Up @@ -33,7 +36,7 @@ async def get_command_error(self, ctx: Context, error: Exception) -> None:
:param error: The error that was raised during command execution.
:type error: Exception
"""
warning(f"Error: {error}. Issued by {ctx.author}")
logger.warning(f"Error: {error}. Issued by {ctx.author}")

if isinstance(error, CommandNotFound):
await send_command_help(ctx)
Expand All @@ -57,7 +60,7 @@ async def get_command_error(self, ctx: Context, error: Exception) -> None:

@Cog.listener("on_app_command_error")
async def get_app_command_error(
self, interaction: Optional[Interaction], _: Exception
self, interaction: Interaction | None, _: Exception
) -> None:
"""Event listener for command errors that occurred during an interaction.
It sends an error message to the user.
Expand Down
3 changes: 1 addition & 2 deletions bot/extensions/extension_cog.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from typing import List

from discord import Embed
from discord.app_commands import Choice, autocomplete
Expand All @@ -25,7 +24,7 @@ def extension_autocomplete(state: bool):
:return: An autocomplete function.
"""

async def inner_autocomplete(_, current: str) -> List[Choice]:
async def inner_autocomplete(_, current: str) -> list[Choice]:
"""Autocomplete function for extensions.

:param current: The current word being autocompleted.
Expand Down
5 changes: 3 additions & 2 deletions bot/extensions/fun_cog.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
from json import loads
from random import choice as random_choice

Expand Down Expand Up @@ -80,8 +81,8 @@ async def quote_command(self, ctx: Context) -> None:
:param ctx: The context in which the command was called.
:type ctx: Context
"""
response = get(
"https://api.forismatic.com/api/1.0/?method=getQuote&format=json&lang=en"
response = await asyncio.to_thread(
get, "https://api.forismatic.com/api/1.0/?method=getQuote&format=json&lang=en"
)

if response.ok:
Expand Down
2 changes: 1 addition & 1 deletion bot/extensions/grace_cog.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from discord import Embed, Interaction
from discord.app_commands import Choice, autocomplete
from discord.ext.commands import Cog, Context, hybrid_command, has_permissions
from discord.ext.commands import Cog, Context, has_permissions, hybrid_command
from discord.ui import Button
from emoji import emojize

Expand Down
16 changes: 8 additions & 8 deletions bot/extensions/language_cog.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from logging import warning
import logging

logger = logging.getLogger(__name__)

from discord import Embed, Message
from discord.ext.commands import Cog, Context, has_permissions, hybrid_group
Expand Down Expand Up @@ -55,7 +57,7 @@ async def name_react(self, message: Message) -> None:
"""
grace_trigger = Trigger.find_by(name="Grace")
if grace_trigger is None:
warning('Missing trigger entry for "Grace"')
logger.warning('Missing trigger entry for "Grace"')
return

if self.bot.user.mentioned_in(message) and not message.content.startswith(
Expand All @@ -82,11 +84,11 @@ async def penguin_react(self, message: Message) -> None:
"""
linus_trigger = Trigger.find_by(name="Linus")
if linus_trigger is None:
warning('Missing trigger entry for "Linus"')
logger.warning('Missing trigger entry for "Linus"')
return

message_tokens = self.tokenizer.tokenize(message.content)
tokenlist = list(map(lambda s: s.lower(), message_tokens))
tokenlist = [s.lower() for s in message_tokens]
linustarget = [i for i, x in enumerate(tokenlist) if x in linus_trigger.words]
# Get the indices of all linuses in the message

Expand All @@ -97,9 +99,7 @@ async def penguin_react(self, message: Message) -> None:
if (
tokenlist[linusindex + 1] == "tech"
and tokenlist[linusindex + 2] == "tips"
):
fail = True
elif (
) or (
tokenlist[linusindex + 1] == "and"
and tokenlist[linusindex + 2] == "lucy"
):
Expand Down Expand Up @@ -143,7 +143,7 @@ async def triggers_group(self, ctx) -> None:
if ctx.invoked_subcommand is None:
trigger = Trigger.find_by(name="Linus")
if trigger is None:
warning('Missing trigger entry for "Linus"')
logger.warning('Missing trigger entry for "Linus"')
return

embed = Embed(
Expand Down
3 changes: 1 addition & 2 deletions bot/extensions/mermaid_cog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
from typing import Optional

from discord import Embed, Message
from discord.ext.commands import Cog, Context, command
Expand Down Expand Up @@ -83,7 +82,7 @@ def extract_code_block(
help="Generate a diagram from mermaid script",
usage="՝՝՝\nMermaid script goes here...\n՝՝՝",
)
async def mermaid(self, ctx: Context, *, content: Optional[str]):
async def mermaid(self, ctx: Context, *, content: str | None):
"""Generates a mermaid diagram

Reply with this command to a message that contains a code block with
Expand Down
17 changes: 9 additions & 8 deletions bot/extensions/moderation_cog.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
from logging import info
from typing import Optional
import logging
from datetime import UTC, datetime

logger = logging.getLogger(__name__)

from discord import Member, Message, Reaction
from discord.ext.commands import Cog, Context, has_permissions, hybrid_command
Expand All @@ -24,7 +25,7 @@ def moderation_channel(self):
@hybrid_command(name="purge", help="Deletes n amount of messages.")
@has_permissions(manage_messages=True)
async def purge(
self, ctx: Context, limit: int, reason: Optional[str] = "No reason given"
self, ctx: Context, limit: int, reason: str | None = "No reason given"
) -> None:
"""Purge a specified number of messages from the channel.

Expand Down Expand Up @@ -60,7 +61,7 @@ async def on_reaction_add(self, reaction: Reaction, member: Member) -> None:
)

if author.bot or is_already_reacted:
return None
return

match demojize(str(reaction.emoji)):
case ":SOS_button:":
Expand All @@ -76,7 +77,7 @@ async def on_reaction_add(self, reaction: Reaction, member: Member) -> None:
f"If you need some help, read the <#{guidelines.channel_id}> and open a post in <#{help.channel_id}>!"
)
case _:
return None
return

# Grace also reacts and log the reaction
# because some people remove their reaction afterward
Expand All @@ -98,11 +99,11 @@ async def on_member_join(self, member) -> None:
"""
minimum_account_age = app.config.get("moderation", "minimum_account_age")
account_age_in_days = (
datetime.now().replace(tzinfo=None) - member.created_at.replace(tzinfo=None)
datetime.now(tz=UTC) - member.created_at.astimezone(UTC)
).days

if account_age_in_days < minimum_account_age:
info(f"{member} kicked due to account age restriction!")
logger.info(f"{member} kicked due to account age restriction!")

log = danger("KICK", f"{member} has been kicked.")
log.add_field(
Expand Down
8 changes: 3 additions & 5 deletions bot/extensions/pun_cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ async def pun_react(self, message: Message) -> None:
tokenlist = set(map(str.lower, message_tokens))

pun_words = PunWord.distinct().all()
word_set = set(map(lambda pun_word: pun_word.word, pun_words))
word_set = {pun_word.word for pun_word in pun_words}

matches = tokenlist.intersection(word_set)
invoked_at = message.created_at.replace(tzinfo=None)
Expand All @@ -54,7 +54,7 @@ async def pun_react(self, message: Message) -> None:
matched_pun_words = filter(
lambda pun_word: pun_word.word in matches, pun_words
)
puns = map(lambda pun_word: Pun.find(pun_word.pun_id), matched_pun_words)
puns = (Pun.find(pun_word.pun_id) for pun_word in matched_pun_words)
puns = list(filter(lambda pun: pun.can_invoke_at_time(invoked_at), puns))

for pun_word in matched_pun_words:
Expand All @@ -81,9 +81,7 @@ async def puns_group(self, ctx: Context) -> None:
@has_permissions(administrator=True)
async def list_puns(self, ctx: Context) -> None:
if ctx.invoked_subcommand is None:
pun_texts_with_ids = map(
lambda pun: "{}.\t{}".format(pun.id, pun.text), Pun.all()
)
pun_texts_with_ids = (f"{pun.id}.\t{pun.text}" for pun in Pun.all())

embed = Embed(
color=self.bot.default_color,
Expand Down
5 changes: 2 additions & 3 deletions bot/extensions/reddit_cog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
from typing import List

from discord import Embed, Message
from discord.ext.commands import Cog
Expand All @@ -20,7 +19,7 @@ def moderation_channel(self):
"""Returns the moderation channel"""
return self.bot.get_channel_by_name("moderation_logs")

async def notify_moderation(self, message: Message, blacklisted: List[str]):
async def notify_moderation(self, message: Message, blacklisted: list[str]):
"""Notifies moderators about a blacklisted subreddit mention

:param message: Message that contained blacklisted subreddits
Expand All @@ -35,7 +34,7 @@ async def notify_moderation(self, message: Message, blacklisted: List[str]):
)
await log.send(self.moderation_channel)

async def extract_subreddits(self, message: Message) -> List[List]:
async def extract_subreddits(self, message: Message) -> list[list]:
"""Extracts and filters all mentioned subreddits from a message

:param message: Message from which to extract subreddits
Expand Down
14 changes: 8 additions & 6 deletions bot/extensions/reminder_cog.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import re
from datetime import datetime, timedelta, tzinfo
from datetime import UTC, datetime, timedelta, tzinfo
from io import BytesIO
from re import Match

from discord import Embed, File
from discord.ext.commands import Cog, hybrid_command, Context
from discord.ext.commands import Cog, Context, hybrid_command
from pytz import timezone
from typing import Match
from io import BytesIO

from bot.grace import Grace


Expand Down Expand Up @@ -51,7 +53,7 @@ def _build_embed(self, title: str, message: str, author: str) -> Embed:
color=self.bot.default_color,
title=f"**{title}**",
description=message,
timestamp=datetime.now(),
timestamp=datetime.now(tz=UTC),
)

embed.set_author(name=author)
Expand Down Expand Up @@ -108,7 +110,7 @@ async def reminder(self, ctx: Context, timer: str, *, message: str) -> None:
"date",
run_date=reminder_time,
args=[ctx, message],
id=f"reminder_{ctx.author.id}_{datetime.now().timestamp()}",
id=f"reminder_{ctx.author.id}_{datetime.now(tz=UTC).timestamp()}",
)
)

Expand Down
Loading
Loading