3 changed files with 115 additions and 46 deletions
@ -0,0 +1,95 @@ |
|||||
|
from disco.gateway.packets import OPCode |
||||
|
from disco.types.channel import Channel |
||||
|
from telecom import TelecomConnection, AvConvPlayable |
||||
|
|
||||
|
|
||||
|
class VoiceConnection(object): |
||||
|
def __init__(self, client, guild_id): |
||||
|
self.client = client |
||||
|
self.guild_id = guild_id |
||||
|
self.channel_id = None |
||||
|
self._conn = None |
||||
|
self._voice_server_update_listener = self.client.events.on( |
||||
|
'VoiceServerUpdate', |
||||
|
self._on_voice_server_update, |
||||
|
) |
||||
|
|
||||
|
self._mute = False |
||||
|
self._deaf = False |
||||
|
|
||||
|
@property |
||||
|
def mute(self): |
||||
|
return self._mute |
||||
|
|
||||
|
@property |
||||
|
def deaf(self): |
||||
|
return self._deaf |
||||
|
|
||||
|
@mute.setter |
||||
|
def mute(self, value): |
||||
|
if value is self._mute: |
||||
|
return |
||||
|
|
||||
|
self._mute = value |
||||
|
self._send_voice_state_update() |
||||
|
|
||||
|
@deaf.setter |
||||
|
def deaf(self, value): |
||||
|
if value is self._deaf: |
||||
|
return |
||||
|
|
||||
|
self._deaf = value |
||||
|
self._send_voice_state_update() |
||||
|
|
||||
|
@classmethod |
||||
|
def from_channel(self, channel): |
||||
|
assert channel.is_voice, 'Cannot connect to a non voice channel' |
||||
|
conn = VoiceConnection(channel.client, channel.guild_id) |
||||
|
conn.connect(channel.id) |
||||
|
return conn |
||||
|
|
||||
|
def set_channel(self, channel_or_id): |
||||
|
if channel_or_id and isinstance(channel_or_id, Channel): |
||||
|
channel_or_id = channel_or_id.id |
||||
|
|
||||
|
self.channel_id = channel_or_id |
||||
|
self._send_voice_state_update() |
||||
|
|
||||
|
def connect(self, channel_id): |
||||
|
assert self._conn is None, 'Already connected' |
||||
|
|
||||
|
self.set_channel(channel_id) |
||||
|
|
||||
|
self._conn = TelecomConnection( |
||||
|
self.client.state.me.id, |
||||
|
self.guild_id, |
||||
|
self.client.gw.session_id, |
||||
|
) |
||||
|
|
||||
|
def disconnect(self): |
||||
|
assert self._conn is not None, 'Not connected' |
||||
|
|
||||
|
# Send disconnection |
||||
|
self.set_channel(None) |
||||
|
|
||||
|
# Delete our connection so it will get GC'd |
||||
|
del self._conn |
||||
|
self._conn = None |
||||
|
|
||||
|
def play_file(self, url): |
||||
|
self._conn.play(AvConvPlayable(url)) |
||||
|
|
||||
|
def _on_voice_server_update(self, event): |
||||
|
if not self._conn or event.guild_id != self.guild_id: |
||||
|
return |
||||
|
|
||||
|
self._conn.update_server_info(event.endpoint, event.token) |
||||
|
|
||||
|
def _send_voice_state_update(self): |
||||
|
self.client.gw.send(OPCode.VOICE_STATE_UPDATE, { |
||||
|
'self_mute': self._mute, |
||||
|
'self_deaf': self._deaf, |
||||
|
'self_video': False, |
||||
|
'guild_id': self.guild_id, |
||||
|
'channel_id': self.channel_id, |
||||
|
}) |
@ -1,56 +1,30 @@ |
|||||
from disco.bot import Plugin |
from disco.bot import Plugin |
||||
from disco.bot.command import CommandError |
from disco.voice import VoiceConnection |
||||
from disco.voice.player import Player |
|
||||
from disco.voice.playable import YoutubeDLInput, BufferedOpusEncoderPlayable |
|
||||
from disco.voice.client import VoiceException |
|
||||
|
|
||||
|
|
||||
class MusicPlugin(Plugin): |
class MusicPlugin(Plugin): |
||||
def load(self, ctx): |
def load(self, data): |
||||
super(MusicPlugin, self).load(ctx) |
super(MusicPlugin, self).load(data) |
||||
self.guilds = {} |
self._connections = {} |
||||
|
|
||||
@Plugin.command('join') |
@Plugin.command('join') |
||||
def on_join(self, event): |
def on_join(self, event): |
||||
if event.guild.id in self.guilds: |
vs = event.guild.get_member(event.author).get_voice_state() |
||||
return event.msg.reply("I'm already playing music here.") |
if not vs: |
||||
|
return event.msg.reply('you are not in a voice channel') |
||||
|
|
||||
state = event.guild.get_member(event.author).get_voice_state() |
if event.guild.id in self._connections: |
||||
if not state: |
if self._connections[event.guild.id].channel_id == vs.channel_id: |
||||
return event.msg.reply('You must be connected to voice to use that command.') |
return event.msg.reply('already in that channel') |
||||
|
else: |
||||
|
self._connections[event.guild.id].set_channel(vs.channel) |
||||
|
return |
||||
|
|
||||
try: |
self._connections[event.guild.id] = VoiceConnection.from_channel(vs.channel) |
||||
client = state.channel.connect() |
|
||||
except VoiceException as e: |
|
||||
return event.msg.reply('Failed to connect to voice: `{}`'.format(e)) |
|
||||
|
|
||||
self.guilds[event.guild.id] = Player(client) |
@Plugin.command('play', '<song:str>') |
||||
self.guilds[event.guild.id].complete.wait() |
def on_play(self, event, song=None): |
||||
del self.guilds[event.guild.id] |
if event.guild.id not in self._connections: |
||||
|
return event.msg.reply('not in voice here') |
||||
|
|
||||
def get_player(self, guild_id): |
self._connections[event.guild.id].play_file(song) |
||||
if guild_id not in self.guilds: |
|
||||
raise CommandError("I'm not currently playing music here.") |
|
||||
return self.guilds.get(guild_id) |
|
||||
|
|
||||
@Plugin.command('leave') |
|
||||
def on_leave(self, event): |
|
||||
player = self.get_player(event.guild.id) |
|
||||
player.disconnect() |
|
||||
|
|
||||
@Plugin.command('play', '<url:str>') |
|
||||
def on_play(self, event, url): |
|
||||
item = YoutubeDLInput(url).pipe(BufferedOpusEncoderPlayable) |
|
||||
self.get_player(event.guild.id).queue.append(item) |
|
||||
|
|
||||
@Plugin.command('pause') |
|
||||
def on_pause(self, event): |
|
||||
self.get_player(event.guild.id).pause() |
|
||||
|
|
||||
@Plugin.command('resume') |
|
||||
def on_resume(self, event): |
|
||||
self.get_player(event.guild.id).resume() |
|
||||
|
|
||||
@Plugin.command('kill') |
|
||||
def on_kill(self, event): |
|
||||
self.get_player(event.guild.id).client.ws.sock.shutdown() |
|
||||
|
Loading…
Reference in new issue