mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 11:34:18 +00:00
Idk?
This commit is contained in:
parent
e11fd430d1
commit
a86f2edd0e
14 changed files with 132 additions and 57 deletions
|
@ -2,12 +2,14 @@
|
|||
from .ping import PingCommand
|
||||
from .version import VersionCommand
|
||||
from .summon import SummonCommand
|
||||
from .play import PlayCommand
|
||||
|
||||
# Enter the commands of your Pack here!
|
||||
available_commands = [
|
||||
PingCommand,
|
||||
VersionCommand,
|
||||
SummonCommand,
|
||||
PlayCommand,
|
||||
]
|
||||
|
||||
# Don't change this, it should automatically generate __all__
|
||||
|
|
28
royalnet/backpack/commands/play.py
Normal file
28
royalnet/backpack/commands/play.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
from royalnet.commands import *
|
||||
|
||||
try:
|
||||
import discord
|
||||
except ImportError:
|
||||
discord = None
|
||||
|
||||
|
||||
class PlayCommand(Command):
|
||||
# TODO: possibly move this in another pack
|
||||
|
||||
name: str = "play"
|
||||
|
||||
description = "Download a file located at an URL and play it on Discord."
|
||||
|
||||
syntax = "[url]"
|
||||
|
||||
async def run(self, args: CommandArgs, data: CommandData) -> None:
|
||||
if self.interface.name != "discord":
|
||||
raise UnsupportedError()
|
||||
msg: "discord.Message" = data.message
|
||||
guild: "discord.Guild" = msg.guild
|
||||
url: str = args.joined()
|
||||
response: dict = await self.interface.call_herald_event("discord", "play", {
|
||||
"guild_id": guild.id,
|
||||
"url": url,
|
||||
})
|
||||
await data.reply(f"✅ !")
|
|
@ -1,9 +1,11 @@
|
|||
# Imports go here!
|
||||
from .summon import SummonEvent
|
||||
from .play import PlayEvent
|
||||
|
||||
# Enter the commands of your Pack here!
|
||||
available_events = [
|
||||
SummonEvent,
|
||||
PlayEvent
|
||||
]
|
||||
|
||||
# Don't change this, it should automatically generate __all__
|
||||
|
|
54
royalnet/backpack/events/play.py
Normal file
54
royalnet/backpack/events/play.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
from typing import Optional
|
||||
from royalnet.commands import *
|
||||
from royalnet.serf.discord import DiscordSerf
|
||||
from royalnet.serf.discord.discordbard import YtdlDiscord, DiscordBard
|
||||
from royalnet.utils import asyncify
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import discord
|
||||
except ImportError:
|
||||
discord = None
|
||||
|
||||
|
||||
class PlayEvent(Event):
|
||||
name = "play"
|
||||
|
||||
async def run(self, *, url: str, guild_id: Optional[int] = None, guild_name: Optional[str] = None, **kwargs):
|
||||
if not isinstance(self.serf, DiscordSerf):
|
||||
raise UnsupportedError("Play can't be called on interfaces other than Discord.")
|
||||
if discord is None:
|
||||
raise UnsupportedError("'discord' extra is not installed.")
|
||||
# Variables
|
||||
client = self.serf.client
|
||||
# Find the guild
|
||||
guild: Optional["discord.Guild"] = None
|
||||
if guild_id is not None:
|
||||
guild = client.get_guild(guild_id)
|
||||
elif guild_name is not None:
|
||||
for g in client.guilds:
|
||||
if g.name == guild_name:
|
||||
guild = g
|
||||
break
|
||||
if guild is None:
|
||||
raise InvalidInputError("No guild_id or guild_name specified.")
|
||||
# Find the bard
|
||||
bard: Optional[DiscordBard] = self.serf.bards.get(guild)
|
||||
if bard is None:
|
||||
raise CommandError("Bot is not connected to voice chat.")
|
||||
# Create the YtdlDiscords
|
||||
log.debug(f"Downloading: {url}")
|
||||
try:
|
||||
ytdl = await YtdlDiscord.from_url(url)
|
||||
except Exception as exc:
|
||||
breakpoint()
|
||||
return
|
||||
# Add the YtdlDiscords to the queue
|
||||
log.debug(f"Adding to bard: {ytdl}")
|
||||
for ytd in ytdl:
|
||||
await bard.add(ytd)
|
||||
# Run the bard
|
||||
log.debug(f"Running voice for: {guild}")
|
||||
await self.serf.voice_run(guild)
|
|
@ -11,7 +11,7 @@ except ImportError:
|
|||
class SummonEvent(Event):
|
||||
name = "summon"
|
||||
|
||||
async def run(self, *, channel_name: str, guild_id: Optional[int], user_id: Optional[int], **kwargs):
|
||||
async def run(self, *, channel_name: str, guild_id: Optional[int] = None, user_id: Optional[int] = None, **kwargs):
|
||||
if not isinstance(self.serf, DiscordSerf):
|
||||
raise UnsupportedError("Summon can't be called on interfaces other than Discord.")
|
||||
if discord is None:
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from .ytdlinfo import YtdlInfo
|
||||
from .ytdlfile import YtdlFile
|
||||
from .ytdlmp3 import YtdlMp3
|
||||
from .ytdldiscord import YtdlDiscord
|
||||
from .errors import *
|
||||
|
||||
try:
|
||||
|
@ -19,4 +18,5 @@ __all__ = [
|
|||
"NotFoundError",
|
||||
"MultipleFilesError",
|
||||
"FileAudioSource",
|
||||
"UnsupportedError",
|
||||
]
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
"""A :class:`Serf` implementation for Discord.
|
||||
|
||||
It is pretty unstable, compared to the rest of the bot, but it *should* work."""
|
||||
|
||||
from .createrichembed import create_rich_embed
|
||||
from .escape import escape
|
||||
from .discordserf import DiscordSerf
|
||||
from .fileaudiosource import FileAudioSource
|
||||
from . import discordbard
|
||||
|
||||
__all__ = [
|
||||
|
@ -9,5 +12,4 @@ __all__ = [
|
|||
"escape",
|
||||
"DiscordSerf",
|
||||
"discordbard",
|
||||
"FileAudioSource",
|
||||
]
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from .discordbard import DiscordBard
|
||||
from .dbstack import DBStack
|
||||
from .dbqueue import DBQueue
|
||||
from .fileaudiosource import FileAudioSource
|
||||
from .ytdldiscord import YtdlDiscord
|
||||
|
||||
__all__ = [
|
||||
"DBStack",
|
||||
"DBQueue",
|
||||
"DiscordBard",
|
||||
"FileAudioSource",
|
||||
"YtdlDiscord",
|
||||
]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from royalnet.bard import FileAudioSource, YtdlDiscord
|
||||
from royalnet.bard import FileAudioSource
|
||||
from typing import List, AsyncGenerator, Tuple, Any, Dict, Optional
|
||||
from .discordbard import DiscordBard
|
||||
from .ytdldiscord import YtdlDiscord
|
||||
|
||||
try:
|
||||
import discord
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
from typing import List, AsyncGenerator, Tuple, Any, Dict, Optional
|
||||
from royalnet.bard import FileAudioSource, YtdlDiscord
|
||||
from .discordbard import DiscordBard
|
||||
|
||||
try:
|
||||
import discord
|
||||
except ImportError:
|
||||
discord = None
|
||||
|
||||
|
||||
class DBStack(DiscordBard):
|
||||
"""A First-In-Last-Out music queue.
|
||||
|
||||
Not really sure if it is going to be useful..."""
|
||||
def __init__(self, voice_client: "discord.VoiceClient"):
|
||||
super().__init__(voice_client)
|
||||
self.list: List[YtdlDiscord] = []
|
||||
|
||||
async def _generator(self) -> AsyncGenerator[Optional[FileAudioSource], Tuple[Tuple[Any, ...], Dict[str, Any]]]:
|
||||
yield
|
||||
while True:
|
||||
try:
|
||||
ytd = self.list.pop()
|
||||
except IndexError:
|
||||
yield None
|
||||
else:
|
||||
async with ytd.spawn_audiosource() as fas:
|
||||
yield fas
|
||||
|
||||
async def add(self, ytd: YtdlDiscord):
|
||||
self.list.append(ytd)
|
||||
|
||||
async def peek(self) -> List[YtdlDiscord]:
|
||||
return self.list
|
||||
|
||||
async def remove(self, ytd: YtdlDiscord):
|
||||
self.list.remove(ytd)
|
||||
|
||||
async def cleanup(self) -> None:
|
||||
for ytd in self.list:
|
||||
await ytd.delete_asap()
|
|
@ -1,5 +1,7 @@
|
|||
from typing import Optional, AsyncGenerator, List, Tuple, Any, Dict
|
||||
from royalnet.bard import YtdlDiscord, FileAudioSource, UnsupportedError
|
||||
from royalnet.bard import UnsupportedError
|
||||
from .fileaudiosource import FileAudioSource
|
||||
from .ytdldiscord import YtdlDiscord
|
||||
|
||||
try:
|
||||
import discord
|
||||
|
@ -20,7 +22,7 @@ class DiscordBard:
|
|||
self.voice_client: "discord.VoiceClient" = voice_client
|
||||
"""The voice client that this :class:`DiscordBard` refers to."""
|
||||
|
||||
self.now_playing: Optional[YtdlDiscord] = None
|
||||
self.now_playing: Optional[FileAudioSource] = None
|
||||
"""The :class:`YtdlDiscord` that's currently being played."""
|
||||
|
||||
self.generator: \
|
||||
|
@ -40,7 +42,7 @@ class DiscordBard:
|
|||
"""Create an instance of the :class:`DiscordBard`, and initialize its async generator."""
|
||||
bard = cls(voice_client=voice_client)
|
||||
# noinspection PyTypeChecker
|
||||
none = bard.generator.asend(None)
|
||||
none = await bard.generator.asend(None)
|
||||
assert none is None
|
||||
return bard
|
||||
|
||||
|
@ -90,3 +92,8 @@ class DiscordBard:
|
|||
Raises:
|
||||
UnsupportedError: If :meth:`.peek` is unsupported."""
|
||||
return len(await self.peek())
|
||||
|
||||
async def stop(self):
|
||||
"""Stop the playback of the current song."""
|
||||
if self.now_playing is not None:
|
||||
self.now_playing.stop()
|
||||
|
|
|
@ -6,7 +6,7 @@ from royalnet.utils import asyncify, MultiLock
|
|||
from royalnet.bard import YtdlInfo, YtdlFile
|
||||
|
||||
try:
|
||||
from royalnet.serf.discord.fileaudiosource import FileAudioSource
|
||||
from .fileaudiosource import FileAudioSource
|
||||
except ImportError:
|
||||
FileAudioSource = None
|
||||
|
||||
|
@ -72,5 +72,7 @@ class YtdlDiscord:
|
|||
if FileAudioSource is None:
|
||||
raise ImportError("'discord' extra is not installed")
|
||||
await self.convert_to_pcm()
|
||||
with open(self.pcm_filename, "rb") as stream:
|
||||
yield FileAudioSource(stream)
|
||||
async with self.lock.normal():
|
||||
with open(self.pcm_filename, "rb") as stream:
|
||||
fas = FileAudioSource(stream)
|
||||
yield fas
|
|
@ -247,7 +247,23 @@ class DiscordSerf(Serf):
|
|||
# TODO: safely move the bot somewhere else
|
||||
raise CommandError("The bot is already connected in another channel.\n"
|
||||
" Please disconnect it before resummoning!")
|
||||
self.bards[channel.guild] = DBQueue(voice_client=voice_client)
|
||||
self.bards[channel.guild] = await DBQueue.create(voice_client=voice_client)
|
||||
|
||||
async def voice_change(self, guild: "discord.Guild", bard: Type[DiscordBard]):
|
||||
"""Safely change the :class:`DiscordBard` for a guild."""
|
||||
async def voice_run(self, guild: "discord.Guild"):
|
||||
"""Send the data from the bard to the voice websocket for a specific client."""
|
||||
bard: Optional[DiscordBard] = self.bards.get(guild)
|
||||
if bard is None:
|
||||
return
|
||||
|
||||
def finished_playing(error=None):
|
||||
if error:
|
||||
log.error(f"Finished playing with error: {error}")
|
||||
return
|
||||
self.loop.create_task(self.voice_run(guild))
|
||||
|
||||
if bard.now_playing is None:
|
||||
fas = await bard.next()
|
||||
# FIXME: possible race condition here, pls check
|
||||
bard = self.bards.get(guild)
|
||||
if bard.voice_client is not None and bard.voice_client.is_connected():
|
||||
bard.voice_client.play(fas, after=finished_playing)
|
||||
|
|
Loading…
Reference in a new issue