408 lines
14 KiB
Python
408 lines
14 KiB
Python
import json, function as func
|
|
|
|
from typing import List
|
|
|
|
from discord import Member, VoiceChannel
|
|
from discord.ext import commands
|
|
from voicelink import Player, Track, Playlist, NodePool, connect_channel, decode
|
|
|
|
class TempCtx():
|
|
def __init__(self, author: Member, channel: VoiceChannel) -> None:
|
|
self.author = author
|
|
self.channel = channel
|
|
self.guild = channel.guild
|
|
|
|
def missingPermission(user_id:int):
|
|
payload = {"op": "errorMsg", "level": "info", "msg": "Only the DJ or admins may use this funciton!"}
|
|
payload["user_id"] = user_id
|
|
return payload
|
|
|
|
def error_msg(msg: str, *, user_id: int = None, guild_id: int = None, level: str = "info"):
|
|
payload = {"op": "errorMsg", "level": level, "msg": msg}
|
|
if user_id:
|
|
payload["user_id"] = user_id
|
|
if guild_id:
|
|
payload["guild_id"] = guild_id
|
|
|
|
return payload
|
|
|
|
async def connect_channel(member: Member, bot: commands.Bot):
|
|
if not member.voice:
|
|
return
|
|
|
|
channel = member.voice.channel
|
|
try:
|
|
player: Player = await channel.connect(cls=Player(bot, channel, TempCtx(member, channel)))
|
|
await player.send_ws({"op": "createPlayer", "members_id": [member.id for member in channel.members]})
|
|
return player
|
|
except:
|
|
return
|
|
|
|
async def initPlayer(player: Player, member: Member, data: dict):
|
|
player._ipc_connection = True
|
|
return {
|
|
"op": "initPlayer",
|
|
"guild_id": player.guild.id,
|
|
"user_id": data.get("user_id"),
|
|
"users": [{
|
|
"user_id": member.id,
|
|
"avatar_url": member.display_avatar.url,
|
|
"name": member.name
|
|
} for member in player.channel.members ],
|
|
"tracks": [ track.track_id for track in player.queue._queue ],
|
|
"repeat_mode": player.queue.repeat.lower(),
|
|
"channel_name": player.channel.name,
|
|
"current_queue_position": player.queue._position if player.is_playing else player.queue._position + 1,
|
|
"current_position": 0 or player.position if player.is_playing else 0,
|
|
"is_playing": player.is_playing,
|
|
"is_paused": player.is_paused,
|
|
"is_dj": player.is_privileged(member, check_user_join=False),
|
|
"autoplay": player.settings.get("autoplay", False)
|
|
}
|
|
|
|
async def skipTo(player: Player, member: Member, data: dict):
|
|
if not player.is_privileged(member):
|
|
if player.current and member == player.current.requester:
|
|
pass
|
|
|
|
elif member in player.skip_votes:
|
|
return error_msg(player.get_msg('voted', 'EN'), user_id=member.id)
|
|
else:
|
|
player.skip_votes.add(member)
|
|
if len(player.skip_votes) >= (required := player.required()):
|
|
pass
|
|
else:
|
|
return error_msg(player.get_msg('skipVote', 'EN').format(member, len(player.skip_votes), required), guild_id=player.guild.id)
|
|
|
|
index = data.get("index", 1)
|
|
if index > 1:
|
|
player.queue.skipto(index)
|
|
|
|
if player.queue._repeat == 1:
|
|
await player.set_repeat("off")
|
|
await player.stop()
|
|
|
|
async def backTo(player: Player, member: Member, data: dict):
|
|
if not player.is_privileged(member):
|
|
if player.current and member == player.current.requester:
|
|
pass
|
|
|
|
elif member in player.skip_votes:
|
|
return error_msg(player.get_msg('voted', 'EN'), user_id=member.id)
|
|
else:
|
|
player.skip_votes.add(member)
|
|
if len(player.skip_votes) >= (required := player.required()):
|
|
pass
|
|
else:
|
|
return error_msg(player.get_msg('backVote', 'EN').format(member, len(player.skip_votes), required), guild_id=player.guild.id)
|
|
|
|
index = data.get("index", 1)
|
|
if not player.is_playing:
|
|
player.queue.backto(index)
|
|
await player.do_next()
|
|
else:
|
|
player.queue.backto(index + 1)
|
|
await player.stop()
|
|
|
|
async def moveTrack(player: Player, member: Member, data: dict):
|
|
if not player.is_privileged(member):
|
|
return missingPermission(member.id)
|
|
|
|
c = player.queue._position - 1
|
|
position = data.get("position")
|
|
new_position = data.get("newPosition")
|
|
|
|
moveItem = player.queue._queue[position]
|
|
player.queue._queue.remove(moveItem)
|
|
player.queue._queue.insert(new_position, moveItem)
|
|
|
|
if position > c and new_position <= c:
|
|
player.queue._position += 1
|
|
|
|
elif position < c and new_position >= c:
|
|
player.queue._position -= 1
|
|
|
|
elif position == c:
|
|
player.queue._position = new_position + 1
|
|
|
|
return {
|
|
"op": "moveTrack",
|
|
"position": {
|
|
"index": position - c,
|
|
"track_id": moveItem.track_id
|
|
},
|
|
"newPosition": {
|
|
"index": new_position - c
|
|
},
|
|
"guild_id": player.guild.id,
|
|
"requester_id": member.id,
|
|
"skip_users": [member.id]
|
|
}
|
|
|
|
async def addTracks(player: Player, member: Member, data: dict):
|
|
raw_tracks = data.get("tracks", [])
|
|
tracks = [Track(
|
|
track_id=track_id,
|
|
info=decode(track_id),
|
|
requester=member
|
|
) for track_id in raw_tracks]
|
|
|
|
await player.add_track(tracks)
|
|
|
|
if not player.is_playing:
|
|
await player.do_next()
|
|
|
|
async def getTracks(player: Player, member: Member, data: dict):
|
|
query = data.get("query", None)
|
|
|
|
if query:
|
|
payload = {"op": "getTracks", "user_id": member.id}
|
|
tracks = await player.get_tracks(query, requester=member)
|
|
if not tracks:
|
|
return payload
|
|
|
|
if isinstance(tracks, Playlist):
|
|
tracks = [ track for track in tracks.tracks[:50] ]
|
|
|
|
payload["tracks"] = [ track.track_id for track in tracks ]
|
|
return payload
|
|
|
|
async def shuffleTrack(player: Player, member: Member, data: dict):
|
|
if not player.is_privileged(member):
|
|
|
|
if member in player.shuffle_votes:
|
|
return error_msg(player.get_msg('voted'), user_id=member.id)
|
|
else:
|
|
player.shuffle_votes.add(member)
|
|
if len(player.shuffle_votes) >= (required := player.required()):
|
|
pass
|
|
else:
|
|
return error_msg(player.get_msg('shuffleVote').format(member, len(player.skip_votes), required), guild_id=player.guild.id)
|
|
|
|
await player.shuffle(data.get("type", "queue"), member)
|
|
|
|
async def repeatTrack(player: Player, member: Member, data: dict):
|
|
if not player.is_privileged(member):
|
|
return missingPermission(member.id)
|
|
|
|
await player.set_repeat()
|
|
|
|
async def removeTrack(player: Player, member: Member, data: dict):
|
|
if not player.is_privileged(member):
|
|
return missingPermission(member.id)
|
|
|
|
position = data.get("position")
|
|
verify_id = data.get("track_id")
|
|
|
|
track = player.queue._queue[position]
|
|
if track.track_id == verify_id:
|
|
player.queue._queue.remove(track)
|
|
|
|
if position < player.queue._position:
|
|
player.queue._position -= 1
|
|
|
|
return {
|
|
"op": "removeTrack",
|
|
"positions": [position],
|
|
"track_ids": [track.track_id],
|
|
"current_queue_position": player.queue._position,
|
|
"requester_id": member.id,
|
|
"guild_id": player.guild.id
|
|
}
|
|
|
|
async def updatePause(player: Player, member: Member, data: dict):
|
|
pause = data.get("pause", True)
|
|
if not player.is_privileged(member):
|
|
if pause:
|
|
if member in player.pause_votes:
|
|
return error_msg(player.get_msg('voted'), user_id=member.id)
|
|
else:
|
|
player.pause_votes.add(member)
|
|
if len(player.pause_votes) >= (required := player.required()):
|
|
pass
|
|
else:
|
|
return error_msg(player.get_msg('pauseVote').format(member, len(player.pause_votes), required), guild_id=player.guild.id)
|
|
else:
|
|
if member in player.resume_votes:
|
|
return error_msg(player.get_msg('voted'), user_id=member.id)
|
|
else:
|
|
player.resume_votes.add(member)
|
|
if len(player.resume_votes) >= (required := player.required()):
|
|
pass
|
|
else:
|
|
return error_msg(player.get_msg('resumeVote').format(member, len(player.resume_votes), required), guild_id=player.guild.id)
|
|
|
|
if pause:
|
|
player.pause_votes.clear()
|
|
else:
|
|
player.resume_votes.clear()
|
|
|
|
await player.set_pause(pause, member)
|
|
|
|
async def updatePosition(player: Player, member: Member, data: dict):
|
|
if not player.is_privileged(member):
|
|
return missingPermission(member.id)
|
|
|
|
position = data.get("position");
|
|
await player.seek(position, member);
|
|
|
|
async def toggleAutoplay(player: Player, member: Member, data: dict):
|
|
if not player.is_privileged(member):
|
|
return error_msg(player.get_msg('missingPerms_autoplay'))
|
|
|
|
check = data.get("status", False)
|
|
player.settings['autoplay'] = check
|
|
|
|
if not player.is_playing:
|
|
await player.do_next()
|
|
|
|
return {
|
|
"op": "toggleAutoplay",
|
|
"status": check,
|
|
"requester_id": member.id
|
|
}
|
|
|
|
async def closeConnection(player: Player, member: Member, data: dict):
|
|
player._ipc_connection = False
|
|
|
|
async def getPlaylists(member: Member, data: dict):
|
|
playlists: dict = await func.get_playlist(member.id, "playlist")
|
|
if not playlists:
|
|
return
|
|
|
|
for pId, pList in playlists.copy().items():
|
|
if "type" in pList:
|
|
if pList["type"] == "link":
|
|
tracks: Playlist = await NodePool.get_node().get_tracks(pList["uri"], requester=member)
|
|
if tracks:
|
|
playlists[pId]["tracks"] = [ track.track_id for track in tracks.tracks ]
|
|
|
|
elif pList["type"] == "share":
|
|
playlist = await func.get_playlist(pList["user"], "playlist", pList["referId"])
|
|
if playlist:
|
|
if member.id not in playlist["perms"]["read"]:
|
|
await func.update_playlist(member.id, {f"playlist.{pId}": 1}, mode=False)
|
|
del playlists[pId]
|
|
continue
|
|
playlists[pId]["tracks"] = playlist["tracks"]
|
|
|
|
return {
|
|
"op": "getPlaylists",
|
|
"playlists": playlists,
|
|
"user_id": member.id
|
|
}
|
|
|
|
async def removePlaylist(member: Member, data:dict):
|
|
pId = data.get("pId")
|
|
isShare = data.get("isShare", False)
|
|
|
|
if pId == 200:
|
|
return
|
|
|
|
if isShare:
|
|
refer_user = data.get("refer_user")
|
|
await func.update_playlist(refer_user, {f"playlist.{pId}.perms.read": member.id}, pull=True, mode=False)
|
|
|
|
await func.update_playlist(member.id, {f'playlist.{pId}': 1}, mode=False)
|
|
|
|
async def addPlaylistTrack(member: Member, data: dict):
|
|
track_id = data.get("track")
|
|
pId = data.get("pId")
|
|
if not track_id or not pId:
|
|
return
|
|
|
|
playlist: dict = await func.get_playlist(member.id, 'playlist', pId)
|
|
if not playlist:
|
|
return
|
|
|
|
rank, max_p, max_t = await func.checkroles(member.id)
|
|
if len(playlist["tracks"]) >= max_t:
|
|
return error_msg(func.get_lang(member.guild.id, "playlistlimited").format(max_t), user_id=member.id)
|
|
|
|
if track_id in playlist['tracks']:
|
|
return error_msg(func.get_lang(member.guild.id, "playlistrepeated"), user_id=member.id)
|
|
|
|
await func.update_playlist(member.id, {f'playlist.{pId}.tracks': track_id}, push=True)
|
|
|
|
async def removePlaylistTrack(member: Member, data: dict):
|
|
track_id = data.get("track")
|
|
pId = data.get("pId")
|
|
if not track_id or not pId:
|
|
return
|
|
|
|
await func.update_playlist(member.id, {f'playlist.{pId}.tracks': track_id }, pull=True, mode=False)
|
|
|
|
methods = {
|
|
"initPlayer": [initPlayer, False],
|
|
"skipTo": [skipTo, False],
|
|
"backTo": [backTo, False],
|
|
"moveTrack": [moveTrack, False],
|
|
"addTracks": [addTracks, True],
|
|
"getTracks": [getTracks, True],
|
|
"shuffleTrack": [shuffleTrack, False],
|
|
"repeatTrack": [repeatTrack, False],
|
|
"removeTrack": [removeTrack, False],
|
|
"updatePause": [updatePause, False],
|
|
"updatePosition": [updatePosition, False],
|
|
"toggleAutoplay": [toggleAutoplay, False],
|
|
"getPlaylists": [getPlaylists, False],
|
|
"removePlaylist": [removePlaylist, False],
|
|
"addPlaylistTrack": [addPlaylistTrack, False],
|
|
"removePlaylistTrack": [removePlaylistTrack, False]
|
|
}
|
|
|
|
async def process_methods(websocket, bot: commands.Bot, data: dict) -> None:
|
|
method = methods.get(data.get("op", ""), None)
|
|
if not method:
|
|
return
|
|
|
|
guild, member = None, None
|
|
guild_id = data.get("guild_id", None)
|
|
user_id = data.get("user_id", None)
|
|
if not user_id:
|
|
return
|
|
|
|
if guild_id is None:
|
|
user = bot.get_user(user_id)
|
|
if not user:
|
|
return
|
|
|
|
for g in user.mutual_guilds:
|
|
m = g.get_member(user.id)
|
|
if m.voice and m.voice.channel:
|
|
guild = g
|
|
member = m
|
|
break
|
|
else:
|
|
guild = bot.get_guild(guild_id)
|
|
member = guild.get_member(user_id)
|
|
|
|
if not member:
|
|
return
|
|
|
|
try:
|
|
if 'player' in method[0].__code__.co_varnames:
|
|
if not guild:
|
|
return
|
|
|
|
player: Player = guild.voice_client
|
|
if not player:
|
|
if not method[1]:
|
|
return
|
|
player: Player = await connect_channel(member, bot)
|
|
|
|
resp: dict = await method[0](player, member, data)
|
|
else:
|
|
resp: dict = await method[0](member, data)
|
|
|
|
if resp:
|
|
await websocket.send(json.dumps(resp))
|
|
|
|
except Exception as e:
|
|
payload = {
|
|
"op": "errorMsg",
|
|
"level": "error",
|
|
"msg": str(e),
|
|
"user_id": member.id
|
|
}
|
|
await websocket.send(json.dumps(payload)) |