Merge branch '28-use-pre-commit' into 'develop'
Resolve "Use pre-commit" Closes #28 See merge request ekzyis/musicube!24
This commit is contained in:
commit
5ef9de8ebc
|
@ -0,0 +1,19 @@
|
||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.2.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-added-large-files
|
||||||
|
- id: double-quote-string-fixer
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||||
|
rev: v1.6.0
|
||||||
|
hooks:
|
||||||
|
- id: autopep8
|
||||||
|
- repo: https://github.com/pycqa/isort
|
||||||
|
rev: 5.10.1
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
|
@ -4,19 +4,25 @@ async-timeout==3.0.1
|
||||||
attrs==21.2.0
|
attrs==21.2.0
|
||||||
autopep8==1.5.7
|
autopep8==1.5.7
|
||||||
cffi==1.14.6
|
cffi==1.14.6
|
||||||
|
cfgv==3.3.1
|
||||||
chardet==4.0.0
|
chardet==4.0.0
|
||||||
coverage==5.5
|
coverage==5.5
|
||||||
discord==1.7.3
|
discord==1.7.3
|
||||||
discord.py==1.7.3
|
discord.py==1.7.3
|
||||||
|
distlib==0.3.4
|
||||||
|
filelock==3.6.0
|
||||||
|
identify==2.4.12
|
||||||
idna==3.2
|
idna==3.2
|
||||||
iniconfig==1.1.1
|
iniconfig==1.1.1
|
||||||
isort==5.9.3
|
isort==5.9.3
|
||||||
lazy-object-proxy==1.6.0
|
lazy-object-proxy==1.6.0
|
||||||
mccabe==0.6.1
|
mccabe==0.6.1
|
||||||
multidict==5.1.0
|
multidict==5.1.0
|
||||||
|
nodeenv==1.6.0
|
||||||
packaging==21.0
|
packaging==21.0
|
||||||
platformdirs==2.3.0
|
platformdirs==2.3.0
|
||||||
pluggy==1.0.0
|
pluggy==1.0.0
|
||||||
|
pre-commit==2.18.1
|
||||||
py==1.10.0
|
py==1.10.0
|
||||||
pycodestyle==2.7.0
|
pycodestyle==2.7.0
|
||||||
pycparser==2.20
|
pycparser==2.20
|
||||||
|
@ -28,9 +34,11 @@ pytest-asyncio==0.16.0
|
||||||
pytest-cov==2.12.1
|
pytest-cov==2.12.1
|
||||||
pytest-mock==3.6.1
|
pytest-mock==3.6.1
|
||||||
python-dotenv==0.19.0
|
python-dotenv==0.19.0
|
||||||
|
PyYAML==6.0
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
toml==0.10.2
|
toml==0.10.2
|
||||||
typing-extensions==3.10.0.2
|
typing-extensions==3.10.0.2
|
||||||
|
virtualenv==20.14.1
|
||||||
wrapt==1.12.1
|
wrapt==1.12.1
|
||||||
yarl==1.6.3
|
yarl==1.6.3
|
||||||
youtube-dl==2021.12.17
|
youtube-dl==2021.12.17
|
||||||
|
|
58
src/bot.py
58
src/bot.py
|
@ -1,18 +1,18 @@
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import asyncio
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from distutils.util import strtobool
|
from distutils.util import strtobool
|
||||||
import logging
|
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
import youtube_dl
|
||||||
from discord.ext import commands, tasks
|
from discord.ext import commands, tasks
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import youtube_dl
|
|
||||||
|
|
||||||
from error import ErrorHandler
|
from error import ErrorHandler
|
||||||
from message import NowPlayingMessage, QueuedMessage, ErrorMessage
|
from log import DiscordLogger, create_logger
|
||||||
from log import create_logger, DiscordLogger
|
from message import ErrorMessage, NowPlayingMessage, QueuedMessage
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
@ -55,10 +55,10 @@ class Music(commands.Cog):
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
self._handle_playback.start()
|
self._handle_playback.start()
|
||||||
|
|
||||||
self.logger = logger if logger else create_logger("bot")
|
self.logger = logger if logger else create_logger('bot')
|
||||||
if log_channel_id := os.environ.get("DISCORD_LOG_CHANNEL_ID", None):
|
if log_channel_id := os.environ.get('DISCORD_LOG_CHANNEL_ID', None):
|
||||||
clear_on_startup = os.environ.get("DISCORD_LOG_CHANNEL_CLEAR_ON_STARTUP", False)
|
clear_on_startup = os.environ.get('DISCORD_LOG_CHANNEL_CLEAR_ON_STARTUP', False)
|
||||||
if clear_on_startup == "":
|
if clear_on_startup == '':
|
||||||
clear_on_startup = False
|
clear_on_startup = False
|
||||||
if isinstance(clear_on_startup, str):
|
if isinstance(clear_on_startup, str):
|
||||||
clear_on_startup = bool(strtobool(clear_on_startup))
|
clear_on_startup = bool(strtobool(clear_on_startup))
|
||||||
|
@ -70,7 +70,7 @@ class Music(commands.Cog):
|
||||||
|
|
||||||
def _next(self):
|
def _next(self):
|
||||||
"""Trigger playback of next song."""
|
"""Trigger playback of next song."""
|
||||||
self.logger.info("Song finished. Triggering playback of next song")
|
self.logger.info('Song finished. Triggering playback of next song')
|
||||||
self._queue.task_done()
|
self._queue.task_done()
|
||||||
self._current_skip_message = None
|
self._current_skip_message = None
|
||||||
if self._queue_lock.locked():
|
if self._queue_lock.locked():
|
||||||
|
@ -80,11 +80,11 @@ class Music(commands.Cog):
|
||||||
async def _handle_playback(self):
|
async def _handle_playback(self):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self.logger.info("Waiting for queue lock to acquire ...")
|
self.logger.info('Waiting for queue lock to acquire ...')
|
||||||
await self._queue_lock.acquire()
|
await self._queue_lock.acquire()
|
||||||
self.logger.info("Queue lock acquired! Waiting for song queue to return a song ...")
|
self.logger.info('Queue lock acquired! Waiting for song queue to return a song ...')
|
||||||
ctx, song = await self._queue.get()
|
ctx, song = await self._queue.get()
|
||||||
self.logger.info("Queue returned a song!")
|
self.logger.info('Queue returned a song!')
|
||||||
if ctx.voice_client is None:
|
if ctx.voice_client is None:
|
||||||
# Bot is no longer in a voice channel.
|
# Bot is no longer in a voice channel.
|
||||||
# This could be the case because a stop command was issued.
|
# This could be the case because a stop command was issued.
|
||||||
|
@ -96,7 +96,7 @@ class Music(commands.Cog):
|
||||||
|
|
||||||
def after(err):
|
def after(err):
|
||||||
if err:
|
if err:
|
||||||
self.logger.error("Player error: %s", err)
|
self.logger.error('Player error: %s', err)
|
||||||
self._next()
|
self._next()
|
||||||
self.logger.info('Now playing song "%s"', song.title)
|
self.logger.info('Now playing song "%s"', song.title)
|
||||||
ctx.voice_client.play(audio, after=after)
|
ctx.voice_client.play(audio, after=after)
|
||||||
|
@ -105,14 +105,14 @@ class Music(commands.Cog):
|
||||||
await self._add_skip_button(msg)
|
await self._add_skip_button(msg)
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.logger.error("Error during playback: %s", err)
|
self.logger.error('Error during playback: %s', err)
|
||||||
if ctx:
|
if ctx:
|
||||||
embed = ErrorMessage(str(err))
|
embed = ErrorMessage(str(err))
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
self._next()
|
self._next()
|
||||||
|
|
||||||
async def _add_skip_button(self, msg):
|
async def _add_skip_button(self, msg):
|
||||||
await msg.add_reaction("⏭️")
|
await msg.add_reaction('⏭️')
|
||||||
self._current_skip_message = msg
|
self._current_skip_message = msg
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
|
@ -120,7 +120,7 @@ class Music(commands.Cog):
|
||||||
if not user.bot \
|
if not user.bot \
|
||||||
and self._current_skip_message \
|
and self._current_skip_message \
|
||||||
and reaction.message.id == self._current_skip_message.id \
|
and reaction.message.id == self._current_skip_message.id \
|
||||||
and reaction.emoji == "⏭️":
|
and reaction.emoji == '⏭️':
|
||||||
voice_client = reaction.message.guild.voice_client
|
voice_client = reaction.message.guild.voice_client
|
||||||
self._skip(voice_client)
|
self._skip(voice_client)
|
||||||
|
|
||||||
|
@ -148,10 +148,10 @@ class Music(commands.Cog):
|
||||||
def _skip(self, voice_client):
|
def _skip(self, voice_client):
|
||||||
"""Skip to next song."""
|
"""Skip to next song."""
|
||||||
if voice_client is None or not voice_client.is_playing():
|
if voice_client is None or not voice_client.is_playing():
|
||||||
raise commands.CommandError("No song playing")
|
raise commands.CommandError('No song playing')
|
||||||
# This skips to next song because the bot does not differentiate between
|
# This skips to next song because the bot does not differentiate between
|
||||||
# a song stopping because it is finished or because it was manually stopped.
|
# a song stopping because it is finished or because it was manually stopped.
|
||||||
self.logger.info("Skipping song")
|
self.logger.info('Skipping song')
|
||||||
voice_client.stop()
|
voice_client.stop()
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
|
@ -161,37 +161,37 @@ class Music(commands.Cog):
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def stop(self, ctx):
|
async def stop(self, ctx):
|
||||||
self.logger.info("Stopping playback")
|
self.logger.info('Stopping playback')
|
||||||
await ctx.voice_client.disconnect()
|
await ctx.voice_client.disconnect()
|
||||||
|
|
||||||
@play.before_invoke
|
@play.before_invoke
|
||||||
async def ensure_voice(self, ctx):
|
async def ensure_voice(self, ctx):
|
||||||
if ctx.voice_client is None:
|
if ctx.voice_client is None:
|
||||||
if ctx.author.voice:
|
if ctx.author.voice:
|
||||||
self.logger.info("Connecting to voice channel ...")
|
self.logger.info('Connecting to voice channel ...')
|
||||||
await ctx.author.voice.channel.connect()
|
await ctx.author.voice.channel.connect()
|
||||||
self.logger.info("Connected")
|
self.logger.info('Connected')
|
||||||
else:
|
else:
|
||||||
raise commands.CommandError("Author not connected to a voice channel")
|
raise commands.CommandError('Author not connected to a voice channel')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
prefix = os.environ.get("DISCORD_CMD_PREFIX", "!")
|
prefix = os.environ.get('DISCORD_CMD_PREFIX', '!')
|
||||||
logger = create_logger("bot")
|
logger = create_logger('bot')
|
||||||
bot = commands.Bot(command_prefix=commands.when_mentioned_or(prefix),
|
bot = commands.Bot(command_prefix=commands.when_mentioned_or(prefix),
|
||||||
description='Relatively simple music bot example')
|
description='Relatively simple music bot example')
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
logger.info("Logged in as %s (%s)", bot.user, bot.user.id)
|
logger.info('Logged in as %s (%s)', bot.user, bot.user.id)
|
||||||
logger.info('------')
|
logger.info('------')
|
||||||
|
|
||||||
bot.add_cog(Music(bot, logger=logger))
|
bot.add_cog(Music(bot, logger=logger))
|
||||||
bot.add_cog(ErrorHandler(bot, logger=logger))
|
bot.add_cog(ErrorHandler(bot, logger=logger))
|
||||||
|
|
||||||
token = os.environ.get("DISCORD_BOT_TOKEN", None)
|
token = os.environ.get('DISCORD_BOT_TOKEN', None)
|
||||||
if not token:
|
if not token:
|
||||||
logger.warning("Discord bot token not found")
|
logger.warning('Discord bot token not found')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
bot.run(token)
|
bot.run(token)
|
||||||
|
|
|
@ -20,16 +20,16 @@ class ErrorHandler(commands.Cog):
|
||||||
self.logger.error(error)
|
self.logger.error(error)
|
||||||
return
|
return
|
||||||
if isinstance(error, commands.MissingPermissions):
|
if isinstance(error, commands.MissingPermissions):
|
||||||
message = "You are missing the required permissions to run this command!"
|
message = 'You are missing the required permissions to run this command!'
|
||||||
elif isinstance(error, commands.UserInputError):
|
elif isinstance(error, commands.UserInputError):
|
||||||
message = "Something about your input was wrong, please check your input and try again!"
|
message = 'Something about your input was wrong, please check your input and try again!'
|
||||||
elif isinstance(error, commands.CommandError):
|
elif isinstance(error, commands.CommandError):
|
||||||
message = str(error)
|
message = str(error)
|
||||||
else:
|
else:
|
||||||
message = "Oh no! Something went wrong while running the command!"
|
message = 'Oh no! Something went wrong while running the command!'
|
||||||
|
|
||||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
||||||
message = ansi_escape.sub("", message)
|
message = ansi_escape.sub('', message)
|
||||||
|
|
||||||
self.logger.error('Error during command "%s": %s', command_name, message)
|
self.logger.error('Error during command "%s": %s', command_name, message)
|
||||||
embed = ErrorMessage(message, command_name=command_name)
|
embed = ErrorMessage(message, command_name=command_name)
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from logging import LogRecord
|
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from logging import LogRecord
|
||||||
|
|
||||||
from discord import TextChannel
|
from discord import TextChannel
|
||||||
from discord.ext.commands import Bot as DiscordBot
|
from discord.ext.commands import Bot as DiscordBot
|
||||||
|
|
||||||
|
|
||||||
__LOG_FORMAT__ = '%(asctime)s %(name)-5s %(levelname)-8s %(message)s'
|
__LOG_FORMAT__ = '%(asctime)s %(name)-5s %(levelname)-8s %(message)s'
|
||||||
__DATE_FORMAT__ = '%Y-%m-%d %H:%M:%S'
|
__DATE_FORMAT__ = '%Y-%m-%d %H:%M:%S'
|
||||||
|
|
||||||
|
@ -21,8 +20,8 @@ def create_logger(name: str) -> logging.Logger:
|
||||||
stream_handler = logging.StreamHandler(stream=sys.stdout)
|
stream_handler = logging.StreamHandler(stream=sys.stdout)
|
||||||
|
|
||||||
COMMIT = os.getenv('GIT_COMMIT') or 'no-commit'
|
COMMIT = os.getenv('GIT_COMMIT') or 'no-commit'
|
||||||
timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
|
timestamp = datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
|
||||||
log_path = f"{COMMIT}_{timestamp}.log"
|
log_path = f'{COMMIT}_{timestamp}.log'
|
||||||
|
|
||||||
file_handler = logging.FileHandler(log_path)
|
file_handler = logging.FileHandler(log_path)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import discord
|
||||||
|
|
||||||
class BotMessage(discord.Embed):
|
class BotMessage(discord.Embed):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
title = kwargs.pop("title", None)[:256]
|
title = kwargs.pop('title', None)[:256]
|
||||||
if title is not None:
|
if title is not None:
|
||||||
# Max embed title length is 256
|
# Max embed title length is 256
|
||||||
title = title[:256]
|
title = title[:256]
|
||||||
|
@ -21,7 +21,7 @@ class ErrorMessage(BotMessage):
|
||||||
if command_name:
|
if command_name:
|
||||||
title = f'Error during command "{command_name}"'
|
title = f'Error during command "{command_name}"'
|
||||||
description = None
|
description = None
|
||||||
if match := re.search(r"(?P<error>\w+Error): ?(ERROR: ?)?(?P<message>.*): ?Traceback", message):
|
if match := re.search(r'(?P<error>\w+Error): ?(ERROR: ?)?(?P<message>.*): ?Traceback', message):
|
||||||
description = f"{match.group('error')}: {match.group('message')}"
|
description = f"{match.group('error')}: {match.group('message')}"
|
||||||
super().__init__(
|
super().__init__(
|
||||||
title=title,
|
title=title,
|
||||||
|
@ -33,7 +33,7 @@ class ErrorMessage(BotMessage):
|
||||||
class NowPlayingMessage(BotMessage):
|
class NowPlayingMessage(BotMessage):
|
||||||
def __init__(self, title, url):
|
def __init__(self, title, url):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
title=f"Now playing: {title}",
|
title=f'Now playing: {title}',
|
||||||
description=url,
|
description=url,
|
||||||
color=discord.Color.green()
|
color=discord.Color.green()
|
||||||
)
|
)
|
||||||
|
@ -42,7 +42,7 @@ class NowPlayingMessage(BotMessage):
|
||||||
class QueuedMessage(BotMessage):
|
class QueuedMessage(BotMessage):
|
||||||
def __init__(self, title, url):
|
def __init__(self, title, url):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
title=f"Queued: {title}",
|
title=f'Queued: {title}',
|
||||||
description=url,
|
description=url,
|
||||||
color=discord.Color.blue()
|
color=discord.Color.blue()
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
import glob
|
|
||||||
import os
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
@ -15,7 +15,7 @@ sys.path.insert(0, SRC_PATH)
|
||||||
from bot import Music
|
from bot import Music
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session", autouse=True)
|
@pytest.fixture(scope='session', autouse=True)
|
||||||
def global_teardown():
|
def global_teardown():
|
||||||
yield
|
yield
|
||||||
logs = glob.glob('*.log')
|
logs = glob.glob('*.log')
|
||||||
|
@ -40,7 +40,7 @@ def mbot(bot):
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def ctx(mocker: MockerFixture):
|
def ctx(mocker: MockerFixture):
|
||||||
ctx_mock = mocker.patch('discord.ext.commands.Context', autospec=True)
|
ctx_mock = mocker.patch('discord.ext.commands.Context', autospec=True)
|
||||||
ctx_mock.voice_client.stop = lambda: ctx_mock.voice_client.play.call_args.kwargs["after"](None)
|
ctx_mock.voice_client.stop = lambda: ctx_mock.voice_client.play.call_args.kwargs['after'](None)
|
||||||
return ctx_mock
|
return ctx_mock
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from unittest.mock import Mock, AsyncMock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
@ -11,7 +11,7 @@ async def test_bot_ensure_voice(mbot, ctx):
|
||||||
ctx.voice_client = None
|
ctx.voice_client = None
|
||||||
ctx.author.voice = AsyncMock()
|
ctx.author.voice = AsyncMock()
|
||||||
await mbot.ensure_voice(ctx)
|
await mbot.ensure_voice(ctx)
|
||||||
assert ctx.author.voice.channel.connect.call_count == 1, "Did not connect to voice channel of author"
|
assert ctx.author.voice.channel.connect.call_count == 1, 'Did not connect to voice channel of author'
|
||||||
ctx.reset_mock(return_value=True)
|
ctx.reset_mock(return_value=True)
|
||||||
|
|
||||||
# TEST: Error if author not inside a channel
|
# TEST: Error if author not inside a channel
|
||||||
|
@ -22,7 +22,7 @@ async def test_bot_ensure_voice(mbot, ctx):
|
||||||
|
|
||||||
|
|
||||||
def mock_ytdl_extract_info(ytdl, url, title):
|
def mock_ytdl_extract_info(ytdl, url, title):
|
||||||
ytdl.extract_info.return_value = {"entries": [{"url": url, "title": title}]}
|
ytdl.extract_info.return_value = {'entries': [{'url': url, 'title': title}]}
|
||||||
|
|
||||||
|
|
||||||
def mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio):
|
def mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio):
|
||||||
|
@ -38,66 +38,66 @@ async def test_bot_playback(mbot, ctx):
|
||||||
|
|
||||||
# TEST: First song queued is immediately played
|
# TEST: First song queued is immediately played
|
||||||
ctx.voice_client.is_playing.return_value = False
|
ctx.voice_client.is_playing.return_value = False
|
||||||
url = "https://www.youtube.com/watch?v=Wr9LZ1hAFpQ"
|
url = 'https://www.youtube.com/watch?v=Wr9LZ1hAFpQ'
|
||||||
title = "In Flames - Deliver Us (Official Video)"
|
title = 'In Flames - Deliver Us (Official Video)'
|
||||||
mock_ytdl_extract_info(ytdl, url, title)
|
mock_ytdl_extract_info(ytdl, url, title)
|
||||||
deliver_us_audio = mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio)
|
deliver_us_audio = mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio)
|
||||||
query = 'in flames deliver us'
|
query = 'in flames deliver us'
|
||||||
# pylint: disable=too-many-function-args
|
# pylint: disable=too-many-function-args
|
||||||
await mbot.play(mbot, ctx, query=query)
|
await mbot.play(mbot, ctx, query=query)
|
||||||
assert \
|
assert \
|
||||||
ytdl.extract_info.call_args.args == (query,) and ytdl.extract_info.call_args.kwargs == {"download": False}, \
|
ytdl.extract_info.call_args.args == (query,) and ytdl.extract_info.call_args.kwargs == {'download': False}, \
|
||||||
f"ytdl.extract_info was not called with {query}, {{ download: False }}"
|
f'ytdl.extract_info was not called with {query}, {{ download: False }}'
|
||||||
assert \
|
assert \
|
||||||
ffmpeg_pcm_audio.call_args is None, \
|
ffmpeg_pcm_audio.call_args is None, \
|
||||||
f"FFmpegPCMAudio was immediately called with {url} instead of being queued"
|
f'FFmpegPCMAudio was immediately called with {url} instead of being queued'
|
||||||
assert \
|
assert \
|
||||||
ctx.voice_client.play.call_args is None, \
|
ctx.voice_client.play.call_args is None, \
|
||||||
"Did immediately playback audio instead of being queued"
|
'Did immediately playback audio instead of being queued'
|
||||||
assert \
|
assert \
|
||||||
ctx.send.call_args is None, \
|
ctx.send.call_args is None, \
|
||||||
"Did immediately send 'Now playing:' message of being queued"
|
"Did immediately send 'Now playing:' message of being queued"
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
assert \
|
assert \
|
||||||
ffmpeg_pcm_audio.call_args.args == (url,), \
|
ffmpeg_pcm_audio.call_args.args == (url,), \
|
||||||
f"FFmpegPCMAudio was not called with {url}"
|
f'FFmpegPCMAudio was not called with {url}'
|
||||||
assert \
|
assert \
|
||||||
ctx.voice_client.play.call_args.args == (deliver_us_audio,), \
|
ctx.voice_client.play.call_args.args == (deliver_us_audio,), \
|
||||||
"Did not playback correct audio"
|
'Did not playback correct audio'
|
||||||
embed = ctx.send.call_args.kwargs["embed"]
|
embed = ctx.send.call_args.kwargs['embed']
|
||||||
assert embed.title == f"Now playing: {title}", "Did not send 'Now playing:' message"
|
assert embed.title == f'Now playing: {title}', "Did not send 'Now playing:' message"
|
||||||
|
|
||||||
# TEST: Following songs are put inside a queue
|
# TEST: Following songs are put inside a queue
|
||||||
ctx.voice_client.is_playing.return_value = True
|
ctx.voice_client.is_playing.return_value = True
|
||||||
url = "https://www.youtube.com/watch?v=pMDcYX2wRSg"
|
url = 'https://www.youtube.com/watch?v=pMDcYX2wRSg'
|
||||||
title = "Three Days Grace - Time of Dying (lyrics)"
|
title = 'Three Days Grace - Time of Dying (lyrics)'
|
||||||
mock_ytdl_extract_info(ytdl, url, title)
|
mock_ytdl_extract_info(ytdl, url, title)
|
||||||
time_of_dying_audio = mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio)
|
time_of_dying_audio = mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio)
|
||||||
# pylint: disable=too-many-function-args
|
# pylint: disable=too-many-function-args
|
||||||
query = "three days grace time of dying"
|
query = 'three days grace time of dying'
|
||||||
await mbot.play(mbot, ctx, query=query)
|
await mbot.play(mbot, ctx, query=query)
|
||||||
assert \
|
assert \
|
||||||
ytdl.extract_info.call_args.args == (query,) and ytdl.extract_info.call_args.kwargs == {"download": False}, \
|
ytdl.extract_info.call_args.args == (query,) and ytdl.extract_info.call_args.kwargs == {'download': False}, \
|
||||||
f"ytdl.extract_info was not called with {query}, {{ download: False }}"
|
f'ytdl.extract_info was not called with {query}, {{ download: False }}'
|
||||||
assert \
|
assert \
|
||||||
not ffmpeg_pcm_audio.call_args.args == (url,), \
|
not ffmpeg_pcm_audio.call_args.args == (url,), \
|
||||||
f"FFmpegPCMAudio was immediately called with {url} instead of being queued"
|
f'FFmpegPCMAudio was immediately called with {url} instead of being queued'
|
||||||
assert \
|
assert \
|
||||||
not ctx.voice_client.play.call_args.args == (time_of_dying_audio,), \
|
not ctx.voice_client.play.call_args.args == (time_of_dying_audio,), \
|
||||||
"Did immediately playback audio instead of being queued"
|
'Did immediately playback audio instead of being queued'
|
||||||
embed = ctx.send.call_args.kwargs["embed"]
|
embed = ctx.send.call_args.kwargs['embed']
|
||||||
assert embed.title == f"Queued: {title}", "Did not send 'Queued:' message"
|
assert embed.title == f'Queued: {title}', "Did not send 'Queued:' message"
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
# Assert that there is still no playback because previous song is not finished yet
|
# Assert that there is still no playback because previous song is not finished yet
|
||||||
assert \
|
assert \
|
||||||
not ffmpeg_pcm_audio.call_args.args == (url,), \
|
not ffmpeg_pcm_audio.call_args.args == (url,), \
|
||||||
f"FFmpegPCMAudio was called with {url} before previous song finished"
|
f'FFmpegPCMAudio was called with {url} before previous song finished'
|
||||||
# Execute callback for song finish event
|
# Execute callback for song finish event
|
||||||
ctx.voice_client.play.call_args.kwargs["after"](None)
|
ctx.voice_client.play.call_args.kwargs['after'](None)
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
assert \
|
assert \
|
||||||
ctx.voice_client.play.call_args.args == (time_of_dying_audio,), \
|
ctx.voice_client.play.call_args.args == (time_of_dying_audio,), \
|
||||||
"Did not queue next song"
|
'Did not queue next song'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
@ -109,8 +109,8 @@ async def test_bot_skip(mbot, ctx):
|
||||||
|
|
||||||
# Queue first song
|
# Queue first song
|
||||||
ctx.voice_client.is_playing.return_value = False
|
ctx.voice_client.is_playing.return_value = False
|
||||||
url = "https://www.youtube.com/watch?v=Wr9LZ1hAFpQ"
|
url = 'https://www.youtube.com/watch?v=Wr9LZ1hAFpQ'
|
||||||
title = "In Flames - Deliver Us (Official Video)"
|
title = 'In Flames - Deliver Us (Official Video)'
|
||||||
mock_ytdl_extract_info(ytdl, url, title)
|
mock_ytdl_extract_info(ytdl, url, title)
|
||||||
deliver_us_audio = mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio)
|
deliver_us_audio = mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio)
|
||||||
query = 'in flames deliver us'
|
query = 'in flames deliver us'
|
||||||
|
@ -119,28 +119,28 @@ async def test_bot_skip(mbot, ctx):
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
assert \
|
assert \
|
||||||
ctx.voice_client.play.call_args.args == (deliver_us_audio,), \
|
ctx.voice_client.play.call_args.args == (deliver_us_audio,), \
|
||||||
"Did not playback correct audio"
|
'Did not playback correct audio'
|
||||||
|
|
||||||
# Queue second song
|
# Queue second song
|
||||||
ctx.voice_client.is_playing.return_value = True
|
ctx.voice_client.is_playing.return_value = True
|
||||||
url = "https://www.youtube.com/watch?v=pMDcYX2wRSg"
|
url = 'https://www.youtube.com/watch?v=pMDcYX2wRSg'
|
||||||
title = "Three Days Grace - Time of Dying (lyrics)"
|
title = 'Three Days Grace - Time of Dying (lyrics)'
|
||||||
mock_ytdl_extract_info(ytdl, url, title)
|
mock_ytdl_extract_info(ytdl, url, title)
|
||||||
time_of_dying_audio = mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio)
|
time_of_dying_audio = mock_ffmpeg_pcm_audio(ffmpeg_pcm_audio)
|
||||||
# pylint: disable=too-many-function-args
|
# pylint: disable=too-many-function-args
|
||||||
query = "three days grace time of dying"
|
query = 'three days grace time of dying'
|
||||||
await mbot.play(mbot, ctx, query=query)
|
await mbot.play(mbot, ctx, query=query)
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
assert \
|
assert \
|
||||||
not ctx.voice_client.play.call_args.args == (time_of_dying_audio,), \
|
not ctx.voice_client.play.call_args.args == (time_of_dying_audio,), \
|
||||||
"Did immediately playback audio instead of being queued"
|
'Did immediately playback audio instead of being queued'
|
||||||
|
|
||||||
# Now skip first song
|
# Now skip first song
|
||||||
await mbot.skip(mbot, ctx)
|
await mbot.skip(mbot, ctx)
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
assert \
|
assert \
|
||||||
ctx.voice_client.play.call_args.args == (time_of_dying_audio,), \
|
ctx.voice_client.play.call_args.args == (time_of_dying_audio,), \
|
||||||
"Did not skip song"
|
'Did not skip song'
|
||||||
|
|
||||||
# TEST: Error if no song playing
|
# TEST: Error if no song playing
|
||||||
ctx.voice_client.is_playing.return_value = False
|
ctx.voice_client.is_playing.return_value = False
|
||||||
|
|
Loading…
Reference in New Issue