implemented discord adapter
This commit is contained in:
@@ -122,7 +122,7 @@ update the discord bot into a thin frontend that talks to the backend and verify
|
|||||||
|
|
||||||
** evidence
|
** evidence
|
||||||
- commit:
|
- commit:
|
||||||
- tests:
|
- tests: https://youtu.be/20HxMMSqRyg?si=3v7mN2L88c_FxpQR 18m
|
||||||
1. start backend: `python3 -m uvicorn youdis.main:app --host 127.0.0.1 --port 8000`
|
1. start backend: `python3 -m uvicorn youdis.main:app --host 127.0.0.1 --port 8000`
|
||||||
2. create local env file: `cp .env.example .env`
|
2. create local env file: `cp .env.example .env`
|
||||||
3. add `api_token` to `.env`
|
3. add `api_token` to `.env`
|
||||||
@@ -142,17 +142,47 @@ KeyError: 'youtube'
|
|||||||
#+end_src
|
#+end_src
|
||||||
:end:
|
:end:
|
||||||
|
|
||||||
#+end_:out:
|
|
||||||
#+begin_src
|
|
||||||
|
|
||||||
6. confirm channel response says the job was submitted to backend
|
6. confirm channel response says the job was submitted to backend
|
||||||
7. confirm requester receives DM updates for accepted/running/completed or failed
|
7. confirm requester receives DM updates for accepted/running/completed or failed
|
||||||
|
:out:
|
||||||
|
accepted job 4ef6fde5-57cc-478c-b0e2-18458c693fa4 for https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
||||||
|
state=running | phase=running | [youtube] dQw4w9WgXcQ: Downloading webpage | path=/home/user/proj/youdis/downloads
|
||||||
|
state=running | phase=downloading | [download] Destination: /home/user/proj/youdis/downloads/Rick_Astley/NA/NANARickAstley-_Never_Gonna_Give_You_Up_Official_Video_4K_Remaster.f401.mp4 | path=/home/user/proj/youdis/downloads/Rick_Astley/NA/NANARickAstley-_Never_Gonna_Give_You_Up_Official_Video_4K_Remaster.f401.mp4
|
||||||
|
state=completed | [Metadata] There isn't any metadata to add | path=/home/user/proj/youdis/downloads/Rick_Astley/NA/NANARickAstley-_Never_Gonna_Give_You_Up_Official_Video_4K_Remaster.mp4
|
||||||
|
:end:
|
||||||
8. while first job is active, submit another `/youtube` and confirm busy behavior
|
8. while first job is active, submit another `/youtube` and confirm busy behavior
|
||||||
|
:out:
|
||||||
|
#+begin_src shell
|
||||||
|
Error: AttributeError
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/user/proj/youdis/venv/lib/python3.10/site-packages/interactions/client/client.py", line 1900, in __dispatch_interaction
|
||||||
|
response = await callback
|
||||||
|
File "/home/user/proj/youdis/venv/lib/python3.10/site-packages/interactions/client/client.py", line 1771, in _run_slash_command
|
||||||
|
return await command(ctx, **ctx.kwargs)
|
||||||
|
File "/home/user/proj/youdis/venv/lib/python3.10/site-packages/interactions/models/internal/command.py", line 132, in __call__
|
||||||
|
await self.call_callback(self.callback, context)
|
||||||
|
File "/home/user/proj/youdis/venv/lib/python3.10/site-packages/interactions/models/internal/application_commands.py", line 833, in call_callback
|
||||||
|
return await self.call_with_binding(callback, ctx, *new_args, **new_kwargs)
|
||||||
|
File "/home/user/proj/youdis/venv/lib/python3.10/site-packages/interactions/models/internal/callback.py", line 43, in call_with_binding
|
||||||
|
return await callback(*args, **kwargs)
|
||||||
|
File "/home/user/proj/youdis/youdis/adapters/discord.py", line 167, in youtube
|
||||||
|
await ctx.channel.send(f"Submitted <{url}> to the backend. Status updates via DM.")
|
||||||
|
AttributeError: 'NoneType' object has no attribute 'send'
|
||||||
|
#+end_src
|
||||||
|
busy: busy with https://youtu.be/20HxMMSqRyg?si=3v7mN2L88c_FxpQR
|
||||||
|
state=running | phase=running | cancel requested | path=/home/user/proj/youdis/downloads/James_Hoffmann/NA/NANAThe_Beginner_s_Guide_To_Latte_Art.f401.mp4
|
||||||
|
:end:
|
||||||
9. run `/status` and confirm it reflects current or last backend job
|
9. run `/status` and confirm it reflects current or last backend job
|
||||||
|
:out:
|
||||||
|
last job: state=completed | [Metadata] There isn't any metadata to add | path=/home/user/proj/youdis/downloads/Rick_Astley/NA/NANARickAstley-_Never_Gonna_Give_You_Up_Official_Video_4K_Remaster.mp4
|
||||||
|
:end:
|
||||||
10. run `/interrupt` as owner and confirm cancellation is surfaced via DM
|
10. run `/interrupt` as owner and confirm cancellation is surfaced via DM
|
||||||
- datetime:
|
:out:
|
||||||
|
last job: state=cancelled | cancelled | path=/home/user/proj/youdis/downloads/James_Hoffmann/NA/NANAThe_Beginner_s_Guide_To_Latte_Art.f401.mp4
|
||||||
|
:end:
|
||||||
|
- datetime:[2026-04-02 Thu 11:52]
|
||||||
|
|
||||||
*** testing tests
|
*** org-block tests
|
||||||
#+begin_src shell :dir ~/proj/youdis :results output verbatim
|
#+begin_src shell :dir ~/proj/youdis :results output verbatim
|
||||||
source ./venv/bin/activate
|
source ./venv/bin/activate
|
||||||
python3 -m uvicorn youdis.main:app --host 127.0.0.1 --port 8000
|
python3 -m uvicorn youdis.main:app --host 127.0.0.1 --port 8000
|
||||||
@@ -177,7 +207,7 @@ python ./youdis.py
|
|||||||
- progress updates are currently implemented by polling `/jobs/current` and DMing only when the summary changes
|
- progress updates are currently implemented by polling `/jobs/current` and DMing only when the summary changes
|
||||||
- legacy auth/user-management commands were removed from the active adapter path and should be cleaned up formally in `2.0.3`
|
- legacy auth/user-management commands were removed from the active adapter path and should be cleaned up formally in `2.0.3`
|
||||||
- `.env` is now supported for local/dev convenience, while real environment variables still override it in prod/docker
|
- `.env` is now supported for local/dev convenience, while real environment variables still override it in prod/docker
|
||||||
|
- submitting via DM doesn't work
|
||||||
* [ ] 2.0.3: remove deprecated discord-bot functionality (2)
|
* [ ] 2.0.3: remove deprecated discord-bot functionality (2)
|
||||||
delete or retire legacy bot behaviors that no longer fit once the backend split is in place
|
delete or retire legacy bot behaviors that no longer fit once the backend split is in place
|
||||||
** pm notes
|
** pm notes
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ async def dm(ctx: interactions.SlashContext, message: str) -> None:
|
|||||||
await ctx.author.send(message)
|
await ctx.author.send(message)
|
||||||
|
|
||||||
|
|
||||||
|
async def respond(ctx: interactions.SlashContext, message: str) -> None:
|
||||||
|
if ctx.channel is not None:
|
||||||
|
await ctx.channel.send(message)
|
||||||
|
return
|
||||||
|
await dm(ctx, message)
|
||||||
|
|
||||||
|
|
||||||
async def poll_job_updates(ctx: interactions.SlashContext, job_id: str) -> None:
|
async def poll_job_updates(ctx: interactions.SlashContext, job_id: str) -> None:
|
||||||
last_sent = None
|
last_sent = None
|
||||||
try:
|
try:
|
||||||
@@ -105,14 +112,18 @@ def ensure_poll_task(ctx: interactions.SlashContext, job_id: str) -> None:
|
|||||||
return
|
return
|
||||||
poll_tasks[job_id] = asyncio.create_task(poll_job_updates(ctx, job_id))
|
poll_tasks[job_id] = asyncio.create_task(poll_job_updates(ctx, job_id))
|
||||||
|
|
||||||
|
@bot.listen()
|
||||||
@interactions.listen()
|
|
||||||
async def on_startup():
|
async def on_startup():
|
||||||
await get_session()
|
await get_session()
|
||||||
print(f"discord adapter configured for backend {BACKEND_URL}")
|
print(f"discord adapter configured for backend {BACKEND_URL}")
|
||||||
|
print(f"discord adapter default scope: {DEFAULT_SCOPE}")
|
||||||
|
print(f"discord adapter command cache keys: {sorted(bot._interaction_lookup.keys())}")
|
||||||
|
|
||||||
|
@bot.listen()
|
||||||
|
async def on_ready():
|
||||||
|
print(f"registered commands: {bot.application_commands}")
|
||||||
|
|
||||||
@interactions.listen()
|
@bot.listen()
|
||||||
async def on_shutdown():
|
async def on_shutdown():
|
||||||
global http_session
|
global http_session
|
||||||
for task in list(poll_tasks.values()):
|
for task in list(poll_tasks.values()):
|
||||||
@@ -151,16 +162,16 @@ async def youtube(ctx: interactions.SlashContext, url: str):
|
|||||||
state = job.get("state")
|
state = job.get("state")
|
||||||
job_id = job.get("job_id", "unknown")
|
job_id = job.get("job_id", "unknown")
|
||||||
if state == "busy":
|
if state == "busy":
|
||||||
await ctx.channel.send(f"Backend is busy with another job. Details via DM.")
|
await respond(ctx, "Backend is busy with another job. Details via DM.")
|
||||||
await dm(ctx, f"busy: {job.get('message')}")
|
await dm(ctx, f"busy: {job.get('message')}")
|
||||||
return
|
return
|
||||||
|
|
||||||
if state != "accepted":
|
if state != "accepted":
|
||||||
await ctx.channel.send("Backend rejected the request. Details via DM.")
|
await respond(ctx, "Backend rejected the request. Details via DM.")
|
||||||
await dm(ctx, format_status_message(job))
|
await dm(ctx, format_status_message(job))
|
||||||
return
|
return
|
||||||
|
|
||||||
await ctx.channel.send(f"Submitted <{url}> to the backend. Status updates via DM.")
|
await respond(ctx, f"Submitted <{url}> to the backend. Status updates via DM.")
|
||||||
await dm(ctx, f"accepted job {job_id} for <{url}>")
|
await dm(ctx, f"accepted job {job_id} for <{url}>")
|
||||||
ensure_poll_task(ctx, job_id)
|
ensure_poll_task(ctx, job_id)
|
||||||
|
|
||||||
@@ -211,4 +222,7 @@ def main() -> None:
|
|||||||
api_token = getenv("DISCORD_BOT_TOKEN")
|
api_token = getenv("DISCORD_BOT_TOKEN")
|
||||||
if not api_token:
|
if not api_token:
|
||||||
raise ValueError("API token not set. Retrieve from your Discord bot.")
|
raise ValueError("API token not set. Retrieve from your Discord bot.")
|
||||||
|
bot.add_command(youtube)
|
||||||
|
bot.add_command(status)
|
||||||
|
bot.add_command(interrupt)
|
||||||
bot.start(api_token)
|
bot.start(api_token)
|
||||||
|
|||||||
Reference in New Issue
Block a user