Merge pull request #8 from Matthww/dev

Dev
This commit is contained in:
2021-11-03 09:51:08 +01:00
committed by GitHub
6 changed files with 140 additions and 49 deletions
+17 -4
View File
@@ -1,3 +1,5 @@
from typing import Optional
import discord import discord
from discord.ext import tasks, commands from discord.ext import tasks, commands
@@ -43,7 +45,9 @@ class InformationCog(commands.Cog, name="Information"):
@commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user)
async def invite(self, ctx: Context): async def invite(self, ctx: Context):
"""Gets the invite link!""" """Gets the invite link!"""
await EmbedGenerator.Message(ctx, "Add our bot to your server:", self.bot.invite_link) await EmbedGenerator.Message(
ctx, "Add our bot to your server:", self.bot.invite_link
)
@commands.command( @commands.command(
name="wlinfo", name="wlinfo",
@@ -74,11 +78,20 @@ class InformationCog(commands.Cog, name="Information"):
) )
await ctx.send(fmt) await ctx.send(fmt)
from utils import database
database.AutoJoin.get_channels()
@commands.command(name="help", aliases=["about", "info"], slash_command=True) @commands.command(name="help", aliases=["about", "info"], slash_command=True)
@commands.cooldown(1, 1, commands.BucketType.user) @commands.cooldown(1, 1, commands.BucketType.user)
async def about(self, ctx: Context): async def about(
"""Exobot command list""" self,
if None: ctx: Context,
command: Optional[str] = commands.Option(
description="Show help for a command or category"
),
):
"""ChristmasBot command list"""
if command:
entity = self.bot.get_cog(command) or self.bot.get_command(command) entity = self.bot.get_cog(command) or self.bot.get_command(command)
if entity is None: if entity is None:
+89 -35
View File
@@ -1,13 +1,21 @@
import asyncio
import re import re
from typing import Optional
from aioredis.client import Redis
import discord import discord
from discord.channel import TextChannel
from discord.ext.commands.context import Context from discord.ext.commands.context import Context
import lavalink import lavalink
from discord.ext import commands from discord.ext import commands
from lavalink.models import DefaultPlayer from lavalink.models import AudioTrack, DefaultPlayer
from bot import ChristmasBot from bot import ChristmasBot
from utils.EmbedGenerator import EmbedGenerator from utils.EmbedGenerator import EmbedGenerator
from utils.database import AutoJoin
from context import CustomContext
from discord import Embed
url_rx = re.compile(r"https?://(?:www\.)?.+") url_rx = re.compile(r"https?://(?:www\.)?.+")
@@ -16,15 +24,7 @@ class LavalinkVoiceClient(discord.VoiceClient):
def __init__(self, client: discord.Client, channel: discord.abc.Connectable): def __init__(self, client: discord.Client, channel: discord.abc.Connectable):
self.client = client self.client = client
self.channel = channel self.channel = channel
# ensure there exists a client already self.lavalink = self.client.lavalink
if hasattr(self.client, "lavalink"):
self.lavalink = self.client.lavalink
else:
self.client.lavalink = lavalink.Client(client.user.id)
self.client.lavalink.add_node(
"localhost", 2333, "youshallnotpass", "us", "default-node"
)
self.lavalink = self.client.lavalink
async def on_voice_server_update(self, data): async def on_voice_server_update(self, data):
# the data needs to be transformed before being handed down to # the data needs to be transformed before being handed down to
@@ -86,6 +86,46 @@ class Music(commands.Cog):
) )
self.bot.lavalink.add_event_hook(self.track_hook) self.bot.lavalink.add_event_hook(self.track_hook)
await self.async_init()
async def async_init(self):
redis_result = await AutoJoin.get_channels(self.bot._redis_client)
while len(self.bot.lavalink.node_manager.available_nodes) == 0:
await asyncio.sleep(1)
for guild_id, (voicechannel_id, textchannel_id) in redis_result.items():
player = self.bot.lavalink.player_manager.create(guild_id)
player.store('channel', textchannel_id)
voice_channel = await self.bot.fetch_channel(voicechannel_id)
await voice_channel.connect(cls=LavalinkVoiceClient)
if not player.is_playing:
await self.fill_player_queue(
player, self.bot.config["queue_buffer_size"]
)
await player.play()
textchannel = await self.bot.fetch_channel(textchannel_id)
await textchannel.send("Automatically joined the voice channel")
async def fill_player_queue(self, player: DefaultPlayer, buffer: Optional[int] = 1):
pipeline = self.bot._redis_client.pipeline()
for _ in range(buffer):
pipeline.randomkey()
queries = await pipeline.execute()
print(queries)
# Get the results for the query from Lavalink.
for query in queries:
result = await player.node.get_tracks(query)
print(result)
if not result or not result["tracks"]:
continue
track = lavalink.models.AudioTrack(
result["tracks"][0], self.bot.user.id, recommended=False
)
player.add(requester=self.bot.user.id, track=track)
def cog_unload(self): def cog_unload(self):
"""Cog unload handler. This removes any event hooks that were registered.""" """Cog unload handler. This removes any event hooks that were registered."""
@@ -94,9 +134,13 @@ class Music(commands.Cog):
async def cog_before_invoke(self, ctx): async def cog_before_invoke(self, ctx):
"""Command before-invoke handler.""" """Command before-invoke handler."""
guild_check = ctx.guild is not None guild_check = ctx.guild is not None
# This is essentially the same as `@commands.guild_only()` # This is essentially the same as `@commands.guild_only()`
# except it saves us repeating ourselves (and also a few lines). # except it saves us repeating ourselves (and also a few lines).
if not hasattr(self.bot, "lavalink"):
await ctx.send("Still starting please wait a moment.")
if guild_check: if guild_check:
await self.ensure_voice(ctx) await self.ensure_voice(ctx)
# Ensure that the bot and command author share a mutual voicechannel. # Ensure that the bot and command author share a mutual voicechannel.
@@ -124,7 +168,7 @@ class Music(commands.Cog):
# These are commands that require the bot to join a voicechannel (i.e. initiating playback). # These are commands that require the bot to join a voicechannel (i.e. initiating playback).
# Commands such as volume/skip etc don't require the bot to be in a voicechannel so don't need listing here. # Commands such as volume/skip etc don't require the bot to be in a voicechannel so don't need listing here.
should_connect = ctx.command.name in ("play",) should_connect = ctx.command.name in ("connect",)
if not ctx.author.voice or not ctx.author.voice.channel: if not ctx.author.voice or not ctx.author.voice.channel:
# Our cog_command_error handler catches this and sends it to the voicechannel. # Our cog_command_error handler catches this and sends it to the voicechannel.
@@ -159,39 +203,49 @@ class Music(commands.Cog):
guild_id = int(event.player.guild_id) guild_id = int(event.player.guild_id)
guild = self.bot.get_guild(guild_id) guild = self.bot.get_guild(guild_id)
await guild.voice_client.disconnect(force=True) await guild.voice_client.disconnect(force=True)
elif isinstance(event, lavalink.events.TrackStartEvent):
channel_id = int(event.player.fetch("channel"))
channel: TextChannel = self.bot.get_channel(channel_id)
@commands.command(name="play", aliases=["p", "connect", "join"]) color = self.bot.colors["embed"]
@commands.guild_only() current_track: AudioTrack = event.player.current
async def play(self, ctx: Context): embed = Embed(
"""Searches and plays a song from a given query.""" title="Now playing:",
description=f"[{current_track.title}]({current_track.uri})",
color=color,
)
await channel.send(embed=embed)
elif isinstance(event, lavalink.events.TrackEndEvent):
await self.fill_player_queue(event.player, 1)
@commands.command(name="connect", aliases=["p", "play", "join"])
async def play(self, ctx: CustomContext):
"""Starts playing Christmas bangers"""
# Get the player for this guild from cache. # Get the player for this guild from cache.
player: DefaultPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id) player: DefaultPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id)
query = "https://youtu.be/VcIt_AcOPjs" await self.fill_player_queue(player, self.bot.config["queue_buffer_size"])
# Get the results for the query from Lavalink.
results = await player.node.get_tracks(query)
# Results could be None if Lavalink returns an invalid response (non-JSON/non-200 (OK)).
# ALternatively, resullts['tracks'] could be an empty array if the query yielded no tracks.
if not results or not results["tracks"]:
return await EmbedGenerator.Title(ctx, "Nothing found!")
# Theoretically songs will always be TRACK_LOADED
track = results["tracks"][0]
await EmbedGenerator.Message(
ctx, "Track Enqueued", f'[{track["info"]["title"]}]({track["info"]["uri"]})'
)
track = lavalink.models.AudioTrack(track, ctx.author.id, recommended=True)
player.add(requester=ctx.author.id, track=track)
if not player.is_playing: if not player.is_playing:
await player.play() await player.play()
await ctx.send("Started playing")
@commands.command(name="skip", aliases=["next"])
async def skip(self, ctx: Context):
"""I heard this song way too often"""
player: DefaultPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id)
await player.skip()
await ctx.send("Skipped current song")
@commands.command(name="queue")
async def queue(self, ctx: Context):
"""Ghetto queue"""
player: DefaultPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id)
await EmbedGenerator.Message(ctx, "Queue:", player.queue)
@commands.command(name="disconnect", aliases=["dc", "stop"]) @commands.command(name="disconnect", aliases=["dc", "stop"])
@commands.guild_only()
async def disconnect(self, ctx: Context): async def disconnect(self, ctx: Context):
"""Disconnects the player from the voice channel and clears its queue.""" """Disconnects ChristmasBot"""
player: DefaultPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id) player: DefaultPlayer = self.bot.lavalink.player_manager.get(ctx.guild.id)
if not player.is_connected: if not player.is_connected:
+11 -8
View File
@@ -1,5 +1,7 @@
from context import CustomContext
from discord.ext import commands from discord.ext import commands
from utils.EmbedGenerator import EmbedGenerator from utils.EmbedGenerator import EmbedGenerator
from utils.database import AutoJoin
from bot import ChristmasBot from bot import ChristmasBot
from discord.ext.commands import Context from discord.ext.commands import Context
@@ -10,27 +12,28 @@ class SettingsCog(commands.Cog, name="Settings"):
@commands.group(aliases=["aj"], invoke_without_command=True) @commands.group(aliases=["aj"], invoke_without_command=True)
@commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user)
async def autojoin(self, ctx: Context): async def autojoin(self, ctx: CustomContext):
await EmbedGenerator.Message( await EmbedGenerator.Message(
ctx, ctx,
"Autojoin", "Autojoin",
f"Usage:\n\n`{ctx.prefix}autojoin set`\n`{ctx.prefix}autojoin unset`", f"Usage:\n\n`{ctx.prefix}autojoin set`\n`{ctx.prefix}autojoin unset`",
) )
@autojoin.command(name="set") @autojoin.command(name="enable")
@commands.has_permissions(manage_channels=True) @commands.has_permissions(manage_channels=True)
@commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user)
async def autojoin_set(self, ctx: Context): async def autojoin_set(self, ctx: CustomContext):
vc = ctx.author.voice.channel voicechannel_id = ctx.author.voice.channel.id
await AutoJoin.update_channel(self.bot, ctx.guild.id, vc.id) textchannel_id = ctx.message.channel.id
await AutoJoin.update_channel(ctx.get_redis(), ctx.guild.id, voicechannel_id, textchannel_id)
await EmbedGenerator.Message(ctx, "Autojoin", "`enabled`") await EmbedGenerator.Message(ctx, "Autojoin", "`enabled`")
@autojoin.command(name="unset") @autojoin.command(name="disable")
@commands.has_permissions(manage_channels=True) @commands.has_permissions(manage_channels=True)
@commands.cooldown(rate=1, per=5, type=commands.BucketType.user) @commands.cooldown(rate=1, per=5, type=commands.BucketType.user)
async def autojoin_del(self, ctx: Context): async def autojoin_del(self, ctx: CustomContext):
vc = ctx.author.voice.channel vc = ctx.author.voice.channel
await AutoJoin.del_channel(self.bot, ctx.guild.id) await AutoJoin.del_channel(ctx.get_redis(), ctx.guild.id)
await EmbedGenerator.Message(ctx, "Autojoin", "`disabled`") await EmbedGenerator.Message(ctx, "Autojoin", "`disabled`")
+2 -1
View File
@@ -18,5 +18,6 @@
"description": "A sample bot description" "description": "A sample bot description"
}, },
"cogs": ["cogs.owner", "cogs.settings", "cogs.information", "cogs.music"], "cogs": ["cogs.owner", "cogs.settings", "cogs.information", "cogs.music"],
"slash_command_guilds": [] "slash_command_guilds": [],
"queue_buffer_size": 5
} }
+1 -1
View File
@@ -39,4 +39,4 @@ class EmbedGenerator:
em.set_footer(text=f"Requested by: {ctx.author}", icon_url=avatar) em.set_footer(text=f"Requested by: {ctx.author}", icon_url=avatar)
if kwargs.get("no_send", False): if kwargs.get("no_send", False):
return em return em
return await ctx.send(embed=em, **kwargs) return await ctx.send(embed=em, **kwargs)
+20
View File
@@ -0,0 +1,20 @@
from aioredis import Redis
from typing import Sequence
class AutoJoin:
@staticmethod
async def get_channels(redis: Redis) -> Sequence[tuple]:
# return all channels
channels = await redis.hgetall(name="autojoin")
return {key: value.split("-") for key, value in channels.items()}
@staticmethod
async def update_channel(redis: Redis, guild_id, voicechannel_id, textchannel_id):
await redis.hset(
name="autojoin", key=guild_id, value=f"{voicechannel_id}-{textchannel_id}"
)
@staticmethod
async def del_channel(redis: Redis, guild_id):
await redis.hdel("autojoin", guild_id)