猜歌名,加上撤回策略。
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import threading
|
import threading
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
import os
|
||||||
from flask import Blueprint, render_template, jsonify, request, current_app, session
|
|
||||||
from .auth import login_required
|
|
||||||
from loguru import logger
|
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from flask import Blueprint, render_template, jsonify, request, current_app, session
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
from .auth import login_required
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
# 创建消息推送管理蓝图
|
# 创建消息推送管理蓝图
|
||||||
message_push_bp = Blueprint('message_push', __name__, url_prefix='/message_push')
|
message_push_bp = Blueprint('message_push', __name__, url_prefix='/message_push')
|
||||||
@@ -19,6 +20,9 @@ message_thread_pool = ThreadPoolExecutor(max_workers=10, thread_name_prefix="mes
|
|||||||
shared_loop = None
|
shared_loop = None
|
||||||
loop_lock = threading.Lock()
|
loop_lock = threading.Lock()
|
||||||
|
|
||||||
|
# 允许的图片文件扩展名
|
||||||
|
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_loop():
|
def get_or_create_loop():
|
||||||
"""获取或创建共享的事件循环"""
|
"""获取或创建共享的事件循环"""
|
||||||
@@ -62,6 +66,10 @@ def send_message_in_thread(func, *args, **kwargs):
|
|||||||
message_thread_pool.submit(run)
|
message_thread_pool.submit(run)
|
||||||
|
|
||||||
|
|
||||||
|
def allowed_file(filename):
|
||||||
|
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
# 消息推送管理页面
|
# 消息推送管理页面
|
||||||
@message_push_bp.route('/')
|
@message_push_bp.route('/')
|
||||||
@login_required
|
@login_required
|
||||||
@@ -363,3 +371,47 @@ def api_statistics():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"获取任务统计信息失败: {e}")
|
logger.error(f"获取任务统计信息失败: {e}")
|
||||||
return jsonify({"success": False, "error": str(e)}), 500
|
return jsonify({"success": False, "error": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@message_push_bp.route('/api/upload', methods=['POST'])
|
||||||
|
def upload_file():
|
||||||
|
"""处理图片上传"""
|
||||||
|
if 'file' not in request.files:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'message': '没有文件'
|
||||||
|
})
|
||||||
|
|
||||||
|
file = request.files['file']
|
||||||
|
if file.filename == '':
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'message': '没有选择文件'
|
||||||
|
})
|
||||||
|
|
||||||
|
if file and allowed_file(file.filename):
|
||||||
|
# 生成安全的文件名
|
||||||
|
filename = secure_filename(file.filename)
|
||||||
|
# 生成唯一文件名
|
||||||
|
unique_filename = f"{uuid.uuid4().hex}_{filename}"
|
||||||
|
|
||||||
|
# 确保上传目录存在
|
||||||
|
upload_folder = os.path.join(current_app.root_path, 'static', 'uploads')
|
||||||
|
os.makedirs(upload_folder, exist_ok=True)
|
||||||
|
|
||||||
|
# 保存文件
|
||||||
|
file_path = os.path.join(upload_folder, unique_filename)
|
||||||
|
file.save(file_path)
|
||||||
|
|
||||||
|
# 返回文件的绝对路径
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'data': {
|
||||||
|
'url': file_path # 返回绝对路径
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'message': '不支持的文件类型'
|
||||||
|
})
|
||||||
|
|||||||
@@ -553,9 +553,11 @@ new Vue({
|
|||||||
this.dialogTitle = '编辑任务';
|
this.dialogTitle = '编辑任务';
|
||||||
this.taskForm = { ...task };
|
this.taskForm = { ...task };
|
||||||
if (task.content_image) {
|
if (task.content_image) {
|
||||||
|
// 编辑时显示图片
|
||||||
|
const fileName = task.content_image.split('/').pop();
|
||||||
this.imageList = [{
|
this.imageList = [{
|
||||||
name: '已上传图片',
|
name: '已上传图片',
|
||||||
url: task.content_image
|
url: `/static/uploads/${fileName}` // 显示时使用相对路径
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
this.taskDialogVisible = true;
|
this.taskDialogVisible = true;
|
||||||
@@ -685,10 +687,12 @@ new Vue({
|
|||||||
// 图片上传相关
|
// 图片上传相关
|
||||||
handleImageSuccess(response, file) {
|
handleImageSuccess(response, file) {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
this.taskForm.content_image = response.data.url;
|
this.taskForm.content_image = response.data.url; // 存储绝对路径
|
||||||
|
// 显示时使用文件名
|
||||||
|
const fileName = file.name;
|
||||||
this.imageList = [{
|
this.imageList = [{
|
||||||
name: file.name,
|
name: fileName,
|
||||||
url: response.data.url
|
url: `/static/uploads/${response.data.url.split('/').pop()}` // 显示时使用相对路径
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
this.$message.error('上传失败');
|
this.$message.error('上传失败');
|
||||||
@@ -711,7 +715,9 @@ new Vue({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleImagePreview(file) {
|
handleImagePreview(file) {
|
||||||
this.previewUrl = file.url;
|
// 预览时使用相对路径
|
||||||
|
const fileName = file.url.split('/').pop();
|
||||||
|
this.previewUrl = `/static/uploads/${fileName}`;
|
||||||
this.previewVisible = true;
|
this.previewVisible = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from typing import Dict, Any, List, Optional, Tuple
|
|||||||
from base.plugin_common.message_plugin_interface import MessagePluginInterface
|
from base.plugin_common.message_plugin_interface import MessagePluginInterface
|
||||||
from base.plugin_common.plugin_interface import PluginStatus
|
from base.plugin_common.plugin_interface import PluginStatus
|
||||||
from utils.decorator.plugin_decorators import plugin_stats_decorator
|
from utils.decorator.plugin_decorators import plugin_stats_decorator
|
||||||
|
from utils.revoke.message_auto_revoke import MessageAutoRevoke
|
||||||
from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager
|
from utils.robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager
|
||||||
from utils.decorator.points_decorator import plugin_points_cost, points_reward_decorator
|
from utils.decorator.points_decorator import plugin_points_cost, points_reward_decorator
|
||||||
from wechat_ipad import WechatAPIClient
|
from wechat_ipad import WechatAPIClient
|
||||||
@@ -116,6 +117,7 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.revoke = None
|
||||||
self.feature = self.register_feature()
|
self.feature = self.register_feature()
|
||||||
self.redis_db = None
|
self.redis_db = None
|
||||||
|
|
||||||
@@ -202,6 +204,7 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
roomid = message.get("roomid", "")
|
roomid = message.get("roomid", "")
|
||||||
gbm: GroupBotManager = message.get("gbm")
|
gbm: GroupBotManager = message.get("gbm")
|
||||||
bot: WechatAPIClient = message.get("bot")
|
bot: WechatAPIClient = message.get("bot")
|
||||||
|
self.revoke: MessageAutoRevoke = message.get("revoke")
|
||||||
|
|
||||||
# 使用roomid或sender作为游戏会话ID
|
# 使用roomid或sender作为游戏会话ID
|
||||||
session_id = roomid if roomid else sender
|
session_id = roomid if roomid else sender
|
||||||
@@ -224,9 +227,10 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
if content: # 有内容,视为答案
|
if content: # 有内容,视为答案
|
||||||
return await self._check_answer(message)
|
return await self._check_answer(message)
|
||||||
else: # 没有内容,提示已有游戏在进行中
|
else: # 没有内容,提示已有游戏在进行中
|
||||||
await bot.send_text_message(session_id,
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id,
|
||||||
f"⚠️ 当前已有猜歌游戏在进行中,请直接回复 [猜歌名 歌名] 进行猜测\n或回复 [猜歌名 下一首] 跳过当前歌曲",
|
f"⚠️ 当前已有猜歌游戏在进行中,请直接回复 [猜歌名 歌名] 进行猜测\n或回复 [猜歌名 下一首] 跳过当前歌曲",
|
||||||
sender)
|
sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 4)
|
||||||
return True, "已有游戏进行中"
|
return True, "已有游戏进行中"
|
||||||
|
|
||||||
# 否则开始新游戏(可以指定歌手或随机)
|
# 否则开始新游戏(可以指定歌手或随机)
|
||||||
@@ -239,7 +243,8 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
# 搜索歌曲
|
# 搜索歌曲
|
||||||
song_info = await self._get_random_song(singer_name)
|
song_info = await self._get_random_song(singer_name)
|
||||||
if not song_info or not song_info.get("play_url"):
|
if not song_info or not song_info.get("play_url"):
|
||||||
await bot.send_text_message(session_id, f"❌未找到{singer_name or '随机'}歌曲,请重试", sender)
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id, f"❌未找到{singer_name or '随机'}歌曲,请重试", sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 4)
|
||||||
return False, "未找到歌曲"
|
return False, "未找到歌曲"
|
||||||
|
|
||||||
# 保存游戏会话
|
# 保存游戏会话
|
||||||
@@ -258,9 +263,10 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
self.redis_db.save_game_session(session_id, game_data)
|
self.redis_db.save_game_session(session_id, game_data)
|
||||||
|
|
||||||
# 发送游戏开始消息
|
# 发送游戏开始消息
|
||||||
await bot.send_text_message(session_id,
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id,
|
||||||
f"🎵 猜歌名游戏开始!\n请听10秒钟歌曲片段,然后回复[猜歌名 歌名]来猜测歌曲名称。\n回复[猜歌名 下一首]可跳过当前歌曲。\n歌手: {song_info.get('singer_name', '未知')}",
|
f"🎵 猜歌名游戏开始!\n请听10秒钟歌曲片段,然后回复[猜歌名 歌名]来猜测歌曲名称。\n回复[猜歌名 下一首]可跳过当前歌曲。\n歌手: {song_info.get('singer_name', '未知')}",
|
||||||
sender)
|
sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 30)
|
||||||
|
|
||||||
# 发送10秒音频片段
|
# 发送10秒音频片段
|
||||||
await self._send_song_clip(bot, song_info, session_id)
|
await self._send_song_clip(bot, song_info, session_id)
|
||||||
@@ -269,7 +275,8 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.LOG.error(f"开始猜歌游戏出错: {e}")
|
self.LOG.error(f"开始猜歌游戏出错: {e}")
|
||||||
await bot.send_text_message(session_id, f"❌开始猜歌游戏出错,请稍后重试", sender)
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id, f"❌开始猜歌游戏出错,请稍后重试", sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 5)
|
||||||
return False, f"处理出错: {e}"
|
return False, f"处理出错: {e}"
|
||||||
|
|
||||||
@points_reward_decorator(5, "game", "猜歌名游戏", FEATURE_KEY)
|
@points_reward_decorator(5, "game", "猜歌名游戏", FEATURE_KEY)
|
||||||
@@ -293,6 +300,8 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
game_data = self.redis_db.get_game_session(session_id)
|
game_data = self.redis_db.get_game_session(session_id)
|
||||||
|
|
||||||
if not game_data:
|
if not game_data:
|
||||||
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id, "没有进行中的游戏", sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 5)
|
||||||
return False, "没有进行中的游戏"
|
return False, "没有进行中的游戏"
|
||||||
|
|
||||||
correct_answer = game_data.get("song_name", "")
|
correct_answer = game_data.get("song_name", "")
|
||||||
@@ -326,14 +335,17 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
self.redis_db.save_game_session(session_id, game_data)
|
self.redis_db.save_game_session(session_id, game_data)
|
||||||
|
|
||||||
# 告知用户答案错误
|
# 告知用户答案错误
|
||||||
await bot.send_text_message(session_id, f"❌ 答案错误,请继续猜测!")
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id, f"❌ 答案错误,请继续猜测!", sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 4)
|
||||||
|
|
||||||
return False, "答案错误"
|
return False, "答案错误"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.LOG.error(f"检查答案出错: {e}")
|
self.LOG.error(f"检查答案出错: {e}")
|
||||||
session_id = message.get("roomid", "") or message.get("sender", "")
|
session_id = message.get("roomid", "") or message.get("sender", "")
|
||||||
sender = message.get("sender", "")
|
sender = message.get("sender", "")
|
||||||
bot = message.get("bot")
|
bot = message.get("bot")
|
||||||
await bot.send_text_message(session_id, f"❌检查答案出错,请稍后重试", sender)
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id, f"❌检查答案出错,请稍后重试", sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 4)
|
||||||
return False, f"处理出错: {e}"
|
return False, f"处理出错: {e}"
|
||||||
|
|
||||||
async def _get_random_song(self, singer_name: Optional[str]) -> Dict[str, Any]:
|
async def _get_random_song(self, singer_name: Optional[str]) -> Dict[str, Any]:
|
||||||
@@ -471,7 +483,8 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
current_game = self.redis_db.get_game_session(session_id)
|
current_game = self.redis_db.get_game_session(session_id)
|
||||||
|
|
||||||
if not current_game or current_game.get("status") != "playing":
|
if not current_game or current_game.get("status") != "playing":
|
||||||
await bot.send_text_message(session_id, f"⚠️ 当前没有进行中的猜歌游戏", sender)
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id, f"⚠️ 当前没有进行中的猜歌游戏", sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 4)
|
||||||
return False, "没有进行中的游戏"
|
return False, "没有进行中的游戏"
|
||||||
|
|
||||||
# 显示当前歌曲答案
|
# 显示当前歌曲答案
|
||||||
@@ -492,5 +505,6 @@ class GuessSongPlugin(MessagePluginInterface):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.LOG.error(f"跳过当前歌曲出错: {e}")
|
self.LOG.error(f"跳过当前歌曲出错: {e}")
|
||||||
await bot.send_text_message(session_id, f"❌跳过当前歌曲出错,请稍后重试", sender)
|
client_msg_id, create_time, new_msg_id = await bot.send_text_message(session_id, f"❌跳过当前歌曲出错,请稍后重试", sender)
|
||||||
|
self.revoke.add_message_to_revoke(session_id, client_msg_id, create_time, new_msg_id, 4)
|
||||||
return False, f"处理出错: {e}"
|
return False, f"处理出错: {e}"
|
||||||
|
|||||||
Reference in New Issue
Block a user