Fix: Resolve webhook send timeout context error

ISSUE
=====
When using the manual webhook message feature via API, the following error occurred:
- 'Timeout context manager should be used inside a task'
- 'NoneType' object is not iterable (when sending without files)

The error happened because Discord.py's webhook operations were being awaited
directly in the FastAPI endpoint context, rather than within a task running in
the bot's event loop.

SOLUTION
========
Refactored /manual/send-webhook endpoint to properly handle async operations:

1. Moved webhook creation inside task function
   - get_or_create_webhooks_for_channel() now runs in send_webhook_message()
   - All Discord operations (webhook selection, sending) happen inside the task
   - Follows same pattern as working /manual/send endpoint

2. Fixed file parameter handling
   - Changed from 'files=discord_files if discord_files else None'
   - To conditional: only pass files parameter when list is non-empty
   - Discord.py's webhook.send() cannot iterate over None, requires list or omit

3. Maintained proper file reading
   - File content still read in endpoint context (before form closes)
   - File data passed to task as pre-read byte arrays
   - Prevents form closure issues

TECHNICAL DETAILS
=================
- Discord.py HTTP operations use timeout context managers
- Context managers must run inside bot's event loop (via create_task)
- FastAPI endpoint context is separate from bot's event loop
- Solution: Wrap all Discord API calls in async task function
- Pattern: Read files → Create task → Task handles Discord operations

TESTING
=======
- Manual webhook sending now works without timeout errors
- Both personas (Miku/Evil) send correctly
- File attachments work properly
- Messages without files send correctly
This commit is contained in:
2026-01-07 13:44:13 +02:00
parent caab444c08
commit ed5994ec78
3 changed files with 272 additions and 15 deletions

View File

@@ -13811,6 +13811,93 @@
"content": "Ugh, a new merchandise announcement. How... thrilling. \\*scoffs\\* A cheap T-shirt with a stupid graphic design. You're actually excited about this? What is wrong with you? 🖤",
"attachments": [],
"reactions": []
},
{
"timestamp": "2026-01-06T13:35:41.323842",
"message_id": 1458091656904446015,
"is_bot_message": true,
"content": "https://fxtwitter.com/GoodSmile_US/status/2008267317768061038",
"attachments": [],
"reactions": [],
"embeds": [
{
"footer": {
"text": "FxTwitter",
"icon_url": "https://assets.fxembed.com/logos/fxtwitter64.png",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/gFi3EIEMfICdEDMLFVTF3ob0J1xkZnKdnkq0-xEsgGc/https/assets.fxembed.com/logos/fxtwitter64.png"
},
"image": {
"url": "https://pbs.twimg.com/media/G97Me43WwAEVGA-.jpg?name=orig",
"proxy_url": "https://images-ext-1.discordapp.net/external/2aIFJJ54mcDnbylcTjzIkmFNbdekb7CJOdSSiZR0i6M/%3Fname%3Dorig/https/pbs.twimg.com/media/G97Me43WwAEVGA-.jpg",
"width": 1000,
"height": 1000,
"content_type": "image/jpeg",
"placeholder": "J2cFZw6ZaGeJeZdwhMd3OJaLdAaIR5AF",
"placeholder_version": 1,
"flags": 0
},
"author": {
"name": "GoodSmile_US (@GoodSmile_US)",
"url": "https://x.com/GoodSmile_US/status/2008267317768061038",
"icon_url": "https://pbs.twimg.com/profile_images/1764583971831394305/dpXogB0U_200x200.png",
"proxy_icon_url": "https://images-ext-1.discordapp.net/external/cGKAum5015cgr2haNcfupJxMvXRz8BYDHkQiNufWpCI/https/pbs.twimg.com/profile_images/1764583971831394305/dpXogB0U_200x200.png"
},
"flags": 0,
"color": 6513919,
"timestamp": "2026-01-05T20:00:15+00:00",
"type": "rich",
"description": "Take flight on an epic expedition with Hatsune Miku\\: 100th Adventure Ver\\.\\! This adventurous soul is the hunt for hidden treasures and new horizons for your display\\. Preorder now to score a GSC Exclusive Bonus Ornament\\!\n\nShop\\: [goodsmile.link/kS5cg5](https://goodsmile.link/kS5cg5)\n\n[#HatsuneMiku](https://x.com/hashtag/HatsuneMiku) [#Goodsmile](https://x.com/hashtag/Goodsmile)\n\n**[🔁](https://x.com/intent/retweet?tweet_id=2008267317768061038) 13[❤️](https://x.com/intent/like?tweet_id=2008267317768061038) 144👁 11\\.2K**",
"url": "https://fxtwitter.com/GoodSmile_US/status/2008267317768061038"
}
]
},
{
"timestamp": "2026-01-06T13:35:41.894187",
"message_id": 1458091659454709881,
"is_bot_message": true,
"content": "Hey there! 🌸 Just saw the cutest tweet from Goodsmile Online Shop U.S. They're releasing the HATSUNE MIKU 100TH ADVENTURE VER. figure and it's SO ADORBS! Can't wait to get my hands on it! 😍",
"attachments": [],
"reactions": []
},
{
"timestamp": "2026-01-06T13:57:09.480215",
"message_id": 1458097060317499636,
"is_bot_message": false,
"content": "Looks wonderful!",
"attachments": [],
"reactions": []
},
{
"timestamp": "2026-01-06T13:57:20.857539",
"message_id": 1458097107054493849,
"is_bot_message": true,
"content": "😊 \\*yawn\\* Oh, thank you, Diamond! Miku's a bit sleepy right now, but it's always nice to chat with you! 🌙 What's on your mind? 🤔",
"attachments": [],
"reactions": []
},
{
"timestamp": "2026-01-06T13:58:22.070172",
"message_id": 1458097364957921323,
"is_bot_message": false,
"content": "I'm making something to eat, feeling a bit hungry, you should go take a nap if you're tired",
"attachments": [],
"reactions": []
},
{
"timestamp": "2026-01-06T13:58:34.023074",
"message_id": 1458097414589255780,
"is_bot_message": true,
"content": "😊 \\*stifles a yawn\\* That sounds delicious, Diamond! Miku might just take your advice... 🙏 \\*blinks slowly\\* Zzz... maybe I'll just rest my eyes for a bit... 😴",
"attachments": [],
"reactions": [
{
"emoji": "💖",
"reactor_id": 236553184401817601,
"reactor_name": "Diamond",
"is_bot": false,
"added_at": "2026-01-06T14:46:51.561111"
}
]
}
]
}