feat(群级配置): 新增MySQL+Redis持久缓存并接入进群欢迎差异化配置
新增群级插件配置表与服务层,采用MySQL持久化+Redis长期缓存(TTL=-1);后台新增群级插件配置管理页面与API,支持按群按插件维护JSON配置并在修改后同步回填MySQL和刷新Redis;已将群成员变更监控插件接入该配置,支持欢迎文案与卡片URL等按群差异化。
This commit is contained in:
@@ -6,10 +6,11 @@ from base.plugin_common.message_plugin_interface import MessagePluginInterface
|
||||
from base.plugin_common.plugin_interface import PluginStatus
|
||||
from db.connection import DBConnectionManager
|
||||
from db.contacts_db import ContactsDBOperator
|
||||
from utils.group_plugin_config_service import GroupPluginConfigService
|
||||
from utils.robot_cmd.robot_command import PermissionStatus, GroupBotManager
|
||||
from utils.wechat.contact_manager import ContactManager
|
||||
from wechat_ipad import WechatAPIClient
|
||||
from wechat_ipad.models.appmsg_xml import LINK_XML_WELCOME
|
||||
from wechat_ipad.models.appmsg_xml import LINK_XML_NORMAL, LINK_XML_WELCOME
|
||||
|
||||
|
||||
class GroupMemberChangePlugin(MessagePluginInterface):
|
||||
@@ -62,6 +63,10 @@ class GroupMemberChangePlugin(MessagePluginInterface):
|
||||
def initialize(self, context: Dict[str, Any]) -> bool:
|
||||
"""初始化插件"""
|
||||
self.LOG.debug(f"正在初始化 {self.name} 插件...")
|
||||
# 注入群级插件配置服务(由机器人系统上下文提供):
|
||||
# 1. 优先通过该服务读取“按群差异化”的欢迎文案与卡片配置;
|
||||
# 2. 未配置时保持原有默认欢迎行为,确保兼容老群。
|
||||
self.group_plugin_config_service: Optional[GroupPluginConfigService] = context.get("group_plugin_config_service")
|
||||
|
||||
self.LOG.debug(f"{self.name} 插件初始化完成")
|
||||
return True
|
||||
@@ -157,9 +162,23 @@ class GroupMemberChangePlugin(MessagePluginInterface):
|
||||
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
member_wxids = [wxid]
|
||||
await bot.send_at_message(roomid, f"👏欢迎 {nickname} 加入群聊!🎉", member_wxids)
|
||||
members = await bot.get_chatroom_member_detail(wxid, roomid)
|
||||
head_url = members.get("SmallHeadImgUrl") or members.get("BigHeadImgUrl") or ""
|
||||
welcome_cfg = self._get_group_welcome_config(roomid)
|
||||
variables = {
|
||||
"nickname": nickname,
|
||||
"wxid": wxid,
|
||||
"group_id": roomid,
|
||||
"now": now,
|
||||
"head_url": head_url,
|
||||
}
|
||||
# 文本欢迎:支持后台按群关闭和模板自定义。
|
||||
if bool(welcome_cfg.get("welcome_text_enabled", True)):
|
||||
welcome_text = self._safe_format(
|
||||
welcome_cfg.get("welcome_text_template", "👏欢迎 {nickname} 加入群聊!🎉"),
|
||||
variables
|
||||
)
|
||||
await bot.send_at_message(roomid, welcome_text, member_wxids)
|
||||
try:
|
||||
# 更新联系人信息
|
||||
ContactManager.get_instance().update_head_image(wxid, head_url)
|
||||
@@ -170,9 +189,14 @@ class GroupMemberChangePlugin(MessagePluginInterface):
|
||||
contact_db.save_chatroom_member_simple(roomid, member_details)
|
||||
except Exception as e:
|
||||
self.LOG.warning(f"新增群员信息失败: {e}")
|
||||
xml_content = f"{LINK_XML_WELCOME}".format(nickname=nickname, now=now, head_url=head_url)
|
||||
|
||||
await bot.send_link_xml_message(xml_content, roomid)
|
||||
# 欢迎卡片:支持后台按群关闭,且可配置标题/描述/URL/缩略图。
|
||||
if bool(welcome_cfg.get("welcome_card_enabled", True)):
|
||||
if self.group_plugin_config_service:
|
||||
xml_content = self._build_custom_welcome_card_xml(welcome_cfg, variables)
|
||||
else:
|
||||
# 老流程兼容:当未接入配置服务时沿用原模板。
|
||||
xml_content = f"{LINK_XML_WELCOME}".format(nickname=nickname, now=now, head_url=head_url)
|
||||
await bot.send_link_xml_message(xml_content, roomid)
|
||||
return True, "已发送进群欢迎语"
|
||||
return False, "无需执行"
|
||||
|
||||
@@ -254,3 +278,52 @@ class GroupMemberChangePlugin(MessagePluginInterface):
|
||||
self.LOG.warning(f"解析新成员信息失败: {e}")
|
||||
|
||||
return new_members
|
||||
|
||||
@staticmethod
|
||||
def _safe_format(template: str, variables: Dict[str, Any]) -> str:
|
||||
"""安全格式化模板,缺失变量时保留原占位符。"""
|
||||
text = str(template or "")
|
||||
for key, value in (variables or {}).items():
|
||||
text = text.replace(f"{{{key}}}", str(value or ""))
|
||||
return text
|
||||
|
||||
def _get_group_welcome_config(self, group_id: str) -> Dict[str, Any]:
|
||||
"""读取群级欢迎配置,未配置时返回默认值。"""
|
||||
default_cfg = {
|
||||
"welcome_text_enabled": True,
|
||||
"welcome_text_template": "👏欢迎 {nickname} 加入群聊!🎉",
|
||||
"welcome_card_enabled": True,
|
||||
"card_title_template": "👏欢迎 {nickname} 加入群聊!🎉",
|
||||
"card_desc_template": "⌚时间:{now}",
|
||||
"card_url": "https://newsnow.busiyi.world/",
|
||||
"card_thumb_url": "{head_url}",
|
||||
}
|
||||
if not self.group_plugin_config_service:
|
||||
return default_cfg
|
||||
try:
|
||||
cfg = self.group_plugin_config_service.get_config(
|
||||
group_id=group_id,
|
||||
plugin_name=self.name,
|
||||
config_key="welcome",
|
||||
default=default_cfg,
|
||||
)
|
||||
if not isinstance(cfg, dict):
|
||||
return default_cfg
|
||||
# 默认值兜底,避免后台只配置部分字段时缺项。
|
||||
return {**default_cfg, **cfg}
|
||||
except Exception as e:
|
||||
self.LOG.warning(f"读取群级欢迎配置失败,回退默认配置: group={group_id}, error={e}")
|
||||
return default_cfg
|
||||
|
||||
def _build_custom_welcome_card_xml(self, cfg: Dict[str, Any], variables: Dict[str, Any]) -> str:
|
||||
"""根据群级配置构建欢迎卡片 XML。"""
|
||||
title = self._safe_format(cfg.get("card_title_template", ""), variables)
|
||||
desc = self._safe_format(cfg.get("card_desc_template", ""), variables)
|
||||
url = self._safe_format(cfg.get("card_url", ""), variables)
|
||||
thumb_url = self._safe_format(cfg.get("card_thumb_url", ""), variables)
|
||||
return LINK_XML_NORMAL.format(
|
||||
title=title,
|
||||
des=desc,
|
||||
url=url,
|
||||
thumburl=thumb_url,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user