Skip to content

Commit

Permalink
Merge pull request #16 from hwsuk/feat/feedback-command
Browse files Browse the repository at this point in the history
Add feedback slash command
  • Loading branch information
emberdex authored Oct 27, 2023
2 parents 5506a79 + 1bb7d4e commit 3301e8f
Show file tree
Hide file tree
Showing 15 changed files with 378 additions and 20 deletions.
1 change: 1 addition & 0 deletions docs/configuration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ This section aims to document all of the different fields in the `config.json` f
| `verify.discord_minimum_account_age_days` | The minimum age of a Discord account, in days, before the user is allowed to link their accounts. |
| `verify.reddit_minimum_account_age_days` | The minimum age of a Reddit account, in days, before the user is allowed to link their accounts. |
| `verify.token_prefix` | The text that prefixes the verification token sent to the user when verifying their Reddit account. |
| `feedback.feedback_channel_id` | The text channel to send new feedback requests to. |

### Roles Configuration

Expand Down
Binary file added docs/images/cex.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/feedback-step1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/feedback-step2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,26 @@ Allows a user to search recently sold items on eBay to get an idea of how to pri

![A screenshot showing the result of the `/ebay` slash command on the Harmony Discord bot](images/ebay.png)

### `/cex`

- **Who can use this:** Any user.

Allows a user to search items on CeX UK to get an idea of how to price any items they wish to sell.

`/cex` takes one argument, the item to search for, and produces results based on the API's response to your query:

![A screenshot showing the result of the `/cex` slash command on the Harmony Discord bot](images/cex.png)

### `/feedback`

- **Who can use this:** Any user.

Allows a user to give feedback for other members of the server to vote on.

![A screenshot showing the modal dialog that appears when you initiate a feedback request](images/feedback-step1.png)

![A screenshot showing the modal dialog that appears when you complete a feedback request](images/feedback-step2.png)

## App Commands

These commands can be run by right-clicking on a user or message, and selecting the Apps submenu.
Expand Down
3 changes: 2 additions & 1 deletion harmony_cogs/ebay.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import bs4
import json
import httpx
import typing
import discord
import statistics
import urllib.parse
Expand Down Expand Up @@ -39,7 +40,7 @@ def __init__(self, bot: commands.Bot):
)
@app_commands.guild_only
@app_commands.guilds(discord.Object(int(config["discord"]["guild_id"])))
async def ebay(self, interaction: discord.Interaction, search_query: str) -> None:
async def ebay(self, interaction: discord.Interaction, search_query: str) -> typing.NoReturn:
"""
Method invoked when the user performs the eBay search slash command.
:param interaction: The interaction to use to send messages.
Expand Down
54 changes: 54 additions & 0 deletions harmony_cogs/feedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import json
import typing
import discord

import harmony_services.db
import harmony_ui.feedback

from loguru import logger
from discord import app_commands
from discord.ext import commands


with open("config.json", "r") as f:
config = json.load(f)

feedback_channel_id = int(config["feedback"]["feedback_channel_id"])
discord_guild_id = int(config["discord"]["guild_id"])


class Feedback(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot

self.feedback_channel = bot.get_guild(discord_guild_id).get_channel(feedback_channel_id)
if not self.feedback_channel:
logger.error(f"Feedback channel with ID {feedback_channel_id} doesn't exist.")
raise RuntimeError()

@app_commands.command(
name='feedback',
description='Create feedback items for the community to vote on.'
)
@app_commands.guild_only
@app_commands.guilds(discord.Object(int(config["discord"]["guild_id"])))
async def feedback(self, interaction: discord.Interaction) -> typing.NoReturn:
"""
Method invoked when the user performs the feedback slash command.
:param interaction: The interaction to use to respond to the user.
:return: Nothing.
"""
await interaction.response.send_modal(harmony_ui.feedback.CreateFeedbackItemModal(self.feedback_channel))

@commands.Cog.listener()
async def on_raw_message_delete(self, payload: discord.RawMessageDeleteEvent) -> typing.NoReturn:
"""
Delete feedback data when the message containing its voting view is deleted.
:param payload: The data about which message was deleted.
:return: Nothing.
"""
logger.info(f"Deleting feedback data because message with ID {payload.message_id} "
f"was deleted from #{self.feedback_channel.name}")

if payload.channel_id == self.feedback_channel.id:
harmony_services.db.delete_feedback_data(payload.message_id)
10 changes: 5 additions & 5 deletions harmony_cogs/verify.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json
import typing
import discord

import harmony_ui
import harmony_ui.verify

Expand All @@ -20,7 +20,7 @@


class Verify(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
def __init__(self, bot: commands.Bot) -> typing.NoReturn:
self.bot = bot

whois_context_menu = app_commands.ContextMenu(
Expand All @@ -41,7 +41,7 @@ def __init__(self, bot: commands.Bot) -> None:
check_reddit_accounts_task.start(self.bot)
check_discord_roles_task.start(self.bot)

def cog_unload(self) -> None:
def cog_unload(self) -> typing.NoReturn:
check_reddit_accounts_task.cancel()
check_discord_roles_task.cancel()

Expand All @@ -51,7 +51,7 @@ def cog_unload(self) -> None:
)
@app_commands.guild_only
@app_commands.guilds(discord.Object(int(config["discord"]["guild_id"])))
async def display_verification_dialog(self, interaction: discord.Interaction) -> None:
async def display_verification_dialog(self, interaction: discord.Interaction) -> typing.NoReturn:
"""
Command to display the verification model, to allow users to verify their Reddit accounts.
:param interaction: The interaction context for this command.
Expand Down Expand Up @@ -185,5 +185,5 @@ async def display_whois_result(self, interaction: discord.Interaction, member: d
await interaction.response.send_message(embed=embed, ephemeral=True)


async def setup(bot: commands.Bot) -> None:
async def setup(bot: commands.Bot) -> typing.NoReturn:
await bot.add_cog(Verify(bot))
17 changes: 17 additions & 0 deletions harmony_models/feedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import datetime
import mongoengine


class FeedbackVote(mongoengine.EmbeddedDocument):
discord_username = mongoengine.StringField(required=True)
vote_timestamp = mongoengine.DateTimeField(default=datetime.datetime.utcnow)
vote_weight = mongoengine.IntField(required=True, max_value=1, min_value=-1)


class FeedbackItem(mongoengine.Document):
author_username = mongoengine.StringField(required=True)
creation_timestamp = mongoengine.DateTimeField(default=datetime.datetime.utcnow)
feedback_title = mongoengine.StringField(required=True, max_length=200)
feedback_description = mongoengine.StringField(required=True, max_length=1500)
discord_message_id = mongoengine.LongField(required=True, unique=True)
votes = mongoengine.EmbeddedDocumentListField(FeedbackVote, default=[])
22 changes: 22 additions & 0 deletions harmony_services/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import mongoengine

import harmony_models.verify as verify_models
import harmony_models.feedback as feedback_models

with open("config.json", "r") as f:
config = json.load(f)
Expand Down Expand Up @@ -77,3 +78,24 @@ def has_verification_data(discord_user_id: int) -> bool:
:return: True if the user has verification data, otherwise False.
"""
return get_verification_data(discord_user_id=discord_user_id) is not None


def get_feedback_data(message_id: int) -> typing.Optional[feedback_models.FeedbackItem]:
"""
Fetch feedback data by the message ID containing its voting view.
:param message_id: The message ID to fetch.
:return: The feedback item, if it exists.
"""
return feedback_models.FeedbackItem.objects(discord_message_id=message_id).first()


def delete_feedback_data(message_id: int) -> typing.NoReturn:
"""
Delete feedback data by the message ID containing its voting view.
:param message_id: The feedback data to delete, by the Discord message ID containing its voting view.
:return: Nothing.
"""
feedback_data = get_feedback_data(message_id)

if feedback_data:
feedback_data.delete()
6 changes: 3 additions & 3 deletions harmony_services/reddit.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
verification_message_template = None


def load_verification_message_template() -> None:
def load_verification_message_template() -> typing.NoReturn:
"""
Load the verification message template as markdown, and verify that it has the correct template variables.
:return: Nothing.
Expand Down Expand Up @@ -150,7 +150,7 @@ def send_verification_message(
verification_code: str,
subreddit_name: str,
guild_name: str
) -> None:
) -> typing.NoReturn:
"""
Send a message to the user with their verification code in it.
:param username: The user to send the message to.
Expand All @@ -175,7 +175,7 @@ def send_verification_message(
redditor.message(subject="Your /r/HardwareSwapUK Discord verification code", message=message_contents)


def update_user_flair(username: str, flair_text: str, css_class_name: str) -> None:
def update_user_flair(username: str, flair_text: str, css_class_name: str) -> typing.NoReturn:
"""
Update a user's flair.
:param username: The username of the user whose flair should be updated.
Expand Down
5 changes: 3 additions & 2 deletions harmony_ui/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import random
import string
import traceback
import typing
import discord
import traceback

from loguru import logger


async def handle_error(interaction: discord.Interaction, error: Exception) -> None:
async def handle_error(interaction: discord.Interaction, error: Exception) -> typing.NoReturn:
"""
Handle an exception encountered during an interaction.
:param interaction: The interaction in which the exception was raised.
Expand Down
2 changes: 1 addition & 1 deletion harmony_ui/cex.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(
original_interaction: discord.Interaction,
original_search_query: str
):
super().__init__()
super().__init__(timeout=None)

self.original_interaction = original_interaction
self.current_result_index = 0
Expand Down
Loading

0 comments on commit 3301e8f

Please sign in to comment.