Merge pull request #332 from contributors/feishu-event-handlers
This commit is contained in:
@@ -254,6 +254,12 @@ class FeishuChannel(BaseChannel):
|
|||||||
self._processed_message_ids: OrderedDict[str, None] = OrderedDict() # Ordered dedup cache
|
self._processed_message_ids: OrderedDict[str, None] = OrderedDict() # Ordered dedup cache
|
||||||
self._loop: asyncio.AbstractEventLoop | None = None
|
self._loop: asyncio.AbstractEventLoop | None = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _register_optional_event(builder: Any, method_name: str, handler: Any) -> Any:
|
||||||
|
"""Register an event handler only when the SDK supports it."""
|
||||||
|
method = getattr(builder, method_name, None)
|
||||||
|
return method(handler) if callable(method) else builder
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def start(self) -> None:
|
||||||
"""Start the Feishu bot with WebSocket long connection."""
|
"""Start the Feishu bot with WebSocket long connection."""
|
||||||
if not FEISHU_AVAILABLE:
|
if not FEISHU_AVAILABLE:
|
||||||
@@ -274,14 +280,24 @@ class FeishuChannel(BaseChannel):
|
|||||||
.app_secret(self.config.app_secret) \
|
.app_secret(self.config.app_secret) \
|
||||||
.log_level(lark.LogLevel.INFO) \
|
.log_level(lark.LogLevel.INFO) \
|
||||||
.build()
|
.build()
|
||||||
|
builder = lark.EventDispatcherHandler.builder(
|
||||||
# Create event handler (only register message receive, ignore other events)
|
|
||||||
event_handler = lark.EventDispatcherHandler.builder(
|
|
||||||
self.config.encrypt_key or "",
|
self.config.encrypt_key or "",
|
||||||
self.config.verification_token or "",
|
self.config.verification_token or "",
|
||||||
).register_p2_im_message_receive_v1(
|
).register_p2_im_message_receive_v1(
|
||||||
self._on_message_sync
|
self._on_message_sync
|
||||||
).build()
|
)
|
||||||
|
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_message_read_v1", self._on_message_read
|
||||||
|
)
|
||||||
|
builder = self._register_optional_event(
|
||||||
|
builder,
|
||||||
|
"register_p2_im_chat_access_event_bot_p2p_chat_entered_v1",
|
||||||
|
self._on_bot_p2p_chat_entered,
|
||||||
|
)
|
||||||
|
event_handler = builder.build()
|
||||||
|
|
||||||
# Create WebSocket client for long connection
|
# Create WebSocket client for long connection
|
||||||
self._ws_client = lark.ws.Client(
|
self._ws_client = lark.ws.Client(
|
||||||
@@ -842,7 +858,7 @@ class FeishuChannel(BaseChannel):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error sending Feishu message: {}", e)
|
logger.error("Error sending Feishu message: {}", e)
|
||||||
|
|
||||||
def _on_message_sync(self, data: "P2ImMessageReceiveV1") -> None:
|
def _on_message_sync(self, data: Any) -> None:
|
||||||
"""
|
"""
|
||||||
Sync handler for incoming messages (called from WebSocket thread).
|
Sync handler for incoming messages (called from WebSocket thread).
|
||||||
Schedules async handling in the main event loop.
|
Schedules async handling in the main event loop.
|
||||||
@@ -850,7 +866,7 @@ class FeishuChannel(BaseChannel):
|
|||||||
if self._loop and self._loop.is_running():
|
if self._loop and self._loop.is_running():
|
||||||
asyncio.run_coroutine_threadsafe(self._on_message(data), self._loop)
|
asyncio.run_coroutine_threadsafe(self._on_message(data), self._loop)
|
||||||
|
|
||||||
async def _on_message(self, data: "P2ImMessageReceiveV1") -> None:
|
async def _on_message(self, data: Any) -> None:
|
||||||
"""Handle incoming message from Feishu."""
|
"""Handle incoming message from Feishu."""
|
||||||
try:
|
try:
|
||||||
event = data.event
|
event = data.event
|
||||||
@@ -954,3 +970,16 @@ class FeishuChannel(BaseChannel):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error processing Feishu message: {}", e)
|
logger.error("Error processing Feishu message: {}", e)
|
||||||
|
|
||||||
|
def _on_reaction_created(self, data: Any) -> None:
|
||||||
|
"""Ignore reaction 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
|
||||||
|
|
||||||
|
def _on_bot_p2p_chat_entered(self, data: Any) -> None:
|
||||||
|
"""Ignore p2p-enter events when a user opens a bot chat."""
|
||||||
|
logger.debug("Bot entered p2p chat (user opened chat window)")
|
||||||
|
pass
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from nanobot.channels.feishu import _extract_post_content
|
from nanobot.channels.feishu import FeishuChannel, _extract_post_content
|
||||||
|
|
||||||
|
|
||||||
def test_extract_post_content_supports_post_wrapper_shape() -> None:
|
def test_extract_post_content_supports_post_wrapper_shape() -> None:
|
||||||
@@ -38,3 +38,28 @@ def test_extract_post_content_keeps_direct_shape_behavior() -> None:
|
|||||||
|
|
||||||
assert text == "Daily report"
|
assert text == "Daily report"
|
||||||
assert image_keys == ["img_a", "img_b"]
|
assert image_keys == ["img_a", "img_b"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_register_optional_event_keeps_builder_when_method_missing() -> None:
|
||||||
|
class Builder:
|
||||||
|
pass
|
||||||
|
|
||||||
|
builder = Builder()
|
||||||
|
same = FeishuChannel._register_optional_event(builder, "missing", object())
|
||||||
|
assert same is builder
|
||||||
|
|
||||||
|
|
||||||
|
def test_register_optional_event_calls_supported_method() -> None:
|
||||||
|
called = []
|
||||||
|
|
||||||
|
class Builder:
|
||||||
|
def register_event(self, handler):
|
||||||
|
called.append(handler)
|
||||||
|
return self
|
||||||
|
|
||||||
|
builder = Builder()
|
||||||
|
handler = object()
|
||||||
|
same = FeishuChannel._register_optional_event(builder, "register_event", handler)
|
||||||
|
|
||||||
|
assert same is builder
|
||||||
|
assert called == [handler]
|
||||||
|
|||||||
Reference in New Issue
Block a user