async def sync_users_(event): """Syncs sticker list users. (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.' ) async with DB_ENGINE.connect() as connector: response = await connector.execute( select([sticker_counter_model.user_id ]).distinct(sticker_counter_model.user_id), ) results = await response.fetchall() user_ids = [result[0] for result in results] guild_users = GUILD__NEKO_DUNGEON.users user_ids_to_remove = [ user_id for user_id in user_ids if (user_id not in guild_users) ] if user_ids_to_remove: await connector.execute(STICKER_COUNTER_TABLE.delete().where( sticker_counter_model.user_id.in_(user_ids_to_remove))) return f'Unused user entries removed: {len(user_ids_to_remove)}'
async def __call__(self, command_name): command_source = self.sources.get(command_name, None) if (command_source is None): abort(f'Command not found: {command_name!r}') page = command_source[0] if len(command_source) > 1: components = Row( Button( emoji=EMOJI_LEFT, custom_id=f'source.{self.command_type}.{command_name}._', enabled=False, ), Button( emoji=EMOJI_RIGHT, custom_id=f'source.{self.command_type}.{command_name}.1', enabled=True, ), ) else: components = None yield InteractionResponse(page, components=components)
async def leave(client, event): """Leave from the current game, pls no.""" game = ACTIVE_GAMES.get(event.channel.id, None) if game is None: abort('Nothing to leave from.') return Embed(None, game.leave_user(event.user), color=KANAKO_COLOR)
async def info(client, event): """Shows information about the current game.""" game = ACTIVE_GAMES.get(event.channel.id, None) if game is None: abort('There is no active game at the channel.') return game.get_information()
async def all_users( client, event, ): """Shows the new users of the guild.""" if not event.user.has_role(ROLE__NEKO_DUNGEON__MODERATOR): abort('Hacker trying to hack Discord.') users = [] guild = event.guild for user in guild.users.values(): joined_at = user.guild_profiles[guild].joined_at if (joined_at is not None): users.append((joined_at, user)) users.sort(reverse=True) embeds = [] for index, (joined_at, user) in enumerate(users, 1): if index % 10 == 1: embed = Embed('Joined users') embeds.append(embed) add_user_field(embed, index, joined_at, user) await Pagination(client, event, embeds)
async def is_banned(client, event, user: ('user', 'Who should I check?')): """Checks whether the user is banned.""" if (not event.user.has_role(ROLE__NEKO_DUNGEON__TESTER)) and ( not event.user_permissions.can_ban_users): abort('You need to have `ban users` permissions to do this.') if not event.channel.cached_permissions_for(client).can_ban_users: abort('I need to have `ban users` permissions to do this.') yield # acknowledge the event try: ban_entry = await client.guild_ban_get(event.guild, user) except DiscordException as err: if err.code == ERROR_CODES.unknown_ban: ban_entry = None else: raise embed = Embed(f'Ban entry for {user:f}').add_thumbnail(user.avatar_url) if ban_entry is None: embed.description = 'The user **NOT YET** banned.' else: embed.description = 'The user is banned.' reason = ban_entry.reason if reason is None: reason = '*No reason was specified.*' embed.add_field('Reason:', reason) yield embed
async def latest_users( client, event, ): """Shows the new users of the guild.""" if not event.user.has_role(ROLE__NEKO_DUNGEON__MODERATOR): abort('Hacker trying to hack Discord.') date_limit = datetime.now() - timedelta(days=7) users = [] guild = event.guild for user in guild.users.values(): # Use created at and not `joined_at`, we can ignore lurkers. created_at = user.guild_profiles[guild].created_at if created_at > date_limit: users.append((created_at, user)) users.sort(reverse=True) del users[10:] embed = Embed('Recently joined users') if users: for index, (joined_at, user) in enumerate(users, 1): add_user_field(embed, index, joined_at, user) else: embed.description = '*none*' return InteractionResponse(embed=embed, allowed_mentions=None)
async def sync_emojis_(event): """Syncs emoji list emojis. (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.') async with DB_ENGINE.connect() as connector: response = await connector.execute( select([emoji_counter_model.emoji_id]).distinct(emoji_counter_model.emoji_id), ) results = await response.fetchall() emoji_ids = [result[0] for result in results] guild_emojis = GUILD__NEKO_DUNGEON.emojis emoji_ids_to_remove = [emoji_id for emoji_id in emoji_ids if (emoji_id not in guild_emojis)] if emoji_ids_to_remove: await connector.execute( EMOJI_COUNTER_TABLE.delete().where( emoji_counter_model.emoji_id.in_(emoji_ids_to_remove) ) ) return f'Unused emoji entries removed: {len(emoji_ids_to_remove)}'
def get_player(client, event): player = client.solarlink.get_player(event.guild_id) if player is None: abort('No player in this server!') return player
async def safe_booru(client, event, tags: ('str', 'Some tags to spice it up?') = '', ): """Some safe images?""" guild = event.guild if (guild is None) or guild.partial: abort(f'Please invite me, {client:f} first!') return answer_booru(client, event, tags, SAFE_BOORU, SAFE_BANNED)
def get_extension_by_name(name): extension = EXTENSION_LOADER.get_extension(name) if (extension is None): abort('There is no extension with the specified name.') if extension.locked: abort('The extension is locked, probably for reason.') return extension
async def role_claimer(event): """Role claimer message. (Owner only)""" # Double check. if not event.user.has_role(ROLE_OWNER): abort('Owner only') return InteractionResponse('Claim role by clicking on it', components=ROLE_CLAIMER_COMPONENTS)
async def join(client, event): """Join to the currently active game inside of the channel!""" game = ACTIVE_GAMES.get(event.channel.id, None) if game is None: abort('There is nothing to join into at the channel.') if isinstance(game, KanakoRunner): abort('The game is already started, oof.') return Embed(None, game.join_user(event.user), color=KANAKO_COLOR)
async def start_(client, event): """Starts the current game.""" game = ACTIVE_GAMES.get(event.channel.id, None) if game is None: abort('There is no active game at the channel.') if isinstance(game, KanakoRunner): abort('The game is already started, oof.') return game.start_user(event.user)
async def role_( client, event, role: ('role', 'Select the role to show information of.'), ): """Shows the information about a role.""" if role.partial: abort('I must be in the guild, where the role is.') embed = Embed(f'Role information for: {role.name}', color=role.color) embed.add_field('Position', str(role.position), inline=True) embed.add_field('Id', str(role.id), inline=True) embed.add_field('Separated', 'true' if role.separated else 'false', inline=True) embed.add_field('Mentionable', 'true' if role.mentionable else 'false', inline=True) manager_type = role.manager_type if manager_type is RoleManagerType.none: managed_description = 'false' else: if manager_type is RoleManagerType.unset: await client.sync_roles(role.guild) manager_type = role.manager_type if manager_type is RoleManagerType.bot: managed_description = f'Special role for bot: {role.manager:f}' elif manager_type is RoleManagerType.booster: managed_description = 'Role for the boosters of the guild.' elif manager_type is RoleManagerType.integration: managed_description = f'Special role for integration: {role.manager.name}' elif manager_type is RoleManagerType.unknown: managed_description = 'Some new things.. Never heard of them.' else: managed_description = 'I have no clue.' embed.add_field('Managed', managed_description, inline=True) color = role.color embed.add_field('color', f'html: {color.as_html}\n' f'rgb: {color.as_rgb}\n' f'int: {color:d}', inline=True) created_at = role.created_at embed.add_field('Created at', f'{created_at:{DATETIME_FORMAT_CODE}}\n' f'{elapsed_time(created_at)} ago', inline=True) return embed
async def show_map(client, event, map_: ([('hiragana', 'hiragana'), ('katakana', 'katakana')], 'Choose a map to display!')): """Shows the selected map!""" 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` permission to execute this command.' ) pages = MAP_SHOWCASES[map_] await KanakoPagination(client, event, pages)
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 next_( client, event, ): """Plays the next song.""" player = get_player(client, event) track = player.get_current() if track is None: abort('Nothing to skip.') if track.user is not event.user: abort( 'Sorry, the track was added by {event.user:m}, so only they can skip.' ) await player.skip() return f'Track skipped: {create_track_repr(track, None)}'
async def nsfw_booru(client, event, tags: ('str', 'Some tags to spice it up?') = '', ): """Some not so safe images? You perv!""" guild = event.guild if (guild is None) or guild.partial: abort(f'Please invite me, {client:f} first!') channel = event.channel if not channel.nsfw: if 'koishi' in tags.lower(): description = 'I love you too\~,\nbut this is not the right place to lewd.' else: description = 'Onii chaan\~,\nthis is not the right place to lewd.' abort(description) return answer_booru(client, event, tags, NSFW_BOORU, NSFW_BANNED)
async def guild_( event, field: ([(name, name) for name in GUILD_FIELDS], 'Which fields should I show?') = DEFAULT_GUILD_FILED, ): """Shows some information about the guild.""" guild = event.guild if guild.partial: abort('I must be in the guild to execute this command.') embed = Embed(guild.name, color=(guild.icon_hash & 0xFFFFFF if (guild.icon_type is ICON_TYPE_NONE) else (guild.id >> 22) & 0xFFFFFF)).add_thumbnail( guild.icon_url_as(size=128)) GUILD_FIELDS[field](guild, embed, True) return embed
async def seek( client, event, seconds: (float, 'Where to seek?'), ): """Seek the track.""" player = get_player(client, event) track = player.get_current() if track is None: abort('No songs are being played right now!') duration = track.duration if (seconds < 0.0) or (seconds > duration): abort( f'Cannot seek to {seconds:.2f} seconds. Please define a value between `0` and {duration:.0f}.' ) await player.seek(seconds) return 'Seeked the current track!'
async def enable_ping( client, event, allow: ('bool', 'Enable?') = True, ): """Enables the ping command in your guild.""" guild = event.guild if guild is None: abort('Guild only command.') if not event.user_permissions.can_administrator: abort('You must have administrator permission to use this command.') application_commands = await client.application_command_guild_get_all(guild ) for application_command in application_commands: # If you are not working with overlapping names, a name check should be enough. if application_command.name == ping.name: command_present = True break else: command_present = False if allow: if command_present: content = 'The command is already present.' else: await client.application_command_guild_create( guild, ping.get_schema()) content = 'The command has been added.' else: if command_present: await client.application_command_guild_delete( guild, application_command) content = 'The command has been disabled.' else: content = 'The command is not present.' return Embed('Success', content)
async def debug_command(client, event, command_name: (str, 'The command\'s name.') ): """Gets debug information about the given command.""" if not client.is_owner(event.user): abort('Owner only.') if not command_name: abort('Empty command name.') application_commands = await client.application_command_guild_get_all(GUILD__NEKO_DUNGEON) for application_command in application_commands: if application_command.name == command_name: break else: abort('Command could not be found.') try: permission = await client.application_command_permission_get(GUILD__NEKO_DUNGEON, application_command) except DiscordException as err: if err.code == ERROR_CODES.unknown_application_command_permissions: permission = None else: raise text_parts = [ '**Application command**:\n' 'Name : `', application_command.name, '`\n' 'Id : `', repr(application_command.id), '`\n' 'Allow by default : `', repr(application_command.allow_by_default), '`\n' '**Permission overwrites**:\n' ] if permission is None: overwrites = None else: overwrites = permission.overwrites if overwrites is None: text_parts.append('*none*') else: for index, overwrite in enumerate(overwrites): text_parts.append(repr(index)) text_parts.append('.: type: `') text_parts.append(overwrite.type.name) text_parts.append('`; id: `') text_parts.append(repr(overwrite.target_id)) target_name = overwrite.target.name if target_name: text_parts.append('`; name: `') text_parts.append(target_name) text_parts.append('`; allowed: `') text_parts.append(repr(overwrite.allow)) text_parts.append('`\n') return ''.join(text_parts)
async def character(client, event, name: ('str', 'Who\'s?'), ): """Shows you the given Touhou character's portrait.""" name_length = len(name) if name_length == 0: abort('Empty name was given.') if name_length > 10: name_length = 10 diversity = 0.2+(10-name_length)*0.02 matcheds = get_close_matches(name, TOUHOU_NAMES, n=1, cutoff=1.0-diversity) if matcheds: return TOUHOU_NAME_RELATIONS[matcheds[0]](client, event) else: embed = Embed('No match', color=BOORU_COLOR) matcheds = get_close_matches(name, TOUHOU_NAMES, n=10, cutoff=0.8-diversity) if matcheds: field_value_parts = [] for index, matched in enumerate(matcheds, 1): field_value_parts.append(str(index)) field_value_parts.append('.: **') field_value_parts.append(matched) field_value_parts.append('**') name = TOUHOU_NAME_RELATIONS[matched].title if matched != name: field_value_parts.append(' [') field_value_parts.append(name) field_value_parts.append(']') field_value_parts.append('\n') del field_value_parts[-1] embed.add_field('Close matches:', ''.join(field_value_parts)) return embed
async def register( event, name: ('str', 'Please provide a name to register.'), ): """Registers the specified extension by it's name.""" check_permission(event) extension = EXTENSION_LOADER.get_extension(name) if (extension is not None): abort( f'There is already an extension added with the given name: `{extension.name}`.' ) try: EXTENSION_LOADER.add(name) except TypeError: title = f'Registering {name!r} extension failed.' description = 'There is no such extension.' else: title = f'Registering {name!r} was successful.' description = None return Embed(title, description)
async def welcome_screen_(client, event): """Shows the guild's welcome screen.""" 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 execute this command') yield welcome_screen = await client.welcome_screen_get(guild) if welcome_screen is None: yield Embed( description=f'**{guild.name}** *has no welcome screen enabled*.') return description = welcome_screen.description if (description is None): description = '*TOP THINGS TO DO HERE*' else: description = f'{welcome_screen.description}\n\n*TOP THINGS TO DO HERE*' embed = Embed(f'Welcome to **{guild.name}**', description) icon_url = guild.icon_url if (icon_url is not None): embed.add_thumbnail(icon_url) welcome_channels = welcome_screen.welcome_channels if (welcome_channels is not None): for welcome_channel in welcome_channels: embed.add_field( f'{welcome_channel.emoji:e} {welcome_channel.description}', f'#{welcome_channel.channel:d}') yield embed
async def guild_icon( event, choice: (GUILD_ICON_CHOICES, 'Which icon of the guild?') = 'icon', ): """Shows the guild's icon.""" guild = event.guild if (guild is None) or guild.partial: abort( 'The command unavailable in guilds, where the application\'s bot is not in.' ) if choice == 'icon': name = 'icon' url = guild.icon_url_as(size=4096) hash_value = guild.icon_hash components = GUILD_ICON_ICON_COMPONENTS elif choice == 'banner': name = 'banner' url = guild.banner_url_as(size=4096) hash_value = guild.banner_hash components = GUILD_ICON_BANNER_COMPONENTS elif choice == 'discovery_splash': name = 'discovery splash' url = guild.discovery_splash_url_as(size=4096) hash_value = guild.discovery_splash_hash components = GUILD_ICON_ICON_COMPONENTS else: name = 'invite splash' url = guild.invite_splash_url_as(size=4096) hash_value = guild.invite_splash_hash components = GUILD_ICON_DISCOVERY_SPLASH_COMPONENTS embed = create_embed(guild, name, url, hash_value) return InteractionResponse(embed=embed, components=components)
async def invite_create( client, event, permanent: ('bool', 'Create permanent?') = False, ): """I create an invite for you!""" 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 execute the command.') if not event.user_permissions.can_create_instant_invite: abort( 'You must have `create instant invite` permission to invoke this command.' ) if not guild.cached_permissions_for(client).can_create_instant_invite: abort( 'I must have `create instant invite` permission invite to execute this command.' ) yield if permanent: invite = await client.vanity_invite_get(guild) if invite is None: max_age = 0 max_uses = 0 else: invite = None max_age = 21600 max_uses = 1 if invite is None: yield invite = await client.invite_create_preferred(guild, max_age=max_age, max_uses=max_uses) if invite is None: content = '**Error**\nI do not have enough permission to create invite from the guild\'s preferred channel.' else: content = invite.url yield content
async def play( client, event, song_name: ('str', 'The name of the song to play'), ): """Play a song.""" guild = event.guild if (guild is None): abort('You need to be in a voice channel!') user = event.user state = guild.voice_states.get(event.user.id, None) if state is None: abort('You need to be in a voice channel!') if not is_url(song_name): song_name = f'ytsearch:{song_name}' yield result = await client.solarlink.get_tracks(song_name) # No result? if result is None: track = None else: tracks = result.tracks # If we received a playlist, it can be empty as well if tracks: selected_track_index = result.selected_track_index if selected_track_index == -1: selected_track_index = 0 track = tracks[selected_track_index] else: # It is empty track = None if track is None: abort('No songs found. Please try again!') player = client.solarlink.get_player(event.guild_id) if player is None: player = await client.solarlink.join_voice(state.channel) await player.append(track, user=user) yield ( f'Track added to queue!\n' f'- Name: [{track.title}]({track.url})\n' f'- Duration: {duration_to_string(track.duration)}' )
async def in_role( client, event, role_1: ('role', 'Select a role.'), role_2: ('role', 'Double role!') = None, role_3: ('role', 'Triple role!') = None, role_4: ('role', 'Quadra role!') = None, role_5: ('role', 'Penta role!') = None, role_6: ('role', 'Epic!') = None, role_7: ('role', 'Legendary!') = None, role_8: ('role', 'Mythical!') = None, role_9: ('role', 'Lunatic!') = None, ): """Shows the users with the given roles.""" 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.') roles = set() for role in role_1, role_2, role_3, role_4, role_5, role_6, role_7, role_8, role_9: if role is None: continue if role.guild is guild: roles.add(role) continue abort(f'Role {role.name}, [{role.id}] is bound to an other guild.') users = [] for user in guild.users.values(): try: guild_profile = user.guild_profiles[guild] except KeyError: continue guild_profile_roles = guild_profile.roles if guild_profile_roles is None: continue if not roles.issubset(guild_profile_roles): continue users.append(user) pages = InRolePageGetter(users, guild, roles) await Pagination(client, event, pages, check=partial_func(in_role_pagination_check, event.user))