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

Various changes to quotes (including mod deletion) #468

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
233 changes: 188 additions & 45 deletions cogs/quotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
# Other utils
import random
from .utils.paginator import Pages
from .utils.checks import is_moderator

GEN_SPACE_SYMBOLS = re.compile(r"[,“”\".?!]")
GEN_BLANK_SYMBOLS = re.compile(r"['()`]")
Expand All @@ -39,6 +40,18 @@
DEFAULT_AVATAR = "https://cdn.discordapp.com/embed/avatars/0.png"


class UserToIdConverter(commands.Converter):
async def convert(self, ctx, argument):
try:
user = await commands.MemberConverter().convert(ctx, argument)
return user.id
except commands.BadArgument:
try:
return int(argument)
except ValueError:
raise commands.BadArgument("Could not find user")


class Quotes(commands.Cog):
def __init__(self, bot):
self.bot = bot
Expand Down Expand Up @@ -129,14 +142,16 @@ async def add_quotes(self,

def check(reaction, user):
# returns True if all the following is true:
# The user who reacted is either the quoter or the quoted person
# The user who reacted is either the quoter, the quoted person, or a moderator
# The user who reacted isn't the bot
# The react is the delete emoji
# The react is on the "Quote added." message
return (
user == ctx.message.author
or user == member) and user != self.bot.user and str(
reaction.emoji) == '🚮' and reaction.message.id == msg.id
return all(
((user == ctx.message.author or user == member
or discord.utils.get(user.roles,
name=self.bot.config.moderator_role)),
user != self.bot.user, str(reaction.emoji) == '🚮',
reaction.message.id == msg.id))

try:
await self.bot.wait_for('reaction_add', check=check, timeout=120)
Expand Down Expand Up @@ -201,12 +216,14 @@ async def quotes(self, ctx, str1: str = None, *, str2: str = None):

def check(reaction, user):
# returns True if all the following is true:
# The user who reacted is the one that called the command or a moderator
# The user who reacted isn't the bot
# The react is the ok emoji
# The react is on the "Quote not found." message
return (user == ctx.message.author and user != self.bot.user
) and (str(reaction.emoji) == '🆗'
and reaction.message.id == msg.id)
return all(((user == ctx.message.author or discord.utils.get(
user.roles, name=self.bot.config.moderator_role)),
user != self.bot.user, str(reaction.emoji) == '🆗',
reaction.message.id == msg.id))

try:
await self.bot.wait_for('reaction_add',
Expand Down Expand Up @@ -274,55 +291,61 @@ async def list_quotes(self, ctx, author: discord.Member = None):

p = Pages(ctx,
item_list=quote_list_text,
title='Quotes from {}'.format(quote_author.display_name))
title='Quotes from {}'.format(quote_author.display_name),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with f-strings for consistency (same for line 390)

return_user_on_edit=True)

await p.paginate()
user_deleting = await p.paginate()

def msg_check(msg):
try:
return (0 <= int(msg.content) <= len(quote_list)
and msg.author.id == author_id
and msg.channel == ctx.message.channel)
return all((0 <= int(msg.content) <= len(quote_list),
msg.author == user_deleting,
msg.channel == ctx.message.channel,
msg.author != self.bot.user))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

except ValueError:
return False

while p.edit_mode:
await ctx.send(
'Delete option selected. Enter a number to specify which '
'quote you want to delete, or enter 0 to return.',
delete_after=60)

try:
message = await self.bot.wait_for('message',
check=msg_check,
timeout=60)

except asyncio.TimeoutError:
if (user_deleting == author
or discord.utils.get(user_deleting.roles,
name=self.bot.config.moderator_role)):
await ctx.send(
'Command timeout. You may want to run the command again.',
'Delete option selected. Enter a number to specify which '
'quote you want to delete, or enter 0 to return.',
delete_after=60)
break

else:
index = int(message.content) - 1
if index == -1:
await ctx.send('Exit delq.', delete_after=60)
try:
message = await self.bot.wait_for('message',
check=msg_check,
timeout=60)

except asyncio.TimeoutError:
await ctx.send(
'Command timeout. You may want to run the command again.',
delete_after=60)
break

else:
t = (quote_list[index][0], quote_list[index][2])
del quote_list[index]
c.execute('DELETE FROM Quotes WHERE ID = ? AND Quote = ?',
t)
conn.commit()
index = int(message.content) - 1
if index == -1:
await ctx.send('Exit delq.', delete_after=60)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you early return here to avoid indenting everything?

else:
t = (quote_list[index][0], quote_list[index][2])
del quote_list[index]
c.execute(
'DELETE FROM Quotes WHERE ID = ? AND Quote = ?', t)
conn.commit()
self.rebuild_mc()

await ctx.send('Quote deleted', delete_after=60)
await message.delete()
await ctx.send('Quote deleted', delete_after=60)
await message.delete()

p.itemList = [
f'[{i}] {quote[2]}'
for i, quote in enumerate(quote_list, 1)
]
p.itemList = [
f'[{i}] {quote[2]}'
for i, quote in enumerate(quote_list, 1)
]

await p.paginate()
await p.paginate()

conn.commit()
conn.close()
Expand Down Expand Up @@ -388,10 +411,59 @@ async def all_quotes(self, ctx, *, query):
p = Pages(ctx,
item_list=quote_list_text,
title='Quotes that contain "{}"'.format(query),
editable_content=False,
current_page=pagenum)
current_page=pagenum,
return_user_on_edit=True)

await p.paginate()
user_deleting = await p.paginate()

def msg_check(msg):
try:
return all((0 <= int(msg.content) <= len(quote_list),
msg.author == user_deleting,
msg.channel == ctx.message.channel))
except ValueError:
return False

while p.edit_mode:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this kinda looks like its duplicating a bunch of code - whats up there?

if discord.utils.get(user_deleting.roles,
name=self.bot.config.moderator_role):
await ctx.send(
'Delete option selected. Enter a number to specify which '
'quote you want to delete, or enter 0 to return.',
delete_after=60)

try:
message = await self.bot.wait_for('message',
check=msg_check,
timeout=60)

except asyncio.TimeoutError:
await ctx.send(
'Command timeout. You may want to run the command again.',
delete_after=60)
break

else:
index = int(message.content) - 1
if index == -1:
await ctx.send('Exit delq.', delete_after=60)
else:
t = (quote_list[index][0], quote_list[index][2])
del quote_list[index]
c.execute(
'DELETE FROM Quotes WHERE ID = ? AND Quote = ?', t)
conn.commit()
self.rebuild_mc()

await ctx.send('Quote deleted', delete_after=60)
await message.delete()

p.itemList = [
f'[{i}] {quote[2]}'
for i, quote in enumerate(quote_list, 1)
]

await p.paginate()

@commands.command(aliases=['gen'])
async def generate(self, ctx, seed: str = None, min_length: int = 1):
Expand Down Expand Up @@ -466,6 +538,77 @@ async def generate(self, ctx, seed: str = None, min_length: int = 1):

await ctx.send(' '.join(longest_sentence))

@commands.command()
@is_moderator()
async def purgequotes(self, ctx, user: str):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you make this purge_quotes and add an alias purgequotes to keep the function names pep8?

"""
Mod-only: Purge all quote from user
Argument: A user (ID can be used even if the account is deleted)
"""
try:
id = await UserToIdConverter().convert(ctx, user)
except commands.BadArgument as err:
await ctx.send(err)
return

def msg_check(msg):
return all(
(msg.author == ctx.message.author, msg.channel == ctx.channel))

conn = sqlite3.connect(self.bot.config.db_path)
c = conn.cursor()
c.execute('SELECT * FROM Quotes WHERE ID = ?', (id, ))
quote_list = c.fetchall()

if not quote_list:
await ctx.send('No quote found.')
return

quote_list_text = [
f'[{i}] {quote[2]}' for i, quote in enumerate(quote_list, 1)
]

p = Pages(ctx,
item_list=quote_list_text,
title="List of quotes to be deleted",
editable_content_emoji="🆗",
return_user_on_edit=True)

confirm_message = await ctx.send(
f"Do you want to delete the following {len(quote_list)} quotes?\n"
f"Please press 🆗 to confirm (You will be asked to confirm one more time)"
)
user_deleting = await p.paginate()

while p.edit_mode:
if user_deleting == ctx.message.author:
if user_deleting == ctx.message.author:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double if??

await ctx.send(
"Please type `yes` to confirm the deletion. (Type anything otherwise)"
)
try:
confirmation_msg = await self.bot.wait_for(
'message', check=msg_check, timeout=60)
except asyncio.TimeoutError:
await ctx.send("Command timed out.")
return

confirmation_str = confirmation_msg.content
if confirmation_str.lower() != "yes":
await ctx.send("Exiting without deleting.")
return

c.execute('DELETE FROM Quotes WHERE ID = ?', (id, ))
conn.commit()
self.rebuild_mc()
await ctx.send(
f"Successfully deleted {len(quote_list)} quotes")
break
await p.paginate()

await confirm_message.delete()
conn.close()


def setup(bot):
bot.add_cog(Quotes(bot))