import base64 import os from typing import Union import aiofiles from wechat_ipad.models.friend_circle_info import build_friend_circle_xml from wechat_ipad.providers.server_864.base import Server864APIClientBase class FriendCircleMixin(Server864APIClientBase): """864 朋友圈相关接口。""" async def get_friend_circle_list(self, max_id: int = 0, first_page_md5: str = "") -> dict: data = await self._request_data( "post", "/sns/SendSnsTimeLine", json_body={"UserName": "", "FirstPageMD5": first_page_md5, "MaxID": int(max_id)}, timeout=30, ) return dict(data or {}) async def get_friend_circle_detail(self, towxid: str, max_id: int = 0, first_page_md5: str = "") -> dict: data = await self._request_data( "post", "/sns/SendSnsUserPage", json_body={"UserName": towxid, "FirstPageMD5": first_page_md5, "MaxID": int(max_id)}, timeout=30, ) return dict(data or {}) async def get_friend_circle_id_detail(self, object_id: Union[str, int], towxid: str = "") -> dict: del towxid data = await self._request_data( "post", "/sns/SendSnsObjectDetailById", json_body={"Id": str(object_id), "BlackList": [], "LocationVal": 0, "Location": {}}, timeout=30, ) return dict(data or {}) async def publish_friend_circle( self, content: str, media_items: list[dict] | None = None, blacklist: str = "", with_user_list: str = "", ) -> dict: payload = { "ContentStyle": 1 if media_items else 0, "ContentUrl": "", "Description": "", "Privacy": 0, "Content": content, "MediaList": list(media_items or []), "WithUserList": [item for item in str(with_user_list or "").split(",") if item], "GroupUserList": [], "BlackList": [item for item in str(blacklist or "").split(",") if item], "LocationInfo": None, } data = await self._request_data("post", "/sns/SendFriendCircle", json_body=payload, timeout=60) return dict(data or {}) async def friend_circle_comment( self, object_id: str, content: str = "", type: int = 2, reply_comment_id: int = 0, ) -> dict: payload = { "SnsCommentList": [{ "OpType": int(type), "ItemID": str(object_id), "ToUserName": "", "Content": content, "CreateTime": 0, "ReplyCommentID": int(reply_comment_id), "ReplyItem": {"UserName": "", "NickName": "", "OpType": 0, "Source": 0}, }], "Tx": False, } data = await self._request_data("post", "/sns/SendSnsComment", json_body=payload, timeout=30) return {"result": data} async def friend_circle_operation(self, object_id: str, type: int, comment_id: int = 0) -> dict: payload = { "SnsObjectOpList": [{ "SnsObjID": str(object_id), "OpType": int(type), "DataLen": 0, "Data": [], "Ext": int(comment_id), }] } data = await self._request_data("post", "/sns/SendSnsObjectOp", json_body=payload, timeout=30) return {"result": data} async def upload_friend_circle_media(self, media: Union[str, bytes, os.PathLike]) -> dict: if isinstance(media, str): media_base64 = media.split(",", 1)[1] if "," in media else media elif isinstance(media, bytes): media_base64 = base64.b64encode(media).decode() elif isinstance(media, os.PathLike): async with aiofiles.open(media, "rb") as f: media_base64 = base64.b64encode(await f.read()).decode() else: raise ValueError("media should be str, bytes, or path") data = await self._request_data( "post", "/sns/UploadFriendCircleImage", json_body={"ImageDataList": [media_base64], "VideoDataList": []}, timeout=60, ) return {"result": data} async def publish_friend_circle_by_xml(self, content: str, media_items: list[dict] | None = None) -> dict: """为后续扩展保留 XML 版本朋友圈发送入口。""" xml_content = build_friend_circle_xml(self.wxid, content, media_items=media_items) data = await self._request_data("post", "/sns/SendFriendCircleByXMl", json_body=xml_content, timeout=60) return {"result": data}