async def add_emoji(client, event, emoji: ('str', 'The emoji to add.'), name: ('str', 'Custom name to add the emoji with.') = None): """Adds an emoji to the guild.""" if not client.is_owner(event.user): abort('Owner only!') emoji = parse_emoji(emoji) if emoji is None: abort('That\'s not an emoji.') if emoji.is_unicode_emoji(): abort('Cannot add unicode emojis') if name is None: name = emoji.name else: if len(name) > 32: abort('Name length can be max 32.') embed = Embed('Are you sure to add this emoji?').add_field( 'Name:', name).add_image(emoji.url) message = yield InteractionResponse(embed=embed, components=ADD_EMOJI_COMPONENTS) try: component_interaction = await wait_for_component_interaction( message, timeout=300.0, check=functools.partial(check_is_user_same, event.user)) except TimeoutError: component_interaction = None cancelled = True else: if component_interaction.interaction == ADD_EMOJI_BUTTON_CANCEL: cancelled = True else: cancelled = False if cancelled: embed.title = 'Adding emoji has been cancelled.' else: embed.title = 'Emoji has been added!' async with client.http.get(emoji.url) as response: emoji_data = await response.read() await client.emoji_create(event.guild, name, emoji_data) yield InteractionResponse(embed=embed, components=None, message=message, event=component_interaction)
async def edit_( client, event, sticker_name: ('str', 'The sticker\'s name to delete', 'sticker'), new_name: ( 'str', 'New name for the sticker', ) = None, new_emoji_value: (str, 'Emoji representation of the sticker.', 'new_emoji') = None, new_description: (str, 'Description for the sticker.') = None, ): """Edits the given sticker. (You must have emoji-council role)""" if not event.user.has_role(ROLE__NEKO_DUNGEON__EMOJI_MANAGER): abort( f'You must have {ROLE__NEKO_DUNGEON__EMOJI_MANAGER:m} role to invoke this command.' ) sticker = event.guild.get_sticker_like(sticker_name) if (sticker is None): abort(f'No sticker matched the given name: {sticker_name!r}.') anything_to_edit = False if (new_name is not None): if (sticker.name != new_name): name_length = len(new_name) if (name_length < 2) or (name_length > 32): abort( f'Sticker name\'s length can be in range [2:32], got {name_length!r}, {new_name!r}.' ) anything_to_edit = True else: new_name = None if (new_emoji_value is not None): new_emoji = parse_emoji(new_emoji_value) if new_emoji is None: abort(f'{new_emoji_value} cannot be interpreted as an emoji.') if new_emoji.is_custom_emoji(): abort(f'Only unicode can be used, got {new_emoji:e}') tags = sticker.tags if (tags is None) or (len(tags) != 1) or (next(iter(tags)) != new_emoji.name): anything_to_edit = True else: new_emoji = None else: new_emoji = None if (new_description is not None): description_length = len(new_description) if (description_length > 100): abort( f'Sticker description\'s length can be in range [0:100], got {description_length!r}, ' f'{new_description!r}.') if (sticker.description != new_description): anything_to_edit = True else: new_description = None if not anything_to_edit: abort('No differences were provided.') embed = Embed('Confirmation', f'Are you sure to edit {sticker.name!r} sticker?') if (new_name is not None): embed.add_field('Name', f'{sticker.name} -> {new_name}') if (new_emoji is not None): embed.add_field('Tags', f'{", ".join(sticker.tags)} -> {new_emoji.name}') if (new_description is not None): embed.add_field('Description', f'{sticker.description} -> {new_description}') message = yield InteractionResponse(embed=embed, components=STICKER_EDIT_COMPONENTS, allowed_mentions=None) try: component_interaction = await wait_for_component_interaction( message, timeout=300.0, check=partial_func(check_sticker_editor, event.user)) except TimeoutError: embed.title = 'Timeout' embed.description = f'Sticker {sticker.name!r} was not edited.' # Edit the source message with the source interaction yield InteractionResponse(embed=embed, components=None, allowed_mentions=None, message=message) return if component_interaction.interaction == STICKER_EDIT_BUTTON_CANCEL: embed.title = 'Cancelled' embed.description = f'Sticker {sticker.name!r} was not edited.' # Edit the source message with the component interaction yield InteractionResponse(embed=embed, components=None, allowed_mentions=None, event=component_interaction) return # Acknowledge the event await client.interaction_component_acknowledge(component_interaction) kwargs = {} if (new_name is not None): kwargs['name'] = new_name if (new_emoji is not None): kwargs['emoji_representation'] = new_emoji if (new_description is not None): kwargs['description'] = new_description try: await client.sticker_guild_edit(sticker, **kwargs) except ConnectionError: # No internet, let it be return except DiscordException as err: if err.code == ERROR_CODES.unknown_sticker: failure = False else: failure = True embed.title = 'Failure' embed.description = repr(err) else: failure = False if not failure: embed.title = 'Success' embed.description = f'Sticker {sticker.name!r} has been successfully edited.' # Edit the source message yield InteractionResponse(embed=embed, message=message, components=None)
async def delete_( client, event, sticker_name: ('str', 'The sticker\'s name to delete', 'sticker'), ): """Deletes the given sticker. (You must have emoji-council role)""" if not event.user.has_role(ROLE__NEKO_DUNGEON__EMOJI_MANAGER): abort( f'You must have {ROLE__NEKO_DUNGEON__EMOJI_MANAGER:m} role to invoke this command.' ) sticker = event.guild.get_sticker_like(sticker_name) if (sticker is None): abort(f'No sticker matched the given name: {sticker_name!r}.') embed = Embed( 'Confirmation', f'Are you sure to delete {sticker.name!r} ({sticker.id}) sticker forever?' ) message = yield InteractionResponse(embed=embed, components=STICKER_DELETE_COMPONENTS, allowed_mentions=None) try: component_interaction = await wait_for_component_interaction( message, timeout=300.0, check=partial_func(check_sticker_deleter, event.user)) except TimeoutError: embed.title = 'Timeout' embed.description = f'Sticker {sticker.name!r} was not deleted.' # Edit the source message with the source interaction yield InteractionResponse(embed=embed, components=None, allowed_mentions=None, message=message) return if component_interaction.interaction == STICKER_DELETE_BUTTON_CANCEL: embed.title = 'Cancelled' embed.description = f'Sticker {sticker.name!r} was not deleted.' # Edit the source message with the component interaction yield InteractionResponse(embed=embed, components=None, allowed_mentions=None, event=component_interaction) return # Acknowledge the event await client.interaction_component_acknowledge(component_interaction) try: await client.sticker_guild_delete(sticker) except ConnectionError: # No internet, let it be return except DiscordException as err: if err.code == ERROR_CODES.unknown_sticker: failure = False else: failure = True embed.title = 'Failure' embed.description = repr(err) else: failure = False if not failure: embed.title = 'Success' embed.description = f'Sticker {sticker.name!r} has been deleted successfully.' # Edit the source message yield InteractionResponse(embed=embed, message=message, components=None)
async def yeet( client, event, user: ('user', 'Select the user to yeet!'), reason: ('str', 'Any reason why you would want to yeet?') = None, delete_message_days: (range(8), 'Delete previous messages?') = 0, notify_user: ('bool', 'Whether the user should get DM about the ban.') = True, ): """Yeets someone out of the guild. You must have ban users permission.""" # Check permissions guild = event.guild if guild is None: abort('Guild only command.') if guild not in client.guild_profiles: abort('I must be in the guild to do this.') if not event.user_permissions.can_ban_users: abort('You must have yeet users permission to use this command.') if not guild.cached_permissions_for(client).can_ban_users: abort(f'{client.name_at(guild)} cannot yeet in the guild.') if not event.user.has_higher_role_than_at(user, guild): abort('You must have higher role than the person to be yeeted.') if not client.has_higher_role_than_at(user, guild): abort('I must have higher role than the person to yeeted.') # Ask, whether the user should be banned. if (reason is not None) and (not reason): reason = None embed = Embed('Confirmation', f'Are you sure to yeet {user.mention} from {guild.name}?'). \ add_field('Delete message day', str(delete_message_days), inline=True). \ add_field('Notify user', 'true' if notify_user else 'false', inline=True). \ add_field('Reason', '*No reason provided.*' if reason is None else reason) message = yield InteractionResponse(embed=embed, components=BAN_COMPONENTS, allowed_mentions=None) # Wait for user input try: component_interaction = await wait_for_component_interaction( message, timeout=300.0, check=partial_func(check_banner, event.user)) except TimeoutError: embed.title = 'Timeout' embed.description = f'{user.mention} was not yeeted from {guild.name}.' # Edit the source message with the source interaction yield InteractionResponse(embed=embed, components=None, allowed_mentions=None, message=message) return if component_interaction.interaction == BAN_BUTTON_CANCEL: embed.title = 'Cancelled' embed.description = f'{user.mention} was not yeeted from {guild.name}.' # Edit the source message with the component interaction yield InteractionResponse(embed=embed, components=None, allowed_mentions=None, event=component_interaction) return # Acknowledge the event await client.interaction_component_acknowledge(component_interaction) # Try to notify the user. Ignore bot notifications. if notify_user: if user.is_bot: notify_note = None else: try: channel = await client.channel_private_create(user) except BaseException as err: if isinstance(err, ConnectionError): return # We cannot help no internet raise embed = Embed('Yeeted', f'You were yeeted from {guild.name}.'). \ add_field('Reason', '*No reason provided.*' if reason is None else reason) try: await client.message_create(channel, embed=embed) except BaseException as err: if isinstance(err, ConnectionError): return # We cannot help no internet elif isinstance(err, DiscordException) and ( err.code == ERROR_CODES.cannot_message_user): notify_note = 'Notification cannot be delivered: user has DM disabled.' else: raise else: notify_note = None else: notify_note = None if reason is None: caller = event.user reason = f'Requested by: {caller.full_name} [{caller.id}]' await client.guild_ban_add(guild, user, delete_message_days=delete_message_days, reason=reason) embed = Embed('Hecatia yeah!', f'{user.full_name} has been yeeted.') if (notify_note is not None): embed.add_footer(notify_note) # Edit the source message yield InteractionResponse(embed=embed, message=message, components=None)
async def runner(self): client = self.client channel = self.channel answers = self.answers buffer = ReuBytesIO() embed = Embed(color=KANAKO_COLOR).add_image( 'attachment://guess_me.png').add_footer('') time_till_notify = CIRCLE_TIME - 10 for index, (question, answer) in enumerate(self.map, 1): embed.footer.text = f'{index} / {len(self.map)}' if self.possibilities: self.options = self.generate_options(answer) embed.description = '\n'.join([ f'**{index}.: {value}**' for index, value in enumerate(self.options, 1) ]) try: await client.message_create(channel, embed=embed, file=('guess_me.png', draw(buffer, question))) except BaseException as err: self.cancel() if isinstance(err, ConnectionError): return if isinstance(err, DiscordException): if err.code in ( ERROR_CODES.unknown_channel, # channel deleted ERROR_CODES.missing_access, # client removed ERROR_CODES. missing_permissions, # permissions changed meanwhile ): return await client.events.error(client, f'{self.__class__.__name__}.runner', err) return circle_start = LOOP_TIME() self.waiter = waiter = Future(KOKORO) future_or_timeout(waiter, time_till_notify) try: await waiter except TimeoutError: Task(self.notify_late_users(), KOKORO) self.waiter = waiter = Future(KOKORO) future_or_timeout(waiter, 10) try: await waiter except TimeoutError: leavers = [] users = self.users for index in reversed(range(len(users))): user = users[index] if user in answers: continue leavers.append(user) del users[index] for element in self.history: del element.answers[index] if len(self.users) == 0: self.cancel() embed = Embed( None, 'No-one gave answer in time, cancelling the game.', color=KANAKO_COLOR) else: embed = Embed(None, '\n'.join([ 'Users timed out:', *(user.full_name for user in leavers) ]), color=KANAKO_COLOR) try: await client.message_create(channel, embed=embed) except BaseException as err: self.cancel() if isinstance(err, ConnectionError): return if isinstance(err, DiscordException): if err.code in ( ERROR_CODES. unknown_channel, # channel deleted ERROR_CODES. missing_access, # client removed ERROR_CODES. missing_permissions, # permissions changed meanwhile ): return await client.events.error( client, f'{self.__class__.__name__}.runner', err) return if self.cancelled: return self.waiter = None element = HistoryElement() element.question = question element.answer = answer element.options = self.options element.answers = [(value[0], value[1] - circle_start) for value in (answers[user.id] for user in self.users)] self.history.append(element) answers.clear() embed.title = f'Last answer: {answer}' self.cancel() embed = Embed(embed.title, color=KANAKO_COLOR) try: await client.message_create(channel, embed=embed) except BaseException as err: self.cancel() if isinstance(err, ConnectionError): return if isinstance(err, DiscordException): if err.code in ( ERROR_CODES.unknown_channel, # channel deleted ERROR_CODES.missing_access, # client removed ERROR_CODES. missing_permissions, # permissions changed meanwhile ): return await client.events.error(client, f'{self.__class__.__name__}.runner', err) return await GameStatistics(self)
async def buy(client, event, item : ([(item.name, item.id) for item in BUYABLE], 'Select the item to buy nya!'), amount : (int, 'How much items would you want to buy?'), ): """Buy?""" try: item = ITEMS[item] except KeyError: abort('Item not available.') permissions = event.channel.cached_permissions_for(client) if (not permissions.can_send_messages) or (not permissions.can_add_reactions): abort('I need `send messages` and `add reactions` permissions to execute the command.') yield user = event.user async with DB_ENGINE.connect() as connector: response = await connector.execute( select([currency_model.total_love]). \ where(currency_model.user_id==user.id)) results = await response.fetchall() if results: total_love = results[0] else: total_love = 0 embed = Embed('Confirm buying', f'Selected item: {item.emoji:e} **{item.name}**\n' f'Amount: **{amount}**\n' f'\n' f'Price: {calculate_buy_cost(item.market_cost, amount)} {EMOJI__HEART_CURRENCY:e}\n' f'Budget: {total_love} {EMOJI__HEART_CURRENCY:e}' ) embed.add_author(user.avaar_url, user.full_name) embed.add_footer('The prices of context of demand and supply.') message = await client.message_create(event.channel, embed=embed) await client.reaction_add(message, item.emoji) await client.reaction_add(message, CONFIRM_NAH) try: event = await wait_for_reaction(client, message, partial_func(check_confirm_emoji, item.emoji), 300.0) except TimeoutError: return if event.emoji is CONFIRM_NAH: embed.title = 'Buying cancelled' else: user = event.user async with DB_ENGINE.connect() as connector: response = await connector.execute( select([currency_model.total_love, currency_model.total_allocated]). \ where(currency_model.user_id==user.id)) results = await response.fetchall() if results: total_love, total_allocated = results[0] else: total_love = total_allocated = 0 if total_love == 0: amount = cost = 0 else: amount, cost = calculate_buyable_and_cost(item.market_cost, amount, total_love-total_allocated) item.market_cost += amount if cost == 0: new_love = total_love else: new_love = total_love-cost await connector.execute(update(currency_model.user_id==user.id). \ values(total_love = new_love)) response = await connector.execute(select([item_model.id, item_model.amount]). \ where(item_model.user_id==user.id).where(item_model.type==item.id)) results = await response.fetchall() if results: row_id, actual_amount = results[0] new_amount = actual_amount+amount to_execute = ITEM_TABLE.update().values( amount=new_amount ).where(item_model.id==row_id) else: to_execute = ITEM_TABLE.insert().values( user_id = user.id, amount = amount, type = item.id ) await connector.execute(to_execute) embed.title = 'Buying confirmed' embed.description = ( f'Selected item: {item.emoji:e} **{item.name}**\n' f'Bought mount: **{amount}**\n' f'\n' f'Hearts: {total_love} {EMOJI__HEART_CURRENCY:e} -> {new_love} {EMOJI__HEART_CURRENCY:e}' ) await client.message_edit(message, embed=embed)
async def run(self): client = self.client channel = self.channel answers = self.answers buffer = ReuBytesIO() embed = Embed(color=KANAKO_COLOR) embed.add_image('attachment://guessme.png') embed.add_footer('') time_till_notify = CIRCLE_TIME - 10 for index, (question, answer) in enumerate(self.map, 1): embed.footer.text = f'{index} / {len(self.map)}' if self.possibilities: self.options = self.generate_options(answer) embed.description = '\n'.join([ f'**{index}.: {value}**' for index, value in enumerate(self.options, 1) ]) try: await client.message_create(channel, embed=embed, file=('guessme.png', draw(buffer, question))) except DiscordException: return self.cancel() circle_start = LOOP_TIME() waiter = sleep(time_till_notify, client.loop) self.waiter = waiter try: await waiter Task( self.send_or_except( Embed( 'Hurry! Only 10 seconds left!', '\n'.join([ user.full_name for user in self.users if user.id not in answers ]), KANAKO_COLOR)), client.loop) waiter = sleep(time_till_notify, client.loop) self.waiter = waiter await waiter self.calculate_leavers() except CancelledError: pass except InterruptedError as err: return self.cancel() element = history_element() element.question = question element.answer = answer element.options = self.options element.answers = [(value[0], value[1] - circle_start) for value in (answers[user.id] for user in self.users)] self.history.append(element) answers.clear() embed.title = f'Last answer: {answer}' await self.send_or_except(Embed(embed.title, '', KANAKO_COLOR)) del ACTIVE_GAMES[channel.id] client.events.message_create.remove(self.channel, self) self.running = False await game_statistics(self)