from __future__ import annotations import logging from .config import _apply_env_overrides, _dict_to_account from .constants import ( ENDPOINT_SEND_MESSAGE, MAX_TEXT_LENGTH, ) from .format import chunk_text, markdown_to_flockml, strip_markdown_for_plain from .types import FlockOutboundResult from .utils import create_http_client, flock_api_call logger = logging.getLogger(__name__) text_chunk_limit = MAX_TEXT_LENGTH def chunker(text: str, limit: int, ctx: object | None = None) -> list[str]: return chunk_text(text, max(limit, 100)) async def send_text( target_id: str, content: str, *, reply_to_id: str | None = None, thread_id: str | None = None, mention_ids: list[str] | None = None, notification: str | None = None, send_as: dict | None = None, on_behalf_of: str | None = None, visible_to: list[str] | None = None, account_id: str = "default", config: dict, ) -> FlockOutboundResult: account_data = config.get("accounts", {}).get(account_id, {}) account = _dict_to_account(account_data) account = _apply_env_overrides(account) if not account.bot_token and not account.incoming_webhook_url: return FlockOutboundResult( success=False, error="No valid send method configured (bot_token or incoming_webhook_url required)", ) flockml = markdown_to_flockml(content) plain_text = strip_markdown_for_plain(content) if account.bot_token: return await _send_via_bot_token( account.bot_token, target_id, plain_text, flockml, reply_to_id, thread_id, mention_ids, notification, send_as, on_behalf_of, visible_to, ) if account.incoming_webhook_url: return await _send_via_webhook(account.incoming_webhook_url, plain_text, flockml) return FlockOutboundResult(success=False, error="No valid send method configured") async def _send_via_bot_token( bot_token: str, target_id: str, plain_text: str, flockml: str, reply_to_id: str | None, thread_id: str | None, mention_ids: list[str] | None, notification: str | None, send_as: dict | None, on_behalf_of: str | None, visible_to: list[str] | None, ) -> FlockOutboundResult: payload: dict = { "to": target_id, "text": plain_text, "flockml": flockml, } if thread_id: payload["threadId"] = thread_id if mention_ids: payload["mentions"] = mention_ids if notification: payload["notification"] = notification if send_as: payload["sendAs"] = send_as if on_behalf_of: payload["onBehalfOf"] = on_behalf_of if visible_to: payload["visibleTo"] = visible_to client = create_http_client() try: result = await flock_api_call(client, ENDPOINT_SEND_MESSAGE, bot_token, payload) msg_uid = result.get("uid", "") logger.info("Flock message sent to %s, uid=%s", target_id, msg_uid) return FlockOutboundResult(success=True, message_uid=msg_uid) except Exception as e: logger.error("Flock send via bot token failed: %s", e) retryable = getattr(e, "retryable", False) return FlockOutboundResult(success=False, error=str(e), retryable=retryable) finally: await client.aclose() async def _send_via_webhook( incoming_webhook_url: str, plain_text: str, flockml: str, ) -> FlockOutboundResult: client = create_http_client() try: resp = await client.post( incoming_webhook_url, json={"text": plain_text, "flockml": flockml}, ) if resp.status_code == 200: logger.info("Flock message sent via incoming webhook") return FlockOutboundResult(success=True) logger.warning("Flock webhook send failed: HTTP %d", resp.status_code) return FlockOutboundResult( success=False, error=f"Incoming webhook returned HTTP {resp.status_code}", ) except Exception as e: logger.error("Flock webhook send error: %s", e) return FlockOutboundResult(success=False, error=str(e)) finally: await client.aclose() async def send_media( target_id: str, media_url: str, media_type: str, *, reply_to_id: str | None = None, thread_id: str | None = None, account_id: str = "default", config: dict, ) -> FlockOutboundResult: account_data = config.get("accounts", {}).get(account_id, {}) account = _dict_to_account(account_data) account = _apply_env_overrides(account) if not account.bot_token: return FlockOutboundResult(success=False, error="bot_token required for media upload") if media_type == "image": payload: dict = { "to": target_id, "text": "", "attachments": [ { "title": "Image", "views": { "image": { "original": {"src": media_url}, "thumbnail": {"src": media_url}, } }, } ], } if thread_id: payload["threadId"] = thread_id client = create_http_client() try: result = await flock_api_call(client, ENDPOINT_SEND_MESSAGE, account.bot_token, payload) return FlockOutboundResult(success=True, message_uid=result.get("uid", "")) except Exception as e: logger.error("Flock image send failed: %s", e) retryable = getattr(e, "retryable", False) return FlockOutboundResult(success=False, error=str(e), retryable=retryable) finally: await client.aclose() return FlockOutboundResult( success=False, error=f"Unsupported media type: {media_type}. Use 'image' or use media.upload_file for files.", ) async def send_payload( ctx: object, config: dict, account_id: str = "default", ) -> FlockOutboundResult: target_id = getattr(ctx, "target_id", "") content = getattr(ctx, "content", "") thread_id = getattr(ctx, "thread_id", None) reply_to_id = getattr(ctx, "reply_to_id", None) mention_ids = getattr(ctx, "mention_ids", None) notification = getattr(ctx, "notification", None) send_as = getattr(ctx, "send_as", None) on_behalf_of = getattr(ctx, "on_behalf_of", None) visible_to = getattr(ctx, "visible_to", None) return await send_text( target_id, content, reply_to_id=reply_to_id, thread_id=thread_id, mention_ids=mention_ids, notification=notification, send_as=send_as, on_behalf_of=on_behalf_of, visible_to=visible_to, account_id=account_id, config=config, )