From a94a2068e2979d383eb88024dfbec14c8419efcc Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 13 Oct 2026 18:32:14 +0400 Subject: [PATCH] refac --- backend/open_webui/routers/channels.py | 21 ++++--- backend/open_webui/utils/apps.py | 45 ++++++++++++-- .../components/channel/AppChannelReply.svelte | 4 +- .../Message/UserStatusLinkPreview.svelte | 9 ++- .../MarkdownInlineTokens/MentionToken.svelte | 62 +++++++++++++++++-- 5 files changed, 117 insertions(+), 24 deletions(-) diff --git a/backend/open_webui/routers/channels.py b/backend/open_webui/routers/channels.py index b0ba2fc400..8554f22d5c 100644 --- a/backend/open_webui/routers/channels.py +++ b/backend/open_webui/routers/channels.py @@ -52,10 +52,10 @@ from open_webui.tasks import create_task from open_webui.utils.access_control import filter_allowed_access_grants, has_permission from open_webui.utils.apps import ( check_app_channel_access, - generate_app_channel_reply, get_app_by_id, get_app_response, has_app_access, + send_app_mention, ) from open_webui.utils.auth import get_admin_user, get_verified_user from open_webui.utils.channels import extract_mentions, replace_mentions @@ -1110,11 +1110,10 @@ async def app_channel_reply_handler(channel_id, message_id, user_id): user = await Users.get_user_by_id(user_id, db=db) if not user: continue - value = await generate_app_channel_reply( + await send_app_mention( app, user, channel_id, - 'channel.mention', {'text': text, 'message_id': message_id, 'parent_id': parent_id}, operation_id=message_id, db=db, @@ -1124,11 +1123,15 @@ async def app_channel_reply_handler(channel_id, message_id, user_id): ) except Exception as exc: log.warning('App mention delivery failed: %s', type(exc).__name__) - value = { - 'type': 'error', - 'content': 'App could not respond. Check access and connection status before retrying.', - } - if value.get('type') != 'ack' and value.get('visibility') != 'channel': + status_code = getattr(exc, 'status_code', None) + if status_code == 504: + detail = 'App delivery was not confirmed. It may have accepted the message; check the channel before retrying.' + elif status_code in (401, 403): + detail = 'App access was denied. Check permissions before retrying.' + elif status_code == 503: + detail = 'App is unavailable or busy. The message was not accepted.' + else: + detail = 'App delivery failed. Check its connection and server logs before retrying.' await emit_to_users( 'events:app', { @@ -1137,7 +1140,7 @@ async def app_channel_reply_handler(channel_id, message_id, user_id): 'channel_id': channel_id, 'message_id': message_id, 'parent_id': parent_id, - 'reply': value, + 'reply': {'type': 'error', 'content': detail}, }, [user_id], ) diff --git a/backend/open_webui/utils/apps.py b/backend/open_webui/utils/apps.py index 4f163de999..f3edc7d133 100644 --- a/backend/open_webui/utils/apps.py +++ b/backend/open_webui/utils/apps.py @@ -460,8 +460,8 @@ async def update_app_message(db, app, message_id, form, if_match): return value -async def generate_app_channel_reply( - app, user, channel_id, event_type, data, *, db: AsyncSession, source=None, operation_id=None, scope=None +async def send_app_invocation( + app, user, channel_id, event_type, data, *, db: AsyncSession, source=None, operation_id=None, scope=None, timeout=3 ): await check_app_invocation_access(app, channel_id, user, db=db) require_scope(app, scope) @@ -488,11 +488,10 @@ async def generate_app_channel_reply( 'execution_token': token, 'source': source, } - checked_key = (app.api_key, app.data['manifest']['public_key']) - # Release the read transaction before HTTP; recheck access after the response. + # Release the read transaction before socket delivery. await db.commit() try: - status, raw = await send_app_event(app, envelope) + status, raw = await send_app_event(app, envelope, timeout=timeout) except HTTPException as exc: if exc.status_code == 504: raise HTTPException(504, 'App response timed out; execution outcome may be unknown') from None @@ -509,7 +508,41 @@ async def generate_app_channel_reply( 503: 'App is busy or shutting down. The event was not accepted.', 504: 'App processing timed out; execution outcome may be unknown.', }.get(status, f'App returned HTTP {status}. Check the app server logs.') - raise HTTPException(504 if status == 504 else 502, detail) + raise HTTPException(status if status in (401, 403, 503, 504) else 502, detail) + return raw + + +async def send_app_mention(app, user, channel_id, data, *, operation_id, scope, db): + receipt = await send_app_invocation( + app, + user, + channel_id, + 'channel.mention', + data, + operation_id=operation_id, + scope=scope, + db=db, + timeout=10, + ) + if receipt != {'received': True}: + raise HTTPException(502, 'App returned an invalid delivery receipt') + + +async def generate_app_channel_reply( + app, user, channel_id, event_type, data, *, db: AsyncSession, source=None, operation_id=None, scope=None +): + checked_key = (app.api_key, app.data['manifest']['public_key']) + raw = await send_app_invocation( + app, + user, + channel_id, + event_type, + data, + db=db, + source=source, + operation_id=operation_id, + scope=scope, + ) app = await get_app_by_id(app.id, require_active=True, db=db) if (app.api_key, app.data['manifest']['public_key']) != checked_key: raise HTTPException(403, 'App credentials changed') diff --git a/src/lib/components/channel/AppChannelReply.svelte b/src/lib/components/channel/AppChannelReply.svelte index 22bd456c39..b2a02c8403 100644 --- a/src/lib/components/channel/AppChannelReply.svelte +++ b/src/lib/components/channel/AppChannelReply.svelte @@ -62,7 +62,9 @@ {#if reply?.title}

{reply.title}

{/if} {#if reply?.content}{/if} {#each reply.embeds ?? [] as embed}{/each} - {#if reply?.type === 'error'}

The app could not complete this operation.

{/if} + {#if reply?.type === 'error' && !reply?.content}

+ The app could not complete this operation. +

{/if} {#if error}{/if} {#if reply?.type === 'download'}