Dieser Artikel ist eine Fortsetzung des vorherigen Discord Bot mit Aufnahmefunktion beginnend mit Python: (3) Zusammenarbeit mit der Datenbank.
Dieses Mal werden wir eine Jukebox-Funktion hinzufügen, die die zuvor auf dem Server vorbereitete Musik mit einem Befehl wiedergibt. Wenn Sie discord.py verwenden, können Sie Audio mit einem sehr einfachen Vorgang abspielen. Hier fügen wir eine Funktion hinzu, die eine kontinuierliche Wiedergabe ermöglicht, indem eine Song-Warteschlange implementiert wird, anstatt sie einfach abzuspielen.
Wir planen insgesamt 7 Mal zu schreiben und haben bis zu 5 Artikel fertig geschrieben.
In discord.py handelt es sich um einen Teil, der nicht direkt manipuliert wird, sodass nicht viel zu beachten ist, sondern zusätzlich zur REST-API, die beim Senden von Posts mit HTTP-Anforderungen und beim Abrufen von Serverinformationen bidirektional verwendet wird Es gibt drei Methoden zum Senden und Empfangen von Informationen: WebSocket-Kommunikation für die Kommunikation und RTP-Kommunikation zum Senden und Empfangen von Sprache.
Im Fall von discord.py ist es ein sehr intuitiver Mechanismus, eine Instanz des Sprachkanals aus dem Kontext usw. abzurufen und eine Verbindung zum Sprachkanal herzustellen, indem das Collout "Verbinden" dieser Instanz ausgeführt wird. Was von diesem connect
zurückgegeben wird, ist eine Instanz der VoiceClient
-Klasse.
In "VoiceClient" sind die oben genannten Teile wie WebSocket-Kommunikation und andere verschlüsselte Kommunikation ausgeblendet. Wenn Sie die Sprachkanalinformationen für jeden Server bearbeiten möchten, können Sie daher verschiedene Vorgänge für diese VoiceClient-Instanz ausführen.
Wenn Sie die Verarbeitung von Sprachkanälen von mehreren Servern in Betracht ziehen, ist ein Mechanismus zum Verbinden dieser Instanz mit dem Server (ID) erforderlich. Hier betreiben wir VoiceClient mit einem Wörterbucharray für die Verwaltung wie Offizielle Implementierung im discord.py-Repository. Versuchen Sie dann, die Musik lokal abzuspielen (auf dem Server, auf dem der Bot ausgeführt wird).
Wie oben erwähnt, ist das Betreten und Verlassen des Sprachkanals sehr einfach. Hier erstellen wir ein Zahnrad namens "Voice", betreten den Raum mit "$ join" und gehen mit "$ Leave". Ein Beispiel für die Implementierung ist wie folgt.
python:./src/app/dbot/cogs/Voice.py
import discord
from discord.ext import commands
from typing import Dict
from dbot.core.bot import DBot
class Voice(commands.Cog):
def __init__(self, bot: DBot):
self.bot = bot
self.voice_clients: Dict[int, discord.VoiceClient] = {}
@commands.command()
async def join(self, ctx: commands.Context):
#Nicht am Sprachkanal teilnehmen
if not ctx.author.voice or not ctx.author.voice.channel:
return await ctx.send('Treten Sie zuerst dem Sprachkanal bei')
vc = await ctx.author.voice.channel.connect()
self.voice_clients[ctx.guild.id] = vc
@commands.command()
async def leave(self, ctx: commands.Context):
vc = self.voice_clients.get(ctx.guild.id)
if vc is None:
return await ctx.send('Ich bin dem Sprachkanal noch nicht beigetreten')
await vc.disconnect()
del self.voice_clients[ctx.guild.id]
def setup(bot):
return bot.add_cog(Voice(bot))
Die Eigenschaft voice
von discord.Member
gibt eine Instanz der VoiceState-Klasse zurück, wenn das Mitglied einem Sprachkanal angeschlossen ist. Dieser VoiceState verfügt zusätzlich zu dem Status, z. B. Stummschaltung des Mitglieds, über eine Eigenschaft namens "channel". Unter Bezugnahme darauf wird eine Instanz der Sprachkanalklasse (discord.VoiceChannel) abgerufen, die sich derzeit im Member befindet. tun können.
Das Aufrufen des "Connect" -Kollouts von "VoiceChannel" gibt "VoiceClient" zurück und der Bot tritt dem Sprachkanal bei. Der VoiceClient wird im Wörterbuch mit der Server-ID als Schlüssel gespeichert. Im Gegenteil, "$ Leave" sucht in der Server-ID nach "VoiceClient" und ruft das Collout "Disconnect" auf. Der Ein- / Ausstiegsprozess ist nun abgeschlossen.
Bereiten Sie zunächst die Musik für die Wiedergabe vor und speichern Sie sie im Ordner . / Src / app / music /
.
Geben Sie ihm einen geeigneten Namen, um es zu implementieren, damit es anhand des Namens sucht. Spielen Sie vorerst mit "$ play song title" und hören Sie mit "$ stop" auf.
python:./src/app/dbot/cogs/Voice.py
import discord
from glob import glob
import os
from discord.ext import commands
from typing import Dict
from dbot.core.bot import DBot
class Voice(commands.Cog):
#Abkürzung
@commands.command()
async def play(self, ctx: commands.Context, *, title: str = ''):
vc: discord.VoiceClient = self.voice_clients.get(ctx.guild.id)
if vc is None:
await ctx.invoke(self.join)
vc = self.voice_clients[ctx.guild.id]
music_pathes = glob('./music/**.mp3')
music_titles = [
os.path.basename(path).rstrip('.mp3')
for path in music_pathes
]
if not title in music_titles:
return await ctx.send('Es gibt kein bestimmtes Lied.')
idx = music_titles.index(title)
src = discord.FFmpegPCMAudio(music_pathes[idx])
vc.play(src)
await ctx.send(f'{title}Spielen')
@commands.command()
async def stop(self, ctx: commands.Context):
vc: discord.VoiceClient = self.voice_clients.get(ctx.guild.id)
if vc is None:
return await ctx.send('Bot ist dem Sprachkanal noch nicht beigetreten')
if not vc.is_playing:
return await ctx.send('Schon gestoppt')
await vc.stop()
await ctx.send('Gestoppt')
#Abkürzung
Um Musik mit duscird.py abzuspielen, müssen Sie die AudioSource an den VoiceClient Play Collout übergeben. Ffmpeg ist erforderlich, um AudioSource zu erstellen. Wenn Sie eine ffmpeg-Umgebung haben, können Sie sehr einfach Musik abspielen, indem Sie einfach den Pfad zur Datei angeben.
Um die Wiedergabe zu stoppen, rufen Sie das Collout vc.stop auf.
In der aktuellen Implementierung wechselt die Musik, sobald Sie "$ play" aufrufen. Ändern Sie dies in eine Spezifikation, die der Warteschlange Musik hinzufügt, wenn "$ play" ausgeführt wird, und die nächste Musik abspielt, wenn die vorherige Musik beendet ist (sogenannte Wiedergabeliste).
Verwenden Sie "asyncio.Queue" und "asyncio.Event", um die Wiedergabeliste zu implementieren. Sie werden häufig für Implementierungen verwendet, die warten, bis ein Element zur Warteschlange hinzugefügt wird, und für Implementierungen, die warten, bis ein Flag an einer anderen Stelle gesetzt wird.
python:./src/app/dbot/cogs/Voice.py
import discord
from glob import glob
import os
import asyncio
from discord.ext import commands
from typing import Dict
from dbot.core.bot import DBot
class AudioQueue(asyncio.Queue):
def __init__(self):
super().__init__(100)
def __getitem__(self, idx):
return self._queue[idx]
def to_list(self):
return list(self._queue)
def reset(self):
self._queue.clear()
class AudioStatus:
def __init__(self, ctx: commands.Context, vc: discord.VoiceClient):
self.vc: discord.VoiceClient = vc
self.ctx: commands.Context = ctx
self.queue = AudioQueue()
self.playing = asyncio.Event()
asyncio.create_task(self.playing_task())
async def add_audio(self, title, path):
await self.queue.put([title, path])
def get_list(self):
return self.queue.to_list()
async def playing_task(self):
while True:
self.playing.clear()
try:
title, path = await asyncio.wait_for(self.queue.get(), timeout=180)
except asyncio.TimeoutError:
asyncio.create_task(self.leave())
src = discord.FFmpegPCMAudio(path)
self.vc.play(src, after=self.play_next)
await self.ctx.send(f'{title}Spielen...')
await self.playing.wait()
def play_next(self, err=None):
self.playing.set()
async def leave(self):
self.queue.reset()
if self.vc:
await self.vc.disconnect()
self.vc = None
@property
def is_playing(self):
return self.vc.is_playing()
def stop(self):
self.vc.stop()
class Voice(commands.Cog):
def __init__(self, bot: DBot):
self.bot = bot
self.audio_statuses: Dict[int, AudioStatus] = {}
@commands.command()
async def join(self, ctx: commands.Context):
#Nicht am Sprachkanal teilnehmen
if not ctx.author.voice or not ctx.author.voice.channel:
return await ctx.send('Treten Sie zuerst dem Sprachkanal bei')
vc = await ctx.author.voice.channel.connect()
self.audio_statuses[ctx.guild.id] = AudioStatus(ctx, vc)
@commands.command()
async def play(self, ctx: commands.Context, *, title: str = ''):
status = self.audio_statuses.get(ctx.guild.id)
if status is None:
await ctx.invoke(self.join)
status = self.audio_statuses[ctx.guild.id]
music_pathes = glob('./music/**.mp3')
music_titles = [
os.path.basename(path).rstrip('.mp3')
for path in music_pathes
]
if not title in music_titles:
return await ctx.send('Es gibt kein bestimmtes Lied.')
idx = music_titles.index(title)
await status.add_audio(title, music_pathes[idx])
await ctx.send(f'{title}Wurde zur Wiedergabeliste hinzugefügt')
@commands.command()
async def stop(self, ctx: commands.Context):
status = self.audio_statuses.get(ctx.guild.id)
if status is None:
return await ctx.send('Bot ist dem Sprachkanal noch nicht beigetreten')
if not status.is_playing:
return await ctx.send('Schon gestoppt')
await status.stop()
await ctx.send('Gestoppt')
@commands.command()
async def leave(self, ctx: commands.Context):
status = self.audio_statuses.get(ctx.guild.id)
if status is None:
return await ctx.send('Ich bin dem Sprachkanal noch nicht beigetreten')
await status.leave()
del self.audio_statuses[ctx.guild.id]
@commands.command()
async def queue(self, ctx: commands.Context):
status = self.audio_statuses.get(ctx.guild.id)
if status is None:
return await ctx.send('Treten Sie zuerst dem Sprachkanal bei')
queue = status.get_list()
songs = ""
for i, (title, _) in enumerate(queue):
songs += f"{i+1}. {title}\n"
await ctx.send(songs)
def setup(bot):
return bot.add_cog(Voice(bot))
Erstellen Sie eine neue Klasse mit dem Namen "AudioStatus", um VoiceClient- und Serverinformationen zusammen zu registrieren und die Instanz zu speichern.
AudioStatus
ruft bei der Initialisierung eine Funktion namens asyncio.create_task
auf. Wie der Name schon sagt, wird die Aufgabe des Musikspielens aus dem Collout erstellt. Auf diese Weise ist ein asynchroner Zugriff möglich, z. B. das Antworten auf Befehle von anderen Servern, während andere Aufgaben auf der Aufgabenseite ausgeführt werden. Die Eigenschaft play
ist asyncio.Event
, die verwendet wird, wenn Sie ein Flag setzen möchten, wenn eine bestimmte Bedingung erfüllt ist, und warten, ohne etwas zu tun, bis das Flag gesetzt ist. play_task
ruft clear
und wait
auf und play_next
ruft set
auf, aber "setze das Flag auf False
"," warte bis das Flag True
wird "und" Es ist ein Prozess, bei dem "das Flag auf" True "gesetzt wird". play_next
wird an das after-Argument von vc.play
übergeben, das dem Argument gibt, was Sie tun möchten, wenn die Wiedergabe eines Songs beendet ist. Durch Setzen des Flags für die Wiedergabe des nächsten Songs am Ende des Songs wird die Schleife "play_task" erneut gestartet.
Die Eigenschaft "queue" dieses "AudioStatus" erbt "asyncio.Queue" und erleichtert das Abrufen der Songliste. Diese "asyncio.Queue" hat jedoch keinen Wert, der beim Aufrufen von "get" zurückgegeben werden kann. Wartet dort darauf, dass neue Elemente im laufenden Betrieb hinzugefügt werden. Dadurch ist es möglich, eine Wartezeit für die Song-Eingabe festzulegen. Und wenn 3 Minuten lang keine Eingabe erfolgt, wird das Kollout "Verlassen" aufgerufen und der Sprachkanal automatisch verlassen.
Dies ermöglicht das Hinzufügen einer Musikwiedergabefunktion mit einem Song-Cue.
Die Musikwiedergabefunktion ist jetzt implementiert! Ich bin froh, dass es einfach war! Die Sprachaufnahmefunktion ist genauso einfach, nicht wahr? ?? ??
Es scheint, dass dies nicht gesagt werden kann. Als Vorbereitung für die Implementierung der Aufnahmefunktion werden wir uns genauer ansehen, wie die API-Funktion von Discord gehandhabt wird, indem wir sie direkt kontaktieren und implementieren, ohne discord.py zu verwenden. ..
Recommended Posts