diff --git a/icons/playlet-fortune-wheel.png b/icons/playlet-fortune-wheel.png
new file mode 100644
index 0000000..d329492
Binary files /dev/null and b/icons/playlet-fortune-wheel.png differ
diff --git a/package.json b/package.json
index b03dfce..194733d 100644
--- a/package.json
+++ b/package.json
@@ -1052,5 +1052,18 @@
"author": "cddjr",
"level": 1,
"v2": true
+ },
+ "PlayletFortuneWheel": {
+ "name": "PlayLet幸运大转盘",
+ "description": "每日自动抽奖,坚持抽奖,越抽越幸运...",
+ "labels": "站点",
+ "version": "1.1.0",
+ "icon": "playlet-fortune-wheel.png",
+ "author": "ArvinChen9539",
+ "level": 1,
+ "v2": true,
+ "history": {
+ "v1.1.0": "修复抽中彩虹id时报错的问题\n修复抽奖发生异常时没有提示和终止的问题"
+ }
}
-}
\ No newline at end of file
+}
diff --git a/plugins/playletfortunewheel/__init__.py b/plugins/playletfortunewheel/__init__.py
new file mode 100644
index 0000000..7b6758a
--- /dev/null
+++ b/plugins/playletfortunewheel/__init__.py
@@ -0,0 +1,936 @@
+import pytz
+import requests
+import re
+import time
+
+from datetime import datetime, timedelta
+from typing import Any, List, Dict, Tuple, Optional
+
+from apscheduler.triggers.cron import CronTrigger
+from apscheduler.schedulers.background import BackgroundScheduler
+
+from app.log import logger
+from app.core.config import settings
+from app.plugins import _PluginBase
+from app.schemas import NotificationType
+from app.db.site_oper import SiteOper
+
+
+class PlayletFortuneWheel(_PluginBase):
+ # 插件名称
+ plugin_name = "Playlet幸运大转盘"
+ # 插件描述
+ plugin_desc = "每日抽奖,越抽越有"
+ # 插件图标
+ plugin_icon = "playlet-fortune-wheel.png"
+ # 插件版本
+ plugin_version = "1.1.0"
+ # 插件作者
+ plugin_author = "ArvinChen9539"
+ # 作者主页
+ author_url = "https://github.com/ArvinChen9539"
+ # 插件配置项ID前缀
+ plugin_config_prefix = "playletfortunewheel_"
+ # 加载顺序
+ plugin_order = 25
+ # 可使用的用户级别
+ auth_level = 2
+
+ # 基本设置
+ _enabled: bool = False
+ _onlyonce: bool = False
+ _notify: bool = True
+ _use_proxy: bool = False
+ _auto_cookie: bool = True
+
+ # 只抽免费
+ _only_free: bool = False
+
+ # 保存最后一次抽奖报告
+ _last_report: Optional[str] = None
+
+ # 参数
+ _cookie: Optional[str] = None
+ _cron: Optional[str] = None
+ _max_raffle_num: Optional[int] = None
+
+ _site_url: str = "https://playletpt.xyz/"
+
+ # 定时器
+ _scheduler: Optional[BackgroundScheduler] = None
+
+ # 站点操作实例
+ _siteoper = None
+
+ def init_plugin(self, config: Optional[dict] = None) -> None:
+ """
+ 初始化插件
+ """
+ # 停止现有任务
+ self.stop_service()
+
+ # 创建站点操作实例
+ self._siteoper = SiteOper()
+
+ if config:
+ self._enabled = config.get("enabled", False)
+ self._cron = config.get("cron", '0 9 * * *')
+ self._max_raffle_num = config.get("max_raffle_num")
+ self._cookie = config.get("cookie")
+ self._notify = config.get("notify", True)
+ self._onlyonce = config.get("onlyonce", False)
+ self._use_proxy = config.get("use_proxy", False)
+ self._only_free = config.get("only_free", False)
+ self._auto_cookie = config.get("auto_cookie", True)
+ self._last_report = config.get("last_report")
+
+ # 处理自动获取cookie
+ if self._auto_cookie:
+ self._cookie = self.get_site_cookie()
+ else:
+ self._cookie = config.get("cookie")
+
+ if self._onlyonce:
+ try:
+ self._scheduler = BackgroundScheduler(timezone=settings.TZ)
+ logger.info(f"Playlet幸运大转盘服务启动,立即运行一次")
+
+ # 执行每日任务
+ self._scheduler.add_job(func=self._auto_task, trigger='date',
+ run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3),
+ name="Playlet幸运大转盘-自动执行")
+
+ # 关闭一次性开关
+ self._onlyonce = False
+ self.update_config({
+ "onlyonce": False,
+ "cron": self._cron,
+ "max_raffle_num": self._max_raffle_num,
+ "enabled": self._enabled,
+ "cookie": self._cookie,
+ "notify": self._notify,
+ "use_proxy": self._use_proxy,
+ "only_free": self._only_free,
+ "auto_cookie": self._auto_cookie,
+ "last_report": self._last_report
+ })
+
+ # 启动任务
+ if self._scheduler.get_jobs():
+ self._scheduler.print_jobs()
+ self._scheduler.start()
+ except Exception as e:
+ logger.error(f"Playlet幸运大转盘服务启动失败: {str(e)}")
+
+ # 清理Cookie无效值
+ @staticmethod
+ def clean_cookie_value(cookie_value):
+ # 移除前导和尾随空白字符
+ cleaned = cookie_value.strip()
+ # 移除非法字符
+ cleaned = ''.join(char for char in cleaned if char not in ['\r', '\n'])
+ return cleaned
+
+ # 执行抽奖
+ def exec_raffle(self):
+ raffle_url = self._site_url + "/fortune-wheel-spin.php"
+
+ # content-type: multipart/form-data
+ self.headers = {
+ "cookie": self.clean_cookie_value(self._cookie),
+ "referer": self._site_url,
+ # "content-type": "multipart/form-data",
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0"
+ }
+
+ results = []
+
+ # 获取代理设置
+ proxies = self._get_proxies()
+
+ response = requests.get(self._site_url + "/fortune-wheel.php", headers=self.headers, proxies=proxies)
+ response_data = response.text
+ # 正则截取id="free-count">和
之间的字符串
+ free_count_html = re.search(r'id="free-count">(.*?)
', response_data)
+ today_count_html = re.search(r'id="today-count">(.*?)', response_data)
+ free_count = 0
+ today_num_str = ''
+ if free_count_html:
+ free_count = int(free_count_html.group(1))
+
+ if today_count_html:
+ today_num_str = today_count_html.group(1)
+
+ if not today_num_str:
+ logger.error(f"登录异常")
+ return results
+ # 将today_num_str 拆分成今日次数和已用次数两个数字变量 字符串的格式为 "今日次数 / 已用次数"
+ used_count, today_count = map(int, today_num_str.split("/"))
+ # 今日剩余次数
+ remain_count = today_count - used_count
+ logger.info(f"免费抽奖次数:{free_count},今日剩余次数:{remain_count},已用抽奖次数:{used_count}")
+
+ if self._only_free:
+ exec_count = free_count
+ logger.info(f"使用剩余免费次数:{exec_count}")
+ else:
+ if not self._max_raffle_num or int(self._max_raffle_num) >= remain_count:
+ exec_count = remain_count
+ logger.info(f"使用剩余抽奖次数:{exec_count}")
+ else:
+ exec_count = int(self._max_raffle_num)
+ logger.info(f"使用最大抽奖次数:{exec_count}")
+
+ if exec_count > 0:
+ # 只能进行1次 10次 20次 50次的抽取 需要把exec_count转换为调用多次
+ all_results = []
+
+ while exec_count > 0:
+ num = 1
+ if exec_count >= 50:
+ num = 50
+ elif exec_count >= 20:
+ num = 20
+ elif exec_count >= 10:
+ num = 10
+
+
+ # 解析返回结果
+ try:
+ logger.info(f"执行抽奖次数{num}")
+ response = requests.post(raffle_url, headers=self.headers, files={"count": (None, num)},
+ proxies=proxies)
+ response_json = response.json()
+ flag = response_json.get("success", False)
+ if not flag:
+ logger.error(f"抽奖失败: {str(response_json)}")
+ error_msg = response_json.get("message", "未知错误")
+ results = self.process_raffle_results({"success": True, "results": all_results})
+ results.append("")
+ results.append(f"❌ 抽奖失败: {error_msg}")
+ results.append("")
+ results.append(f"🎯 剩余次数: {remain_count - len(all_results)}")
+ return results
+
+ # 累积结果
+ all_results.extend(response_json["results"])
+ exec_count -= num
+ logger.info(f"抽奖成功")
+ except Exception as e:
+ logger.error(f"转换接口返回数据时异常: {str(e)}",e)
+ results = self.process_raffle_results({"success": True, "results": all_results})
+ results.append("")
+ results.append(f"❌ 执行异常: {str(e)}")
+ return results
+
+ # 间隔2秒后执行
+ time.sleep(2)
+
+ results = self.process_raffle_results({"success": True, "results": all_results})
+
+ else:
+ logger.info(f"抽奖次数已用完")
+
+ return results
+
+ def process_raffle_results(self, response_data: dict) -> List[str]:
+ results = []
+
+ if not response_data.get("success", False):
+ error_msg = response_data.get("message", "未知错误")
+ results.append(f"❌ 抽奖失败: {error_msg}")
+ return results
+
+ # 获取抽奖结果列表
+ raffle_results = response_data.get("results", [])
+
+ if not raffle_results:
+ results.append("ℹ️ 暂无抽奖结果")
+ return results
+
+ # 分类统计各类奖励
+ prize_stats = {}
+ grade_stats = {}
+ total_count = len(raffle_results)
+ win_count = 0 # 中奖次数(非"谢谢参与")
+
+ # 图标映射
+ type_icons = {
+ "upload": "📤",
+ "attendance_card": "📋",
+ "vip": "⭐",
+ "bonus": "💎",
+ "nothing": "😞",
+ "invite_perm": "🎉",
+ "invite_temp": "🎉",
+ "rainbow_id" : "🌈",
+ }
+ type_name = {
+ "upload": "流量",
+ "attendance_card": "道具",
+ "vip": "会员",
+ "bonus": "魔力",
+ "nothing": "谢谢参与",
+ "invite_perm": "永久邀请",
+ "invite_temp": "临时邀请",
+ "rainbow_id" : "彩虹ID"
+ }
+
+ grade_icons = {
+ "1": "🥇",
+ "2": "🥈",
+ "3": "🥉",
+ "4": "🏅",
+ "5": "🏅",
+ "6": "🏅",
+ "7": "🎖️",
+ "8": "🎖️",
+ "9": "🎖️",
+ "10": "🎗️",
+ "11": "🎗️",
+ "12": "🎗️"
+ }
+
+ # 统计数据
+ for item in raffle_results:
+ result = item.get("result", {})
+ prize = item.get("prize", {})
+ grade = item.get("grade", "未知等级")
+
+ # 提取等级数字
+ grade_num = re.search(r'(\d+)等奖', grade)
+ grade_key = grade_num.group(1) if grade_num else "未知"
+
+ # 统计等级分布
+ grade_stats[grade] = grade_stats.get(grade, 0) + 1
+
+ # 统计奖励类型
+ status = result.get("status", "")
+ if status == "nothing":
+ prize_type = "nothing"
+ prize_name = "谢谢参与"
+ else:
+ prize_type = result.get("type", "unknown")
+ prize_name = prize.get("name", "未知奖励")
+ win_count += 1
+
+ # 按奖励类型统计
+ if prize_type not in prize_stats:
+ prize_stats[prize_type] = {
+ "count": 0,
+ "details": {},
+ "icon": type_icons.get(prize_type, "🎁")
+ }
+
+ prize_stats[prize_type]["count"] += 1
+
+ # 统计具体奖励详情
+ if status != "nothing":
+ value = result.get("value", 0)
+ unit = result.get("unit", "")
+ detail_key = f"{prize_name} ({unit})"
+
+ if detail_key not in prize_stats[prize_type]["details"]:
+ prize_stats[prize_type]["details"][detail_key] = {
+ "count": 0,
+ "total_value": 0
+ }
+
+ prize_stats[prize_type]["details"][detail_key]["count"] += 1
+ prize_stats[prize_type]["details"][detail_key]["total_value"] += value
+
+ # 生成报告
+ results.append(f"🎰 总抽奖次数: {total_count}")
+ results.append(f"🎯 中奖次数: {win_count}")
+ results.append(f"💔 谢谢参与: {total_count - win_count}")
+
+ if win_count > 0:
+ win_rate = (win_count / total_count) * 100
+ results.append(f"📊 中奖率: {win_rate:.1f}%")
+
+ # 添加分隔线
+ results.append("─" * 40)
+
+ # 按奖励类型展示详情
+ results.append("🏆 奖励详情:")
+ for prize_type, stat in prize_stats.items():
+ if prize_type == "nothing":
+ continue
+
+ icon = stat["icon"]
+ count = stat["count"]
+ results.append(f" {icon} {type_name.get(prize_type,'未知') or prize_type.upper()} 类奖励 ({count}次)")
+
+ for detail, info in stat["details"].items():
+ total_value = info["total_value"]
+ detail_count = info["count"]
+ results.append(f" 🎁 {detail}: {total_value} ({detail_count}次)")
+
+ results.append("")
+
+ # 添加分隔线
+ results.append("─" * 40)
+
+ # 等级分布统计
+ results.append("🏅 等级分布:")
+ # 按等级排序显示
+ sorted_grades = sorted(grade_stats.items(),
+ key=lambda x: int(re.search(r'(\d+)等奖', x[0]).group(1)) if re.search(r'(\d+)等奖',
+ x[0]) else 99)
+
+ for grade, count in sorted_grades:
+ grade_num = re.search(r'(\d+)等奖', grade)
+ if grade_num:
+ grade_key = grade_num.group(1)
+ icon = grade_icons.get(grade_key, "🎗️")
+ else:
+ icon = "❓"
+ results.append(f" {icon} {grade}: {count}次")
+
+ return results
+
+ def _auto_task(self):
+ """
+ 执行每日自动抽奖
+ """
+ try:
+ logger.info("执行每日自动抽奖")
+ results = self.exec_raffle() # 免费次数
+
+ # 生成报告
+ if results:
+ report = self.generate_report(results)
+
+ # 发送通知
+ if self._notify:
+ self.post_message(
+ mtype=NotificationType.SiteMessage,
+ title="【Playlet幸运大转盘】每日任务完成",
+ text=report)
+ self._last_report = report
+ self.update_config({
+ "onlyonce": False,
+ "cron": self._cron,
+ "max_raffle_num": self._max_raffle_num,
+ "enabled": self._enabled,
+ "cookie": self._cookie,
+ "notify": self._notify,
+ "use_proxy": self._use_proxy,
+ "only_free": self._only_free,
+ "auto_cookie": self._auto_cookie,
+ "last_report": self._last_report
+ })
+ logger.info(f"每日抽奖任务完成:\n{report}")
+ else:
+ logger.info("抽奖次数已用完,未发送通知")
+
+ except Exception as e:
+ logger.error(f"执行每日抽奖任务时发生异常: {str(e)}")
+ logger.error("异常详情: ", exc_info=True)
+
+ def generate_report(self, results: List[str]) -> str:
+ """
+ 生成完整的抽奖报告
+ :param results: 抽奖结果列表
+ :return: 格式化的报告文本
+ """
+ try:
+ if not results:
+ return "ℹ️ 没有抽奖次数"
+
+ # 生成报告
+ report = "🎮 Playlet幸运大转盘抽奖报告\n"
+ report += "━━━━━━━━━━━━━━\n"
+
+ # 添加抽奖结果
+ report += "\n".join(results)
+
+ # 添加时间戳
+ report += f"\n\n⏱️ 抽奖时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
+
+ return report
+
+ except Exception as e:
+ logger.error(f"生成报告时发生异常: {str(e)}")
+ return "❌ 生成报告时发生错误,请检查日志以获取更多信息。"
+
+ def _get_proxies(self):
+ """
+ 获取代理设置
+ """
+ if not self._use_proxy:
+ logger.info("未启用代理")
+ return None
+
+ try:
+ # 获取系统代理设置
+ if hasattr(settings, 'PROXY') and settings.PROXY:
+ logger.info(f"使用系统代理: {settings.PROXY}")
+ return settings.PROXY
+ else:
+ logger.warning("系统代理未配置")
+ return None
+ except Exception as e:
+ logger.error(f"获取代理设置出错: {str(e)}")
+ return None
+
+ def get_site_cookie(self, domain: str = 'playletpt.xyz') -> str:
+ """
+ 获取站点cookie
+
+ Args:
+ domain: 站点域名,默认为织梦站点
+
+ Returns:
+ str: 有效的cookie字符串,如果获取失败则返回空字符串
+ """
+ try:
+ # 优先使用手动配置的cookie
+ if self._cookie:
+ if str(self._cookie).strip().lower() == "cookie":
+ logger.warning("手动配置的cookie无效")
+ return ""
+ return self._cookie
+
+ # 如果手动配置的cookie无效,则从站点配置获取
+ site = self._siteoper.get_by_domain(domain)
+ if not site:
+ logger.warning(f"未找到站点: {domain}")
+ return ""
+
+ cookie = site.cookie
+ if not cookie or str(cookie).strip().lower() == "cookie":
+ logger.warning(f"站点 {domain} 的cookie无效")
+ return ""
+
+ # 将获取到的cookie保存到实例变量
+ self._cookie = cookie
+ return cookie
+
+ except Exception as e:
+ logger.error(f"获取站点cookie失败: {str(e)}")
+ return ""
+
+ def get_state(self) -> bool:
+ """获取插件状态"""
+ return bool(self._enabled)
+
+ @staticmethod
+ def get_command() -> List[Dict[str, Any]]:
+ """获取命令"""
+ pass
+
+ def get_api(self) -> List[Dict[str, Any]]:
+ """获取API"""
+ pass
+
+ def get_page(self) -> List[dict]:
+ """数据页面"""
+ pass
+
+ def get_service(self) -> List[Dict[str, Any]]:
+ """
+ 注册插件公共服务
+ """
+ service = []
+ if self._cron:
+ service.append({
+ "id": "autoPlayletFortuneWheel",
+ "name": "Playlet幸运大转盘 - 自动执行",
+ "trigger": CronTrigger.from_crontab(self._cron),
+ "func": self._auto_task,
+ "kwargs": {}
+ })
+
+ if service:
+ return service
+
+ def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
+ """
+ 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构
+ """
+ # 动态判断MoviePilot版本,决定定时任务输入框组件类型
+ version = getattr(settings, "VERSION_FLAG", "v1")
+ cron_field_component = "VCronField" if version == "v2" else "VTextField"
+ return [
+ {
+ 'component': 'VForm',
+ 'content': [
+ # 基本设置
+ {
+ 'component': 'VCard',
+ 'props': {
+ 'variant': 'flat',
+ 'class': 'mb-6',
+ 'color': 'surface'
+ },
+ 'content': [
+ {
+ 'component': 'VCardItem',
+ 'props': {
+ 'class': 'pa-6'
+ },
+ 'content': [
+ {
+ 'component': 'VCardTitle',
+ 'props': {
+ 'class': 'd-flex align-center text-h6'
+ },
+ 'content': [
+ {
+ 'component': 'VIcon',
+ 'props': {
+ 'style': 'color: #16b1ff',
+ 'class': 'mr-3',
+ 'size': 'default'
+ },
+ 'text': 'mdi-cog'
+ },
+ {
+ 'component': 'span',
+ 'text': '基本设置'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ 'component': 'VCardText',
+ 'props': {
+ 'class': 'px-6 pb-6'
+ },
+ 'content': [
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 3
+ },
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'enabled',
+ 'label': '启用插件',
+ 'color': 'primary',
+ 'hide-details': True
+ }
+ }
+ ]
+ },
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 3
+ },
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'use_proxy',
+ 'label': '使用代理',
+ 'color': 'primary',
+ 'hide-details': True
+ }
+ }
+ ]
+ },
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 3
+ },
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'notify',
+ 'label': '开启通知',
+ 'color': 'primary',
+ 'hide-details': True
+ }
+ }
+ ]
+ },
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 3
+ },
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'onlyonce',
+ 'label': '立即运行一次',
+ 'color': 'primary',
+ 'hide-details': True
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ # 功能设置
+ {
+ 'component': 'VCard',
+ 'props': {
+ 'variant': 'flat',
+ 'class': 'mb-6',
+ 'color': 'surface'
+ },
+ 'content': [
+ {
+ 'component': 'VCardItem',
+ 'props': {
+ 'class': 'pa-6'
+ },
+ 'content': [
+ {
+ 'component': 'VCardTitle',
+ 'props': {
+ 'class': 'd-flex align-center text-h6'
+ },
+ 'content': [
+ {
+ 'component': 'VIcon',
+ 'props': {
+ 'style': 'color: #16b1ff',
+ 'class': 'mr-3',
+ 'size': 'default'
+ },
+ 'text': 'mdi-tools'
+ },
+ {
+ 'component': 'span',
+ 'text': '功能设置'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ 'component': 'VCardText',
+ 'props': {
+ 'class': 'px-6 pb-6'
+ },
+ 'content': [
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 3
+ },
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'auto_cookie',
+ 'label': '使用站点Cookie',
+ 'color': 'primary',
+ 'hide-details': True
+ }
+ }
+ ]
+ },
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 3
+ },
+ 'content': [
+ {
+ 'component': 'VSwitch',
+ 'props': {
+ 'model': 'only_free',
+ 'label': '只抽免费',
+ 'color': 'primary',
+ 'hide-details': True
+ }
+ }
+ ]
+ },
+ ]
+ },
+ {
+ 'component': 'VRow',
+ 'content': [
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 4
+ },
+ 'content': [
+ {
+ 'component': 'VTextField',
+ 'props': {
+ 'model': 'cookie',
+ 'label': '站点Cookie',
+ 'variant': 'outlined',
+ 'color': 'primary',
+ 'hide-details': True,
+ 'class': 'mt-2',
+ 'disabled': 'auto_cookie'
+ }
+ }
+ ]
+ },
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 4
+ },
+ 'content': [
+ {
+ 'component': cron_field_component, # 动态切换
+ 'props': {
+ 'model': 'cron',
+ 'label': '执行周期(cron)',
+ 'variant': 'outlined',
+ 'color': 'primary',
+ 'hide-details': True,
+ 'placeholder': '默认每天执行',
+ 'class': 'mt-2'
+ }
+ }
+ ]
+ },
+ {
+ 'component': 'VCol',
+ 'props': {
+ 'cols': 12,
+ 'sm': 4
+ },
+ 'content': [
+ {
+ 'component': "VTextField", # 动态切换
+ 'props': {
+ 'model': 'max_raffle_num',
+ 'label': '最大抽奖次数',
+ 'variant': 'outlined',
+ 'color': 'primary',
+ 'hide-details': True,
+ 'placeholder': '默认全部抽完',
+ 'class': 'mt-2'
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ # 使用说明
+ {
+ 'component': 'VCard',
+ 'props': {
+ 'variant': 'flat',
+ 'class': 'mb-6',
+ 'color': 'surface'
+ },
+ 'content': [
+ {
+ 'component': 'VCardItem',
+ 'props': {
+ 'class': 'pa-6'
+ },
+ 'content': [
+ {
+ 'component': 'VCardTitle',
+ 'props': {
+ 'class': 'd-flex align-center text-h6'
+ },
+ 'content': [
+ {
+ 'component': 'VIcon',
+ 'props': {
+ 'style': 'color: #16b1ff',
+ 'class': 'mr-3',
+ 'size': 'default'
+ },
+ 'text': 'mdi-treasure-chest'
+ },
+ {
+ 'component': 'span',
+ 'text': '最后一次抽奖报告'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ 'component': 'VCardText',
+ 'props': {
+ 'class': 'px-6 pb-6'
+ },
+ 'content': [
+ {
+ 'component': 'div',
+ 'props': {
+ 'class': 'text-body-1'
+ },
+ 'content': [
+ {
+ 'component': 'div',
+ 'props': {
+ 'class': 'mb-4 text-pre-wrap'
+ },
+ 'content': [
+ {
+ 'component': 'div',
+ 'class': 'text-subtitle-1 font-weight-bold mb-2 ',
+ 'text': self._last_report or '暂无数据,可以点击立即运行一次查看'
+ },
+ ]
+ },
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ], {
+ "enabled": False,
+ "onlyonce": False,
+ "notify": True,
+ "use_proxy": False,
+ "only_free": False,
+ "cookie": "",
+ "auto_cookie": True,
+ "cron": "0 9 * * *",
+ "max_raffle_num": None,
+ "last_report": "",
+ }
+
+ def stop_service(self) -> None:
+ """
+ 退出插件
+ """
+ try:
+ if self._scheduler:
+ self._scheduler.remove_all_jobs()
+ if self._scheduler.running:
+ self._scheduler.shutdown()
+ self._scheduler = None
+ except Exception as e:
+ logger.error("退出插件失败:%s" % str(e))