From 3ac095ec219993e9bf64e3c17893955aaa792425 Mon Sep 17 00:00:00 2001 From: CramMK Date: Thu, 13 Feb 2020 17:05:21 +0100 Subject: [PATCH] Add voice, Change yml, Change media --- .gitignore | 3 +- Dockerfile | 4 +- README.md | 12 ++--- aquabot.py | 6 +-- cogs/anime.py | 8 ++-- cogs/utility.py | 34 ++++++++++++--- cogs/voice.py | 94 +++++++++++++++++++++++++++++----------- cogs/welcome.py | 2 +- config/config_example.py | 4 -- config/media.py | 8 ++-- config/status.py | 1 - loadconfig.py | 10 ++--- 12 files changed, 122 insertions(+), 64 deletions(-) delete mode 100644 config/config_example.py diff --git a/.gitignore b/.gitignore index 3fc115b..2aef5c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # own config file -config/config.py +config.py #deprecated +config.yml # caches and build environment __pycache__/ diff --git a/Dockerfile b/Dockerfile index efa7560..b2294c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,9 @@ FROM python:3.6 -MAINTAINER Marco Thomas - RUN mkdir /aquabot-docker WORKDIR /aquabot-docker COPY . /aquabot-docker RUN pip install --user -r requirements.txt -CMD ["python", "aquabot.py"] +CMD ["python3", "aquabot.py"] diff --git a/README.md b/README.md index accfb5a..a123d9b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ AquaBot This bot is my first personal project so expect some minor (or bigger) problems here and there. Also note that this bot is still in its very early stages, so there is no -guaranty that all features work! +guarantee that all features work! Support and report requests are handled via Discord (Link above). @@ -20,7 +20,7 @@ Installation - Docker + Clone this repository with `git clone https://github.com/CramMK/aquabot` -+ Create a `config/config.py`, using the `config/config_example.py` as a ++ Create a `config` file as described below guideline + Launch the Container @@ -35,10 +35,10 @@ Installation - pip + Use `pip install --user -r requirements.txt` to install all dependencies needed for the bot -+ Create a `config/config.py`, using the `config/config_example.py` as a ++ Create a `config` file as described below guideline -+ Finally, run `python aquabot` ++ Finally, run `python3 aquabot` Commands ------ @@ -54,8 +54,8 @@ commands: Config ------ -To use the bot you need to add a `config/config.py` file. For reference, see -`config/config_example.py`. +To use the bot you need to add a `config/config.yml` file. For reference, see +`config/config_example.yml`. Requirements ------------ diff --git a/aquabot.py b/aquabot.py index a99c788..6b95e66 100644 --- a/aquabot.py +++ b/aquabot.py @@ -30,8 +30,8 @@ logger.addHandler(handler) # INIT THE BOT bot = commands.Bot( - command_prefix=loadconfig.__prefix__, - description="Holy Goddess Aqua!") + command_prefix=loadconfig.__prefix__, + description="Holy Goddess Aqua!") # LOAD COGS SPECIFIED IN 'config/cogs.py' for cog in loadconfig.__cogs__: @@ -49,7 +49,7 @@ async def activity(): status = f"{new_activity[1]} | {loadconfig.__prefix__}aquabot" activity = discord.Activity(name=status, type=new_activity[0]) await bot.change_presence(activity=activity) - await asyncio.sleep(15) # Time in minutes + await asyncio.sleep(10) # Time in minutes # BOT STARTING EVENT @bot.event diff --git a/cogs/anime.py b/cogs/anime.py index 0eb0323..b6d0e72 100644 --- a/cogs/anime.py +++ b/cogs/anime.py @@ -25,8 +25,8 @@ class Anime(commands.Cog): Sends a random anime gif or pic """ # Choose either a gif or a pic -> config/media.py - media_type = random.choice(loadconfig.__anime_media__) - media = random.choice(media) + media_type = random.choice(loadconfig.__media_anime__) + media = random.choice(media_type) await ctx.send(media) @commands.command(name="waifumedia") @@ -34,10 +34,8 @@ class Anime(commands.Cog): """ Sends a random pic of a waifu (list in config/media.py) """ - # Dictionary - waifus = loadconfig.__waifu_media__ try: - media = random.choice(waifus.get(waifu)) + media = random.choice(loadconfig.__media_waifu__[waifu]) await ctx.send(media) except KeyError as error: text = ( diff --git a/cogs/utility.py b/cogs/utility.py index 6f050c2..8563858 100644 --- a/cogs/utility.py +++ b/cogs/utility.py @@ -6,10 +6,13 @@ Some (more or less) handy utility: https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html """ -# IMPORTS +# IMPORTS - external import discord from discord.ext import commands +# IMPORTS - internal +import loadconfig + # COG INIT class Utility(commands.Cog): def __init__(self, bot): @@ -17,13 +20,34 @@ class Utility(commands.Cog): # COG BODY @commands.command(name="invitelink", aliases=["invite"]) - async def invite_link(self, ctx): + async def invite_link(self, ctx, age: int, uses: int, unique: bool): """ Sends the server's invitelink to chat """ - # TODO fetch this from config so more servers are supported - link = "Here is our invite link: https://discordapp.com/invite/HbYfyJT" - await ctx.send(link) + if age is None: + age = 60 + if uses is None: + uses = 100 + if unique is None: + unique = True + + link = await bot.create_invite( + max_age = age, + max_uses = uses, + unique = uses, + reason = "Created by AquaBot") + + link_embed = discord.Embed(color=discord.Colour.blue()) + link_embed.add_field( + name="Here's and invite to our server:", + value=link, + inline=True) + link_embed.set_footer( + text=f"Age: {age}, Uses: {uses}, Unique: {unique}", + icon_url=loadconfig.__avater__ + ) + + await ctx.send(embed=link_embed) @commands.command(name="pat") diff --git a/cogs/voice.py b/cogs/voice.py index d7dcabb..8117c05 100644 --- a/cogs/voice.py +++ b/cogs/voice.py @@ -5,7 +5,7 @@ Make Aqua be able to join voice channel and play audio: - play https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html -https://stackoverflow.com/questions/56031159/discord-py-rewrite-what-is-the-source-for-youtubedl-to-play-music +https://stackoverflow.com/questions/56060614/how-to-make-a-discord-bot-play-youtube-audio """ # IMPORTS @@ -59,38 +59,80 @@ class Voice(commands.Cog): await ctx.send("I'm not connected to a channel!") + # Begin of YouTube Player + youtube_dl.utils.bug_reports_message = lambda: '' + ytdl_format_options = { + 'format': 'bestaudio/best', + 'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s', + 'restrictfilenames': True, + 'noplaylist': True, + 'nocheckcertificate': True, + 'ignoreerrors': False, + 'logtostderr': False, + 'quiet': True, + 'no_warnings': True, + 'default_search': 'auto', + 'source_address': '0.0.0.0' + } + ffmpeg_options = { + 'options': '-vn' + } + ytdl = youtube_dl.YoutubeDL(ytdl_format_options) + class YTDLSource(discord.PCMVolumeTransformer): + def __init__(self, source, *, data, volume=0.5): + super().__init__(source, volume) + + self.data = data + + self.title = data.get('title') + self.url = data.get('url') + + # Maybe i can make a fancy embed out of this? + self.uploader = data.get('uploader') + self.uploader_url = data.get('uploader_url') + date = data.get('upload_date') + self.upload_date = date[6:8] + '.' + date[4:6] + '.' + date[0:4] + self.title = data.get('title') + self.thumbnail = data.get('thumbnail') + self.description = data.get('description') + self.duration = self.parse_duration(int(data.get('duration'))) + self.tags = data.get('tags') + self.url = data.get('webpage_url') + self.views = data.get('view_count') + self.likes = data.get('like_count') + self.dislikes = data.get('dislike_count') + self.stream_url = data.get('url') + + @classmethod + async def from_url(cls, url, *, loop=None, stream=False): + loop = loop or asyncio.get_event_loop() + data = await loop.run_in_executor( + None, + lambda: ytdl.extract_info(url, download=not stream)) + + if 'entries' in data: + # take first item from a playlist + data = data['entries'][0] + + filename = data['url'] if stream else ytdl.prepare_filename(data) + return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data) + @commands.command(name="play", aliases=["p"]) @commands.guild_only() async def play(self, ctx, url: str): """ - Plays music from YT link specifies + Plays music from YouTube """ - # TODO - try: - if os.path.isfile("song.mp3"): - os.remove("song.mp3") - except PermissionError: - await ctx.send("Wait for the current song to end or use the `stop`command") - return - voice = get(bot.voice_clients, guild=ctx.guild) - youtube_dl_opts = { - 'format': 'bestaudio/best', - 'postprocessors': [{ - 'key': 'FFmpegExtractAudio', - 'preferredcodec': 'mp3', - 'preferredquality': '192', - }], - } - with youtube_dl.YouTubeDL(youtube_dl_opts) as ydl: - ydl.download([url]) - for file in os.listdir("./"): - if file.endswith(".mp3"): - os.rename(file, "song.mp3") - voice.play(discord.FFmpegPCMAudio("song.mp3")) - voice.volume=25 - voice.is_playing() + async with ctx.typing(): + player = await YTDLSource.from_url(url, loop=self.bot.loop) + ctx.voice.play( + player, + after=lambda e: print('Player error: %s' % e) if e else None)) + await ctx.send(f"Now playing: {player.title}") + + # End of YouTube Player # COG ENDING diff --git a/cogs/welcome.py b/cogs/welcome.py index 64d519a..3f37194 100644 --- a/cogs/welcome.py +++ b/cogs/welcome.py @@ -26,7 +26,7 @@ class Welcome(commands.Cog): text = f"Welcome {member.mention} to our useless Discord!" if channel is not None: await channel.send(text) - + await message.add_reaction("\N{THUMBS UP SIGN}") @commands.command(name="hello") async def hello(self, ctx): diff --git a/config/config_example.py b/config/config_example.py deleted file mode 100644 index c1c0e81..0000000 --- a/config/config_example.py +++ /dev/null @@ -1,4 +0,0 @@ -# This is a sample file for how a config file looks like - -__token__ = "Discord Auth Token" -__prefix__ = "Command Prefix" \ No newline at end of file diff --git a/config/media.py b/config/media.py index e8eeef5..13b5ace 100644 --- a/config/media.py +++ b/config/media.py @@ -3,9 +3,9 @@ Media, which can be accessed from the bot. """ # Exports -__anime_media__ = [anime_gifs, anime_pics] +__media_anime__ = [gifs_anime, pics_anime] -__waifu_media__ = { +__media_waifu__ = { "aqua": waifu_aqua, "meugmin": waifu_megumin, "akeno": waifu_akeno, @@ -13,9 +13,9 @@ __waifu_media__ = { } # Internal lists -anime_gifs = [] +gifs_anime = [] -anime_pics = [ +pics_anime = [ "https://i.imgur.com/4xnJN9x.png", ] diff --git a/config/status.py b/config/status.py index fb709a7..abbd578 100644 --- a/config/status.py +++ b/config/status.py @@ -12,4 +12,3 @@ __activity__ = [ (discord.ActivityType.streaming, "Hentai") (discord.ActivityType.custom, "mizu") ] -} diff --git a/loadconfig.py b/loadconfig.py index 2252af4..f6dd9dc 100644 --- a/loadconfig.py +++ b/loadconfig.py @@ -14,16 +14,16 @@ __avatar__ = "https://i.imgur.com/mskM9dH.png" try: with open("config/config.yml") as file: config = yaml.safe_load(file) - for yml_entry in config: - __token__ = config[yml_entry]['token'] - __prefix__ = config[yml_entry]['prefix'] + + __token__ = config['token'] + __prefix__ = config['prefix'] + except yaml.YAMLError as error: print(f"Error while parsing: {error}") try: - from config.config import __token__, __prefix__ from config.cogs import __cogs__ from config.status import __activity__ - from config.media import __anime_media__, __waifu_media__ + from config.media import __media_anime__, __media_waifu__ except ImportError as error: print(f"Error while importing: {error}")