fix(feishu): ensure access token is initialized before fetching bot open_id

The lark-oapi client requires token types to be explicitly configured
so that the SDK can obtain and attach the tenant_access_token to raw
requests. Without this, `_fetch_bot_open_id()` would fail with
"Missing access token for authorization" because the token had not
been provisioned at the time of the call.
This commit is contained in:
wudongxue 2026-03-31 12:52:32 +08:00 committed by Xubin Ren
parent 0291d1f716
commit b3294f79aa

View File

@ -1,6 +1,7 @@
"""Feishu/Lark channel implementation using lark-oapi SDK with WebSocket long connection."""
import asyncio
import importlib.util
import json
import os
import re
@ -11,18 +12,15 @@ from collections import OrderedDict
from dataclasses import dataclass
from typing import Any, Literal
from lark_oapi.api.im.v1.model import P2ImMessageReceiveV1, MentionEvent
from lark_oapi.api.im.v1.model import MentionEvent, P2ImMessageReceiveV1
from loguru import logger
from pydantic import Field
from nanobot.bus.events import OutboundMessage
from nanobot.bus.queue import MessageBus
from nanobot.channels.base import BaseChannel
from nanobot.config.paths import get_media_dir
from nanobot.config.schema import Base
from pydantic import Field
import importlib.util
FEISHU_AVAILABLE = importlib.util.find_spec("lark_oapi") is not None
@ -292,11 +290,13 @@ class FeishuChannel(BaseChannel):
return FeishuConfig().model_dump(by_alias=True)
def __init__(self, config: Any, bus: MessageBus):
import lark_oapi as lark
if isinstance(config, dict):
config = FeishuConfig.model_validate(config)
super().__init__(config, bus)
self.config: FeishuConfig = config
self._client: Any = None
self._client: lark.Client = None
self._ws_client: Any = None
self._ws_thread: threading.Thread | None = None
self._processed_message_ids: OrderedDict[str, None] = OrderedDict() # Ordered dedup cache
@ -340,6 +340,9 @@ class FeishuChannel(BaseChannel):
builder = self._register_optional_event(
builder, "register_p2_im_message_reaction_created_v1", self._on_reaction_created
)
builder = self._register_optional_event(
builder, "register_p2_im_message_reaction_deleted_v1", self._on_reaction_deleted
)
builder = self._register_optional_event(
builder, "register_p2_im_message_message_read_v1", self._on_message_read
)
@ -365,6 +368,7 @@ class FeishuChannel(BaseChannel):
# "This event loop is already running" errors.
def run_ws():
import time
import lark_oapi.ws.client as _lark_ws_client
ws_loop = asyncio.new_event_loop()
@ -418,9 +422,10 @@ class FeishuChannel(BaseChannel):
import lark_oapi as lark
request = (
lark.RawRequest.builder()
lark.BaseRequest.builder()
.http_method(lark.HttpMethod.GET)
.uri("/open-apis/bot/v3/info")
.token_types({lark.AccessTokenType.APP})
.build()
)
response = self._client.request(request)
@ -455,12 +460,12 @@ class FeishuChannel(BaseChannel):
if not key or key not in text:
continue
userID = mention.id or None
if not userID:
user_id_obj = mention.id or None
if not user_id_obj:
continue
open_id = userID.open_id
user_id = userID.user_id
open_id = user_id_obj.open_id
user_id = user_id_obj.user_id
name = mention.name or key
# Format: @姓名 (open_id, user_id: xxx)
@ -1576,7 +1581,6 @@ class FeishuChannel(BaseChannel):
"thread_id": thread_id,
},
)
await self._del_reaction(message_id, reaction_id)
except Exception as e:
logger.error("Error processing Feishu message: {}", e)
@ -1585,6 +1589,10 @@ class FeishuChannel(BaseChannel):
"""Ignore reaction events so they do not generate SDK noise."""
pass
def _on_reaction_deleted(self, data: Any) -> None:
"""Ignore reaction deleted events so they do not generate SDK noise."""
pass
def _on_message_read(self, data: Any) -> None:
"""Ignore read events so they do not generate SDK noise."""
pass