18c6d2b0dd77b38f487747aad1fcd1218aa8c356..f28ac0166536a2a4b68cac685a41ea667f60f7e9
2025-09-03 yj
兼容企业微信
f28ac0 对比 | 目录
2025-08-27 yj
更新
307236 对比 | 目录
7个文件已修改
231 ■■■■■ 已修改文件
app/api/friend_ignore.py 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/models/contact.py 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/services/contact_sync.py 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/services/friend_ignore_service.py 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/services/message_processor.py 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/services/silence_service.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
logs/app.log 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/friend_ignore.py
@@ -17,16 +17,19 @@
class AddFriendsRequest(BaseModel):
    """添加好友到忽略列表请求模型"""
    friends: List[str]
class RemoveFriendRequest(BaseModel):
    """从忽略列表移除好友请求模型"""
    w_id: str
class IgnoreListResponse(BaseModel):
    """忽略列表响应模型"""
    success: bool
    message: str
    data: Set[str] = None
@@ -37,19 +40,16 @@
async def get_ignore_list():
    """
    获取当前的好友忽略列表
    Returns:
        忽略列表响应
    """
    try:
        ignore_list = friend_ignore_service.get_ignore_list()
        count = friend_ignore_service.get_ignore_list_count()
        return IgnoreListResponse(
            success=True,
            message="获取忽略列表成功",
            data=ignore_list,
            count=count
            success=True, message="获取忽略列表成功", data=ignore_list, count=count
        )
    except Exception as e:
        logger.error(f"获取忽略列表失败: {str(e)}")
@@ -60,26 +60,26 @@
async def add_friends_to_ignore_list(request: AddFriendsRequest):
    """
    添加好友到忽略列表
    Args:
        request: 添加好友请求
    Returns:
        操作结果
    """
    try:
        success = friend_ignore_service.add_friends_to_ignore_list(request.friends)
        if success:
            count = friend_ignore_service.get_ignore_list_count()
            return IgnoreListResponse(
                success=True,
                message=f"成功添加 {len(request.friends)} 个好友到忽略列表",
                count=count
                count=count,
            )
        else:
            raise HTTPException(status_code=400, detail="添加好友到忽略列表失败")
    except Exception as e:
        logger.error(f"添加好友到忽略列表失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"添加好友到忽略列表失败: {str(e)}")
@@ -89,26 +89,26 @@
async def remove_friend_from_ignore_list(request: RemoveFriendRequest):
    """
    从忽略列表中移除好友
    Args:
        request: 移除好友请求
    Returns:
        操作结果
    """
    try:
        success = friend_ignore_service.remove_friend_from_ignore_list(request.w_id)
        if success:
            count = friend_ignore_service.get_ignore_list_count()
            return IgnoreListResponse(
                success=True,
                message=f"成功从忽略列表中移除好友: {request.w_id}",
                count=count
                count=count,
            )
        else:
            raise HTTPException(status_code=400, detail="从忽略列表移除好友失败")
    except Exception as e:
        logger.error(f"从忽略列表移除好友失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"从忽略列表移除好友失败: {str(e)}")
@@ -118,22 +118,18 @@
async def clear_ignore_list():
    """
    清空忽略列表
    Returns:
        操作结果
    """
    try:
        success = friend_ignore_service.clear_ignore_list()
        if success:
            return IgnoreListResponse(
                success=True,
                message="成功清空忽略列表",
                count=0
            )
            return IgnoreListResponse(success=True, message="成功清空忽略列表", count=0)
        else:
            raise HTTPException(status_code=400, detail="清空忽略列表失败")
    except Exception as e:
        logger.error(f"清空忽略列表失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"清空忽略列表失败: {str(e)}")
@@ -143,26 +139,24 @@
async def sync_contacts_and_rebuild_ignore_list():
    """
    重新同步联系人并重建忽略列表
    Returns:
        操作结果
    """
    try:
        if not settings.ecloud_w_id:
            raise HTTPException(status_code=400, detail="未配置ecloud_w_id")
        success = contact_sync_service.sync_contacts_on_startup(settings.ecloud_w_id)
        if success:
            count = friend_ignore_service.get_ignore_list_count()
            return IgnoreListResponse(
                success=True,
                message="联系人同步完成,忽略列表已重建",
                count=count
                success=True, message="联系人同步完成,忽略列表已重建", count=count
            )
        else:
            raise HTTPException(status_code=400, detail="联系人同步失败")
    except Exception as e:
        logger.error(f"联系人同步失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"联系人同步失败: {str(e)}")
@@ -185,7 +179,7 @@
        return {
            "success": True,
            "data": status_info,
            "message": f"w_id {w_id} 状态检查完成"
            "message": f"w_id {w_id} 状态检查完成",
        }
    except Exception as e:
@@ -208,7 +202,7 @@
            "success": True,
            "data": whitelist,
            "count": len(whitelist),
            "message": "获取白名单成功"
            "message": "获取白名单成功",
        }
    except Exception as e:
@@ -231,9 +225,9 @@
                "ignore_enabled": settings.friend_ignore_enabled,
                "whitelist": settings.friend_ignore_whitelist,
                "whitelist_count": len(settings.friend_ignore_whitelist),
                "ignore_list_count": friend_ignore_service.get_ignore_list_count()
                "ignore_list_count": friend_ignore_service.get_ignore_list_count(),
            },
            "message": "获取配置信息成功"
            "message": "获取配置信息成功",
        }
    except Exception as e:
app/models/contact.py
@@ -1,6 +1,7 @@
"""
联系人信息模型
"""
from sqlalchemy import Column, String, Integer, DateTime, Text
from sqlalchemy.sql import func
from .database import Base
@@ -8,10 +9,13 @@
class Contact(Base):
    """联系人信息表"""
    __tablename__ = "contacts"
    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    wc_id = Column(String(100), unique=True, index=True, nullable=False, comment="微信ID/群ID")
    wc_id = Column(
        String(100), unique=True, index=True, nullable=False, comment="微信ID/群ID"
    )
    user_name = Column(String(100), nullable=True, comment="微信用户名")
    nick_name = Column(String(100), nullable=True, comment="昵称")
    remark = Column(String(100), nullable=True, comment="备注")
@@ -23,9 +27,11 @@
    small_head = Column(String(500), nullable=True, comment="小头像URL")
    label_list = Column(Text, nullable=True, comment="标签列表")
    v1 = Column(String(200), nullable=True, comment="v1数据")
    work_wc_id = Column(String(100), nullable=True, comment="企业微信id")
    created_at = Column(DateTime, default=func.now(), comment="创建时间")
    updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
    updated_at = Column(
        DateTime, default=func.now(), onupdate=func.now(), comment="更新时间"
    )
    def __repr__(self):
        return f"<Contact(wc_id='{self.wc_id}', nick_name='{self.nick_name}')>"
app/services/contact_sync.py
@@ -133,6 +133,9 @@
                        existing_contact.small_head = contact_data.get("smallHead")
                        existing_contact.label_list = contact_data.get("labelList")
                        existing_contact.v1 = contact_data.get("v1")
                        # 只有当新的workWcId不为空时才更新
                        if contact_data.get("workWcId"):
                            existing_contact.work_wc_id = contact_data.get("workWcId")
                        updated_count += 1
                        logger.debug(f"更新联系人信息: wc_id={wc_id}, nick_name={contact_data.get('nickName')}")
                    else:
@@ -150,6 +153,7 @@
                            small_head=contact_data.get("smallHead"),
                            label_list=contact_data.get("labelList"),
                            v1=contact_data.get("v1"),
                            work_wc_id=contact_data.get("workWcId"),  # 添加企业微信ID
                        )
                        db.add(new_contact)
                        saved_count += 1
@@ -158,6 +162,10 @@
                # 提交事务
                db.commit()
                logger.info(f"联系人信息保存完成: 新增={saved_count}, 更新={updated_count}")
                # 同步联系人后更新Redis忽略列表
                self._update_redis_ignore_list(db)
                return True
        except Exception as e:
@@ -166,6 +174,45 @@
                db.rollback()
            return False
    def _update_redis_ignore_list(self, db: Session) -> bool:
        """
        同步联系人后更新Redis忽略列表
        将数据库中contacts表中work_wc_id不为空的记录的work_wc_id作为wc_id添加到Redis忽略列表
        Args:
            db: 数据库会话
        Returns:
            更新成功返回True,失败返回False
        """
        try:
            # 查询所有work_wc_id不为空且不为空字符串的联系人
            contacts_with_work_wc_id = db.query(Contact).filter(
                Contact.work_wc_id.isnot(None) & (Contact.work_wc_id != "")
            ).all()
            if not contacts_with_work_wc_id:
                logger.info("没有找到work_wc_id不为空的联系人")
                return True
            # 提取所有work_wc_id
            work_wc_ids = [contact.work_wc_id for contact in contacts_with_work_wc_id]
            logger.info(f"找到 {len(work_wc_ids)} 个work_wc_id不为空的联系人")
            # 将这些work_wc_id添加到Redis忽略列表
            success = friend_ignore_service.add_friends_to_ignore_list(work_wc_ids)
            if success:
                logger.info(f"成功将 {len(work_wc_ids)} 个work_wc_id添加到Redis忽略列表")
            else:
                logger.error("添加work_wc_id到Redis忽略列表失败")
            return success
        except Exception as e:
            logger.error(f"更新Redis忽略列表异常: error={str(e)}")
            return False
# 全局联系人同步服务实例
contact_sync_service = ContactSyncService()
app/services/friend_ignore_service.py
@@ -30,9 +30,14 @@
        """
        try:
            with next(get_db()) as db:
                contact = db.query(Contact).filter(Contact.nick_name == nickname).first()
                contact = (
                    db.query(Contact).filter(Contact.nick_name == nickname).first()
                )
                if contact:
                    return contact.wc_id
                    wc_id = contact.wc_id
                    if contact.work_wc_id:
                        wc_id += f",{contact.work_wc_id}"
                    return wc_id
                else:
                    logger.warning(f"未找到昵称为 '{nickname}' 的联系人")
                    return None
@@ -72,12 +77,9 @@
                logger.info("好友列表为空,无需添加到忽略列表")
                return True
            # 清空现有的忽略列表
            redis_queue.redis_client.delete(self.ignore_list_key)
            # 批量添加好友w_id到忽略列表
            # 批量添加好友w_id到忽略列表(不清空现有列表)
            redis_queue.redis_client.sadd(self.ignore_list_key, *friends)
            logger.info(f"已将 {len(friends)} 个好友添加到忽略列表")
            return True
@@ -111,21 +113,26 @@
            # 检查是否在白名单中(通过昵称)
            whitelist_wids = self._get_whitelist_wids()
            if w_id in whitelist_wids:
            if any(w_id in wids for wids in whitelist_wids):
                logger.info(f"w_id在白名单中,不忽略消息: w_id={w_id}")
                return False
            # 检查是否在忽略列表中
            is_in_ignore_list = redis_queue.redis_client.sismember(self.ignore_list_key, w_id)
            is_in_ignore_list = redis_queue.redis_client.sismember(
                self.ignore_list_key, w_id
            )
            if is_in_ignore_list:
                # 如果在忽略列表中,检查是否在测试群组中
                if group_id and silence_service.is_test_group(group_id):
                    logger.info(f"测试群组中的好友消息不被忽略: w_id={w_id}, group_id={group_id}")
                    logger.info(
                        f"测试群组中的好友消息不被忽略: w_id={w_id}, group_id={group_id}"
                    )
                    return False
                logger.info(f"w_id在忽略列表中,忽略消息: w_id={w_id}")
            return is_in_ignore_list
        except Exception as e:
@@ -233,7 +240,7 @@
                "in_ignore_list": False,
                "final_ignored": False,
                "reason": "",
                "whitelist_nicknames": settings.friend_ignore_whitelist
                "whitelist_nicknames": settings.friend_ignore_whitelist,
            }
            if not settings.friend_ignore_enabled:
@@ -244,7 +251,9 @@
                info["reason"] = "在白名单中,不会被忽略"
                return info
            info["in_ignore_list"] = redis_queue.redis_client.sismember(self.ignore_list_key, w_id)
            info["in_ignore_list"] = redis_queue.redis_client.sismember(
                self.ignore_list_key, w_id
            )
            if info["in_ignore_list"]:
                info["final_ignored"] = True
@@ -256,11 +265,7 @@
        except Exception as e:
            logger.error(f"获取忽略状态信息异常: w_id={w_id}, error={str(e)}")
            return {
                "w_id": w_id,
                "error": str(e),
                "final_ignored": False
            }
            return {"w_id": w_id, "error": str(e), "final_ignored": False}
# 全局好友忽略服务实例
app/services/message_processor.py
@@ -94,7 +94,7 @@
            # 异常情况下返回原始content
            return callback_data.get("data", {}).get("content", "")
    def parse_at_mentions(self, ai_answer: str) -> Tuple[str, List[str]]:
    def parse_at_mentions(self, ai_answer: str, from_user: str) -> Tuple[str, List[str]]:
        """
        解析AI回复中的@字符,提取客服名称
@@ -130,10 +130,17 @@
                            db.query(Contact).filter(Contact.nick_name == name).first()
                        )
                        if contact:
                            at_wc_ids.append(contact.wc_id)
                            logger.info(
                                f"找到客服联系人: name={name}, wc_id={contact.wc_id}"
                            )
                            # 如果from_user包含@openim表示是企业微信,使用work_wc_id
                            if "@openim" in from_user:
                                at_wc_ids.append(contact.work_wc_id)
                                logger.info(
                                    f"找到客服联系人-微信: name={name}, wc_id={contact.work_wc_id}"
                                )
                            else:
                                at_wc_ids.append(contact.wc_id)
                                logger.info(
                                    f"找到客服联系人-企业微信: name={name}, wc_id={contact.wc_id}"
                                )
                        else:
                            logger.warning(f"未找到客服联系人: name={name}")
@@ -371,12 +378,14 @@
                    return False
                # 3.2 获取群组中发言次数最多的用户昵称
                most_active_nickname = (
                    group_stats_service.get_most_active_user_nickname(from_group)
                )
                logger.info(
                    f"群组最活跃用户昵称: group={from_group}, nickname={most_active_nickname}"
                )
                # most_active_nickname = (
                #     group_stats_service.get_most_active_user_nickname(from_group)
                # )
                # logger.info(
                #     f"群组最活跃用户昵称: group={from_group}, nickname={most_active_nickname}"
                # )
                # 获取默认客服名称
                nickname = settings.customer_service_default_name
                # 3.3 获取用户在当前群组的conversation_id
                conversation_id = redis_queue.get_conversation_id(from_user, from_group)
@@ -386,7 +395,7 @@
                    query=content,
                    user=from_user,
                    conversation_id=conversation_id,
                    nick_name=most_active_nickname,
                    nick_name=nickname,
                )
                if silence_service.is_silence_active(from_group):
@@ -475,7 +484,7 @@
                success = False
                if ai_answer:
                    # 解析AI回复中的@字符
                    processed_answer, at_wc_ids = self.parse_at_mentions(ai_answer)
                    processed_answer, at_wc_ids = self.parse_at_mentions(ai_answer, from_user)
                    # 判断AI回复是否是结束字符串
                    is_end_str = self.is_end_str(ai_answer)
                    # 发送消息,最多重试3次
@@ -495,10 +504,15 @@
                                    )
                                else:
                                    # 如果该群组静默模式未激活,激活静默模式
                                    silence_service.activate_silence_mode(from_group)
                                    logger.info(
                                    flag = silence_service.activate_silence_mode(from_group)
                                    if flag:
                                        logger.info(
                                        f"AI回复@客服,群组静默模式已激活: fromUser={from_user}, fromGroup={from_group}"
                                    )
                                        )
                                    else:
                                        logger.info(
                                        f"AI回复@客服,未激活静默模式: fromUser={from_user}, fromGroup={from_group}"
                                        )
                                success = True
                                break
                        else:
app/services/silence_service.py
@@ -75,7 +75,7 @@
            # 检查是否为测试群组,如果是则不激活静默模式
            if self.is_test_group(group_id):
                logger.info(f"测试群组不激活静默模式: group_id={group_id}")
                return True
                return False
            if not settings.silence_mode_enabled:
                logger.debug("静默模式功能已禁用")
logs/app.log
@@ -1,3 +1,10 @@
2025-08-26 15:35:24 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-26 15:55:13 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-26 16:08:41 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 17:06:10 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 17:15:21 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 17:20:38 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 17:20:55 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 17:24:47 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 17:25:26 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 17:37:52 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 17:42:02 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 18:01:43 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务
2025-08-27 18:11:34 | INFO | __main__:<module>:132 - 启动E云管家-DifyAI对接服务