Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: images 404'ing #79

Merged
merged 1 commit into from
Mar 1, 2024
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
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ typing_extensions==4.3.0
websockets==10.4
yarl==1.7.2
zonis==1.2.5
types-aiobotocore==2.11.2
aiobotocore==2.11.2
13 changes: 13 additions & 0 deletions suggestions/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
QueueImbalance,
BlocklistedUser,
PartialResponse,
InvalidFileType,
)
from suggestions.http_error_parser import try_parse_http_error
from suggestions.objects import Error, GuildConfig, UserConfig
Expand Down Expand Up @@ -447,6 +448,18 @@ async def on_slash_command_error(
ephemeral=True,
)

elif isinstance(exception, InvalidFileType):
return await interaction.send(
embed=self.error_embed(
"Invalid file type",
"The file you attempted to upload is not an accepted type.\n\n"
"If you believe this is an error please reach out to us via our support discord.",
error_code=ErrorCode.INVALID_FILE_TYPE,
error=error,
),
ephemeral=True,
)

elif isinstance(exception, ConfiguredChannelNoLongerExists):
return await interaction.send(
embed=self.error_embed(
Expand Down
1 change: 1 addition & 0 deletions suggestions/codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class ErrorCode(IntEnum):
QUEUE_IMBALANCE = 20
MISSING_QUEUE_CHANNEL = 21
BLOCKLISTED_USER = 22
INVALID_FILE_TYPE = 23

@classmethod
def from_value(cls, value: int) -> ErrorCode:
Expand Down
33 changes: 21 additions & 12 deletions suggestions/cogs/suggestion_cog.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Optional, cast
from typing import TYPE_CHECKING, Optional

import cooldowns
import disnake
from commons.caching import NonExistentEntry
from bot_base.wraps import WrappedChannel
from disnake import Guild, Localized
from disnake import Guild
from disnake.ext import commands, components

from suggestions import checks, Stats, ErrorCode
from suggestions import checks, Stats
from suggestions.clunk2 import update_suggestion_message
from suggestions.cooldown_bucket import InteractionBucket
from suggestions.exceptions import SuggestionTooLong, ErrorHandled
from suggestions.objects import Suggestion, GuildConfig, UserConfig, QueuedSuggestion
from suggestions.objects import Suggestion, GuildConfig, QueuedSuggestion
from suggestions.objects.suggestion import SuggestionState
from suggestions.utility import r2

if TYPE_CHECKING:
from alaric import Document
Expand Down Expand Up @@ -188,15 +189,23 @@ async def suggest(
)
raise ErrorHandled

image_url = image.url if isinstance(image, disnake.Attachment) else None
if image_url and not guild_config.can_have_images_in_suggestions:
await interaction.send(
self.bot.get_locale(
"SUGGEST_INNER_NO_IMAGES_IN_SUGGESTIONS", interaction.locale
),
ephemeral=True,
image_url = None
if image is not None:
if not guild_config.can_have_images_in_suggestions:
await interaction.send(
self.bot.get_locale(
"SUGGEST_INNER_NO_IMAGES_IN_SUGGESTIONS", interaction.locale
),
ephemeral=True,
)
raise ErrorHandled

image_url = await r2.upload_file_to_r2(
file_name=image.filename,
file_data=await image.read(use_cached=True),
guild_id=interaction.guild_id,
user_id=interaction.author.id,
)
raise ErrorHandled

if guild_config.uses_suggestion_queue:
await QueuedSuggestion.new(
Expand Down
4 changes: 4 additions & 0 deletions suggestions/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@ class BlocklistedUser(CheckFailure):

class PartialResponse(Exception):
"""A garven route returned a partial response when we require a full response"""


class InvalidFileType(disnake.DiscordException):
"""The file you attempted to upload is not allowed."""
Empty file added suggestions/utility/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions suggestions/utility/r2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import hashlib
import logging
import mimetypes
import os
import secrets

from aiobotocore.session import get_session

from suggestions.exceptions import InvalidFileType

log = logging.getLogger(__name__)


async def upload_file_to_r2(
*,
file_name: str,
file_data: bytes,
guild_id: int,
user_id: int,
) -> str:
"""Upload a file to R2 and get the cdn url back"""

session = get_session()
async with session.create_client(
"s3",
endpoint_url=os.environ["ENDPOINT"],
aws_access_key_id=os.environ["ACCESS_KEY"],
aws_secret_access_key=os.environ["SECRET_ACCESS_KEY"],
) as client:
mimetype_guessed, _ = mimetypes.guess_type(file_name)
accepted_mimetypes: dict[str, set[str]] = {
"image/jpeg": {".jpeg", ".jpg"},
"image/png": {".png"},
"image/gif": {".gif"},
"video/mp3": {".mp3"},
"video/mp4": {".mp4"},
"video/mpeg": {".mpeg"},
"video/webm": {".webm"},
"image/webp": {".webp"},
"audio/webp": {".weba"},
}
file_names = accepted_mimetypes.get(mimetype_guessed)
if file_names is None:
raise InvalidFileType

for ext in file_names:
if file_name.endswith(ext):
break
else:
raise InvalidFileType

file_key = hashlib.sha256(file_data + secrets.token_bytes(16)).hexdigest()
key = "{}/{}.{}".format(guild_id, file_key, ext)
await client.put_object(Bucket=os.environ["BUCKET"], Key=key, Body=file_data)
log.debug("User %s in guild %s uploaded an image", user_id, guild_id)

return f"https://cdn.suggestions.bot/{key}"
Loading