import random import time from datetime import datetime, timedelta from apscheduler.jobstores.base import JobLookupError from nonebot.adapters import Bot from nonebot_plugin_alconna import At, UniMessage from nonebot_plugin_apscheduler import scheduler from pydantic import BaseModel from zhenxun.configs.config import BotConfig, Config from zhenxun.models.group_member_info import GroupInfoUser from zhenxun.models.user_console import UserConsole from zhenxun.utils.enum import GoldHandle from zhenxun.utils.exception import InsufficientGold from zhenxun.utils.image_utils import BuildImage, BuildMat, MatType, text2image from zhenxun.utils.message import MessageUtils from zhenxun.utils.platform import PlatformUtils from .model import RussianUser base_config = Config.get("russian") class Russian(BaseModel): at_user: str | None """指定决斗对象""" player1: tuple[str, str] """玩家1id, 昵称""" player2: tuple[str, str] | None = None """玩家2id, 昵称""" money: int """金额""" bullet_num: int """子弹数""" bullet_arr: list[int] = [] """子弹排列""" bullet_index: int = 0 """当前子弹下标""" next_user: str = "" """下一个开枪用户""" time: float = time.time() """创建时间""" win_user: str | None = None """胜利者""" class RussianManage: def __init__(self) -> None: self._data: dict[str, Russian] = {} def __check_is_timeout(self, group_id: str) -> bool: """检查决斗是否超时 参数: group_id: 群组id 返回: bool: 是否超时 """ if russian := self._data.get(group_id): if russian.time + 30 < time.time(): return True return False def __random_bullet(self, num: int) -> list[int]: """随机排列子弹 参数: num: 子弹数量 返回: list[int]: 子弹排列数组 """ bullet_list = [0, 0, 0, 0, 0, 0, 0] for i in random.sample([0, 1, 2, 3, 4, 5, 6], num): bullet_list[i] = 1 return bullet_list def __remove_job(self, group_id: str): """移除定时任务 参数: group_id: 群组id """ try: scheduler.remove_job(f"russian_job_{group_id}") except JobLookupError: pass def __build_job( self, bot: Bot, group_id: str, is_add: bool = False, platform: str | None = None ): """移除定时任务和构建新定时任务 参数: bot: Bot group_id: 群组id is_add: 是否添加新定时任务. platform: 平台 """ self.__remove_job(group_id) if is_add: date = datetime.now() + timedelta(seconds=31) scheduler.add_job( self.__auto_end_game, "date", run_date=date.replace(microsecond=0), id=f"russian_job_{group_id}", args=[bot, group_id, platform], ) async def __auto_end_game(self, bot: Bot, group_id: str, platform: str): """自动结束对决 参数: bot: Bot group_id: 群组id platform: 平台 """ result = await self.settlement(group_id, None, platform) if result: await PlatformUtils.send_message(bot, None, group_id, result) async def add_russian(self, bot: Bot, group_id: str, rus: Russian) -> UniMessage: """添加决斗 参数: bot: Bot group_id: 群组id rus: Russian 返回: UniMessage: 返回消息 """ russian = self._data.get(group_id) if russian: if russian.time + 30 < time.time(): if not russian.player2: return MessageUtils.build_message( f"现在是 {russian.player1[1]} 发起的对决, 请接受对决或等待决斗超时..." ) else: return MessageUtils.build_message( f"{russian.player1[1]} 和 {russian.player2[1]}的对决还未结束!" ) return MessageUtils.build_message( f"现在是 {russian.player1[1]} 发起的对决\n请等待比赛结束后再开始下一轮..." ) max_money = base_config.get("MAX_RUSSIAN_BET_GOLD") if rus.money > max_money: return MessageUtils.build_message(f"太多了!单次金额不能超过{max_money}!") user = await UserConsole.get_user(rus.player1[0]) if user.gold < rus.money: return MessageUtils.build_message("你没有足够的钱支撑起这场挑战") rus.bullet_arr = self.__random_bullet(rus.bullet_num) self._data[group_id] = rus message_list: list[str | At] = [] if rus.at_user: user = await GroupInfoUser.get_or_none( user_id=rus.at_user, group_id=group_id ) message_list = [ f"{rus.player1[1]} 向", At(flag="user", target=rus.at_user), f"发起了决斗!请 {user.user_name if user else rus.at_user} 在30秒内回复‘接受对决’ or ‘拒绝对决’,超时此次决斗作废!", ] else: message_list = [ "若30秒内无人接受挑战则此次对决作废【首次游玩请at我发送 ’帮助俄罗斯轮盘‘ 来查看命令】" ] result = ( "咔 " * rus.bullet_num + f"装填完毕\n挑战金额:{rus.money}\n第一枪的概率为:{float(rus.bullet_num) / 7.0 * 100:.2f}%\n" ) message_list.insert(0, result) self.__build_job(bot, group_id, True) return MessageUtils.build_message(message_list) # type: ignore async def accept( self, bot: Bot, group_id: str, user_id: str, uname: str ) -> UniMessage: """接受对决 参数: bot: Bot group_id: 群组id user_id: 用户id uname: 用户名称 返回: Text | MessageFactory: 返回消息 """ if russian := self._data.get(group_id): if russian.at_user and russian.at_user != user_id: return MessageUtils.build_message("又不是找你决斗,你接受什么啊!气!") if russian.player2: return MessageUtils.build_message( "当前决斗已被其他玩家接受!请等待下局对决!" ) if russian.player1[0] == user_id: return MessageUtils.build_message("你发起的对决,你接受什么啊!气!") user = await UserConsole.get_user(user_id) if user.gold < russian.money: return MessageUtils.build_message("你没有足够的钱来接受这场挑战...") russian.player2 = (user_id, uname) russian.next_user = russian.player1[0] self.__build_job(bot, group_id, True) return MessageUtils.build_message( [ "决斗已经开始!请", At(flag="user", target=russian.player1[0]), "先开枪!", ] ) return MessageUtils.build_message( "目前没有进行的决斗,请发送 装弹 开启决斗吧!" ) def refuse(self, group_id: str, user_id: str, uname: str) -> UniMessage: """拒绝决斗 参数: group_id: 群组id user_id: 用户id uname: 用户名称 返回: Text | MessageFactory: 返回消息 """ if russian := self._data.get(group_id): if russian.at_user: if russian.at_user != user_id: return MessageUtils.build_message( "又不是找你决斗,你拒绝什么啊!气!" ) del self._data[group_id] self.__remove_job(group_id) return MessageUtils.build_message( [ At(flag="user", target=russian.player1[0]), f"{uname}拒绝了你的对决!", ] ) return MessageUtils.build_message("当前决斗并没有指定对手,无法拒绝哦!") return MessageUtils.build_message( "目前没有进行的决斗,请发送 装弹 开启决斗吧!" ) async def shoot( self, bot: Bot, group_id: str, user_id: str, uname: str, platform: str ) -> tuple[UniMessage, UniMessage | None]: """开枪 参数: bot: Bot group_id: 群组id user_id: 用户id uname: 用户名称 platform: 平台 返回: Text | MessageFactory: 返回消息 """ if russian := self._data.get(group_id): if not russian.player2: return ( MessageUtils.build_message("当前还没有玩家接受对决,无法开枪..."), None, ) if user_id not in [russian.player1[0], russian.player2[0]]: """非玩家1和玩家2发送开枪""" return ( MessageUtils.build_message( random.choice( [ f"不要打扰 {russian.player1[1]} 和 {russian.player2[1]} 的决斗啊!", f"给我好好做好一个观众!不然{BotConfig.self_nickname}就要生气了", f"不要捣乱啊baka{uname}!", ] ) ), None, ) if user_id != russian.next_user: """相同玩家连续开枪""" return ( MessageUtils.build_message( f"你的左轮不是连发的!该 {russian.player2[1]} 开枪了!" ), None, ) if russian.bullet_arr[russian.bullet_index] == 1: """去世""" result = MessageUtils.build_message( random.choice( [ '"嘭!",你直接去世了', "眼前一黑,你直接穿越到了异世界...(死亡)", "终究还是你先走一步...", ] ) ) settle = await self.settlement(group_id, user_id, platform) return result, settle else: """存活""" p = ( (russian.bullet_index + russian.bullet_num + 1) / len(russian.bullet_arr) * 100 ) result = ( random.choice( [ "呼呼,没有爆裂的声响,你活了下来", "虽然黑洞洞的枪口很恐怖,但好在没有子弹射出来,你活下来了", '"咔",你没死,看来运气不错', ] ) + f"\n下一枪中弹的概率: {p:.2f}%, 轮到 " ) next_user = ( russian.player2[0] if russian.next_user == russian.player1[0] else russian.player1[0] ) russian.next_user = next_user russian.bullet_index += 1 self.__build_job(bot, group_id, True) return ( MessageUtils.build_message( [result, At(flag="user", target=next_user), " 了!"] ), None, ) return ( MessageUtils.build_message("目前没有进行的决斗,请发送 装弹 开启决斗吧!"), None, ) async def settlement( self, group_id: str, user_id: str | None, platform: str | None = None ) -> UniMessage: """结算 参数: group_id: 群组id user_id: 用户id platform: 平台 返回: Text | MessageFactory: 返回消息 """ if russian := self._data.get(group_id): if not russian.player2: if self.__check_is_timeout(group_id): del self._data[group_id] return MessageUtils.build_message( "规定时间内还未有人接受决斗,当前决斗过期..." ) return MessageUtils.build_message("决斗还未开始,,无法结算哦...") if user_id and user_id not in [russian.player1[0], russian.player2[0]]: return MessageUtils.build_message(f"吃瓜群众不要捣乱!黄牌警告!") if not self.__check_is_timeout(group_id): return MessageUtils.build_message( f"{russian.player1[1]} 和 {russian.player2[1]} 比赛并未超时,请继续比赛..." ) win_user = None lose_user = None if win_user: russian.next_user = ( russian.player1[0] if win_user == russian.player2[0] else russian.player2[0] ) if russian.next_user != russian.player1[0]: win_user = russian.player1 lose_user = russian.player2 else: win_user = russian.player2 lose_user = russian.player1 if win_user and lose_user: rand = 0 if russian.money > 10: rand = random.randint(0, 5) fee = int(russian.money * float(rand) / 100) fee = 1 if fee < 1 and rand != 0 else fee else: fee = 0 winner = await RussianUser.add_count(win_user[0], group_id, "win") loser = await RussianUser.add_count(lose_user[0], group_id, "lose") await RussianUser.money( win_user[0], group_id, "win", russian.money - fee ) await RussianUser.money(lose_user[0], group_id, "lose", russian.money) await UserConsole.add_gold( win_user[0], russian.money - fee, "russian", platform ) try: await UserConsole.reduce_gold( lose_user[0], russian.money, GoldHandle.PLUGIN, "russian", platform, ) except InsufficientGold: if u := await UserConsole.get_user(lose_user[0]): u.gold = 0 await u.save(update_fields=["gold"]) result = [ "这场决斗是 ", At(flag="user", target=win_user[0]), " 胜利了!", ] image = await text2image( f"结算:\n" f"\t胜者:{win_user[1]}\n" f"\t赢取金币:{russian.money - fee}\n" f"\t累计胜场:{winner.win_count}\n" f"\t累计赚取金币:{winner.make_money}\n" f"-------------------\n" f"\t败者:{lose_user[1]}\n" f"\t输掉金币:{russian.money}\n" f"\t累计败场:{loser.fail_count}\n" f"\t累计输掉金币:{loser.lose_money}\n" f"-------------------\n" f"哼哼,{BotConfig.self_nickname}从中收取了 {float(rand)}%({fee}金币) 作为手续费!\n" f"子弹排列:{russian.bullet_arr}", padding=10, color="#f9f6f2", ) self.__remove_job(group_id) result.append(image) del self._data[group_id] return MessageUtils.build_message(result) return MessageUtils.build_message("赢家和输家获取错误...") return MessageUtils.build_message("比赛并没有开始...无法结算...") async def __get_x_index(self, users: list[RussianUser], group_id: str): uid_list = [u.user_id for u in users] group_user_list = await GroupInfoUser.filter( user_id__in=uid_list, group_id=group_id ).all() group_user = {gu.user_id: gu.user_name for gu in group_user_list} data = [] for uid in uid_list: if uid in group_user: data.append(group_user[uid]) else: data.append(uid) return data async def rank( self, user_id: str, group_id: str, rank_type: str, num: int ) -> BuildImage | str: x_index = [] data = [] title = "" x_name = "" if rank_type == "win": users = ( await RussianUser.filter(group_id=group_id, win_count__not=0) .order_by("win_count") .limit(num) ) x_index = await self.__get_x_index(users, group_id) data = [u.win_count for u in users] title = "胜场排行" x_name = "场次" if rank_type == "lose": users = ( await RussianUser.filter(group_id=group_id, fail_count__not=0) .order_by("fail_count") .limit(num) ) x_index = await self.__get_x_index(users, group_id) data = [u.fail_count for u in users] title = "败场排行" x_name = "场次" if rank_type == "a": users = ( await RussianUser.filter(group_id=group_id, make_money__not=0) .order_by("make_money") .limit(num) ) x_index = await self.__get_x_index(users, group_id) data = [u.make_money for u in users] title = "欧洲人排行" x_name = "金币" if rank_type == "b": users = ( await RussianUser.filter(group_id=group_id, lose_money__not=0) .order_by("lose_money") .limit(num) ) x_index = await self.__get_x_index(users, group_id) data = [u.lose_money for u in users] title = "慈善家排行" x_name = "金币" if rank_type == "max_win": users = ( await RussianUser.filter(group_id=group_id, max_winning_streak__not=0) .order_by("max_winning_streak") .limit(num) ) x_index = await self.__get_x_index(users, group_id) data = [u.max_winning_streak for u in users] title = "最高连胜排行" x_name = "场次" if rank_type == "max_lose": users = ( await RussianUser.filter(group_id=group_id, max_losing_streak__not=0) .order_by("max_losing_streak") .limit(num) ) x_index = await self.__get_x_index(users, group_id) data = [u.max_losing_streak for u in users] title = "最高连败排行" x_name = "场次" if not data: return "当前数据为空..." mat = BuildMat(MatType.BARH) mat.x_index = x_index mat.data = data # type: ignore mat.title = title mat.x_name = x_name return await mat.build() russian_manage = RussianManage()