调整 message_util 的发送text方法名,方便替换wcf.send_text。加入了虚拟群组管理功能;
This commit is contained in:
282
admin/dashboard/blueprints/virtual_group.py
Normal file
282
admin/dashboard/blueprints/virtual_group.py
Normal file
@@ -0,0 +1,282 @@
|
||||
import logging
|
||||
import uuid
|
||||
from flask import Blueprint, jsonify, request, current_app, render_template
|
||||
from .auth import login_required
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
virtual_group_bp = Blueprint('virtual_group', __name__)
|
||||
|
||||
# 添加虚拟群组管理视图
|
||||
@virtual_group_bp.route('/virtual_group')
|
||||
@login_required
|
||||
def virtual_group():
|
||||
return render_template('virtual_group_management.html')
|
||||
|
||||
@virtual_group_bp.route('/api/virtual_groups', methods=['GET'])
|
||||
@login_required
|
||||
def get_virtual_groups():
|
||||
"""获取所有虚拟群组"""
|
||||
try:
|
||||
server = current_app.dashboard_server
|
||||
redis_client = server.db_manager.get_redis_connection()
|
||||
|
||||
# 从Redis获取虚拟群组数据
|
||||
chat_groups_json = redis_client.get("group_virtual:chat_groups")
|
||||
if not chat_groups_json:
|
||||
return jsonify({"success": True, "data": {"chatGroups": []}})
|
||||
|
||||
import json
|
||||
chat_groups = json.loads(chat_groups_json)
|
||||
|
||||
# 确保群组名称是最新的
|
||||
for chat_group in chat_groups.get("chatGroups", []):
|
||||
for group in chat_group.get("groups", []):
|
||||
group_id = group.get("id")
|
||||
if group_id:
|
||||
group_name = server.contact_manager.get_nickname(group_id)
|
||||
if group_name:
|
||||
group["name"] = group_name
|
||||
|
||||
return jsonify({"success": True, "data": chat_groups})
|
||||
except Exception as e:
|
||||
logger.error(f"获取虚拟群组失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@virtual_group_bp.route('/api/virtual_groups', methods=['POST'])
|
||||
@login_required
|
||||
def create_virtual_group():
|
||||
"""创建新的虚拟群组"""
|
||||
try:
|
||||
server = current_app.dashboard_server
|
||||
redis_client = server.db_manager.get_redis_connection()
|
||||
data = request.json
|
||||
|
||||
group_name = data.get('name')
|
||||
if not group_name:
|
||||
return jsonify({"success": False, "error": "群组名称不能为空"}), 400
|
||||
|
||||
# 从Redis获取现有虚拟群组
|
||||
import json
|
||||
chat_groups_json = redis_client.get("group_virtual:chat_groups")
|
||||
if chat_groups_json:
|
||||
chat_groups = json.loads(chat_groups_json)
|
||||
else:
|
||||
chat_groups = {"chatGroups": []}
|
||||
|
||||
# 创建新的虚拟群组
|
||||
new_group = {
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": group_name,
|
||||
"groups": []
|
||||
}
|
||||
|
||||
chat_groups["chatGroups"].append(new_group)
|
||||
|
||||
# 保存回Redis
|
||||
redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False))
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": f"虚拟群组 {group_name} 创建成功",
|
||||
"data": new_group
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"创建虚拟群组失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@virtual_group_bp.route('/api/virtual_groups/<group_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def delete_virtual_group(group_id):
|
||||
"""删除虚拟群组"""
|
||||
try:
|
||||
server = current_app.dashboard_server
|
||||
redis_client = server.db_manager.get_redis_connection()
|
||||
|
||||
# 从Redis获取现有虚拟群组
|
||||
import json
|
||||
chat_groups_json = redis_client.get("group_virtual:chat_groups")
|
||||
if not chat_groups_json:
|
||||
return jsonify({"success": False, "error": "虚拟群组不存在"}), 404
|
||||
|
||||
chat_groups = json.loads(chat_groups_json)
|
||||
|
||||
# 查找并删除指定的虚拟群组
|
||||
found = False
|
||||
for i, group in enumerate(chat_groups.get("chatGroups", [])):
|
||||
if group.get("id") == group_id:
|
||||
del chat_groups["chatGroups"][i]
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
return jsonify({"success": False, "error": "虚拟群组不存在"}), 404
|
||||
|
||||
# 保存回Redis
|
||||
redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False))
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "虚拟群组删除成功"
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"删除虚拟群组失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@virtual_group_bp.route('/api/virtual_groups/<group_id>/groups', methods=['POST'])
|
||||
@login_required
|
||||
def add_group_to_virtual(group_id):
|
||||
"""向虚拟群组添加微信群"""
|
||||
try:
|
||||
server = current_app.dashboard_server
|
||||
redis_client = server.db_manager.get_redis_connection()
|
||||
data = request.json
|
||||
|
||||
wx_group_id = data.get('wx_group_id')
|
||||
if not wx_group_id:
|
||||
return jsonify({"success": False, "error": "微信群ID不能为空"}), 400
|
||||
|
||||
# 检查微信群是否存在
|
||||
group_name = server.contact_manager.get_nickname(wx_group_id)
|
||||
if not group_name:
|
||||
return jsonify({"success": False, "error": "微信群不存在或未被机器人识别"}), 404
|
||||
|
||||
# 从Redis获取现有虚拟群组
|
||||
import json
|
||||
chat_groups_json = redis_client.get("group_virtual:chat_groups")
|
||||
if not chat_groups_json:
|
||||
return jsonify({"success": False, "error": "虚拟群组不存在"}), 404
|
||||
|
||||
chat_groups = json.loads(chat_groups_json)
|
||||
|
||||
# 查找指定的虚拟群组
|
||||
found = False
|
||||
for chat_group in chat_groups.get("chatGroups", []):
|
||||
if chat_group.get("id") == group_id:
|
||||
# 检查群是否已在虚拟群组中
|
||||
for group in chat_group.get("groups", []):
|
||||
if group.get("id") == wx_group_id:
|
||||
return jsonify({"success": False, "error": "该微信群已在虚拟群组中"}), 400
|
||||
|
||||
# 添加微信群到虚拟群组
|
||||
chat_group["groups"].append({
|
||||
"id": wx_group_id,
|
||||
"name": group_name
|
||||
})
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
return jsonify({"success": False, "error": "虚拟群组不存在"}), 404
|
||||
|
||||
# 保存回Redis
|
||||
redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False))
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": f"微信群 {group_name} 已添加到虚拟群组",
|
||||
"data": {
|
||||
"id": wx_group_id,
|
||||
"name": group_name
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"向虚拟群组添加微信群失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@virtual_group_bp.route('/api/virtual_groups/<group_id>/groups/<wx_group_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def remove_group_from_virtual(group_id, wx_group_id):
|
||||
"""从虚拟群组移除微信群"""
|
||||
try:
|
||||
server = current_app.dashboard_server
|
||||
redis_client = server.db_manager.get_redis_connection()
|
||||
|
||||
# 从Redis获取现有虚拟群组
|
||||
import json
|
||||
chat_groups_json = redis_client.get("group_virtual:chat_groups")
|
||||
if not chat_groups_json:
|
||||
return jsonify({"success": False, "error": "虚拟群组不存在"}), 404
|
||||
|
||||
chat_groups = json.loads(chat_groups_json)
|
||||
|
||||
# 查找指定的虚拟群组和微信群
|
||||
found_group = False
|
||||
found_wx_group = False
|
||||
for chat_group in chat_groups.get("chatGroups", []):
|
||||
if chat_group.get("id") == group_id:
|
||||
found_group = True
|
||||
for i, group in enumerate(chat_group.get("groups", [])):
|
||||
if group.get("id") == wx_group_id:
|
||||
del chat_group["groups"][i]
|
||||
found_wx_group = True
|
||||
break
|
||||
break
|
||||
|
||||
if not found_group:
|
||||
return jsonify({"success": False, "error": "虚拟群组不存在"}), 404
|
||||
|
||||
if not found_wx_group:
|
||||
return jsonify({"success": False, "error": "该微信群不在虚拟群组中"}), 404
|
||||
|
||||
# 保存回Redis
|
||||
redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False))
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "微信群已从虚拟群组移除"
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"从虚拟群组移除微信群失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@virtual_group_bp.route('/api/virtual_groups/<group_id>', methods=['PUT'])
|
||||
@login_required
|
||||
def update_virtual_group(group_id):
|
||||
"""更新虚拟群组信息"""
|
||||
try:
|
||||
server = current_app.dashboard_server
|
||||
redis_client = server.db_manager.get_redis_connection()
|
||||
data = request.json
|
||||
|
||||
group_name = data.get('name')
|
||||
if not group_name:
|
||||
return jsonify({"success": False, "error": "群组名称不能为空"}), 400
|
||||
|
||||
# 从Redis获取现有虚拟群组
|
||||
import json
|
||||
chat_groups_json = redis_client.get("group_virtual:chat_groups")
|
||||
if not chat_groups_json:
|
||||
return jsonify({"success": False, "error": "虚拟群组不存在"}), 404
|
||||
|
||||
chat_groups = json.loads(chat_groups_json)
|
||||
|
||||
# 查找并更新指定的虚拟群组
|
||||
found = False
|
||||
for chat_group in chat_groups.get("chatGroups", []):
|
||||
if chat_group.get("id") == group_id:
|
||||
chat_group["name"] = group_name
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
return jsonify({"success": False, "error": "虚拟群组不存在"}), 404
|
||||
|
||||
# 保存回Redis
|
||||
redis_client.set("group_virtual:chat_groups", json.dumps(chat_groups, ensure_ascii=False))
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "虚拟群组更新成功",
|
||||
"data": {
|
||||
"id": group_id,
|
||||
"name": group_name
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"更新虚拟群组失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
@@ -128,11 +128,10 @@ class DashboardServer:
|
||||
from admin.dashboard.blueprints.system import system_bp
|
||||
from admin.dashboard.blueprints.main import main_bp
|
||||
from admin.dashboard.blueprints.plugin_routes import plugin_routes
|
||||
from blueprints.virtual_group import virtual_group_bp
|
||||
|
||||
# 将服务器实例存储在应用上下文中
|
||||
app.dashboard_server = self
|
||||
|
||||
# 注册蓝图
|
||||
# 在app.register_blueprint部分添加
|
||||
app.register_blueprint(virtual_group_bp, url_prefix='/virtual_group')
|
||||
app.register_blueprint(auth_bp)
|
||||
app.register_blueprint(main_bp)
|
||||
app.register_blueprint(contacts_bp)
|
||||
|
||||
@@ -166,6 +166,11 @@
|
||||
<i class="el-icon-s-tools"></i>
|
||||
<span slot="title">插件管理</span>
|
||||
</el-menu-item>
|
||||
<!-- 在导航菜单中添加虚拟群组管理 -->
|
||||
<el-menu-item index="12" @click="navigateTo('/virtual_group')">
|
||||
<i class="el-icon-connection"></i>
|
||||
<span slot="title">虚拟群组管理</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
@@ -229,7 +234,8 @@
|
||||
'7': '/messages',
|
||||
'9': '/wx_logs',
|
||||
'10': '/contacts',
|
||||
'11': '/plugins_manage'
|
||||
'11': '/plugins_manage',
|
||||
'12': '/virtual_group'
|
||||
};
|
||||
|
||||
// 如果当前不在对应页面,则跳转
|
||||
|
||||
364
admin/dashboard/templates/virtual_group_management.html
Normal file
364
admin/dashboard/templates/virtual_group_management.html
Normal file
@@ -0,0 +1,364 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}虚拟群组管理 - 机器人管理后台{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- 虚拟群组管理 -->
|
||||
<div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-card shadow="hover">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>虚拟群组管理</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
style="float: right; margin-left: 10px;"
|
||||
@click="showCreateVirtualGroupDialog">
|
||||
创建虚拟群组
|
||||
</el-button>
|
||||
<el-input
|
||||
placeholder="搜索虚拟群组..."
|
||||
v-model="searchQuery"
|
||||
style="width: 200px; float: right"
|
||||
clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<!-- 虚拟群组列表 -->
|
||||
<el-table
|
||||
:data="filteredVirtualGroups"
|
||||
style="width: 100%"
|
||||
border>
|
||||
<el-table-column type="expand">
|
||||
<template slot-scope="props">
|
||||
<el-table
|
||||
:data="props.row.groups"
|
||||
style="width: 100%">
|
||||
<el-table-column label="微信群ID" prop="id" width="280"></el-table-column>
|
||||
<el-table-column label="微信群名称" prop="name"></el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="danger"
|
||||
@click="removeGroupFromVirtual(props.row.id, scope.row.id)">移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div style="margin-top: 10px;">
|
||||
<el-button size="small" type="primary" @click="showAddGroupDialog(props.row)">添加微信群</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="虚拟群组名称" prop="name"></el-table-column>
|
||||
<el-table-column label="包含群数量">
|
||||
<template slot-scope="scope">
|
||||
{% raw %}{{ scope.row.groups.length }}{% endraw %} 个群
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="editVirtualGroup(scope.row)">编辑</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="danger"
|
||||
@click="deleteVirtualGroup(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 创建虚拟群组对话框 -->
|
||||
<el-dialog
|
||||
title="创建虚拟群组"
|
||||
:visible.sync="createVirtualGroupDialogVisible"
|
||||
width="30%">
|
||||
<el-form :model="virtualGroupForm" :rules="virtualGroupRules" ref="virtualGroupForm">
|
||||
<el-form-item label="群组名称" prop="name">
|
||||
<el-input v-model="virtualGroupForm.name" placeholder="请输入虚拟群组名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="createVirtualGroupDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitCreateVirtualGroup">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 编辑虚拟群组对话框 -->
|
||||
<el-dialog
|
||||
title="编辑虚拟群组"
|
||||
:visible.sync="editVirtualGroupDialogVisible"
|
||||
width="30%">
|
||||
<el-form :model="editVirtualGroupForm" :rules="virtualGroupRules" ref="editVirtualGroupForm">
|
||||
<el-form-item label="群组名称" prop="name">
|
||||
<el-input v-model="editVirtualGroupForm.name" placeholder="请输入虚拟群组名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editVirtualGroupDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitEditVirtualGroup">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加微信群对话框 -->
|
||||
<el-dialog
|
||||
title="添加微信群到虚拟群组"
|
||||
:visible.sync="addGroupDialogVisible"
|
||||
width="50%">
|
||||
<el-form :model="addGroupForm" :rules="addGroupRules" ref="addGroupForm">
|
||||
<el-form-item label="选择微信群" prop="wx_group_id">
|
||||
<el-select
|
||||
v-model="addGroupForm.wx_group_id"
|
||||
filterable
|
||||
placeholder="请选择微信群"
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="group in availableGroups"
|
||||
:key="group.wxid"
|
||||
:label="group.name"
|
||||
:value="group.wxid">
|
||||
<span style="float: left">{% raw %}{{ group.name }}{% endraw %}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{% raw %}{{ group.wxid }}{% endraw %}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addGroupDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitAddGroup">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
mixins: [baseApp],
|
||||
data() {
|
||||
return {
|
||||
virtualGroups: [],
|
||||
searchQuery: '',
|
||||
// 创建虚拟群组相关数据
|
||||
createVirtualGroupDialogVisible: false,
|
||||
virtualGroupForm: {
|
||||
name: ''
|
||||
},
|
||||
virtualGroupRules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入虚拟群组名称', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
// 编辑虚拟群组相关数据
|
||||
editVirtualGroupDialogVisible: false,
|
||||
editVirtualGroupForm: {
|
||||
id: '',
|
||||
name: ''
|
||||
},
|
||||
// 添加微信群相关数据
|
||||
addGroupDialogVisible: false,
|
||||
currentVirtualGroupId: '',
|
||||
addGroupForm: {
|
||||
wx_group_id: ''
|
||||
},
|
||||
addGroupRules: {
|
||||
wx_group_id: [
|
||||
{ required: true, message: '请选择微信群', trigger: 'change' }
|
||||
]
|
||||
},
|
||||
availableGroups: [],
|
||||
allGroups: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredVirtualGroups() {
|
||||
if (!this.searchQuery) return this.virtualGroups;
|
||||
const query = this.searchQuery.toLowerCase();
|
||||
return this.virtualGroups.filter(group =>
|
||||
group.name.toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.currentView = '12'; // 设置当前视图为虚拟群组管理
|
||||
this.loadVirtualGroups();
|
||||
this.loadAllGroups();
|
||||
},
|
||||
methods: {
|
||||
loadVirtualGroups() {
|
||||
axios.get('/virtual_group/api/virtual_groups')
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.virtualGroups = response.data.data.chatGroups || [];
|
||||
} else {
|
||||
this.$message.error('加载虚拟群组失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('加载虚拟群组失败:', error);
|
||||
this.$message.error('加载虚拟群组失败');
|
||||
});
|
||||
},
|
||||
loadAllGroups() {
|
||||
axios.get('/contacts/api/groups')
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
const groups = response.data.data.groups;
|
||||
this.allGroups = Object.entries(groups).map(([wxid, name]) => ({
|
||||
wxid,
|
||||
name: name || wxid
|
||||
}));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('加载群组列表失败:', error);
|
||||
this.$message.error('加载群组列表失败');
|
||||
});
|
||||
},
|
||||
showCreateVirtualGroupDialog() {
|
||||
this.virtualGroupForm = { name: '' };
|
||||
this.createVirtualGroupDialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.virtualGroupForm && this.$refs.virtualGroupForm.clearValidate();
|
||||
});
|
||||
},
|
||||
submitCreateVirtualGroup() {
|
||||
this.$refs.virtualGroupForm.validate(valid => {
|
||||
if (valid) {
|
||||
axios.post('/virtual_group/api/virtual_groups', this.virtualGroupForm)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.$message.success('创建虚拟群组成功');
|
||||
this.createVirtualGroupDialogVisible = false;
|
||||
this.loadVirtualGroups();
|
||||
} else {
|
||||
this.$message.error(response.data.error || '创建虚拟群组失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('创建虚拟群组失败:', error);
|
||||
this.$message.error('创建虚拟群组失败');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
editVirtualGroup(group) {
|
||||
this.editVirtualGroupForm = {
|
||||
id: group.id,
|
||||
name: group.name
|
||||
};
|
||||
this.editVirtualGroupDialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.editVirtualGroupForm && this.$refs.editVirtualGroupForm.clearValidate();
|
||||
});
|
||||
},
|
||||
submitEditVirtualGroup() {
|
||||
this.$refs.editVirtualGroupForm.validate(valid => {
|
||||
if (valid) {
|
||||
axios.put(`/virtual_group/api/virtual_groups/${this.editVirtualGroupForm.id}`, {
|
||||
name: this.editVirtualGroupForm.name
|
||||
})
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.$message.success('更新虚拟群组成功');
|
||||
this.editVirtualGroupDialogVisible = false;
|
||||
this.loadVirtualGroups();
|
||||
} else {
|
||||
this.$message.error(response.data.error || '更新虚拟群组失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('更新虚拟群组失败:', error);
|
||||
this.$message.error('更新虚拟群组失败');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
deleteVirtualGroup(group) {
|
||||
this.$confirm(`确定要删除虚拟群组 "${group.name}" 吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
axios.delete(`/virtual_group/api/virtual_groups/${group.id}`)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.$message.success('删除虚拟群组成功');
|
||||
this.loadVirtualGroups();
|
||||
} else {
|
||||
this.$message.error(response.data.error || '删除虚拟群组失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('删除虚拟群组失败:', error);
|
||||
this.$message.error('删除虚拟群组失败');
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
showAddGroupDialog(virtualGroup) {
|
||||
this.currentVirtualGroupId = virtualGroup.id;
|
||||
this.addGroupForm = { wx_group_id: '' };
|
||||
|
||||
// 过滤掉已经在虚拟群组中的微信群
|
||||
const existingGroupIds = virtualGroup.groups.map(g => g.id);
|
||||
this.availableGroups = this.allGroups.filter(g => !existingGroupIds.includes(g.wxid));
|
||||
|
||||
this.addGroupDialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.addGroupForm && this.$refs.addGroupForm.clearValidate();
|
||||
});
|
||||
},
|
||||
submitAddGroup() {
|
||||
this.$refs.addGroupForm.validate(valid => {
|
||||
if (valid) {
|
||||
axios.post(`/virtual_group/api/virtual_groups/${this.currentVirtualGroupId}/groups`, {
|
||||
wx_group_id: this.addGroupForm.wx_group_id
|
||||
})
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.$message.success('添加微信群成功');
|
||||
this.addGroupDialogVisible = false;
|
||||
this.loadVirtualGroups();
|
||||
} else {
|
||||
this.$message.error(response.data.error || '添加微信群失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('添加微信群失败:', error);
|
||||
this.$message.error('添加微信群失败');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
removeGroupFromVirtual(virtualGroupId, wxGroupId) {
|
||||
this.$confirm('确定要从虚拟群组中移除该微信群吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
axios.delete(`/virtual_group/api/virtual_groups/${virtualGroupId}/groups/${wxGroupId}`)
|
||||
.then(response => {
|
||||
if (response.data.success) {
|
||||
this.$message.success('移除微信群成功');
|
||||
this.loadVirtualGroups();
|
||||
} else {
|
||||
this.$message.error(response.data.error || '移除微信群失败');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('移除微信群失败:', error);
|
||||
this.$message.error('移除微信群失败');
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
57
db/group_virtual_redis.py
Normal file
57
db/group_virtual_redis.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict, Any, List, Optional
|
||||
|
||||
from db.connection import DBConnectionManager
|
||||
|
||||
|
||||
class GroupVirtualRedisDB:
|
||||
"""群组虚拟聊天Redis数据库操作类"""
|
||||
|
||||
def __init__(self, db_manager: DBConnectionManager):
|
||||
self.db_manager = db_manager
|
||||
self.redis_client = db_manager.get_redis_connection()
|
||||
self.LOG = logging.getLogger("Plugin.GroupVirtual.Redis")
|
||||
self.chat_groups_key = "group_virtual:chat_groups"
|
||||
|
||||
def load_chat_groups(self) -> Dict[str, Any]:
|
||||
"""从Redis加载虚拟聊天组数据"""
|
||||
try:
|
||||
chat_groups_json = self.redis_client.get(self.chat_groups_key)
|
||||
if chat_groups_json:
|
||||
return json.loads(chat_groups_json)
|
||||
else:
|
||||
# 如果Redis中没有数据,返回空结构
|
||||
return {"chatGroups": []}
|
||||
except Exception as e:
|
||||
self.LOG.error(f"从Redis加载虚拟聊天组数据失败: {e}")
|
||||
return {"chatGroups": []}
|
||||
|
||||
def save_chat_groups(self, chat_groups: Dict[str, Any]) -> bool:
|
||||
"""保存虚拟聊天组数据到Redis"""
|
||||
try:
|
||||
chat_groups_json = json.dumps(chat_groups, ensure_ascii=False)
|
||||
self.redis_client.set(self.chat_groups_key, chat_groups_json)
|
||||
return True
|
||||
except Exception as e:
|
||||
self.LOG.error(f"保存虚拟聊天组数据到Redis失败: {e}")
|
||||
return False
|
||||
|
||||
def get_chat_group(self, chat_group_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""获取指定ID的虚拟聊天组"""
|
||||
chat_groups = self.load_chat_groups()
|
||||
for group in chat_groups.get("chatGroups", []):
|
||||
if group["id"] == chat_group_id:
|
||||
return group
|
||||
return None
|
||||
|
||||
def get_chat_groups_by_group_id(self, group_id: str) -> List[Dict[str, Any]]:
|
||||
"""获取包含指定群的所有虚拟聊天组"""
|
||||
result = []
|
||||
chat_groups = self.load_chat_groups()
|
||||
for chat_group in chat_groups.get("chatGroups", []):
|
||||
for group in chat_group.get("groups", []):
|
||||
if group["id"] == group_id:
|
||||
result.append(chat_group)
|
||||
break
|
||||
return result
|
||||
@@ -18,7 +18,7 @@ class MessageUtil:
|
||||
self.contact_manager = contact_manager
|
||||
self.LOG = logging.getLogger("MessageUtil")
|
||||
|
||||
def send_text_msg(self, msg: str, receiver: str, at_list: str = "") -> None:
|
||||
def send_text(self, msg: str, receiver: str, at_list: str = "") -> None:
|
||||
"""
|
||||
发送文本消息
|
||||
|
||||
@@ -56,11 +56,25 @@ class MessageUtil:
|
||||
"""
|
||||
# 风控处理,随机延迟发送,解决群消息高频发送导致的微信风险
|
||||
time.sleep(random.uniform(0.5, 1.5))
|
||||
|
||||
|
||||
self.LOG.info(f"Sending file to {receiver}: {file_path}")
|
||||
self.wcf.send_file(file_path, receiver)
|
||||
|
||||
def send_rich_text(self, name: str, account: str, title: str, digest: str, url: str, thumburl: str, receiver: str) -> int:
|
||||
def send_image(self, image_path: str, receiver: str) -> None:
|
||||
"""
|
||||
发送文件消息
|
||||
|
||||
:param image_path: 文件路径
|
||||
:param receiver: 接收人wxid或者群id
|
||||
"""
|
||||
# 风控处理,随机延迟发送,解决群消息高频发送导致的微信风险
|
||||
time.sleep(random.uniform(0.5, 1.5))
|
||||
|
||||
self.LOG.info(f"Sending file to {receiver}: {image_path}")
|
||||
self.wcf.send_image(image_path, receiver)
|
||||
|
||||
def send_rich_text(self, name: str, account: str, title: str, digest: str, url: str, thumburl: str,
|
||||
receiver: str) -> int:
|
||||
"""
|
||||
发送富文本消息
|
||||
|
||||
@@ -85,7 +99,7 @@ class MessageUtil:
|
||||
"""
|
||||
# 风控处理,随机延迟发送,解决群消息高频发送导致的微信风险
|
||||
time.sleep(random.uniform(0.5, 1.5))
|
||||
|
||||
|
||||
self.LOG.info(f"Sending rich text to {receiver}: {title}")
|
||||
return self.wcf.send_rich_text(name, account, title, digest, url, thumburl, receiver)
|
||||
|
||||
@@ -100,4 +114,4 @@ class MessageUtil:
|
||||
# 修改使用 allContacts 的地方,改为使用 contact_manager
|
||||
# 例如:
|
||||
# 原来的代码: nickname = self.allContacts.get(wxid, wxid)
|
||||
# 修改为: nickname = self.contact_manager.get_nickname(wxid)
|
||||
# 修改为: nickname = self.contact_manager.get_nickname(wxid)
|
||||
|
||||
@@ -110,8 +110,8 @@ class BeautyLegPlugin(MessagePluginInterface):
|
||||
# 获取随机图片
|
||||
random_file_path = self._get_random_file_from_dir(self.image_folder)
|
||||
if not random_file_path:
|
||||
self.message_util.send_text_msg(f"\n❌未找到美腿图片资源",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"\n❌未找到美腿图片资源",
|
||||
(roomid if roomid else sender), sender)
|
||||
return True, "未找到图片资源"
|
||||
|
||||
# 发送图片
|
||||
|
||||
@@ -134,7 +134,7 @@ class DifyPlugin(MessagePluginInterface):
|
||||
query = re.sub(r"@.*?[\u2005|\s]", "", content).strip()
|
||||
|
||||
if not query:
|
||||
self.message_util.send_text_msg("请在@我的同时提供问题内容", roomid, sender)
|
||||
self.message_util.send_text("请在@我的同时提供问题内容", roomid, sender)
|
||||
return False, "没有提供问题内容"
|
||||
|
||||
# self.message_util.send_text_msg("⏳AI 正在加油,请稍候… 😊", roomid, sender)
|
||||
@@ -153,15 +153,15 @@ class DifyPlugin(MessagePluginInterface):
|
||||
self.message_util.send_file(response, roomid)
|
||||
else:
|
||||
# 如果是普通文本,使用发送文本方法
|
||||
self.message_util.send_text_msg(response, roomid, sender)
|
||||
self.message_util.send_text(response, roomid, sender)
|
||||
return True, "发送成功"
|
||||
else:
|
||||
self.message_util.send_text_msg("❌未能获取到回复,请稍后再试", roomid, sender)
|
||||
self.message_util.send_text("❌未能获取到回复,请稍后再试", roomid, sender)
|
||||
return False, "未获取到回复"
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理Dify聊天请求出错: {e}")
|
||||
self.message_util.send_text_msg(f"❌请求出错:{str(e)}", roomid, sender)
|
||||
self.message_util.send_text(f"❌请求出错:{str(e)}", roomid, sender)
|
||||
return False, f"处理出错: {e}"
|
||||
|
||||
# 原有的命令处理逻辑
|
||||
@@ -170,8 +170,8 @@ class DifyPlugin(MessagePluginInterface):
|
||||
|
||||
# 检查命令格式
|
||||
if len(parts) < 2 or not parts[1].strip():
|
||||
self.message_util.send_text_msg(f"{self.command_format}",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"{self.command_format}",
|
||||
(roomid if roomid else sender), sender)
|
||||
return False, "命令格式错误"
|
||||
|
||||
# 检查权限
|
||||
@@ -201,17 +201,17 @@ class DifyPlugin(MessagePluginInterface):
|
||||
self.message_util.send_file(response, (roomid if roomid else sender))
|
||||
else:
|
||||
# 如果是普通文本,使用发送文本方法
|
||||
self.message_util.send_text_msg(response, (roomid if roomid else sender), sender if roomid else "")
|
||||
self.message_util.send_text(response, (roomid if roomid else sender), sender if roomid else "")
|
||||
return True, "发送成功"
|
||||
else:
|
||||
self.message_util.send_text_msg("❌未能获取到回复,请稍后再试", (roomid if roomid else sender),
|
||||
sender if roomid else "")
|
||||
self.message_util.send_text("❌未能获取到回复,请稍后再试", (roomid if roomid else sender),
|
||||
sender if roomid else "")
|
||||
return False, "未获取到回复"
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理Dify聊天请求出错: {e}")
|
||||
self.message_util.send_text_msg(f"❌请求出错:{str(e)}", (roomid if roomid else sender),
|
||||
sender if roomid else "")
|
||||
self.message_util.send_text(f"❌请求出错:{str(e)}", (roomid if roomid else sender),
|
||||
sender if roomid else "")
|
||||
return False, f"处理出错: {e}"
|
||||
|
||||
def _chat_with_dify(self, session_id: str, user_id: str, query: str) -> Optional[str]:
|
||||
|
||||
@@ -148,8 +148,8 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
self._handle_list_uncompleted_tasks(sender, roomid)
|
||||
return True, "列举未完成任务成功"
|
||||
else:
|
||||
self.message_util.send_text_msg(f"❌未知命令!\n{self.command_format}",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"❌未知命令!\n{self.command_format}",
|
||||
(roomid if roomid else sender), sender)
|
||||
return False, "未知命令"
|
||||
|
||||
except Exception as e:
|
||||
@@ -162,7 +162,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
# 检查并添加群聊
|
||||
if not self.encyclopedia_db.check_group_exists(roomid):
|
||||
self.encyclopedia_db.add_group(roomid)
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"🎉 群 {roomid} 已就位,准备开燥!",
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
@@ -172,7 +172,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
player = self.encyclopedia_db.get_player(sender, roomid)
|
||||
if not player:
|
||||
self.encyclopedia_db.add_player(sender, roomid, wx_nick_name)
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"🎉 哇塞,{wx_nick_name} 你来啦!\n"
|
||||
f"🌟 群 {roomid} 瞬间燃爆!\n"
|
||||
f"🎈 快来接任务,秀翻全场!指令: /t",
|
||||
@@ -181,7 +181,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"加入游戏出错: {e}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😔 加入游戏出错,请稍后再试!",
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
@@ -198,7 +198,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
# 检查并添加群聊
|
||||
if not self.encyclopedia_db.check_group_exists(roomid):
|
||||
self.encyclopedia_db.add_group(roomid)
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"🎉 群 {roomid} 已就位,准备开燥!",
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
@@ -210,7 +210,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
|
||||
# 添加当前用户为玩家
|
||||
self.encyclopedia_db.add_player(sender, roomid, wx_nick_name)
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"🎉 哇塞,{wx_nick_name} 你是第一个玩家!\n"
|
||||
f"🌟 已自动为你加入游戏!\n"
|
||||
f"🎈 现在就为你准备题目...",
|
||||
@@ -223,7 +223,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
|
||||
player_dict = {p['player_id']: p['player_name'] for p in players}
|
||||
if sender not in player_dict:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😅 嘿,你谁啊?\n"
|
||||
f"🌟 先用 /s 报名,不然没法玩哦!",
|
||||
(roomid if roomid else sender),
|
||||
@@ -245,14 +245,14 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
)
|
||||
|
||||
if not active_task_id:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😔 任务创建失败,请稍后再试!",
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
)
|
||||
return
|
||||
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"🎁 {player_dict[sender]},你的专属任务闪亮登场!\n"
|
||||
f"🎀 任务ID: {active_task_id}\n"
|
||||
f"🎈 问题:[{category}]{question}\n"
|
||||
@@ -263,7 +263,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"获取任务出错: {e}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😔 获取任务出错,请稍后再试!",
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
@@ -278,7 +278,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
roomid = message.get("roomid", "")
|
||||
parts = content.split(" ", 2)
|
||||
if len(parts) < 3:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😅 喂,格式不对啊!\n"
|
||||
f"🌟 正确姿势:/a [任务ID] [答案]\n"
|
||||
f"🎈 比如:/a 1 钒",
|
||||
@@ -293,7 +293,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
# 获取玩家信息
|
||||
player = self.encyclopedia_db.get_player(sender, roomid)
|
||||
if not player:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😅 嘿,你是路人甲吗?\n"
|
||||
f"🌟 用 /s 先加入群 {roomid} 吧!",
|
||||
(roomid if roomid else sender),
|
||||
@@ -304,7 +304,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
player_name = player['player_name']
|
||||
|
||||
if not task_id.isdigit():
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😅 喂,任务ID得是数字好吗?\n"
|
||||
f"🌟 比如:1\n"
|
||||
f"🎈 别瞎搞,重新来!",
|
||||
@@ -319,7 +319,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
task_data = self.encyclopedia_db.get_task_by_id(roomid, active_task_id)
|
||||
|
||||
if not task_data:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😔 哎哟,任务 task_{active_task_id} 不翼而飞啦!\n"
|
||||
f"🌼 可能被别人抢先一步咯!",
|
||||
(roomid if roomid else sender),
|
||||
@@ -328,7 +328,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
return False, "0"
|
||||
|
||||
if task_data['status'] == 'completed':
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😄 哈哈,你慢了一步!\n"
|
||||
f"🌟 任务 task_{active_task_id} 已经完结\n"
|
||||
f"🎈 快去抢新任务吧!",
|
||||
@@ -362,7 +362,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
self.encyclopedia_db.complete_task(active_task_id)
|
||||
|
||||
if sender == holder_id:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"🎉 {player_name} 你是天才吗?\n"
|
||||
f"🌟 任务:{question}\n"
|
||||
f"🎈 答对啦,简直无敌!\n"
|
||||
@@ -372,7 +372,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
sender
|
||||
)
|
||||
else:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"🎉 {player_name} 抢答王上线!\n"
|
||||
f"🌟 任务:{question}\n"
|
||||
f"🎈 原主:{holder_name} 被你截胡啦!\n"
|
||||
@@ -385,7 +385,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
# 扣除积分
|
||||
self.encyclopedia_db.update_player_points(sender, roomid, -1)
|
||||
points = -1
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😅 {player_name} 你这是要笑死我吗?\n"
|
||||
f"🌼 任务:{question}\n"
|
||||
f"🎈 你答:{answer}\n"
|
||||
@@ -409,7 +409,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
ranks = self.encyclopedia_db.get_player_ranking(roomid, 10)
|
||||
|
||||
if not ranks:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😔 群 {roomid} 冷冷清清\n"
|
||||
f"🌟 快来一起燥起来吧!",
|
||||
(roomid if roomid else sender),
|
||||
@@ -421,14 +421,14 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
for i, row in enumerate(ranks, 1):
|
||||
rank_text += f"🐓 {i}. {row['player_name']}: {row['points']} 分\n"
|
||||
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
rank_text,
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"显示排行榜出错: {e}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😔 获取排行榜出错,请稍后再试!",
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
@@ -441,7 +441,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
tasks = self.encyclopedia_db.get_active_tasks_in_group(roomid)
|
||||
|
||||
if not tasks:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😄 群 {roomid} 现在一片祥和\n"
|
||||
f"🌟 没任务?快用 /t 搞一个!",
|
||||
(roomid if roomid else sender),
|
||||
@@ -457,14 +457,14 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
f"🌼 大佬:{task['player_name']}\n"
|
||||
)
|
||||
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
task_text,
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"显示活跃任务出错: {e}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😔 获取活跃任务出错,请稍后再试!",
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
@@ -477,7 +477,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
tasks = self.encyclopedia_db.get_active_tasks_in_group(roomid)
|
||||
|
||||
if not tasks:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😄 群 {roomid} 全员开挂?\n"
|
||||
f"🌟 没未完成任务,快用 /t 再战!",
|
||||
(roomid if roomid else sender),
|
||||
@@ -493,14 +493,14 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
f"🌼 主人:{task['player_name']}\n"
|
||||
)
|
||||
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
task_text,
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
)
|
||||
except Exception as e:
|
||||
self.LOG.error(f"列举未完成任务出错: {e}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"😔 获取未完成任务出错,请稍后再试!",
|
||||
(roomid if roomid else sender),
|
||||
sender
|
||||
@@ -542,7 +542,7 @@ class GameTaskPlugin(MessagePluginInterface):
|
||||
)
|
||||
|
||||
if active_task_id:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"🎁 新任务来袭,够不够刺激?\n"
|
||||
f"🎀 任务ID: {active_task_id}\n"
|
||||
f"🌟 幸运鹅:{holder_name}\n"
|
||||
|
||||
@@ -201,7 +201,7 @@ class GroupMemberChangePlugin(MessagePluginInterface):
|
||||
退群时间: {now_time}
|
||||
"""
|
||||
# 使用message_util发送消息
|
||||
self.message_util.send_text_msg(message, group_id)
|
||||
self.message_util.send_text(message, group_id)
|
||||
self.LOG.info(f"已发送退群通知: {nickname} 退出群 {group_id}")
|
||||
|
||||
def _send_join_notification(self, group_id: str, wxid: str, nickname: str):
|
||||
|
||||
141
plugins/group_virtual/README.md
Normal file
141
plugins/group_virtual/README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 跨群聊天插件需求分析与文档
|
||||
|
||||
## 1. 需求概述
|
||||
|
||||
开发一个名为"跨群聊天"的微信机器人插件,该插件允许用户将多个微信群组合成一个虚拟聊天组,实现群组间消息的自动转发,使不同群的成员能够进行跨群交流。
|
||||
|
||||
## 2. 功能需求
|
||||
|
||||
### 2.1 核心功能
|
||||
|
||||
1. **群组管理**
|
||||
- 创建虚拟聊天组
|
||||
- 向虚拟聊天组中添加/删除微信群
|
||||
- 查看当前所有虚拟聊天组及其包含的群
|
||||
- 解散虚拟聊天组
|
||||
|
||||
2. **消息转发**
|
||||
- 自动捕获虚拟聊天组内任一群的消息
|
||||
- 将消息转发至同一虚拟聊天组内的其他群
|
||||
- 支持文本、图片等消息类型的转发(不支持语音、表情)
|
||||
|
||||
3. **消息标识**
|
||||
- 转发的消息需标明原始发送者信息(昵称)
|
||||
- 标明消息来源群组
|
||||
- 避免消息循环转发
|
||||
|
||||
### 2.2 扩展功能
|
||||
|
||||
1. **权限控制**
|
||||
- 设置管理员权限,只有管理员可以管理虚拟聊天组
|
||||
- 支持黑名单功能,可以屏蔽特定用户的消息不被转发
|
||||
|
||||
2. **消息过滤**
|
||||
- 支持关键词过滤,含有特定关键词的消息不转发
|
||||
- 支持消息类型过滤,可选择只转发特定类型的消息(仅支持文本、图片)
|
||||
|
||||
3. **统计分析**
|
||||
- 记录转发消息数量
|
||||
- 统计各群组活跃度
|
||||
|
||||
## 3. 技术架构
|
||||
|
||||
### 3.1 数据结构
|
||||
|
||||
1. **虚拟聊天组(ChatGroup)**
|
||||
- ID: 唯一标识符
|
||||
- 名称: 虚拟聊天组名称
|
||||
- 群组列表: 包含的微信群ID列表
|
||||
- 创建时间
|
||||
- 创建者
|
||||
|
||||
2. **消息记录(Message)**
|
||||
- 消息ID
|
||||
- 原始群ID
|
||||
- 发送者ID
|
||||
- 消息内容
|
||||
- 消息类型
|
||||
- 发送时间
|
||||
- 转发状态
|
||||
|
||||
### 3.2 接口设计
|
||||
|
||||
1. **管理接口**
|
||||
- 创建虚拟聊天组: `createChatGroup(name, creatorId)`
|
||||
- 添加群到虚拟聊天组: `addGroupToChatGroup(chatGroupId, wxGroupId)`
|
||||
- 从虚拟聊天组移除群: `removeGroupFromChatGroup(chatGroupId, wxGroupId)`
|
||||
- 解散虚拟聊天组: `deleteChatGroup(chatGroupId)`
|
||||
- 查询虚拟聊天组: `listChatGroups()`, `getChatGroupDetail(chatGroupId)`
|
||||
|
||||
2. **消息处理接口**
|
||||
- 消息接收: `receiveMessage(wxGroupId, senderId, content, type)`
|
||||
- 消息转发: `forwardMessage(message, targetGroupIds)`
|
||||
- 消息过滤: `filterMessage(message, rules)`
|
||||
|
||||
## 4. 数据存储
|
||||
|
||||
使用JSON文件存储虚拟聊天组配置和相关数据:
|
||||
|
||||
```json
|
||||
{
|
||||
"chatGroups": [
|
||||
{
|
||||
"id": "cg001",
|
||||
"name": "技术交流联盟",
|
||||
"groups": ["wxid_group1", "wxid_group2", "wxid_group3"],
|
||||
"createdAt": "2023-05-01T10:00:00Z",
|
||||
"createdBy": "admin_user_id"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 用户交互
|
||||
|
||||
### 5.1 命令格式
|
||||
|
||||
用户通过在群内发送特定格式的命令来管理虚拟聊天组:
|
||||
|
||||
- 创建虚拟聊天组: `#创建跨群 [名称]`
|
||||
- 添加群到虚拟聊天组: `#添加群 [虚拟聊天组ID] [当前群名称]`
|
||||
- 查看所有虚拟聊天组: `#查看跨群列表`
|
||||
- 查看虚拟聊天组详情: `#查看跨群 [虚拟聊天组ID]`
|
||||
- 退出虚拟聊天组: `#退出跨群 [虚拟聊天组ID]`
|
||||
- 解散虚拟聊天组: `#解散跨群 [虚拟聊天组ID]`
|
||||
|
||||
### 5.2 消息展示格式
|
||||
|
||||
转发的消息格式示例:
|
||||
```
|
||||
[来自群"技术交流群"]
|
||||
张三:这是一条测试消息
|
||||
```
|
||||
|
||||
## 6. 实现步骤
|
||||
|
||||
1. 创建插件基础结构
|
||||
2. 实现数据存储模块
|
||||
3. 实现群组管理功能
|
||||
4. 实现消息捕获与转发功能
|
||||
5. 实现用户命令解析
|
||||
6. 添加消息过滤功能
|
||||
7. 实现权限控制
|
||||
8. 测试与优化
|
||||
|
||||
## 7. 注意事项
|
||||
|
||||
1. 避免消息循环转发
|
||||
2. 确保消息转发的实时性
|
||||
3. 注意微信API的限制,避免触发封号风险
|
||||
4. 保护用户隐私,不存储敏感信息
|
||||
5. 考虑高并发情况下的性能问题
|
||||
|
||||
## 8. 后续优化方向
|
||||
|
||||
1. 支持更多消息类型的转发
|
||||
2. 提供Web管理界面
|
||||
3. 增加更细粒度的权限控制
|
||||
4. 支持定时消息和定向转发
|
||||
5. 添加AI智能过滤功能
|
||||
|
||||
这份需求文档提供了跨群聊天插件的基本框架和实现思路,可以根据实际开发过程进行调整和完善。
|
||||
7
plugins/group_virtual/__init__.py
Normal file
7
plugins/group_virtual/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# 从当前包的main模块导入GroupVirtualPlugin类
|
||||
from .main import GroupVirtualPlugin
|
||||
|
||||
# 提供get_plugin函数,返回插件实例
|
||||
def get_plugin():
|
||||
"""获取插件实例"""
|
||||
return GroupVirtualPlugin()
|
||||
3
plugins/group_virtual/config.toml
Normal file
3
plugins/group_virtual/config.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
enable = true
|
||||
command = ["#创建跨群", "#添加群", "#查看跨群列表", "#查看跨群", "#退出跨群", "#解散跨群"]
|
||||
|
||||
184
plugins/group_virtual/main.py
Normal file
184
plugins/group_virtual/main.py
Normal file
@@ -0,0 +1,184 @@
|
||||
import logging
|
||||
from typing import Dict, Any, List, Optional, Tuple
|
||||
|
||||
from wcferry import Wcf
|
||||
|
||||
from message_util import MessageUtil
|
||||
from plugin_common.message_plugin_interface import MessagePluginInterface
|
||||
from plugin_common.plugin_interface import PluginStatus
|
||||
from utils.decorator.plugin_decorators import plugin_stats_decorator
|
||||
from utils.wechat.contact_manager import ContactManager
|
||||
from db.connection import DBConnectionManager
|
||||
from db.group_virtual_redis import GroupVirtualRedisDB
|
||||
|
||||
|
||||
class GroupVirtualPlugin(MessagePluginInterface):
|
||||
"""跨群聊天插件"""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "跨群聊天"
|
||||
|
||||
@property
|
||||
def version(self) -> str:
|
||||
return "1.0.0"
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return "将多个微信群组合成一个虚拟聊天组,实现群组间消息的自动转发"
|
||||
|
||||
@property
|
||||
def author(self) -> str:
|
||||
return "Trae AI"
|
||||
|
||||
@property
|
||||
def command_prefix(self) -> Optional[str]:
|
||||
return None # 不使用命令前缀,因为不处理命令
|
||||
|
||||
@property
|
||||
def commands(self) -> List[str]:
|
||||
return [] # 不处理任何命令
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.data = None
|
||||
self.message_cache = set() # 用于防止消息循环转发
|
||||
self.group_virtual_redis = None
|
||||
|
||||
def initialize(self, context: Dict[str, Any]) -> bool:
|
||||
"""初始化插件"""
|
||||
self.LOG = logging.getLogger(f"Plugin.{self.name}")
|
||||
self.LOG.info(f"正在初始化 {self.name} 插件...")
|
||||
|
||||
# 保存上下文对象
|
||||
self.wcf: Wcf = context.get("wcf")
|
||||
self.event_system = context.get("event_system")
|
||||
self.message_util: MessageUtil = context.get("message_util")
|
||||
|
||||
# 初始化配置
|
||||
self.enable = self._config.get("GroupVirtual", {}).get("enable", True)
|
||||
|
||||
# 初始化Redis数据库操作类
|
||||
self.db_manager = DBConnectionManager.get_instance()
|
||||
self.group_virtual_redis = GroupVirtualRedisDB(self.db_manager)
|
||||
|
||||
# 从Redis加载数据
|
||||
self.data = self.group_virtual_redis.load_chat_groups()
|
||||
|
||||
self.LOG.info(f"[{self.name}] 插件初始化完成")
|
||||
return True
|
||||
|
||||
def start(self) -> bool:
|
||||
"""启动插件"""
|
||||
self.LOG.info(f"[{self.name}] 插件已启动")
|
||||
self.status = PluginStatus.RUNNING
|
||||
return True
|
||||
|
||||
def stop(self) -> bool:
|
||||
"""停止插件"""
|
||||
self.LOG.info(f"[{self.name}] 插件已停止")
|
||||
self.status = PluginStatus.STOPPED
|
||||
return True
|
||||
|
||||
def can_process(self, message: Dict[str, Any]) -> bool:
|
||||
"""检查是否可以处理该消息"""
|
||||
if not self.enable:
|
||||
return False
|
||||
|
||||
# 只处理群消息
|
||||
roomid = message.get("roomid", "")
|
||||
if not roomid:
|
||||
return False
|
||||
|
||||
# 检查该群是否在任何虚拟聊天组中
|
||||
chat_groups = self._get_chat_groups_by_group_id(roomid)
|
||||
return len(chat_groups) > 0
|
||||
|
||||
@plugin_stats_decorator(plugin_name="跨群聊天")
|
||||
def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
|
||||
"""处理消息"""
|
||||
roomid = message.get("roomid", "")
|
||||
sender = message.get("sender", "")
|
||||
|
||||
# 检查是否是机器人自己发送的消息
|
||||
if sender == self.wcf.self_wxid:
|
||||
return False, "不转发自己的消息"
|
||||
|
||||
# 获取该群所在的所有虚拟聊天组
|
||||
chat_groups = self._get_chat_groups_by_group_id(roomid)
|
||||
if not chat_groups:
|
||||
return False, "该群不在任何虚拟聊天组中"
|
||||
|
||||
# 生成消息唯一标识(用于防止循环转发)
|
||||
message_id = f"{message.get('id')}_{message.get('timestamp')}"
|
||||
if message_id in self.message_cache:
|
||||
return False, "消息已经转发过"
|
||||
|
||||
# 添加到缓存
|
||||
self.message_cache.add(message_id)
|
||||
# 缓存大小控制
|
||||
if len(self.message_cache) > 1000:
|
||||
self.message_cache = set(list(self.message_cache)[-500:])
|
||||
|
||||
# 处理每个虚拟聊天组的转发
|
||||
for chat_group in chat_groups:
|
||||
self._forward_message_in_chat_group(message, chat_group)
|
||||
|
||||
return True, "消息已转发"
|
||||
|
||||
def _forward_message_in_chat_group(self, message: Dict[str, Any], chat_group: Dict[str, Any]):
|
||||
"""在虚拟聊天组内转发消息"""
|
||||
roomid = message.get("roomid", "")
|
||||
sender = message.get("sender", "")
|
||||
con = ContactManager.get_instance()
|
||||
# 获取发送者昵称
|
||||
sender_name = con.get_contact_name(sender) or "未知用户"
|
||||
|
||||
# 获取源群名称
|
||||
source_group_name = None
|
||||
for group in chat_group["groups"]:
|
||||
if group["id"] == roomid:
|
||||
source_group_name = group["name"]
|
||||
break
|
||||
|
||||
if not source_group_name:
|
||||
source_group_name = "未知群组"
|
||||
|
||||
# 构建转发消息前缀
|
||||
prefix = f"[{sender_name}@{source_group_name}]:"
|
||||
|
||||
# 根据消息类型处理
|
||||
msg_type = message.get("type", 0)
|
||||
|
||||
# 文本消息
|
||||
if msg_type == 1:
|
||||
content = message.get("content", "")
|
||||
forward_content = f"{prefix}{content}"
|
||||
|
||||
# 转发到其他群
|
||||
for group in chat_group["groups"]:
|
||||
if group["id"] != roomid: # 不转发回源群
|
||||
self.message_util.send_text(forward_content, group["id"])
|
||||
|
||||
# 图片消息 暂时不支持。
|
||||
# elif msg_type == 3:
|
||||
# # 先发送前缀
|
||||
# for group in chat_group["groups"]:
|
||||
# if group["id"] != roomid:
|
||||
# self.message_util.send_text(f"{prefix}[图片]", group["id"])
|
||||
# # 转发图片
|
||||
# image_path = message.get("file_path", "")
|
||||
# if image_path and os.path.exists(image_path):
|
||||
# self.message_util.send_image(image_path, group["id"])
|
||||
|
||||
def _get_chat_group(self, chat_group_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""获取虚拟聊天组"""
|
||||
return self.group_virtual_redis.get_chat_group(chat_group_id)
|
||||
|
||||
def _get_chat_groups_by_group_id(self, group_id: str) -> List[Dict[str, Any]]:
|
||||
"""获取包含指定群的所有虚拟聊天组"""
|
||||
return self.group_virtual_redis.get_chat_groups_by_group_id(group_id)
|
||||
|
||||
def _save_data(self):
|
||||
"""保存数据到Redis"""
|
||||
self.group_virtual_redis.save_chat_groups(self.data)
|
||||
@@ -240,8 +240,8 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
|
||||
# 如果 sign_stat 已经大于或等于今天的零点,则认为用户已经签到过了
|
||||
if sign_stat >= today_start:
|
||||
self.message_util.send_text_msg(f"您今天已经签到过了!",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"您今天已经签到过了!",
|
||||
(roomid if roomid else sender), sender)
|
||||
return False, "已签到"
|
||||
|
||||
# 在_handle_sign_in方法中,修改断签处理逻辑
|
||||
@@ -316,13 +316,13 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
daily_vocab = self.get_random_vocabulary()
|
||||
output += f"\n今日词汇:{daily_vocab}"
|
||||
|
||||
self.message_util.send_text_msg(output, (roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(output, (roomid if roomid else sender), sender)
|
||||
return True, "签到成功"
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理签到请求出错: {e}")
|
||||
self.message_util.send_text_msg(f"签到出错:{e}",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"签到出错:{e}",
|
||||
(roomid if roomid else sender), sender)
|
||||
return False, f"处理出错: {e}"
|
||||
|
||||
def reset_today_count_if_needed(self):
|
||||
@@ -373,7 +373,7 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
|
||||
# 检查用户是否有签到记录
|
||||
if not user_record:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
"❌ 您还没有签到记录,请先进行签到!",
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
@@ -406,7 +406,7 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
else:
|
||||
# 昨天已经签到了或者断签超过一天,不需要补签
|
||||
self.LOG.info(f"不符合补签条件,last_sign_date: {last_sign_date}, sign_stat: {sign_stat}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
"❌ 您昨天已经签到过了或断签超过一天,不符合补签条件!",
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
@@ -415,7 +415,7 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
# 今天未签到,检查是否符合补签条件(只能补签昨天)
|
||||
if not last_sign_date or last_sign_date < day_before_yesterday:
|
||||
self.LOG.info(f"断签超过一天,last_sign_date: {last_sign_date}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
"❌ 只能补签断签一天的情况!您已断签超过一天或没有签到记录。",
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
@@ -423,7 +423,7 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
|
||||
if last_sign_date >= yesterday:
|
||||
self.LOG.info(f"昨天已签到,last_sign_date: {last_sign_date}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
"❌ 您昨天已经签到过了,不需要补签!",
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
@@ -435,7 +435,7 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
user_points = points_db.get_user_points(sender, roomid)
|
||||
|
||||
if not user_points or user_points["total_points"] < self.makeup_cost:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"❌ 积分不足!补签需要 {self.makeup_cost} 积分,您当前只有 {user_points.get('total_points', 0)} 积分。",
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
@@ -448,7 +448,7 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
)
|
||||
|
||||
if not deduct_success:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"❌ 扣除积分失败:{deduct_result.get('error', '未知错误')}",
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
@@ -507,7 +507,7 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
|
||||
success_message += f"💰 当前积分:{user_points['total_points'] - self.makeup_cost}"
|
||||
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
success_message,
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
@@ -516,7 +516,7 @@ class MessageSignPlugin(MessagePluginInterface):
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理补签请求出错: {e}")
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"❌ 补签出错:{e}",
|
||||
(roomid if roomid else sender), sender
|
||||
)
|
||||
|
||||
@@ -149,8 +149,8 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
elif command[0] in self._commands:
|
||||
return self._handle_transfer_points(message)
|
||||
else:
|
||||
self.message_util.send_text_msg(f"❌未知命令!{self.command_format}",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"❌未知命令!{self.command_format}",
|
||||
(roomid if roomid else sender), sender)
|
||||
return True, "未知命令"
|
||||
|
||||
def _handle_transfer_points(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
|
||||
@@ -164,21 +164,21 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
|
||||
# 检查命令格式
|
||||
if len(command) < 3:
|
||||
self.message_util.send_text_msg(f"❌命令格式错误!积分转账 积分数 @用户",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"❌命令格式错误!积分转账 积分数 @用户",
|
||||
(roomid if roomid else sender), sender)
|
||||
return True, "命令格式错误"
|
||||
|
||||
# 检查积分数是否为正整数
|
||||
if not command[1].isdigit():
|
||||
self.message_util.send_text_msg(f"🈚️转账积分无效(必须为正整数!) \n积分转账 积分数 @用户",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"🈚️转账积分无效(必须为正整数!) \n积分转账 积分数 @用户",
|
||||
(roomid if roomid else sender), sender)
|
||||
return True, "积分无效"
|
||||
|
||||
# 检查@用户是否有效
|
||||
at_users = self.at_list(xml)
|
||||
if len(at_users) != 1:
|
||||
self.message_util.send_text_msg(f"转账失败❌\n🈚️转账人无效! \n积分转账 积分数 @用户",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"转账失败❌\n🈚️转账人无效! \n积分转账 积分数 @用户",
|
||||
(roomid if roomid else sender), sender)
|
||||
return True, "转账人无效"
|
||||
|
||||
reward_points = int(command[1])
|
||||
@@ -197,11 +197,11 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
error_msg = result.get("error", "未知错误")
|
||||
if "积分不足" in error_msg:
|
||||
current_points = result.get("current_points", 0)
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"❌转账失败!\n你的积分不足以进行转账!当前积分:{current_points},你需要 {reward_points} 积分。",
|
||||
(roomid if roomid else sender), sender)
|
||||
else:
|
||||
self.message_util.send_text_msg(
|
||||
self.message_util.send_text(
|
||||
f"❌转账失败!\n{error_msg}",
|
||||
(roomid if roomid else sender), sender)
|
||||
return True, f"转账失败: {error_msg}"
|
||||
@@ -224,13 +224,13 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
f"👤{to_user_name} 当前积分: {to_user.get('total_points', 0)}"
|
||||
)
|
||||
|
||||
self.message_util.send_text_msg(output, (roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(output, (roomid if roomid else sender), sender)
|
||||
return True, "转账成功"
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"积分交易出错: {e}")
|
||||
self.message_util.send_text_msg(f"❌积分交易失败!请稍后重试。错误: {str(e)}",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"❌积分交易失败!请稍后重试。错误: {str(e)}",
|
||||
(roomid if roomid else sender), sender)
|
||||
return True, f"处理出错: {str(e)}"
|
||||
|
||||
def _handle_my_points(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
|
||||
@@ -243,8 +243,8 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
user_points = self.points_db.get_user_points(sender, roomid)
|
||||
|
||||
if not user_points:
|
||||
self.message_util.send_text_msg(f"❌未找到你的积分记录!请先参与积分活动[签到,答题/t]。",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"❌未找到你的积分记录!请先参与积分活动[签到,答题/t]。",
|
||||
(roomid if roomid else sender), sender)
|
||||
return False, "未找到积分记录"
|
||||
|
||||
# 获取用户昵称
|
||||
@@ -299,13 +299,13 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
f"{recent_txs}"
|
||||
)
|
||||
|
||||
self.message_util.send_text_msg(output, (roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(output, (roomid if roomid else sender), sender)
|
||||
return True, "查询积分成功"
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"查询积分出错: {e}")
|
||||
self.message_util.send_text_msg(f"❌查询积分失败!请稍后重试。错误: {str(e)}",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"❌查询积分失败!请稍后重试。错误: {str(e)}",
|
||||
(roomid if roomid else sender), sender)
|
||||
return True, f"处理出错: {str(e)}"
|
||||
|
||||
def _handle_points_ranking(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
|
||||
@@ -315,7 +315,7 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
wcf: Wcf = message.get("wcf")
|
||||
|
||||
if not roomid:
|
||||
self.message_util.send_text_msg("❌积分排行榜仅在群聊中可用!", sender, "")
|
||||
self.message_util.send_text("❌积分排行榜仅在群聊中可用!", sender, "")
|
||||
return True, "非群聊环境"
|
||||
|
||||
try:
|
||||
@@ -323,7 +323,7 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
ranking = self.points_db.get_points_ranking(roomid, 10)
|
||||
|
||||
if not ranking:
|
||||
self.message_util.send_text_msg("❌暂无积分排行数据!请先参与积分活动。", roomid, sender)
|
||||
self.message_util.send_text("❌暂无积分排行数据!请先参与积分活动。", roomid, sender)
|
||||
return True, "无排行数据"
|
||||
|
||||
# 构建排行榜消息
|
||||
@@ -348,12 +348,12 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
f"更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
)
|
||||
|
||||
self.message_util.send_text_msg(output, roomid, sender)
|
||||
self.message_util.send_text(output, roomid, sender)
|
||||
return True, "查询排行榜成功"
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"查询积分排行榜出错: {e}")
|
||||
self.message_util.send_text_msg(f"❌查询积分排行榜失败!请稍后重试。错误: {str(e)}", roomid, sender)
|
||||
self.message_util.send_text(f"❌查询积分排行榜失败!请稍后重试。错误: {str(e)}", roomid, sender)
|
||||
return True, f"处理出错: {str(e)}"
|
||||
|
||||
def at_list(self, xml):
|
||||
@@ -457,13 +457,13 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
|
||||
# 检查是否在群聊中
|
||||
if not roomid:
|
||||
self.message_util.send_text_msg("❌打劫功能仅在群聊中可用!", sender, "")
|
||||
self.message_util.send_text("❌打劫功能仅在群聊中可用!", sender, "")
|
||||
return True, "非群聊环境"
|
||||
|
||||
# 检查时间限制 - 只允许在18:00-21:00之间打劫
|
||||
current_hour = datetime.now().hour
|
||||
if current_hour < 18 or current_hour >= 21:
|
||||
self.message_util.send_text_msg("❌打劫功能仅在晚上18:00-21:00之间开放!请在开放时间再来。", roomid, sender)
|
||||
self.message_util.send_text("❌打劫功能仅在晚上18:00-21:00之间开放!请在开放时间再来。", roomid, sender)
|
||||
return True, "时间限制"
|
||||
|
||||
# 检查是否在押
|
||||
@@ -472,8 +472,8 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
remaining_time = prison_status['end_time'] - datetime.now()
|
||||
hours = int(remaining_time.total_seconds() / 3600)
|
||||
minutes = int((remaining_time.total_seconds() % 3600) / 60)
|
||||
self.message_util.send_text_msg(f"❌你正在服刑!\n剩余时间: {hours}小时{minutes}分钟\n可请求他人花费30积分保释。", roomid,
|
||||
sender)
|
||||
self.message_util.send_text(f"❌你正在服刑!\n剩余时间: {hours}小时{minutes}分钟\n可请求他人花费30积分保释。", roomid,
|
||||
sender)
|
||||
return True, "在押状态"
|
||||
|
||||
# 检查冷却时间
|
||||
@@ -484,15 +484,15 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
if time_passed < self.rob_cooldown:
|
||||
remaining_time = int(self.rob_cooldown - time_passed)
|
||||
minutes, seconds = divmod(remaining_time, 60)
|
||||
self.message_util.send_text_msg(f"❌你最近已经打劫过了,需要冷却 {minutes}分{seconds}秒 后才能再次打劫!",
|
||||
roomid, sender)
|
||||
self.message_util.send_text(f"❌你最近已经打劫过了,需要冷却 {minutes}分{seconds}秒 后才能再次打劫!",
|
||||
roomid, sender)
|
||||
return True, "冷却中"
|
||||
|
||||
# 检查@用户是否有效
|
||||
at_users = self.at_list(xml)
|
||||
if len(at_users) != 1:
|
||||
self.message_util.send_text_msg(f"打劫失败❌\n请指定一个打劫目标!\n打劫 @用户",
|
||||
roomid, sender)
|
||||
self.message_util.send_text(f"打劫失败❌\n请指定一个打劫目标!\n打劫 @用户",
|
||||
roomid, sender)
|
||||
return True, "目标无效"
|
||||
|
||||
target_wxid = next(iter(at_users))
|
||||
@@ -500,7 +500,7 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
|
||||
# 不能打劫自己
|
||||
if target_wxid == robber_wxid:
|
||||
self.message_util.send_text_msg("❌你不能打劫自己!", roomid, sender)
|
||||
self.message_util.send_text("❌你不能打劫自己!", roomid, sender)
|
||||
return True, "不能打劫自己"
|
||||
|
||||
try:
|
||||
@@ -509,11 +509,11 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
target_points = self.points_db.get_user_points(target_wxid, roomid)
|
||||
|
||||
if not robber_points:
|
||||
self.message_util.send_text_msg("❌你没有积分记录,无法进行打劫!请先参与积分活动。", roomid, sender)
|
||||
self.message_util.send_text("❌你没有积分记录,无法进行打劫!请先参与积分活动。", roomid, sender)
|
||||
return True, "打劫者无积分"
|
||||
|
||||
if not target_points:
|
||||
self.message_util.send_text_msg("❌目标没有积分记录,无法进行打劫!", roomid, sender)
|
||||
self.message_util.send_text("❌目标没有积分记录,无法进行打劫!", roomid, sender)
|
||||
return True, "目标无积分"
|
||||
|
||||
robber_total = robber_points.get('total_points', 0)
|
||||
@@ -521,11 +521,11 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
|
||||
# 检查最低积分要求
|
||||
if robber_total < self.rob_min_points:
|
||||
self.message_util.send_text_msg(f"❌你的积分不足 {self.rob_min_points} 点,无法进行打劫!", roomid, sender)
|
||||
self.message_util.send_text(f"❌你的积分不足 {self.rob_min_points} 点,无法进行打劫!", roomid, sender)
|
||||
return True, "打劫者积分不足"
|
||||
|
||||
if target_total < self.rob_min_points:
|
||||
self.message_util.send_text_msg(f"❌目标积分不足 {self.rob_min_points} 点,不值得打劫!", roomid, sender)
|
||||
self.message_util.send_text(f"❌目标积分不足 {self.rob_min_points} 点,不值得打劫!", roomid, sender)
|
||||
return True, "目标积分不足"
|
||||
|
||||
# 获取用户昵称
|
||||
@@ -581,10 +581,10 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
f"👤{target_name} 当前积分: {from_user.get('total_points', 0)}"
|
||||
)
|
||||
|
||||
self.message_util.send_text_msg(output, roomid, sender)
|
||||
self.message_util.send_text(output, roomid, sender)
|
||||
return True, "打劫成功"
|
||||
else:
|
||||
self.message_util.send_text_msg(f"❌打劫过程中出现问题:{result.get('error', '未知错误')}", roomid, sender)
|
||||
self.message_util.send_text(f"❌打劫过程中出现问题:{result.get('error', '未知错误')}", roomid, sender)
|
||||
return True, "打劫失败"
|
||||
else:
|
||||
|
||||
@@ -618,15 +618,15 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
f"👤{robber_name} 当前积分: {from_user.get('total_points', 0)}"
|
||||
)
|
||||
|
||||
self.message_util.send_text_msg(output, roomid, sender)
|
||||
self.message_util.send_text(output, roomid, sender)
|
||||
return True, "打劫失败"
|
||||
else:
|
||||
self.message_util.send_text_msg(f"❌处理打劫惩罚时出现问题:{result.get('error', '未知错误')}", roomid, sender)
|
||||
self.message_util.send_text(f"❌处理打劫惩罚时出现问题:{result.get('error', '未知错误')}", roomid, sender)
|
||||
return True, "处理惩罚失败"
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理打劫请求出错: {e}")
|
||||
self.message_util.send_text_msg(f"❌打劫过程中出现意外:{str(e)}", roomid, sender)
|
||||
self.message_util.send_text(f"❌打劫过程中出现意外:{str(e)}", roomid, sender)
|
||||
return True, f"处理出错: {str(e)}"
|
||||
|
||||
def _handle_bailout(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
|
||||
@@ -638,14 +638,14 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
|
||||
# 检查是否在群聊中
|
||||
if not roomid:
|
||||
self.message_util.send_text_msg("❌保释功能仅在群聊中可用!", sender, "")
|
||||
self.message_util.send_text("❌保释功能仅在群聊中可用!", sender, "")
|
||||
return True, "非群聊环境"
|
||||
|
||||
# 检查@用户是否有效
|
||||
at_users = self.at_list(xml)
|
||||
if len(at_users) != 1:
|
||||
self.message_util.send_text_msg(f"保释失败❌\n请指定一个保释目标!\n保释 @用户",
|
||||
roomid, sender)
|
||||
self.message_util.send_text(f"保释失败❌\n请指定一个保释目标!\n保释 @用户",
|
||||
roomid, sender)
|
||||
return True, "目标无效"
|
||||
|
||||
prisoner_wxid = next(iter(at_users))
|
||||
@@ -653,7 +653,7 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
|
||||
# 不能保释自己
|
||||
if prisoner_wxid == bailout_wxid:
|
||||
self.message_util.send_text_msg("❌你不能保释自己!", roomid, sender)
|
||||
self.message_util.send_text("❌你不能保释自己!", roomid, sender)
|
||||
return True, "不能保释自己"
|
||||
|
||||
try:
|
||||
@@ -672,13 +672,13 @@ class PointTradePlugin(MessagePluginInterface):
|
||||
f"✅ 保释成功!\n"
|
||||
f"👤{bailout_name} 花费30积分保释了 👤{prisoner_name}"
|
||||
)
|
||||
self.message_util.send_text_msg(output, roomid, sender)
|
||||
self.message_util.send_text(output, roomid, sender)
|
||||
return True, "保释成功"
|
||||
else:
|
||||
self.message_util.send_text_msg(f"❌保释失败: {message}", roomid, sender)
|
||||
self.message_util.send_text(f"❌保释失败: {message}", roomid, sender)
|
||||
return True, "保释失败"
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理保释请求出错: {e}")
|
||||
self.message_util.send_text_msg(f"❌保释过程中出现意外:{str(e)}", roomid, sender)
|
||||
self.message_util.send_text(f"❌保释过程中出现意外:{str(e)}", roomid, sender)
|
||||
return True, f"处理出错: {str(e)}"
|
||||
|
||||
@@ -109,8 +109,8 @@ class VideoManPlugin(MessagePluginInterface):
|
||||
file_abspath = self._download_video("https://api.guiguiya.com/api/video/fuji?type=json")
|
||||
|
||||
if not file_abspath:
|
||||
self.message_util.send_text_msg(f"\n❌视频下载失败,请稍后再试",
|
||||
(roomid if roomid else sender), sender)
|
||||
self.message_util.send_text(f"\n❌视频下载失败,请稍后再试",
|
||||
(roomid if roomid else sender), sender)
|
||||
return False, "视频下载失败"
|
||||
|
||||
# 发送视频
|
||||
|
||||
2
robot.py
2
robot.py
@@ -171,7 +171,7 @@ class Robot(Job):
|
||||
if msg.from_group() and self.gbm.get_group_permission(msg.roomid,
|
||||
Feature.AI_CAPABILITY) == PermissionStatus.ENABLED:
|
||||
resp = self.gbm.get_enabled_features(msg.roomid)
|
||||
self.message_util.send_text_msg(resp, (msg.roomid if msg.from_group() else msg.sender), msg.sender)
|
||||
self.message_util.send_text(resp, (msg.roomid if msg.from_group() else msg.sender), msg.sender)
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user