From f28ac0166536a2a4b68cac685a41ea667f60f7e9 Mon Sep 17 00:00:00 2001
From: yj <2077506045@qq.com>
Date: 星期三, 03 九月 2025 14:43:03 +0800
Subject: [PATCH] 兼容企业微信

---
 app/services/ecloud_client.py |  303 +++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 273 insertions(+), 30 deletions(-)

diff --git a/app/services/ecloud_client.py b/app/services/ecloud_client.py
index 5a76116..0a26cac 100644
--- a/app/services/ecloud_client.py
+++ b/app/services/ecloud_client.py
@@ -3,7 +3,7 @@
 """
 
 import requests
-from typing import Optional, Dict, Any
+from typing import Optional, Dict, Any, List
 from loguru import logger
 from config import settings
 
@@ -20,16 +20,40 @@
         self.session = requests.Session()
         self.session.headers.update(self.headers)
 
+    def _filter_keywords(self, content: str) -> str:
+        """
+        杩囨护娑堟伅鍐呭涓殑鍏抽敭璇�
+
+        Args:
+            content: 鍘熷娑堟伅鍐呭
+
+        Returns:
+            杩囨护鍚庣殑娑堟伅鍐呭
+        """
+        if not settings.keyword_filter_enabled or not settings.keyword_filter_keywords:
+            return content
+
+        filtered_content = content
+        for keyword in settings.keyword_filter_keywords:
+            if keyword in filtered_content:
+                filtered_content = filtered_content.replace(keyword, "")
+                logger.info(f"杩囨护鍏抽敭璇�: {keyword}")
+        # 鍘婚櫎鍓嶅悗绌烘牸
+        filtered_content = filtered_content.strip()
+        return filtered_content
+
     def get_contact_info(self, w_id: str, wc_id: str) -> Optional[Dict[str, Any]]:
         """
         鑾峰彇鑱旂郴浜轰俊鎭�
 
         Args:
             w_id: 鐧诲綍瀹炰緥鏍囪瘑
-            wc_id: 濂藉弸寰俊id/缇d
+            wc_id: 濂藉弸寰俊id/缇d锛屽涓娇鐢ㄨ嫳鏂囬�楀彿鍒嗛殧
 
         Returns:
-            鑱旂郴浜轰俊鎭瓧鍏革紝澶辫触杩斿洖None
+            鑱旂郴浜轰俊鎭瓧鍏告垨鍒楄〃锛屽け璐ヨ繑鍥濶one
+            - 鍗曚釜wcId鏃惰繑鍥炲瓧鍏�
+            - 澶氫釜wcId鏃惰繑鍥炲垪琛�
         """
         try:
             url = f"{self.base_url}/getContact"
@@ -45,8 +69,14 @@
             if result.get("code") == "1000":
                 contact_data = result.get("data", [])
                 if contact_data and len(contact_data) > 0:
-                    logger.info(f"鎴愬姛鑾峰彇鑱旂郴浜轰俊鎭�: wcId={wc_id}")
-                    return contact_data[0]  # 杩斿洖绗竴涓仈绯讳汉淇℃伅
+                    # 濡傛灉鏄崟涓獁cId锛岃繑鍥炵涓�涓仈绯讳汉淇℃伅
+                    if "," not in wc_id:
+                        logger.info(f"鎴愬姛鑾峰彇鑱旂郴浜轰俊鎭�: wcId={wc_id}")
+                        return contact_data[0]
+                    else:
+                        # 濡傛灉鏄涓獁cId锛岃繑鍥炲畬鏁村垪琛�
+                        logger.info(f"鎴愬姛鑾峰彇鎵归噺鑱旂郴浜轰俊鎭�: count={len(contact_data)}")
+                        return contact_data
                 else:
                     logger.warning(f"鑱旂郴浜轰俊鎭负绌�: wcId={wc_id}")
                     return None
@@ -63,7 +93,7 @@
             logger.error(f"鑾峰彇鑱旂郴浜轰俊鎭紓甯�: wcId={wc_id}, error={str(e)}")
             return None
 
-    def send_text_message(self, w_id: str, wc_id: str, content: str) -> bool:
+    def send_text_message(self, w_id: str, wc_id: str, content: str, max_retries: int = None) -> bool:
         """
         鍙戦�佹枃鏈秷鎭�
 
@@ -71,38 +101,58 @@
             w_id: 鐧诲綍瀹炰緥鏍囪瘑
             wc_id: 鎺ユ敹浜哄井淇d/缇d
             content: 鏂囨湰鍐呭娑堟伅
+            max_retries: 鏈�澶ч噸璇曟鏁�
 
         Returns:
             鍙戦�佹垚鍔熻繑鍥濼rue锛屽け璐ヨ繑鍥濬alse
         """
-        try:
-            url = f"{self.base_url}/sendText"
-            payload = {"wId": w_id, "wcId": wc_id, "content": content}
+        # 杩囨护鍏抽敭璇�
+        filtered_content = self._filter_keywords(content)
 
-            logger.info(
-                f"鍙戦�佹枃鏈秷鎭�: wId={w_id}, wcId={wc_id}, content_length={len(content)}"
-            )
+        if max_retries is None:
+            from config import settings
+            max_retries = settings.max_retry_count
 
-            response = self.session.post(url, json=payload, timeout=30)
-            response.raise_for_status()
+        retry_count = 0
+        while retry_count <= max_retries:
+            try:
+                url = f"{self.base_url}/sendText"
+                payload = {"wId": w_id, "wcId": wc_id, "content": filtered_content}
 
-            result = response.json()
-
-            if result.get("code") == "1000":
-                logger.info(f"鏂囨湰娑堟伅鍙戦�佹垚鍔�: wcId={wc_id}")
-                return True
-            else:
-                logger.error(
-                    f"鏂囨湰娑堟伅鍙戦�佸け璐�: wcId={wc_id}, code={result.get('code')}, message={result.get('message')}"
+                logger.info(
+                    f"鍙戦�佹枃鏈秷鎭�: wId={w_id}, wcId={wc_id}, content_length={len(filtered_content)}, retry={retry_count}"
                 )
-                return False
 
-        except requests.exceptions.RequestException as e:
-            logger.error(f"鍙戦�佹枃鏈秷鎭綉缁滈敊璇�: wcId={wc_id}, error={str(e)}")
-            return False
-        except Exception as e:
-            logger.error(f"鍙戦�佹枃鏈秷鎭紓甯�: wcId={wc_id}, error={str(e)}")
-            return False
+                response = self.session.post(url, json=payload, timeout=30)
+                response.raise_for_status()
+
+                result = response.json()
+
+                if result.get("code") == "1000":
+                    logger.info(f"鏂囨湰娑堟伅鍙戦�佹垚鍔�: wcId={wc_id}")
+                    return True
+                else:
+                    logger.error(
+                        f"鏂囨湰娑堟伅鍙戦�佸け璐�: wcId={wc_id}, code={result.get('code')}, message={result.get('message')}"
+                    )
+
+            except requests.exceptions.RequestException as e:
+                logger.error(f"鍙戦�佹枃鏈秷鎭綉缁滈敊璇�: wcId={wc_id}, retry={retry_count}, error={str(e)}")
+            except Exception as e:
+                logger.error(f"鍙戦�佹枃鏈秷鎭紓甯�: wcId={wc_id}, retry={retry_count}, error={str(e)}")
+            
+            retry_count += 1
+            if retry_count <= max_retries:
+                from config import settings
+                wait_time = settings.retry_delay * retry_count
+                logger.info(f"绛夊緟閲嶈瘯: wcId={wc_id}, wait_time={wait_time}s")
+                import time
+                time.sleep(wait_time)
+
+        logger.error(
+            f"鏂囨湰娑堟伅鍙戦�佸け璐ワ紝宸茶揪鏈�澶ч噸璇曟鏁�: wcId={wc_id}, max_retries={max_retries}"
+        )
+        return False
 
     def send_group_message(self, w_id: str, group_id: str, content: str) -> bool:
         """
@@ -116,7 +166,200 @@
         Returns:
             鍙戦�佹垚鍔熻繑鍥濼rue锛屽け璐ヨ繑鍥濬alse
         """
-        return self.send_text_message(w_id, group_id, content)
+        # 杩囨护鍏抽敭璇�
+        filtered_content = self._filter_keywords(content)
+        return self.send_text_message(w_id, group_id, filtered_content)
+
+    def init_address_list(self, w_id: str) -> bool:
+        """
+        鍒濆鍖栭�氳褰曞垪琛�
+
+        Args:
+            w_id: 鐧诲綍瀹炰緥鏍囪瘑
+
+        Returns:
+            鍒濆鍖栨垚鍔熻繑鍥濼rue锛屽け璐ヨ繑鍥濬alse
+        """
+        try:
+            url = f"{self.base_url}/initAddressList"
+            payload = {"wId": w_id}
+
+            logger.info(f"鍒濆鍖栭�氳褰曞垪琛�: wId={w_id}")
+
+            response = self.session.post(url, json=payload, timeout=30)
+            response.raise_for_status()
+
+            result = response.json()
+
+            if result.get("code") == "1000":
+                logger.info(f"鍒濆鍖栭�氳褰曞垪琛ㄦ垚鍔�: wId={w_id}")
+                return True
+            else:
+                logger.error(
+                    f"鍒濆鍖栭�氳褰曞垪琛ㄥけ璐�: wId={w_id}, code={result.get('code')}, message={result.get('message')}"
+                )
+                return False
+
+        except requests.exceptions.RequestException as e:
+            logger.error(f"鍒濆鍖栭�氳褰曞垪琛ㄧ綉缁滈敊璇�: wId={w_id}, error={str(e)}")
+            return False
+        except Exception as e:
+            logger.error(f"鍒濆鍖栭�氳褰曞垪琛ㄥ紓甯�: wId={w_id}, error={str(e)}")
+            return False
+
+    def get_address_list(self, w_id: str) -> Optional[Dict[str, Any]]:
+        """
+        鑾峰彇閫氳褰曞垪琛�
+
+        Args:
+            w_id: 鐧诲綍瀹炰緥鏍囪瘑
+
+        Returns:
+            閫氳褰曟暟鎹瓧鍏革紝澶辫触杩斿洖None
+            杩斿洖鏍煎紡: {
+                "chatrooms": [...],  # 缇ょ粍鍒楄〃
+                "friends": [...],    # 濂藉弸鍒楄〃
+                "ghs": [...],        # 鍏紬鍙峰垪琛�
+                "others": [...]      # 鍏朵粬
+            }
+        """
+        try:
+            url = f"{self.base_url}/getAddressList"
+            payload = {"wId": w_id}
+
+            logger.info(f"鑾峰彇閫氳褰曞垪琛�: wId={w_id}")
+
+            response = self.session.post(url, json=payload, timeout=30)
+            response.raise_for_status()
+
+            result = response.json()
+
+            if result.get("code") == "1000":
+                address_data = result.get("data", {})
+                logger.info(f"鎴愬姛鑾峰彇閫氳褰曞垪琛�: wId={w_id}, friends_count={len(address_data.get('friends', []))}")
+                return address_data
+            else:
+                logger.error(
+                    f"鑾峰彇閫氳褰曞垪琛ㄥけ璐�: wId={w_id}, code={result.get('code')}, message={result.get('message')}"
+                )
+                return None
+
+        except requests.exceptions.RequestException as e:
+            logger.error(f"鑾峰彇閫氳褰曞垪琛ㄧ綉缁滈敊璇�: wId={w_id}, error={str(e)}")
+            return None
+        except Exception as e:
+            logger.error(f"鑾峰彇閫氳褰曞垪琛ㄥ紓甯�: wId={w_id}, error={str(e)}")
+            return None
+
+    def send_group_at_message(self, w_id: str, wc_id: str, content: str, at_wc_ids: List[str], max_retries: int = None) -> bool:
+        """
+        鍙戦�佺兢鑱夽娑堟伅
+
+        Args:
+            w_id: 鐧诲綍瀹炰緥鏍囪瘑
+            wc_id: 鎺ユ敹鏂圭兢id
+            content: 鏂囨湰鍐呭娑堟伅锛園鐨勫井淇℃樀绉伴渶瑕佽嚜宸辨嫾鎺ワ紝蹇呴』鎷兼帴鑹剧壒绗﹀彿锛屼笉鐒朵笉鐢熸晥锛�
+            at_wc_ids: 鑹剧壒鐨勫井淇d鍒楄〃
+            max_retries: 鏈�澶ч噸璇曟鏁�
+
+        Returns:
+            鍙戦�佹垚鍔熻繑鍥濼rue锛屽け璐ヨ繑鍥濬alse
+        """
+        # 杩囨护鍏抽敭璇�
+        filtered_content = self._filter_keywords(content)
+
+        if max_retries is None:
+            from config import settings
+            max_retries = settings.max_retry_count
+
+        retry_count = 0
+        while retry_count <= max_retries:
+            try:
+                url = f"{self.base_url}/sendText"
+                # 灏哸t_wc_ids鍒楄〃鐢ㄩ�楀彿鎷兼帴
+                at_str = ",".join(at_wc_ids)
+                payload = {
+                    "wId": w_id,
+                    "wcId": wc_id,
+                    "content": filtered_content,
+                    "at": at_str
+                }
+
+                logger.info(
+                    f"鍙戦�佺兢鑱夽娑堟伅: wId={w_id}, wcId={wc_id}, at={at_str}, content_length={len(filtered_content)}, retry={retry_count}"
+                )
+
+                response = self.session.post(url, json=payload, timeout=30)
+                response.raise_for_status()
+
+                result = response.json()
+
+                if result.get("code") == "1000":
+                    logger.info(f"缇よ亰@娑堟伅鍙戦�佹垚鍔�: wcId={wc_id}, at={at_str}")
+                    return True
+                else:
+                    logger.error(
+                        f"缇よ亰@娑堟伅鍙戦�佸け璐�: wcId={wc_id}, at={at_str}, code={result.get('code')}, message={result.get('message')}"
+                    )
+
+            except requests.exceptions.RequestException as e:
+                logger.error(f"鍙戦�佺兢鑱夽娑堟伅缃戠粶閿欒: wcId={wc_id}, at={at_str}, retry={retry_count}, error={str(e)}")
+            except Exception as e:
+                logger.error(f"鍙戦�佺兢鑱夽娑堟伅寮傚父: wcId={wc_id}, at={at_str}, retry={retry_count}, error={str(e)}")
+
+            retry_count += 1
+            if retry_count <= max_retries:
+                from config import settings
+                wait_time = settings.retry_delay * retry_count
+                logger.info(f"绛夊緟閲嶈瘯: wcId={wc_id}, wait_time={wait_time}s")
+                import time
+                time.sleep(wait_time)
+
+        logger.error(
+            f"缇よ亰@娑堟伅鍙戦�佸け璐ワ紝宸茶揪鏈�澶ч噸璇曟鏁�: wcId={wc_id}, at={at_str}, max_retries={max_retries}"
+        )
+        return False
+
+    def query_online_wechat_list(self) -> Optional[List[Dict[str, str]]]:
+        """
+        鏌ヨ璐﹀彿涓湪绾跨殑寰俊鍒楄〃
+
+        Returns:
+            鍦ㄧ嚎寰俊鍒楄〃锛屾瘡涓厓绱犲寘鍚玾cId鍜寃Id锛屽け璐ヨ繑鍥濶one
+            杩斿洖鏍煎紡: [
+                {
+                    "wcId": "wxid_i6qsbbjenju2",
+                    "wId": "72223018-7f2a-4f4f-bfa3-26e47dbd61"
+                }
+            ]
+        """
+        try:
+            url = f"{self.base_url}/queryLoginWx"
+            payload = {}
+
+            logger.info("鏌ヨ鍦ㄧ嚎寰俊鍒楄〃")
+
+            response = self.session.post(url, json=payload, timeout=30)
+            response.raise_for_status()
+
+            result = response.json()
+
+            if result.get("code") == "1000":
+                online_list = result.get("data", [])
+                logger.info(f"鎴愬姛鏌ヨ鍦ㄧ嚎寰俊鍒楄〃: count={len(online_list)}")
+                return online_list
+            else:
+                logger.warning(
+                    f"鏌ヨ鍦ㄧ嚎寰俊鍒楄〃澶辫触: code={result.get('code')}, message={result.get('message')}"
+                )
+                return []
+
+        except requests.exceptions.RequestException as e:
+            logger.error(f"鏌ヨ鍦ㄧ嚎寰俊鍒楄〃缃戠粶閿欒: error={str(e)}")
+            return None
+        except Exception as e:
+            logger.error(f"鏌ヨ鍦ㄧ嚎寰俊鍒楄〃寮傚父: error={str(e)}")
+            return None
 
 
 # 鍏ㄥ眬E浜戠瀹跺鎴风瀹炰緥

--
Gitblit v1.9.1