Skip to content
Draft
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
52 changes: 41 additions & 11 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,59 @@ permissions:
contents: read

jobs:
build:

ruff:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v3
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install ruff
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
- name: Lint with ruff
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# can also be more harsh :D
# flake8 . --count --ignore=W503,W504 --show-source --statistics
ruff check . --select=E9,F63,F7,F8 --statistics
# exit-zero treats all errors as warnings.
flake8 . --count --exit-zero --max-complexity=10 --statistics
- name: Test with pytest
ruff check . --exit-zero

mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with mypy
run: |
mypy main.py

pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with pytest
run: |
pytest
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ __pycache__
Folder.DotSettings.user
rina_docs
.coverage
.mypy_cache
10 changes: 3 additions & 7 deletions extensions/addons/cogs/otheraddons.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json # to read API json responses
import requests # to read api calls

import aiohttp
import discord
import discord.app_commands as app_commands
import discord.ext.commands as commands
Expand Down Expand Up @@ -297,11 +296,8 @@ async def convert_unit(
"appid": itx.client.api_tokens['Open Exchange Rates'],
"show_alternative": "true",
}
response_api = requests.get(
"https://openexchangerates.org/api/latest.json",
params=params
).text
data = json.loads(response_api)
async with aiohttp.ClientSession() as client, client.get("https://openexchangerates.org/api/latest.json", params=params) as response:
data = await response.json()
if data.get("error", 0):
await itx.response.send_message(
"I'm sorry, something went wrong while trying to get "
Expand Down
18 changes: 7 additions & 11 deletions extensions/addons/cogs/searchaddons.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json # to read API json responses

import requests # to read api calls
import aiohttp # to read api calls
import aiohttp.client_exceptions

import discord
import discord.app_commands as app_commands
Expand Down Expand Up @@ -375,11 +376,8 @@ async def equaldex(self, itx: discord.Interaction[Bot], country_id: str):
"apiKey": equaldex_key,
# "formatted": "true",
}
response = requests.get(
"https://www.equaldex.com/api/region",
params=querystring
)
response_api = response.text
async with aiohttp.ClientSession() as client, client.get("https://www.equaldex.com/api/region", params=querystring) as response:
response_api = await response.text()
# returns -> <pre>{"regions":{...}}</pre> <- so you need to
# remove the <pre> and </pre> parts. It also has some
# <br \/>\r\n strings in there for some reason...? so uh
Expand Down Expand Up @@ -505,11 +503,9 @@ async def math(self, itx: discord.Interaction[Bot], query: str):
"output": "json",
}
try:
api_response: WolframResult = requests.get(
"https://api.wolframalpha.com/v2/query",
params=params
).json()
except requests.exceptions.JSONDecodeError:
async with aiohttp.ClientSession() as client, client.get("https://api.wolframalpha.com/v2/query", params=params) as response:
api_response: WolframResult = await response.json()
except (aiohttp.client_exceptions.ContentTypeError, json.JSONDecodeError):
await itx.followup.send(
"Your input gave a malformed result! Perhaps it took "
"too long to calculate...",
Expand Down
2 changes: 1 addition & 1 deletion extensions/compliments/cogs/compliments.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ async def on_message(self, message: discord.Message):
if called_cute is True:
try:
await message.add_reaction("<:this:960916817801535528>")
except (discord.HTTPException or discord.NotFound):
except (discord.HTTPException, discord.NotFound):
await log_to_guild(
self.client,
message.guild,
Expand Down
2 changes: 1 addition & 1 deletion extensions/help/helppages.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@
}


FIRST_PAGE: int = list(help_pages)[0]
FIRST_PAGE: int = next(iter(help_pages))


aliases: dict[int, list[str]] = {
Expand Down
29 changes: 20 additions & 9 deletions extensions/reminders/objects/reminderobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ async def relaunch_ongoing_reminders(
store new scheduler events.
"""
collection = client.async_rina_db["reminders"]
query = {}
db_data = collection.find(query)
db_data = collection.find({})
async for entry in db_data:
entry: DatabaseData
try:
Expand Down Expand Up @@ -128,7 +127,7 @@ def __init__(
run_date=self.remindertime
)

async def send_reminder(self):
async def send_reminder(self) -> None:
user = await self.client.fetch_user(self.userID)
creationtime = int(self.creationtime.timestamp())
try:
Expand All @@ -143,6 +142,12 @@ async def send_reminder(self):
collection = self.client.rina_db["reminders"]
query = {"userID": self.userID}
db_data: DatabaseData | None = collection.find_one(query)
# If the user isn't in the database, despite a reminder object existing, something has gone *bad*.
#
# But probably not bad enough to crash over, but...
# TODO: should log this
if db_data is None:
return
reminders = db_data["reminders"]
index_subtraction = 0
for reminder_index in range(len(reminders)):
Expand Down Expand Up @@ -412,7 +417,7 @@ async def _create_reminder(
)

await view.wait()
if view.value == 1:
if view.return_interaction is not None:
msg = (f"{itx.user.mention} shared a reminder on <t:{_distance}:F> "
f"for \"{reminder}\"")
copy_view = CopyReminder(
Expand All @@ -421,11 +426,17 @@ async def _create_reminder(
timeout=300
)
try:
await itx.channel.send(
content=msg,
view=copy_view,
allowed_mentions=discord.AllowedMentions.none()
)
if isinstance(itx.channel, discord.abc.Messageable):
await itx.channel.send(
content=msg,
view=copy_view,
allowed_mentions=discord.AllowedMentions.none()
)
else:
await view.return_interaction.response.send_message(
msg, view=copy_view,
allowed_mentions=discord.AllowedMentions.none()
)
except discord.errors.Forbidden:
await view.return_interaction.response.send_message(
msg, view=copy_view,
Expand Down
5 changes: 4 additions & 1 deletion extensions/reminders/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import discord

from typing import cast

from resources.customs import Bot

from extensions.reminders.objects import (
Expand All @@ -14,7 +16,8 @@ def get_user_reminders(
# Check if user has an entry in database yet.
collection = client.rina_db["reminders"]
query = {"userID": user.id}
db_data: DatabaseData = collection.find_one(query)
# We're getting this straight from the DB, so it should be fine
db_data = cast(DatabaseData, collection.find_one(query))
if db_data is None:
collection.insert_one(query)
db_data = collection.find_one(query)
Expand Down
12 changes: 9 additions & 3 deletions extensions/reminders/views/sharereminder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@


class ShareReminder(discord.ui.View):
def __init__(self, timeout=300):
timeout: int|float
return_interaction: discord.Interaction[Bot] | None

# This can be entirely synthesized from the value of return_interaction, given that the API guarantees that the given interaction is not None
@property
def value(self) -> int:
return int(self.return_interaction is not None)

def __init__(self, timeout:float=300):
super().__init__()
self.value = 0
self.timeout = timeout
self.return_interaction: discord.Interaction[Bot] | None = None
self.add_item(create_simple_button(
Expand All @@ -17,6 +24,5 @@ def __init__(self, timeout=300):
)

async def callback(self, interaction: discord.Interaction[Bot]):
self.value = 1
self.return_interaction = interaction
self.stop()
1 change: 1 addition & 0 deletions extensions/settings/objects/server_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
| discord.Role | list[discord.Role]
| discord.User
| discord.VoiceChannel
| discord.ForumChannel
)


Expand Down
15 changes: 9 additions & 6 deletions extensions/settings/objects/server_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import discord

from resources.utils.debug import debug, DebugColor
from .server_attributes import ServerAttributes, GuildAttributeType
from .server_attributes import ServerAttributes, GuildAttributeType, MessageableGuildChannel
from .server_attribute_ids import ServerAttributeIds
from .enabled_modules import EnabledModules

Expand Down Expand Up @@ -123,7 +123,7 @@ def is_attribute_type(val: type):
)
return f

funcs: set[Callable[[int], GuildAttributeType | None]] = set()
funcs: set[Callable[[int], GuildAttributeType| None]] = set()

if is_attribute_type(discord.Guild):
funcs.add(client.get_guild)
Expand All @@ -133,9 +133,10 @@ def is_attribute_type(val: type):
# parse if the type matches exactly.
funcs.add(guild.get_channel_or_thread)
if is_attribute_type(discord.abc.Messageable):
funcs.add(client.get_channel)
# There is no attribute that gives a PrivateChannel
funcs.add(typing.cast(Callable[[int], MessageableGuildChannel|None], client.get_channel))
if is_attribute_type(discord.TextChannel):
funcs.add(client.get_channel)
funcs.add(typing.cast(Callable[[int], discord.TextChannel|None], client.get_channel))
if is_attribute_type(discord.User):
funcs.add(client.get_user)
if is_attribute_type(discord.Role):
Expand All @@ -144,9 +145,9 @@ def is_attribute_type(val: type):
# I think it's safe to assume the stored value was an object of
# the correct type in the first place. As in, it's a
# CategoryChannel id, not a VoiceChannel id.
funcs.add(client.get_channel)
funcs.add(typing.cast(Callable[[int], discord.CategoryChannel|None], client.get_channel))
if is_attribute_type(discord.channel.VoiceChannel):
funcs.add(client.get_channel)
funcs.add(typing.cast(Callable[[int], discord.VoiceChannel|None], client.get_channel))
if is_attribute_type(discord.Emoji):
funcs.add(guild.get_emoji)
if is_attribute_type(int):
Expand Down Expand Up @@ -252,6 +253,8 @@ async def update_query(
class ServerSettings:
DATABASE_KEY = "server_settings"

attributes: ServerAttributes

@staticmethod
def get_original(
attribute: T | list[T]
Expand Down
17 changes: 7 additions & 10 deletions extensions/staffaddons/cogs/staffaddons.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from datetime import datetime, timedelta
# ^ for /delete_week_selfies (within 7 days), and /version startup
# time parsing to discord unix <t:1234:F>
import requests
# to fetch from GitHub and see Rina is running the latest version
import aiohttp # to fetch from GitHub and see Rina is running the latest version

import discord
import discord.app_commands as app_commands
Expand Down Expand Up @@ -171,9 +170,8 @@ async def delete_week_selfies(self, itx: discord.Interaction[Bot]):
async def get_bot_version(self, itx: discord.Interaction[Bot]):
# get most recently pushed bot version
# noinspection LongLine
latest_rina = requests.get(
"https://raw.githubusercontent.com/TransPlace-Devs/uncute-rina/main/main.py" # noqa
).text
async with aiohttp.ClientSession() as client, client.get("https://raw.githubusercontent.com/TransPlace-Devs/uncute-rina/main/main.py") as response:
latest_rina = await response.text()
latest_version = (latest_rina
.split("BOT_VERSION = \"", 1)[1]
.split("\"", 1)[0])
Expand All @@ -187,11 +185,10 @@ async def get_bot_version(self, itx: discord.Interaction[Bot]):
f"(started <t:{unix}:D> at <t:{unix}:T>)",
ephemeral=False)
return
else:
await itx.response.send_message(
f"Bot is currently running on v{itx.client.version} (latest)\n"
f"(started <t:{unix}:D> at <t:{unix}:T>)",
ephemeral=False)
await itx.response.send_message(
f"Bot is currently running on v{itx.client.version} (latest)\n"
f"(started <t:{unix}:D> at <t:{unix}:T>)",
ephemeral=False)

@app_commands.check(is_staff_check)
@app_commands.command(name="update", description="Update slash-commands")
Expand Down
6 changes: 4 additions & 2 deletions extensions/tags/local_tag_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ async def fetch_tags(
if data is None:
return {}

local_tag_list[guild.id] = {name: DatabaseTagObject(**tag_data)
# This should be good, since it comes directly from the DB
local_tag_list[guild.id] = {name: DatabaseTagObject(**tag_data) # type: ignore[typeddict-item]
for name, tag_data in data.items()}
return local_tag_list[guild.id]

Expand All @@ -163,7 +164,8 @@ async def fetch_all_tags(
for guild_id, tag_objects in data.items():
tags = {}
for tag_name, tag_data in tag_objects.items():
tag_object = DatabaseTagObject(**tag_data)
# This should be good, since it comes directly from the DB
tag_object = DatabaseTagObject(**tag_data) # type: ignore[typeddict-item]
tags[tag_name] = tag_object
local_tag_list[guild_id] = tags

Expand Down
2 changes: 1 addition & 1 deletion extensions/watchlist/local_watchlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ async def refetch_watchlist_threads(

# store reference to dict into a shorter variable

failed_fetches = []
failed_fetches: list[discord.Thread] = []
# iterate non-archived threads
for thread in watch_channel.threads:
await _import_thread_to_local_list(
Expand Down
3 changes: 2 additions & 1 deletion main.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ def get_token_data() -> tuple[
missing_tokens.append(key)
continue
tokens[key] = api_keys[key]
tokens = ApiTokenDict(**tokens)
# We manually check totality later (if missing_tokens ...)
tokens = ApiTokenDict(**tokens) # type: ignore[typeddict-item]
except FileNotFoundError:
raise
except json.decoder.JSONDecodeError as ex:
Expand Down
Loading