feat(gateway): add BlueBubbles iMessage platform adapter (#6437)
Adds Apple iMessage as a gateway platform via BlueBubbles macOS server. Architecture: - Webhook-based inbound (event-driven, no polling/dedup needed) - Email/phone → chat GUID resolution for user-friendly addressing - Private API safety (checks helper_connected before tapback/typing) - Inbound attachment downloading (images, audio, documents cached locally) - Markdown stripping for clean iMessage delivery - Smart progress suppression for platforms without message editing Based on PR #5869 by @benjaminsehl (webhook architecture, GUID resolution, Private API safety, progress suppression) with inbound attachment downloading from PR #4588 by @1960697431 (attachment cache routing). Integration points: Platform enum, env config, adapter factory, auth maps, cron delivery, send_message routing, channel directory, platform hints, toolset definition, setup wizard, status display. 27 tests covering config, adapter, webhook parsing, GUID resolution, attachment download routing, toolset consistency, and prompt hints.
This commit is contained in:
@@ -148,6 +148,7 @@ def _handle_send(args):
|
||||
"slack": Platform.SLACK,
|
||||
"whatsapp": Platform.WHATSAPP,
|
||||
"signal": Platform.SIGNAL,
|
||||
"bluebubbles": Platform.BLUEBUBBLES,
|
||||
"matrix": Platform.MATRIX,
|
||||
"mattermost": Platform.MATTERMOST,
|
||||
"homeassistant": Platform.HOMEASSISTANT,
|
||||
@@ -396,6 +397,8 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None,
|
||||
result = await _send_feishu(pconfig, chat_id, chunk, thread_id=thread_id)
|
||||
elif platform == Platform.WECOM:
|
||||
result = await _send_wecom(pconfig.extra, chat_id, chunk)
|
||||
elif platform == Platform.BLUEBUBBLES:
|
||||
result = await _send_bluebubbles(pconfig.extra, chat_id, chunk)
|
||||
else:
|
||||
result = {"error": f"Direct sending not yet implemented for {platform.value}"}
|
||||
|
||||
@@ -870,6 +873,33 @@ async def _send_wecom(extra, chat_id, message):
|
||||
return _error(f"WeCom send failed: {e}")
|
||||
|
||||
|
||||
async def _send_bluebubbles(extra, chat_id, message):
|
||||
"""Send via BlueBubbles iMessage server using the adapter's REST API."""
|
||||
try:
|
||||
from gateway.platforms.bluebubbles import BlueBubblesAdapter, check_bluebubbles_requirements
|
||||
if not check_bluebubbles_requirements():
|
||||
return {"error": "BlueBubbles requirements not met (need aiohttp + httpx)."}
|
||||
except ImportError:
|
||||
return {"error": "BlueBubbles adapter not available."}
|
||||
|
||||
try:
|
||||
from gateway.config import PlatformConfig
|
||||
pconfig = PlatformConfig(extra=extra)
|
||||
adapter = BlueBubblesAdapter(pconfig)
|
||||
connected = await adapter.connect()
|
||||
if not connected:
|
||||
return _error("BlueBubbles: failed to connect to server")
|
||||
try:
|
||||
result = await adapter.send(chat_id, message)
|
||||
if not result.success:
|
||||
return _error(f"BlueBubbles send failed: {result.error}")
|
||||
return {"success": True, "platform": "bluebubbles", "chat_id": chat_id, "message_id": result.message_id}
|
||||
finally:
|
||||
await adapter.disconnect()
|
||||
except Exception as e:
|
||||
return _error(f"BlueBubbles send failed: {e}")
|
||||
|
||||
|
||||
async def _send_feishu(pconfig, chat_id, message, media_files=None, thread_id=None):
|
||||
"""Send via Feishu/Lark using the adapter's send pipeline."""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user