async def discard(self, ctx, name, group=None): id_idol = await self.can_give(ctx, ctx.author, name, group) if not id_idol: return def check(message): return message.author == ctx.author \ and message.channel == ctx.message.channel \ and (message.content.lower() == 'yes' or message.content.lower() == 'y' or message.content.lower() == 'no' or message.content.lower() == 'n') await ctx.send( f'{ctx.author.mention}, are you sure you want to discard **{name}**? (y|yes or n|no)\n' ) try: msg = await self.bot.wait_for('message', timeout=30, check=check) except asyncio.TimeoutError: await ctx.message.add_reaction(u"\u274C") await ctx.send('Discard is cancelled.') else: if msg.content.lower() == 'y' or msg.content.lower() == 'yes': DatabaseDeck.get().give_to(ctx.guild.id, id_idol, ctx.author.id, None) await ctx.message.add_reaction(u"\u2705") await msg.add_reaction(u"\u2705") else: await ctx.send('Discard is cancelled.')
async def wishlist(self, ctx): ids = DatabaseDeck.get().get_wishlist(ctx.guild.id, ctx.author.id) description = '' username = ctx.author.name if ctx.author.nick is None else ctx.author.nick nb_wish = DatabaseDeck.get().get_nb_wish(ctx.guild.id, ctx.author.id) max_wish = DatabaseDeck.get().get_max_wish(ctx.guild.id, ctx.author.id) for id_idol in ids: current_image = DatabaseDeck.get().get_idol_current_image( ctx.guild.id, id_idol) idol = DatabaseIdol.get().get_idol_information( id_idol, current_image) id_owner = DatabaseDeck.get().idol_belongs_to( ctx.guild.id, id_idol) emoji = '' if id_owner: if id_owner == ctx.author.id: emoji = u"\u2705" else: emoji = u"\u274C" description += f'**{idol["name"]}** *{idol["group"]}* {emoji}\n' await ctx.send(embed=discord.Embed( title=f'Wish list of {username} ({nb_wish}/{max_wish})', description=description))
async def give(self, ctx, user, name, group=None): if not ctx.message.mentions: await ctx.message.add_reaction(u"\u274C") await ctx.send('Please specify a user.') return None id_idol = await self.can_give(ctx, ctx.author, name, group) if not id_idol: return user = ctx.message.mentions[0] def check(message): return message.author == user and ( message.content.lower() == 'yes' or message.content.lower() == 'y' or message.content.lower() == 'no' or message.content.lower() == 'n') await ctx.send( f'{user.mention}, {ctx.author.mention} wants to give you **{name}**.\n' f'Type y|yes or n|no.') try: msg = await self.bot.wait_for('message', timeout=30, check=check) except asyncio.TimeoutError: await ctx.message.add_reaction(u"\u274C") await ctx.send('Too late... Give is cancelled.') else: if msg.content.lower() == 'y' or msg.content.lower() == 'yes': DatabaseDeck.get().give_to(ctx.guild.id, id_idol, ctx.author.id, user.id) await ctx.message.add_reaction(u"\u2705") await msg.add_reaction(u"\u2705") else: await ctx.send('Give is cancelled.')
async def time(self, ctx): next_claim = min_until_next_claim(ctx.guild.id, ctx.author.id) username = ctx.author.name if ctx.author.nick is None else ctx.author.nick msg = f'{username}, you ' if next_claim == 0: msg += f'can claim right now!' else: time = divmod(next_claim, 60) msg += f'can\'t claim for another **' + \ (str(time[0]) + 'h ' if time[0] != 0 else '') + f'{str(time[1])} min**.' user_nb_rolls = DatabaseDeck.get().get_nb_rolls( ctx.guild.id, ctx.author.id) max_rolls = DatabaseDeck.get().get_rolls_per_hour(ctx.guild.id) last_roll = DatabaseDeck.get().get_last_roll(ctx.guild.id, ctx.author.id) if not last_roll: user_nb_rolls = 0 else: last_roll = datetime.strptime(last_roll, '%Y-%m-%d %H:%M:%S') now = datetime.now() # If a new hour began if now.date() != last_roll.date() or ( now.date() == last_roll.date() and now.hour != last_roll.hour): user_nb_rolls = 0 msg += f'\nYou have **{max_rolls - user_nb_rolls}** rolls left.\n' \ f'Next rolls reset in **{60 - datetime.now().minute} min**.' await ctx.send(msg)
async def set_time_to_claim(self, ctx, time_to_claim): try: time_to_claim = int(time_to_claim) except ValueError: await ctx.send('Please enter time to claim as number.') await ctx.message.add_reaction(u"\u274C") return DatabaseDeck.get().set_time_to_claim(ctx.guild.id, time_to_claim) await ctx.message.add_reaction(u"\u2705")
async def set_nb_rolls_per_hour(self, ctx, nb_rolls): try: nb_rolls = int(nb_rolls) except ValueError: await ctx.send('Please enter number of rolls as number.') await ctx.message.add_reaction(u"\u274C") return DatabaseDeck.get().set_nb_rolls_per_hour(ctx.guild.id, nb_rolls) await ctx.message.add_reaction(u"\u2705")
async def set_claiming_interval(self, ctx, interval): try: interval = int(interval) except ValueError: await ctx.send('Please enter minutes as number.') await ctx.message.add_reaction(u"\u274C") return DatabaseDeck.get().set_claiming_interval(ctx.guild.id, interval) await ctx.message.add_reaction(u"\u2705")
async def can_give(ctx, author, name, group=None): """Return idol id if the user can give, None otherwise.""" ## Find idol id name = name.strip() if group: group = group.strip() id_idol = None if group: id_idol = DatabaseIdol.get().get_idol_group_id(name, group) else: ids = DatabaseIdol.get().get_idol_ids(name) if ids: id_idol = ids[0] if not id_idol: msg = f'I searched everywhere for **{name}**' if group: msg += f' in the group *{group}*' msg += ' and I couldn\'t find anything.\nPlease check the command.' await ctx.send(msg) return None ## Check if idol belongs to author owner = DatabaseDeck.get().idol_belongs_to(ctx.guild.id, id_idol) if not owner or owner != author.id: await ctx.message.add_reaction(u"\u274C") await ctx.send( f'You don\'t own **{name}**{" from *" + group + "* " if group else ""}...' ) return None return id_idol
async def wishremove(self, ctx, name, group=None): name = name.strip() if group: group = group.strip() id_idol = None if group: id_idol = DatabaseIdol.get().get_idol_group_id(name, group) else: ids = DatabaseIdol.get().get_idol_ids(name) if ids: id_idol = ids[0] if not id_idol: await ctx.message.add_reaction(u"\u274C") await ctx.send( f'Idol **{name}**{" from *" + group + "* " if group else ""} not found.' ) return if DatabaseDeck.get().remove_from_wishlist(ctx.guild.id, id_idol, ctx.author.id): # Green mark await ctx.message.add_reaction(u"\u2705") else: # Red cross await ctx.message.add_reaction(u"\u274C") await ctx.send('You don\'t have this idol in your wish list.')
def min_until_next_claim(id_server, id_user): """Return minutes until next claim (0 if the user can claim now).""" last_claim = DatabaseDeck.get().get_last_claim(id_server, id_user) time_until_claim = 0 if last_claim: claim_interval = DatabaseDeck.get().get_server_configuration( id_server)['claim_interval'] date_last_claim = datetime.strptime(last_claim, '%Y-%m-%d %H:%M:%S') minute_since_last_claim = divmod( (datetime.now() - date_last_claim).seconds, 60)[0] if minute_since_last_claim < claim_interval: time_until_claim = claim_interval - minute_since_last_claim return time_until_claim
async def set_max_wish(self, ctx, max_wish): if not ctx.message.mentions: await ctx.send('Please mention a user.') await ctx.message.add_reaction(u"\u274C") return user = ctx.message.mentions[0] try: max_wish = int(max_wish) except ValueError: await ctx.send('Please enter a number.') await ctx.message.add_reaction(u"\u274C") return DatabaseDeck.get().set_max_wish(ctx.guild.id, user.id, max_wish) await ctx.message.add_reaction(u"\u2705")
async def show_configuration(self, ctx): config = DatabaseDeck.get().get_server_configuration(ctx.guild.id) description = f'Claim interval: {config["claim_interval"]} minutes\n' \ f'Time to claim an idol: {config["time_to_claim"]} seconds\n' \ f'Number of rolls per hour: {config["rolls_per_hour"]}' embed = discord.Embed(title=f'Server *{ctx.guild.name}* configuration', description=description) await ctx.send(embed=embed)
async def list(self, ctx, *, name): ids = DatabaseIdol.get().get_idol_ids(name) description = '' if ids else 'No idols found' for id_idol in ids: image_number = DatabaseDeck.get().get_idol_current_image(ctx.guild.id, id_idol) idol = DatabaseIdol.get().get_idol_information(id_idol, image_number) description += f'**{idol["name"]}** *{idol["group"]}*\n' embed = discord.Embed(title=f'{name} idols', description=description) await ctx.send(embed=embed)
async def profile(self, ctx): user = ctx.author if not ctx.message.mentions else ctx.message.mentions[ 0] ids_deck = DatabaseDeck.get().get_user_deck(ctx.guild.id, user.id) # TODO: handle long messages (>2000 letters) with pages description = '' for id_idol in ids_deck: current_image = DatabaseDeck.get().get_idol_current_image( ctx.guild.id, id_idol) idol = DatabaseIdol.get().get_idol_information( id_idol, current_image) description += f'**{idol["name"]}** *{idol["group"]}*\n' embed = discord.Embed( title=user.name if user.nick is None else user.nick, description=description) embed.set_thumbnail(url=user.avatar_url) await ctx.send(embed=embed)
def min_until_next_roll(id_server, id_user): """Return minutes until next roll (0 if the user can roll now).""" last_roll = DatabaseDeck.get().get_last_roll(id_server, id_user) if not last_roll: return 0 last_roll = datetime.strptime(last_roll, '%Y-%m-%d %H:%M:%S') now = datetime.now() # If a new hour began if now.date() != last_roll.date() or (now.date() == last_roll.date() and now.hour != last_roll.hour): DatabaseDeck.get().set_nb_rolls(id_server, id_user, 0) return 0 max_rolls = DatabaseDeck.get().get_rolls_per_hour(id_server) user_nb_rolls = DatabaseDeck.get().get_nb_rolls(id_server, id_user) if user_nb_rolls < max_rolls: return 0 else: return 60 - now.minute
async def discard_all(self, ctx): letters = string.ascii_letters random_string = 'cancel' while random_string == 'cancel': random_string = ''.join(secrets.choice(letters) for i in range(5)) def check(message): return message.author == ctx.author \ and message.channel == ctx.message.channel \ and (message.content == random_string or message.content.lower() == 'cancel') await ctx.send( f'{ctx.author.mention}, are you sure you want to discard **all your deck**?\n' f'This cannot be undone! Please type *{random_string}* (with case) to confirm ' f'or *cancel* to cancel.') try: msg = await self.bot.wait_for('message', timeout=30, check=check) except asyncio.TimeoutError: await ctx.message.add_reaction(u"\u274C") await ctx.send('Discard is cancelled.') else: if msg.content.lower() == 'cancel': await ctx.message.add_reaction(u"\u274C") await ctx.send('Discard is cancelled.') return ids_deck = DatabaseDeck.get().get_user_deck( ctx.guild.id, ctx.author.id) for id_idol in ids_deck: DatabaseDeck.get().give_to(ctx.guild.id, id_idol, ctx.author.id, None) await ctx.message.add_reaction(u"\u2705") await msg.add_reaction(u"\u2705")
async def profile(self, ctx): user = ctx.author if not ctx.message.mentions else ctx.message.mentions[ 0] ids_deck = DatabaseDeck.get().get_user_deck(ctx.guild.id, user.id) async def send_embed(desc): embed = discord.Embed( title=user.name if user.nick is None else user.nick, description=desc) embed.set_thumbnail(url=user.avatar_url) await ctx.send(embed=embed) idols_text = [] description = '' for id_idol in ids_deck: current_image = DatabaseDeck.get().get_idol_current_image( ctx.guild.id, id_idol) idol = DatabaseIdol.get().get_idol_information( id_idol, current_image) idols_text.append(f'**{idol["name"]}** *{idol["group"]}*') idols_text.sort() current_page = 1 nb_per_page = 20 max_page = math.ceil(len(idols_text) / float(nb_per_page)) embed = discord.Embed( title=user.name if user.nick is None else user.nick, description='\n'.join([ idol for idol in idols_text[(current_page - 1) * nb_per_page:current_page * nb_per_page] ])) embed.set_thumbnail(url=user.avatar_url) embed.set_footer(text=f'{current_page} \\ {max_page}') msg = await ctx.send(embed=embed) if max_page > 1: # Page handler left_emoji = '\U00002B05' right_emoji = '\U000027A1' await msg.add_reaction(left_emoji) await msg.add_reaction(right_emoji) def check(reaction, user): return user != self.bot.user and (str(reaction.emoji) == left_emoji or str(reaction.emoji) == right_emoji) \ and reaction.message.id == msg.id timeout = False while not timeout: try: reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=check) except asyncio.TimeoutError: await msg.clear_reaction(left_emoji) await msg.clear_reaction(right_emoji) timeout = True else: old_page = current_page if reaction.emoji == left_emoji: current_page = current_page - 1 if current_page > 1 else max_page if reaction.emoji == right_emoji: current_page = current_page + 1 if current_page < max_page else 1 await msg.remove_reaction(reaction.emoji, user) # Refresh embed message with the new text if old_page != current_page: embed = discord.Embed( title=user.name if user.nick is None else user.nick, description='\n'.join([ idol for idol in idols_text[ (current_page - 1) * nb_per_page:current_page * nb_per_page] ])) embed.set_thumbnail(url=user.avatar_url) embed.set_footer(text=f'{current_page} \\ {max_page}') await msg.edit(embed=embed)
async def roll(self, ctx): minutes = min_until_next_roll(ctx.guild.id, ctx.author.id) if minutes != 0: await ctx.send( f'You cannot roll right now. The next roll reset is in {minutes} minutes.' ) return id_idol = DatabaseIdol.get().get_random_idol_id() current_image = DatabaseDeck.get().get_idol_current_image( ctx.guild.id, id_idol) idol = DatabaseIdol.get().get_idol_information(id_idol, current_image) if not idol: ctx.send("An error occurred. If this message is exceptional, " "please try again. Otherwise, contact the administrator.") # Mention users if they wish for this idol id_members = DatabaseDeck.get().get_wished_by(ctx.guild.id, id_idol) wish_msg = '' for id_member in id_members: member = ctx.guild.get_member(id_member) # Could be None if the user left the server if member: wish_msg += f'{member.mention} ' if wish_msg: await ctx.send(f'Wished by {wish_msg}') # Update roll information in database DatabaseDeck.get().update_last_roll(ctx.guild.id, ctx.author.id) user_nb_rolls = DatabaseDeck.get().get_nb_rolls( ctx.guild.id, ctx.author.id) DatabaseDeck.get().set_nb_rolls(ctx.guild.id, ctx.author.id, user_nb_rolls + 1) max_rolls = DatabaseDeck.get().get_rolls_per_hour(ctx.guild.id) if max_rolls - user_nb_rolls - 1 == 2: await ctx.send( f'**{ctx.author.name if ctx.author.nick is None else ctx.author.nick}**, 2 uses left.' ) embed = discord.Embed(title=idol['name'], description=idol['group'], colour=secrets.randbelow(0xffffff)) embed.set_image(url=idol['image']) id_owner = DatabaseDeck.get().idol_belongs_to(ctx.guild.id, id_idol) if id_owner: owner = ctx.guild.get_member(id_owner) # Could be None if the user left the server if owner: embed.set_footer( icon_url=owner.avatar_url, text= f'Belongs to {owner.name if not owner.nick else owner.nick}' ) msg = await ctx.send(embed=embed) # Cannot claim if idol already claim if id_owner: return emoji = '\N{TWO HEARTS}' await msg.add_reaction(emoji) def check(reaction, user): return user != self.bot.user and str( reaction.emoji) == emoji and reaction.message.id == msg.id is_claimed_or_timeout = False claim_timeout = DatabaseDeck.get().get_server_configuration( ctx.guild.id)["time_to_claim"] while not is_claimed_or_timeout: try: _, user = await self.bot.wait_for('reaction_add', timeout=claim_timeout, check=check) username = user.name if user.nick is None else user.nick except asyncio.TimeoutError: await msg.clear_reaction(emoji) is_claimed_or_timeout = True else: time_until_claim = min_until_next_claim(ctx.guild.id, user.id) is_claimed_or_timeout = time_until_claim == 0 if is_claimed_or_timeout: DatabaseDeck.get().add_to_deck(ctx.guild.id, idol['id'], user.id) await ctx.send(f'{username} claims {idol["name"]}!') embed.set_footer(icon_url=user.avatar_url, text=f'Belongs to {username}') await msg.edit(embed=embed) else: time = divmod(time_until_claim, 60) await ctx.send( f'{username}, you can\'t claim right now. ' + f'Please wait **' + (str(time[0]) + 'h ' if time[0] != 0 else '') + f'{str(time[1])} min**.')
async def information(self, ctx, name, group=None): # TODO: add more information to the card (all groups...) name = name.strip() if group: group = group.strip() id_idol = None if group: id_idol = DatabaseIdol.get().get_idol_group_id(name, group) else: ids = DatabaseIdol.get().get_idol_ids(name) if ids: id_idol = ids[0] if not id_idol: msg = f'I searched everywhere for **{name}**' if group: msg += f' in the group *{group}*' msg += ' and I couldn\'t find anything.\nPlease check the command.' await ctx.send(msg) return current_image = DatabaseDeck.get().get_idol_current_image( ctx.guild.id, id_idol) idol = DatabaseIdol.get().get_idol_information(id_idol, current_image) embed = discord.Embed(title=idol['name'], description=idol['group'], colour=secrets.randbelow(0xffffff)) id_owner = DatabaseDeck.get().idol_belongs_to(ctx.guild.id, id_idol) # Counter variables total_images = DatabaseIdol.get().get_idol_images_count(id_idol) current_image = parse_int(DatabaseDeck().get().get_idol_current_image( ctx.guild.id, id_idol)) + 1 # Footer have always the picture counter, and eventually the owner info text = f'{current_image} \\ {total_images} \n' if id_owner: owner = ctx.guild.get_member(id_owner) if owner: text = f'{text}Belongs to {owner.name if not owner.nick else owner.nick}' embed.set_footer(icon_url=owner.avatar_url, text=text) else: embed.set_footer(text=text) embed.set_image(url=idol['image']) msg = await ctx.send(embed=embed) left_emoji = '\U00002B05' right_emoji = '\U000027A1' await msg.add_reaction(left_emoji) await msg.add_reaction(right_emoji) def check(reaction, user): return user != self.bot.user and (str(reaction.emoji) == left_emoji or str(reaction.emoji) == right_emoji) \ and reaction.message.id == msg.id timeout = False while not timeout: try: reaction, user = await self.bot.wait_for('reaction_add', timeout=10, check=check) except asyncio.TimeoutError: await msg.clear_reaction(left_emoji) await msg.clear_reaction(right_emoji) timeout = True else: old_image = current_image if reaction.emoji == left_emoji: DatabaseDeck.get().decrement_idol_current_image( ctx.guild.id, id_idol) if reaction.emoji == right_emoji: DatabaseDeck.get().increment_idol_current_image( ctx.guild.id, id_idol) current_image = parse_int( DatabaseDeck().get().get_idol_current_image( ctx.guild.id, id_idol)) + 1 await msg.remove_reaction(reaction.emoji, user) # Refresh embed message with the new picture if changed if old_image != current_image: # Redo the query because image link changed image_number = DatabaseDeck.get().get_idol_current_image( ctx.guild.id, id_idol) idol = DatabaseIdol.get().get_idol_information( id_idol, image_number) embed.set_image(url=idol['image']) text = f'{current_image} \\ {total_images} \n' if id_owner and owner: text = f'{text}Belongs to {owner.name if not owner.nick else owner.nick}' embed.set_footer(icon_url=owner.avatar_url, text=text) else: embed.set_footer(text=text) await msg.edit(embed=embed)
async def list(self, ctx, *, name): ids = DatabaseIdol.get().get_idol_ids(name) if not ids: await ctx.send(f'No *{name}* idol found') return idols_text = [] for id_idol in ids: image_number = DatabaseDeck.get().get_idol_current_image( ctx.guild.id, id_idol) idol = DatabaseIdol.get().get_idol_information( id_idol, image_number) if not idol: continue idols_text.append(f'**{idol["name"]}** *{idol["group"]}*') idols_text.sort() current_page = 1 nb_per_page = 20 max_page = math.ceil(len(idols_text) / float(nb_per_page)) embed = discord.Embed( title=f'*{name}* idols', description='\n'.join([ idol for idol in idols_text[(current_page - 1) * nb_per_page:current_page * nb_per_page] ])) embed.set_footer(text=f'{current_page} \\ {max_page}') msg = await ctx.send(embed=embed) if max_page > 1: # Page handler left_emoji = '\U00002B05' right_emoji = '\U000027A1' await msg.add_reaction(left_emoji) await msg.add_reaction(right_emoji) def check(reaction, user): return user != self.bot.user and ( str(reaction.emoji) == left_emoji or str(reaction.emoji) == right_emoji) \ and reaction.message.id == msg.id timeout = False while not timeout: try: reaction, user = await self.bot.wait_for('reaction_add', timeout=60, check=check) except asyncio.TimeoutError: await msg.clear_reaction(left_emoji) await msg.clear_reaction(right_emoji) timeout = True else: old_page = current_page if reaction.emoji == left_emoji: current_page = current_page - 1 if current_page > 1 else max_page if reaction.emoji == right_emoji: current_page = current_page + 1 if current_page < max_page else 1 await msg.remove_reaction(reaction.emoji, user) # Refresh embed message with the new text if old_page != current_page: embed = discord.Embed( title=f'*{name}* idols', description='\n'.join([ idol for idol in idols_text[ (current_page - 1) * nb_per_page:current_page * nb_per_page] ])) embed.set_footer(text=f'{current_page} \\ {max_page}') await msg.edit(embed=embed)
async def trade(self, ctx, user, name, group=None): if not ctx.message.mentions: await ctx.message.add_reaction(u"\u274C") await ctx.send('Please specify a user.') return None id_idol_give = await self.can_give(ctx, ctx.author, name, group) if not id_idol_give: return user = ctx.message.mentions[0] def check_name_group(message): param = list( filter(None, map(str.strip, message.content.split('"')))) return message.author == user and (not param or 1 <= len(param) <= 2) await ctx.send( f'{user.mention}, {ctx.author.mention} wants to trade with you.\n' f'Who do you want to trade for **{name}** ?\n' f'\nType name ["group"] **("" required around group!)**') try: msg = await self.bot.wait_for('message', timeout=30, check=check_name_group) except asyncio.TimeoutError: await ctx.message.add_reaction(u"\u274C") await ctx.send('Too late... Give is cancelled.') return arg = list(filter(None, map(str.strip, msg.content.split('"')))) name_receive = arg[0] group_receive = [] if len(arg) == 1 else arg[1] id_idol_receive = await self.can_give(ctx, user, name_receive, group_receive) if not id_idol_receive: return def check(message): return message.author == ctx.author and \ (message.content.lower() == 'yes' or message.content.lower() == 'y' or message.content.lower() == 'no' or message.content.lower() == 'n') await ctx.send( f'{user.mention} trades **{name_receive}** for **{name}**.\n' f'{ctx.author.mention}, do you accept? (y|yes or n|no)\n') try: msg = await self.bot.wait_for('message', timeout=30, check=check) except asyncio.TimeoutError: await ctx.message.add_reaction(u"\u274C") await ctx.send('Too late... Give is cancelled.') else: if msg.content.lower() == 'y' or msg.content.lower() == 'yes': DatabaseDeck.get().give_to(ctx.guild.id, id_idol_give, ctx.author.id, user.id) DatabaseDeck.get().give_to(ctx.guild.id, id_idol_receive, user.id, ctx.author.id) await ctx.message.add_reaction(u"\u2705") await msg.add_reaction(u"\u2705") else: await ctx.send('Trade is cancelled.')