diff --git a/example.sh b/example.sh new file mode 100644 index 0000000..72826df --- /dev/null +++ b/example.sh @@ -0,0 +1 @@ +docker run -v v6d3music:/v6data --env v6ca=da5261eb5232b4b08452f25099b53b59d2e308b86aaf9c4204f0aa92569044d7 --env v6caurl=http://172.18.0.2:5900 --env v6taurl=http://172.18.0.3:5910 --name v6d3music -m 4000mb --cpus 3 --network v6d v6d3music diff --git a/requirements.txt b/requirements.txt index 8367210..6346637 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ aiohttp~=3.7.4.post0 discord.py[voice]~=1.7.3 -git+https://gitea.ongoteam.net/PTV/v6d1tokens.git@2dca5338ecec2042f731ff2855225417f66e1372 -git+https://gitea.ongoteam.net/PTV/v6d2ctx.git@087aa39918a147ad9df7de35e6484ccb3efdc6c9 +git+https://gitea.ongoteam.net/PTV/v6d1tokens.git@8b525ee05be22891a1c18d5e96153807f79930be +git+https://gitea.ongoteam.net/PTV/v6d2ctx.git@57919e65069e62fc31666adebd6c107be067b724 youtube_dl~=2021.12.17 diff --git a/v6d3music/app.py b/v6d3music/app.py deleted file mode 100644 index b704ebd..0000000 --- a/v6d3music/app.py +++ /dev/null @@ -1,44 +0,0 @@ -import time - -# noinspection PyPackageRequirements -import discord -from aiohttp import web -from nacl.exceptions import BadSignatureError -from v6d0auth import certs - - -def define_routes(routes: web.RouteTableDef, client: discord.Client): - @routes.get('/') - async def home(_request: web.Request): - return web.Response(body='v6d3music\n') - - @routes.post('/stop') - async def stop(request: web.Request): - try: - assert abs(float(certs.verify(await request.read())) - time.time()) < 1 - except ValueError: - raise web.HTTPBadRequest - except BadSignatureError: - raise web.HTTPUnauthorized - except AssertionError: - raise web.HTTPRequestTimeout - else: - await client.change_presence(status=discord.Status.offline) - await client.close() - raise web.HTTPOk - - -def app_routes(client: discord.Client) -> web.RouteTableDef: - routes = web.RouteTableDef() - define_routes(routes, client) - return routes - - -def app_with_routes(routes: web.RouteTableDef): - app = web.Application() - app.add_routes(routes) - return app - - -def get_app(client: discord.Client) -> web.Application: - return app_with_routes(app_routes(client)) diff --git a/v6d3music/config.py b/v6d3music/config.py index 6f071de..e81eb9d 100644 --- a/v6d3music/config.py +++ b/v6d3music/config.py @@ -1,3 +1,7 @@ import os +from v6d0auth.config import root + prefix = os.getenv('v6prefix', '?/') +myroot = root / 'v6d3music' +myroot.mkdir(exist_ok=True) diff --git a/v6d3music/run-bot.py b/v6d3music/run-bot.py index 19db6b9..0e71f72 100644 --- a/v6d3music/run-bot.py +++ b/v6d3music/run-bot.py @@ -15,17 +15,17 @@ import discord import nacl.hash import youtube_dl from ptvp35 import Db, KVJson -from v6d0auth.config import root -from v6d0auth.run_app import start_app from v6d1tokens.client import request_token -from v6d2ctx.context import Context, of, at, escape, Implicit, monitor, Benchmark, Explicit +from v6d2ctx.context import Context, at, escape, monitor, Benchmark, Explicit +from v6d2ctx.handle_content import handle_content +from v6d2ctx.lock_for import lock_for +from v6d2ctx.serve import serve -from v6d3music.app import get_app -from v6d3music.config import prefix +from v6d3music.config import prefix, myroot loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) -token = loop.run_until_complete(request_token('music')) +token = loop.run_until_complete(request_token('music', 'token')) client = discord.Client( intents=discord.Intents( members=True, @@ -38,14 +38,11 @@ client = discord.Client( reactions=True ), ) -myroot = root / 'v6d3music' -myroot.mkdir(exist_ok=True) volume_db = Db(myroot / 'volume.db', kvrequest_type=KVJson) queue_db = Db(myroot / 'queue.db', kvrequest_type=KVJson) cache_db = Db(myroot / 'cache.db', kvrequest_type=KVJson) cache_root = myroot / 'cache' cache_root.mkdir(exist_ok=True) -cache_locks: dict[str, asyncio.Lock] = {} vcs_restored = False @@ -57,7 +54,7 @@ async def restore_vcs(): for vcgid, vccid, vc_is_paused in vcs: try: guild: discord.Guild = await client.fetch_guild(vcgid) - async with lock_for(guild): + async with lock_for(guild, 'not in a guild'): channels = await guild.fetch_channels() channel: discord.VoiceChannel channel, = [ch for ch in channels if ch.id == vccid] @@ -85,10 +82,6 @@ async def on_ready(): await restore_vcs() -async def handle_command(ctx: Context, name: str, args: list[str]) -> None: - await of('commands', name)(ctx, args) - - @at('commands', 'help') async def help_(ctx: Context, args: list[str]) -> None: match args: @@ -98,25 +91,6 @@ async def help_(ctx: Context, args: list[str]) -> None: await ctx.reply(f'help for {name}: `{name} help`') -locks: dict[discord.Guild, asyncio.Lock] = {} - - -def lock_for(guild: discord.Guild) -> asyncio.Lock: - if guild is None: - raise Explicit('not in a guild') - if guild in locks: - return locks[guild] - else: - return locks.setdefault(guild, asyncio.Lock()) - - -def cache_lock_for(hurl: str) -> asyncio.Lock: - if hurl in cache_locks: - return cache_locks[hurl] - else: - return cache_locks.setdefault(hurl, asyncio.Lock()) - - class YTAudio(discord.AudioSource): source: discord.FFmpegPCMAudio @@ -380,7 +354,7 @@ async def aextract(params: dict, url: str, **kwargs): async def cache_url(hurl: str, rurl: str, override: bool) -> None: - async with cache_lock_for(hurl): + async with lock_for(('cache', hurl), 'cache failed'): if not override and cache_db.get(f'url:{hurl}', None) is not None: return cachable: bool = cache_db.get(f'cachable:{hurl}', False) @@ -541,7 +515,7 @@ async def play(ctx: Context, args: list[str]) -> None: `play url [- effects] ...args` '''.strip()) case _: - async with lock_for(ctx.guild): + 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) @@ -640,7 +614,7 @@ async def queue_(ctx: Context, args: list[str]) -> None: (await queue_for(ctx, create=False)).clear(ctx.member) await ctx.reply('done') case ['resume']: - async with lock_for(ctx.guild): + async with lock_for(ctx.guild, 'not in a guild'): await queue_for(ctx, create=True) case _: raise Explicit("misformatted") @@ -694,30 +668,9 @@ async def resume(ctx: Context, _args: list[str]) -> None: vc.resume() -async def handle_args(message: discord.Message, args: list[str]): - match args: - case []: - return - case [command_name, *command_args]: - ctx = Context(message) - try: - await handle_command(ctx, command_name, command_args) - except Implicit: - pass - except Explicit as e: - await ctx.reply(e.msg) - - @client.event async def on_message(message: discord.Message) -> None: - if message.author.bot: - return - content: str = message.content - if not content.startswith(prefix): - return - content = content.removeprefix(prefix) - args = shlex.split(content) - await handle_args(message, args) + await handle_content(message, message.content, prefix) async def save_queues(): @@ -754,7 +707,6 @@ async def save_job(): async def main(): async with volume_db, queue_db, cache_db: - await start_app(get_app(client)) await client.login(token) loop.create_task(save_job()) if os.getenv('v6monitor'): @@ -763,4 +715,4 @@ async def main(): if __name__ == '__main__': - loop.run_until_complete(main()) + serve(main(), client, loop) diff --git a/v6d3music/stop-bot.py b/v6d3music/stop-bot.py deleted file mode 100644 index 8ba72a9..0000000 --- a/v6d3music/stop-bot.py +++ /dev/null @@ -1,18 +0,0 @@ -import asyncio -import time - -import aiohttp -from v6d0auth import certs -from v6d0auth.config import host, port - - -async def main(): - request = certs.sign(str(time.time()).encode()) - async with aiohttp.ClientSession() as session: - # noinspection HttpUrlsUsage - async with session.post(f'http://{host}:{port}/stop', data=request) as response: - print(response.status) - - -if __name__ == '__main__': - asyncio.run(main())