This commit is contained in:
AF 2022-06-20 00:35:12 +03:00
parent 1021b30790
commit 1d36c3e17e
13 changed files with 282 additions and 264 deletions

200
v6d3music/commands.py Normal file
View File

@ -0,0 +1,200 @@
import shlex
from v6d2ctx.context import Context, Explicit, at
from v6d2ctx.lock_for import lock_for
from v6d3music.core.mainasrc import main_for, queue_for, vc_for
from v6d3music.core.yt_audios import yt_audios
from v6d3music.utils.assert_admin import assert_admin
from v6d3music.utils.catch import catch
from v6d3music.utils.effects_for_preset import effects_for_preset
from v6d3music.utils.options_for_effects import options_for_effects
from v6d3music.utils.presets import allowed_presets
@at('commands', 'help')
async def help_(ctx: Context, args: list[str]) -> None:
match args:
case []:
await ctx.reply('music bot')
case [name]:
await ctx.reply(f'help for {name}: `{name} help`')
@at('commands', '/')
@at('commands', 'play')
async def play(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args,
f'''
`play ...args`
`play url [- effects] ...args`
`play url [+ preset] ...args`
presets: {shlex.join(allowed_presets)}
''',
(), 'help'
)
async with lock_for(ctx.guild, 'not in a guild'):
queue = await queue_for(ctx, create=True)
async for audio in yt_audios(ctx, args):
queue.append(audio)
await ctx.reply('done')
@at('commands', 'skip')
async def skip(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`skip [first] [last]`
''', 'help'
)
match args:
case []:
queue = await queue_for(ctx, create=False)
queue.skip_at(0, ctx.member)
case [pos] if pos.isdecimal():
pos = int(pos)
queue = await queue_for(ctx, create=False)
queue.skip_at(pos, ctx.member)
case [pos0, pos1] if pos0.isdecimal() and pos1.isdecimal():
pos0, pos1 = int(pos0), int(pos1)
queue = await queue_for(ctx, create=False)
for i in range(pos0, pos1 + 1):
if not queue.skip_at(pos0, ctx.member):
pos0 += 1
case _:
raise Explicit('misformatted')
await ctx.reply('done')
@at('commands', 'to')
async def skip_to(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`to [[h]] [m] s`
''', 'help'
)
match args:
case [h, m, s] if h.isdecimal() and m.isdecimal() and s.isdecimal():
seconds = 3600 * int(h) + 60 * int(m) + int(s)
case [m, s] if m.isdecimal() and s.isdecimal():
seconds = 60 * int(m) + int(s)
case [s] if s.isdecimal():
seconds = int(s)
case _:
raise Explicit('misformatted')
queue = await queue_for(ctx, create=False)
queue.queue[0].set_seconds(seconds)
@at('commands', 'effects')
async def effects_(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`effects - effects`
`effects + preset`
''', 'help'
)
match args:
case ['-', effects]:
pass
case ['+', preset]:
effects = effects_for_preset(preset)
case _:
raise Explicit('misformatted')
assert_admin(ctx.member)
queue = await queue_for(ctx, create=False)
yta = queue.queue[0]
seconds = yta.source_seconds()
yta.options = options_for_effects(effects)
yta.set_seconds(seconds)
@at('commands', 'queue')
async def queue_(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`queue`
`queue clear`
`queue resume`
`queue pause`
''', 'help'
)
match args:
case []:
await ctx.long((await (await queue_for(ctx, create=False)).format()).strip() or 'no queue')
case ['clear']:
(await queue_for(ctx, create=False)).clear(ctx.member)
await ctx.reply('done')
case ['resume']:
async with lock_for(ctx.guild, 'not in a guild'):
await queue_for(ctx, create=True)
await ctx.reply('done')
case ['pause']:
async with lock_for(ctx.guild, 'not in a guild'):
vc = await vc_for(ctx, create=True)
vc.pause()
await ctx.reply('done')
case _:
raise Explicit('misformatted')
@at('commands', 'swap')
async def swap(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`swap a b`
''', 'help'
)
match args:
case [a, b] if a.isdecimal() and b.isdecimal():
a, b = int(a), int(b)
(await queue_for(ctx, create=False)).swap(ctx.member, a, b)
case _:
raise Explicit('misformatted')
@at('commands', 'move')
async def move(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`move a b`
''', 'help'
)
match args:
case [a, b] if a.isdecimal() and b.isdecimal():
a, b = int(a), int(b)
(await queue_for(ctx, create=False)).move(ctx.member, a, b)
case _:
raise Explicit('misformatted')
@at('commands', 'volume')
async def volume_(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`volume volume`
''', 'help'
)
match args:
case [volume]:
volume = float(volume)
await (await main_for(ctx, create=False)).set(volume, ctx.member)
case _:
raise Explicit('misformatted')
@at('commands', 'pause')
async def pause(ctx: Context, _args: list[str]) -> None:
vc = await vc_for(ctx, create=False)
vc.pause()
@at('commands', 'resume')
async def resume(ctx: Context, _args: list[str]) -> None:
vc = await vc_for(ctx, create=False)
vc.resume()
def register_commands():
pass

View File

View File

@ -3,11 +3,11 @@ from typing import Any, Optional
from v6d2ctx.context import Context, Explicit, escape from v6d2ctx.context import Context, Explicit, escape
from v6d3music.real_url import real_url from v6d3music.core.real_url import real_url
from v6d3music.core.ytaudio import YTAudio
from v6d3music.utils.assert_admin import assert_admin from v6d3music.utils.assert_admin import assert_admin
from v6d3music.utils.options_for_effects import options_for_effects from v6d3music.utils.options_for_effects import options_for_effects
from v6d3music.utils.presets import allowed_effects from v6d3music.utils.presets import allowed_effects
from v6d3music.ytaudio import YTAudio
async def create_ytaudio( async def create_ytaudio(

View File

@ -3,9 +3,9 @@ from typing import AsyncIterable
from v6d2ctx.context import Context from v6d2ctx.context import Context
from v6d3music.create_ytaudio import create_ytaudio from v6d3music.core.create_ytaudio import create_ytaudio
from v6d3music.core.ytaudio import YTAudio
from v6d3music.utils.info_tuple import info_tuple from v6d3music.utils.info_tuple import info_tuple
from v6d3music.ytaudio import YTAudio
async def create_ytaudios(ctx: Context, infos: list[info_tuple]) -> AsyncIterable[YTAudio]: async def create_ytaudios(ctx: Context, infos: list[info_tuple]) -> AsyncIterable[YTAudio]:

View File

@ -0,0 +1,62 @@
import discord
from v6d2ctx.context import Context, Explicit
from v6d3music.core.mainaudio import MainAudio
from v6d3music.core.queueaudio import QueueAudio
mainasrcs: dict[discord.Guild, MainAudio] = {}
async def raw_vc_for(ctx: Context) -> discord.VoiceClient:
if ctx.guild is None:
raise Explicit('not in a guild')
vc: discord.VoiceProtocol = ctx.guild.voice_client
if vc is None or isinstance(vc, discord.VoiceClient) and not vc.is_connected():
vs: discord.VoiceState = ctx.member.voice
if vs is None:
raise Explicit('not connected')
vch: discord.VoiceChannel = vs.channel
if vch is None:
raise Explicit('not connected')
try:
vc: discord.VoiceProtocol = await vch.connect()
except discord.ClientException:
await ctx.guild.fetch_channels()
raise Explicit('try again later')
assert isinstance(vc, discord.VoiceClient)
return vc
async def main_for_raw_vc(vc: discord.VoiceClient, *, create: bool) -> MainAudio:
if vc.guild in mainasrcs:
source = mainasrcs[vc.guild]
else:
if create:
source = mainasrcs.setdefault(
vc.guild,
await MainAudio.create(vc.guild)
)
else:
raise Explicit('not playing')
if vc.source != source or create and not vc.is_playing():
vc.play(source)
return source
async def vc_main_for(ctx: Context, *, create: bool) -> tuple[discord.VoiceClient, MainAudio]:
vc = await raw_vc_for(ctx)
return vc, await main_for_raw_vc(vc, create=create)
async def vc_for(ctx: Context, *, create: bool) -> discord.VoiceClient:
vc, source = await vc_main_for(ctx, create=create)
return vc
async def main_for(ctx: Context, *, create: bool) -> MainAudio:
vc, source = await vc_main_for(ctx, create=create)
return source
async def queue_for(ctx: Context, *, create: bool) -> QueueAudio:
return (await main_for(ctx, create=create)).queue

View File

@ -3,7 +3,7 @@ from ptvp35 import Db, KVJson
from v6d2ctx.context import Explicit from v6d2ctx.context import Explicit
from v6d3music.config import myroot from v6d3music.config import myroot
from v6d3music.queueaudio import QueueAudio from v6d3music.core.queueaudio import QueueAudio
from v6d3music.utils.assert_admin import assert_admin from v6d3music.utils.assert_admin import assert_admin
volume_db = Db(myroot / 'volume.db', kvrequest_type=KVJson) volume_db = Db(myroot / 'volume.db', kvrequest_type=KVJson)

View File

@ -6,9 +6,9 @@ import discord
from ptvp35 import Db, KVJson from ptvp35 import Db, KVJson
from v6d3music.config import myroot from v6d3music.config import myroot
from v6d3music.core.ytaudio import YTAudio
from v6d3music.utils.assert_admin import assert_admin from v6d3music.utils.assert_admin import assert_admin
from v6d3music.utils.fill import FILL from v6d3music.utils.fill import FILL
from v6d3music.ytaudio import YTAudio
queue_db = Db(myroot / 'queue.db', kvrequest_type=KVJson) queue_db = Db(myroot / 'queue.db', kvrequest_type=KVJson)

View File

@ -4,8 +4,8 @@ from typing import Optional
from v6d2ctx.context import Benchmark from v6d2ctx.context import Benchmark
from v6d3music.core.cache_url import cache_db, cache_url
from v6d3music.utils.bytes_hash import bytes_hash from v6d3music.utils.bytes_hash import bytes_hash
from v6d3music.cache_url import cache_db, cache_url
async def real_url(url: str, override: bool, tor: bool) -> str: async def real_url(url: str, override: bool, tor: bool) -> str:

View File

@ -2,10 +2,10 @@ from typing import AsyncIterable
from v6d2ctx.context import Context from v6d2ctx.context import Context
from v6d3music.create_ytaudios import create_ytaudios from v6d3music.core.create_ytaudios import create_ytaudios
from v6d3music.core.ytaudio import YTAudio
from v6d3music.utils.entries_effects_for_args import entries_effects_for_args from v6d3music.utils.entries_effects_for_args import entries_effects_for_args
from v6d3music.utils.info_tuple import info_tuple from v6d3music.utils.info_tuple import info_tuple
from v6d3music.ytaudio import YTAudio
async def yt_audios(ctx: Context, args: list[str]) -> AsyncIterable[YTAudio]: async def yt_audios(ctx: Context, args: list[str]) -> AsyncIterable[YTAudio]:

View File

@ -7,9 +7,9 @@ from typing import Optional
import discord import discord
from v6d2ctx.context import Benchmark from v6d2ctx.context import Benchmark
from v6d3music.ffmpegnormalaudio import FFmpegNormalAudio from v6d3music.core.ffmpegnormalaudio import FFmpegNormalAudio
from v6d3music.core.real_url import real_url
from v6d3music.utils.fill import FILL from v6d3music.utils.fill import FILL
from v6d3music.real_url import real_url
from v6d3music.utils.sparq import sparq from v6d3music.utils.sparq import sparq

View File

@ -1,30 +1,26 @@
import asyncio import asyncio
import os import os
import shlex
import subprocess import subprocess
import time import time
import discord import discord
from v6d1tokens.client import request_token from v6d1tokens.client import request_token
from v6d2ctx.context import Benchmark, Context, Explicit, at, monitor from v6d2ctx.context import Benchmark, monitor
from v6d2ctx.handle_content import handle_content from v6d2ctx.handle_content import handle_content
from v6d2ctx.lock_for import lock_for from v6d2ctx.lock_for import lock_for
from v6d2ctx.serve import serve from v6d2ctx.serve import serve
from v6d3music.app import MusicAppFactory, session_db from v6d3music.app import MusicAppFactory, session_db
from v6d3music.cache_url import cache_db from v6d3music.commands import register_commands
from v6d3music.config import prefix from v6d3music.config import prefix
from v6d3music.mainaudio import MainAudio, volume_db from v6d3music.core.cache_url import cache_db
from v6d3music.queueaudio import QueueAudio, queue_db from v6d3music.core.mainasrc import main_for_raw_vc, mainasrcs
from v6d3music.utils.assert_admin import assert_admin from v6d3music.core.mainaudio import volume_db
from v6d3music.utils.catch import catch from v6d3music.core.queueaudio import queue_db
from v6d3music.utils.effects_for_preset import effects_for_preset
from v6d3music.utils.options_for_effects import options_for_effects
from v6d3music.utils.presets import allowed_presets
from v6d3music.yt_audios import yt_audios
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
register_commands()
client = discord.Client( client = discord.Client(
intents=discord.Intents( intents=discord.Intents(
members=True, members=True,
@ -36,6 +32,7 @@ client = discord.Client(
guild_messages=True, guild_messages=True,
reactions=True reactions=True
), ),
loop=loop
) )
vcs_restored = False vcs_restored = False
@ -79,250 +76,9 @@ async def on_ready():
await restore_vcs() await restore_vcs()
@at('commands', 'help')
async def help_(ctx: Context, args: list[str]) -> None:
match args:
case []:
await ctx.reply('music bot')
case [name]:
await ctx.reply(f'help for {name}: `{name} help`')
mainasrcs: dict[discord.Guild, MainAudio] = {}
@at('commands', '/')
@at('commands', 'play')
async def play(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args,
f'''
`play ...args`
`play url [- effects] ...args`
`play url [+ preset] ...args`
presets: {shlex.join(allowed_presets)}
''',
(), 'help'
)
async with lock_for(ctx.guild, 'not in a guild'):
queue = await queue_for(ctx, create=True)
async for audio in yt_audios(ctx, args):
queue.append(audio)
await ctx.reply('done')
async def raw_vc_for(ctx: Context) -> discord.VoiceClient:
if ctx.guild is None:
raise Explicit('not in a guild')
vc: discord.VoiceProtocol = ctx.guild.voice_client
if vc is None or isinstance(vc, discord.VoiceClient) and not vc.is_connected():
vs: discord.VoiceState = ctx.member.voice
if vs is None:
raise Explicit('not connected')
vch: discord.VoiceChannel = vs.channel
if vch is None:
raise Explicit('not connected')
try:
vc: discord.VoiceProtocol = await vch.connect()
except discord.ClientException:
await ctx.guild.fetch_channels()
raise Explicit('try again later')
assert isinstance(vc, discord.VoiceClient)
return vc
async def main_for_raw_vc(vc: discord.VoiceClient, *, create: bool) -> MainAudio:
if vc.guild in mainasrcs:
source = mainasrcs[vc.guild]
else:
if create:
source = mainasrcs.setdefault(
vc.guild,
await MainAudio.create(vc.guild)
)
else:
raise Explicit('not playing')
if vc.source != source or create and not vc.is_playing():
vc.play(source)
return source
async def vc_main_for(ctx: Context, *, create: bool) -> tuple[discord.VoiceClient, MainAudio]:
vc = await raw_vc_for(ctx)
return vc, await main_for_raw_vc(vc, create=create)
async def vc_for(ctx: Context, *, create: bool) -> discord.VoiceClient:
vc, source = await vc_main_for(ctx, create=create)
return vc
async def main_for(ctx: Context, *, create: bool) -> MainAudio:
vc, source = await vc_main_for(ctx, create=create)
return source
async def queue_for(ctx: Context, *, create: bool) -> QueueAudio:
return (await main_for(ctx, create=create)).queue
@at('commands', 'skip')
async def skip(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`skip [first] [last]`
''', 'help'
)
match args:
case []:
queue = await queue_for(ctx, create=False)
queue.skip_at(0, ctx.member)
case [pos] if pos.isdecimal():
pos = int(pos)
queue = await queue_for(ctx, create=False)
queue.skip_at(pos, ctx.member)
case [pos0, pos1] if pos0.isdecimal() and pos1.isdecimal():
pos0, pos1 = int(pos0), int(pos1)
queue = await queue_for(ctx, create=False)
for i in range(pos0, pos1 + 1):
if not queue.skip_at(pos0, ctx.member):
pos0 += 1
case _:
raise Explicit('misformatted')
await ctx.reply('done')
@at('commands', 'to')
async def skip_to(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`to [[h]] [m] s`
''', 'help'
)
match args:
case [h, m, s] if h.isdecimal() and m.isdecimal() and s.isdecimal():
seconds = 3600 * int(h) + 60 * int(m) + int(s)
case [m, s] if m.isdecimal() and s.isdecimal():
seconds = 60 * int(m) + int(s)
case [s] if s.isdecimal():
seconds = int(s)
case _:
raise Explicit('misformatted')
queue = await queue_for(ctx, create=False)
queue.queue[0].set_seconds(seconds)
@at('commands', 'effects')
async def effects_(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`effects - effects`
`effects + preset`
''', 'help'
)
match args:
case ['-', effects]:
pass
case ['+', preset]:
effects = effects_for_preset(preset)
case _:
raise Explicit('misformatted')
assert_admin(ctx.member)
queue = await queue_for(ctx, create=False)
yta = queue.queue[0]
seconds = yta.source_seconds()
yta.options = options_for_effects(effects)
yta.set_seconds(seconds)
@at('commands', 'queue')
async def queue_(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`queue`
`queue clear`
`queue resume`
`queue pause`
''', 'help'
)
match args:
case []:
await ctx.long((await (await queue_for(ctx, create=False)).format()).strip() or 'no queue')
case ['clear']:
(await queue_for(ctx, create=False)).clear(ctx.member)
await ctx.reply('done')
case ['resume']:
async with lock_for(ctx.guild, 'not in a guild'):
await queue_for(ctx, create=True)
await ctx.reply('done')
case ['pause']:
async with lock_for(ctx.guild, 'not in a guild'):
vc = await vc_for(ctx, create=True)
vc.pause()
await ctx.reply('done')
case _:
raise Explicit('misformatted')
@at('commands', 'swap')
async def swap(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`swap a b`
''', 'help'
)
match args:
case [a, b] if a.isdecimal() and b.isdecimal():
a, b = int(a), int(b)
(await queue_for(ctx, create=False)).swap(ctx.member, a, b)
case _:
raise Explicit('misformatted')
@at('commands', 'move')
async def move(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`move a b`
''', 'help'
)
match args:
case [a, b] if a.isdecimal() and b.isdecimal():
a, b = int(a), int(b)
(await queue_for(ctx, create=False)).move(ctx.member, a, b)
case _:
raise Explicit('misformatted')
@at('commands', 'volume')
async def volume_(ctx: Context, args: list[str]) -> None:
await catch(
ctx, args, '''
`volume volume`
''', 'help'
)
match args:
case [volume]:
volume = float(volume)
await (await main_for(ctx, create=False)).set(volume, ctx.member)
case _:
raise Explicit('misformatted')
@at('commands', 'pause')
async def pause(ctx: Context, _args: list[str]) -> None:
vc = await vc_for(ctx, create=False)
vc.pause()
@at('commands', 'resume')
async def resume(ctx: Context, _args: list[str]) -> None:
vc = await vc_for(ctx, create=False)
vc.resume()
@client.event @client.event
async def on_message(message: discord.Message) -> None: async def on_message(message: discord.Message) -> None:
print('on message')
await handle_content(message, message.content, prefix) await handle_content(message, message.content, prefix)