async def remove(ctx, arg): server = get_server(ctx) if arg == 'all': server.playlist.deleteAll() if server.voice: server.voice.stop() await ctx.send(embed=MsgEmbed.info('Удалил весь плейлист :fire:')) return if not arg.isdigit(): raise commands.MissingRequiredArgument() else: pos = int(arg) if not 0 <= pos < server.playlist.getLength(): await ctx.send(embed=MsgEmbed.warning( 'Как может в казино быть колода разложена в другом порядке?! Ты чё, бредишь, что ли?! Ёбаный твой рот, а!..' )) return mi = server.playlist.getByPosition(pos) playlist, voice = server.playlist, server.voice if pos == playlist.getPosition() and voice: voice.stop() if playlist.deleteByPosition(pos): await ctx.send( embed=MsgEmbed.info(f'Удалил: {mi.artist} - {mi.title} :fire:')) else: await ctx.send(embed=MsgEmbed.error('Ошибка удаления'))
async def get_voice_channel_by_name() -> Optional[discord.VoiceChannel]: channels = list( filter(lambda x: x.name == channel_name, ctx.guild.voice_channels)) if len(channels) == 0: await ctx.send(embed=MsgEmbed.error( 'Ты инвалид? Название канала неправильное')) return None elif len(channels) == 1: return channels[0] else: choice = [f' {x} ({x.position + 1}-й)\n' for x in channels] choice.insert(0, 'Отмена') answer = await ask_user(ctx, 'выбери канал', choice, icon_url=icons['list']) if answer is None: return None if answer == 0: await ctx.send(embed=MsgEmbed.warning('Подключение отменено')) return None if 1 <= answer <= len(channels): return channels[answer - 1] await ctx.send( embed=MsgEmbed.warning('Ты кто такой, сука, чтоб это делать?')) return None
async def leave(ctx): voice = get_server(ctx).voice if voice and voice.is_connected(): await voice.disconnect() await ctx.send(embed=MsgEmbed.warning('Бот отключился')) else: await ctx.send( embed=MsgEmbed.warning('Этим можно просто брать и обмазываться'))
async def join(ctx, channel_name='', *, reconnect=True) -> bool: async def get_voice_channel() -> Optional[discord.VoiceChannel]: if channel_name == '': if not member_is_connected(ctx.author): await ctx.send( embed=MsgEmbed.error('Присоединись к каналу, мудак')) return None return ctx.author.voice.channel return get_voice_channel_by_name() async def get_voice_channel_by_name() -> Optional[discord.VoiceChannel]: channels = list( filter(lambda x: x.name == channel_name, ctx.guild.voice_channels)) if len(channels) == 0: await ctx.send(embed=MsgEmbed.error( 'Ты инвалид? Название канала неправильное')) return None elif len(channels) == 1: return channels[0] else: choice = [f' {x} ({x.position + 1}-й)\n' for x in channels] choice.insert(0, 'Отмена') answer = await ask_user(ctx, 'выбери канал', choice, icon_url=icons['list']) if answer is None: return None if answer == 0: await ctx.send(embed=MsgEmbed.warning('Подключение отменено')) return None if 1 <= answer <= len(channels): return channels[answer - 1] await ctx.send( embed=MsgEmbed.warning('Ты кто такой, сука, чтоб это делать?')) return None if not reconnect: voice = get_server(ctx).voice if voice and voice.is_connected(): return True channel = await get_voice_channel() if channel is None: return False result = await join_to_channel(ctx, channel) if result == 0: await ctx.send( embed=MsgEmbed.ok(f'Бот подключился к каналу: {channel}')) elif result == 1: await ctx.send( embed=MsgEmbed.warning(f'Бот уже подключен к каналу: {channel}')) elif result == 2: await ctx.send(embed=MsgEmbed.error(f'Ты че наделал?')) else: await ctx.send( embed=MsgEmbed.error('Ничего не понял, но очень интересно')) # Я не хачу пихать return в if'ы, что бы не нарушать гармонию форматирования return 0 <= result <= 1
async def on_command_error(ctx, error): if isinstance(error, commands.CheckFailure): await ctx.send(embed=MsgEmbed.error('Не дорос ещё!')) elif isinstance(error, commands.CommandNotFound): await ctx.send(embed=MsgEmbed.error( f'Такой команды нет! `{ctx.prefix}help` - для справки')) elif isinstance(error, commands.MissingRequiredArgument) or isinstance( error, commands.BadArgument): await ctx.send(embed=MsgEmbed.error( f'Глаза разуй! Такого аргумента нет! `{ctx.prefix}help {ctx.command}` - для справки' )) else: raise error
async def queue(ctx): server = get_server(ctx) if server.playlist.getLength() == 0: await ctx.send(embed=MsgEmbed.info('Плейлист пуст')) else: mi_list = server.playlist.getAll() _list = [ f'{i} : {mi.artist} - {mi.title}' for i, mi in enumerate(mi_list) ] pos = server.playlist.getPosition() _list[pos] = f'**{_list[pos]}**' emb = MsgEmbed.info('') emb.set_author(name='текущий плейлист', icon_url=icons['playlist']) await send_long_list(ctx, _list, emb, MsgEmbed.info(''))
async def get_voice_channel() -> Optional[discord.VoiceChannel]: if channel_name == '': if not member_is_connected(ctx.author): await ctx.send( embed=MsgEmbed.error('Присоединись к каналу, мудак')) return None return ctx.author.voice.channel return get_voice_channel_by_name()
async def add_all(): added_songs, songs_count = 0, len(mi_list) message = await ctx.send(embed=MsgEmbed.info('Загрузка плейлиста...')) for mi in mi_list: if server.playlist.add(mi): added_songs += 1 if added_songs == songs_count: await message.edit( embed=MsgEmbed.ok('Все песни успешно добавлены!')) return True elif added_songs == 0: await message.edit( embed=MsgEmbed.warning('Ни одна песня не была добавлена!')) return False else: await message.edit(embed=MsgEmbed.warning( f'Добавлено: {added_songs}/{songs_count}')) return True
async def add_one(): songs_list = [f' {mi.artist} - {mi.title}' for mi in mi_list] songs_list.insert(0, 'Отмена') answer = await ask_user(ctx, 'выбери песню', songs_list, icon_url=icons['search']) if answer is None: return None if answer == 0: return await cancel() if 1 <= answer <= len(mi_list): mi = mi_list[answer - 1] else: return bad_answer() if server.playlist.add(mi): await ctx.send( embed=MsgEmbed.ok(f'Добавил: {mi.artist} - {mi.title}')) return True else: await ctx.send(embed=MsgEmbed.warning('Ошибка добавления!')) return False
async def mix(ctx): server = get_server(ctx) playlist = server.playlist if playlist.getLength() == 0: await ctx.send(embed=MsgEmbed.warning('Плейлист пуст, животное')) return playlist.mix() playlist.position = playlist.getLength() - 1 await skip(ctx)
async def skip(ctx, count: int = 1): server = get_server(ctx) voice = server.voice playlist = server.playlist if playlist.getLength() == 0: await ctx.send(embed=MsgEmbed.warning('Скипалка не отросла')) return for i in range(count - 1): playlist.next() if voice: voice.stop()
async def ask_user(ctx, title: str, choice: List[str], *, icon_url: str = icons['list'], timeout=10, start_index=0, step_index=1) -> Optional[int]: choice = [f'{i + start_index}. {x}' for i, x in enumerate(choice)] emb = MsgEmbed.info('') emb.set_author(name=title, icon_url=icon_url) await send_long_list(ctx, choice, emb, MsgEmbed.info('')) try: msg = await client.wait_for('message', check=get_message_check(ctx.author), timeout=timeout) except asyncio.exceptions.TimeoutError: await ctx.send(embed=MsgEmbed.warning('Кто не успел - тот опоздал')) return None else: return int(msg.content)
async def about(ctx): await ctx.send(embed=MsgEmbed.info(about_docs)) pass
async def add(ctx, *args) -> Optional[bool]: """ Добавляет в плейлист сервера новые треки args == ['--id', '...'] - поиск плейлиста по id args == ['url'] - поиск плейлиста по ссылке args == [...] - поиск трека по ключевым словам return True - в плейлист был добавлен хотя бы 1 трек return False - в плейлист не было ничего добавлено return None - таймаут, некорректные данные пользователя, ошибка апи """ async def cancel(): await ctx.send(embed=MsgEmbed.warning('Отменено')) return False async def bad_answer(): await ctx.send( embed=MsgEmbed.warning('Ты кто такой, сука, чтоб это делать?')) return None async def add_all(): added_songs, songs_count = 0, len(mi_list) message = await ctx.send(embed=MsgEmbed.info('Загрузка плейлиста...')) for mi in mi_list: if server.playlist.add(mi): added_songs += 1 if added_songs == songs_count: await message.edit( embed=MsgEmbed.ok('Все песни успешно добавлены!')) return True elif added_songs == 0: await message.edit( embed=MsgEmbed.warning('Ни одна песня не была добавлена!')) return False else: await message.edit(embed=MsgEmbed.warning( f'Добавлено: {added_songs}/{songs_count}')) return True async def add_one(): songs_list = [f' {mi.artist} - {mi.title}' for mi in mi_list] songs_list.insert(0, 'Отмена') answer = await ask_user(ctx, 'выбери песню', songs_list, icon_url=icons['search']) if answer is None: return None if answer == 0: return await cancel() if 1 <= answer <= len(mi_list): mi = mi_list[answer - 1] else: return bad_answer() if server.playlist.add(mi): await ctx.send( embed=MsgEmbed.ok(f'Добавил: {mi.artist} - {mi.title}')) return True else: await ctx.send(embed=MsgEmbed.warning('Ошибка добавления!')) return False async def load_playlist(): method = { 0: cancel, 1: add_one, 2: add_all, None: lambda: None }.get( await ask_user(ctx, 'че ты хочешь?', ['Отмена', 'Одна песня', 'Весь плейлист']), bad_answer) return await method() server = get_server(ctx) # используется в подпрограммах (server.playlist) if len(args) == 0: await ctx.send(embed=MsgEmbed.error('Нифига ты быдло')) return # я не смог избавиться от флага if args[0] == '--id': if len(args) < 2: await ctx.send( embed=MsgEmbed.error('Недосдача по аргументам, жмот')) return if len(args) > 2: await ctx.send(embed=MsgEmbed.warning('Слишком много аргументов')) mi_list = VkSearch.byPlaylistId(args[1]) is_playlist = True elif VkSearch.isCorrectPlaylistUrl(args[0]): if len(args) > 1: await ctx.send(embed=MsgEmbed.warning('Слишком много аргументов')) mi_list = VkSearch.byPlaylistUrl(args[0]) is_playlist = True else: mi_list = VkSearch.byString(' '.join(args), 5) is_playlist = False return await add_one() if mi_list is None: await ctx.send(embed=MsgEmbed.error('Ошибка поиска!')) return if mi_list == []: await ctx.send(embed=MsgEmbed.warning('Ничего не найдено!')) return False if is_playlist: return await load_playlist() else: return await add_one()
async def on_voice_state_update(member, before, after): before_channel = before.channel # это понятно after_channel = after.channel # это тоже if before_channel is None: return # если нечего ещё смотреть, выходим # если просто кто-то подключился к каналу, выходим if after_channel and before_channel.id == after_channel.id: return sid = before_channel.guild.id # это понятно (sid - server id) if not sid in servers: return # если нам нечего взять, выходим server = servers[sid] # это понятно voice = server.voice # это тоже if voice is None: return # если нам нечего взять, выходим bot_id = client.user.id # это понятно def is_it_only_robots( members ): # это тоже, но объясню, если весь канал состоит из ботов, то True, в противном случае False for member in members: if member.bot == False: return False return True async def disconnect_with_msg(msg): # это понятно await voice.disconnect() # это тоже await server.ctx.send(embed=MsgEmbed.hearts(msg)) # и это voice_members = voice.channel.members if len(voice_members) > 1: # если мы не одни # если мы не одни и тут только боты, отключаемся if is_it_only_robots(voice_members): await disconnect_with_msg('Пока-пока, безчувственные машины') return # это понятно before_members = before_channel.members if after_channel and after_channel.members[ 0].id == bot_id: # если нас переместили, то преследуем await voice.disconnect() try: voice = await before_channel.connect( ) # само преследование (почему говнокодим, я объясняю в конце функции) except asyncio.exceptions.TimeoutError: await disconnect_with_msg( 'Ай-яй-яй' ) # если мы не смогли преследовать (нет прав), то отключаемся else: await server.ctx.send(embed=MsgEmbed.hearts('Не надо стесняться')) server.voice = voice # тут преследование удастся в любом случае, но если поменять со скоростью света права before_channel, то преследование не удастся return # это понятно if voice.channel.id != before_channel.id: return # я уже забыл, зачем оно if voice.is_connected(): # нас кикают или никого не осталось, отключаемя if after_channel is None: await disconnect_with_msg('Пока-пока') else: # от нас уходят в другой канал, преследуем after_members = after_channel.members # это понятно a_mention = after_members[len(after_members) - 1].mention # это тоже await voice.disconnect() # читабельность, пока try: voice = await after_channel.connect(timeout=5 ) # само преследование except asyncio.exceptions.TimeoutError: await disconnect_with_msg( f'Увидимся, {a_mention}' ) # если мы не смогли преследовать (нет прав), то отключаемся (я бы ещё юзера нахер послал, но ладно) else: await server.ctx.send( embed=MsgEmbed.hearts(f'Ты куда, {a_mention}?')) server.voice = voice
async def playing(ctx): playlist = get_server(ctx).playlist if playlist.getLength() == 0: await ctx.send(embed=MsgEmbed.warning('Плейлист пуст')) return await ctx.send(embed=get_mi_embed(playlist.getCurrent()))
async def disconnect_with_msg(msg): # это понятно await voice.disconnect() # это тоже await server.ctx.send(embed=MsgEmbed.hearts(msg)) # и это
def get_mi_embed(mi: MusicInfo) -> discord.Embed: emb = MsgEmbed.info('') emb.set_author(name=f'{mi.artist} - {mi.title} [ {mi.time} сек ]', icon_url=mi.image_url) return emb
async def bad_answer(): await ctx.send( embed=MsgEmbed.warning('Ты кто такой, сука, чтоб это делать?')) return None
async def cancel(): await ctx.send(embed=MsgEmbed.warning('Отменено')) return False
async def on_error(event, ctx, *args, **kwargs): logger.exception(f'Exception in on_error. Event: {event}') await ctx.send(embed=MsgEmbed.error('!!! ОШИБКА !!!'))