From 3d338b8ba03e9633d91c9ea1040dac9010916ce4 Mon Sep 17 00:00:00 2001 From: shenjack <3695888@qq.com> Date: Sat, 22 Jun 2024 19:10:53 +0800 Subject: [PATCH] =?UTF-8?q?namerena=20=E5=8A=A0=E4=B8=8A=20peek?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ica-rs/ica_typing.py | 11 +- ica-rs/plugins/name_utils/__init__.py | 213 ++++++++++++++++++++++++++ ica-rs/plugins/namerena.py | 72 +++++++-- ica-rs/src/py/call.rs | 2 - ica-rs/src/py/mod.rs | 11 +- 5 files changed, 290 insertions(+), 19 deletions(-) create mode 100644 ica-rs/plugins/name_utils/__init__.py diff --git a/ica-rs/ica_typing.py b/ica-rs/ica_typing.py index 13493b0..8d45b26 100644 --- a/ica-rs/ica_typing.py +++ b/ica-rs/ica_typing.py @@ -1,6 +1,6 @@ # Python 兼容版本 3.8+ -from typing import Callable, Tuple, NewType, TYPE_CHECKING, TypeVar, Optional +from typing import Callable, Tuple, NewType, TYPE_CHECKING, TypeVar, Optional, Union """ ica.rs @@ -270,6 +270,15 @@ class TailchatClient: """向日志中输出警告信息""" +class ReciveMessage(TailchatReciveMessage, IcaNewMessage): + """ + 继承了两边的消息 + 只是用来类型标记, 不能实例化 + """ + def reply_with(self, message: str) -> Union["IcaReplyMessage", "TailchatSendingMessage"]: # type: ignore + ... + + class ConfigData: def __getitem__(self, key: str): ... diff --git a/ica-rs/plugins/name_utils/__init__.py b/ica-rs/plugins/name_utils/__init__.py new file mode 100644 index 0000000..a402c9e --- /dev/null +++ b/ica-rs/plugins/name_utils/__init__.py @@ -0,0 +1,213 @@ +import io + +sklname = [ + "火球术", + "冰冻术", + "雷击术", + "地裂术", + "吸血攻击", + "投毒", + "连击", + "会心一击", + "瘟疫", + "生命之轮", + "狂暴术", + "魅惑", + "加速术", + "减速术", + "诅咒", + "治愈魔法", + "苏生术", + "净化", + "铁壁", + "蓄力", + "聚气", + "潜行", + "血祭", + "分身", + "幻术", + "防御", + "守护", + "伤害反弹", + "护身符", + "护盾", + "反击", + "吞噬", + "召唤亡灵", + "垂死抗争", + "隐匿", + "sklvoid1", + "sklvoid2", + "sklvoid3", + "sklvoid4", + "sklvoid5", +] + +prop_names = [ + "HP", + "攻", + "防", + "速", + "敏", + "魔", + "抗", + "智", + "八维", +] + + +class Player: + def __init__(self) -> None: + self.name = "" + self.team = "" + self.val = [i for i in range(0, 256)] + self.name_base = [0] * 128 + self.name_str = [0] * 256 + self.team_str = [0] * 256 + self.name_len = 0 + self.team_len = 0 + self.name_prop = [0] * 8 + self.skl_id = [i for i in range(0, 40)] + self.skl_freq = [0] * 40 + + def load(self, raw_name: str): + if raw_name == "": + print("错误:输入不能为空。") + return False + if raw_name.count("@") > 1: + print("错误:无法分割名字与战队名,请检查输入。") + return False + name_lst = list(raw_name.rpartition("@")) + if len(name_lst[0]) > 256 or len(name_lst[2]) > 256: + print("错误:名字或战队名长度过大。") + return False + if name_lst[1] == "@": + if name_lst[2] == "": + name_lst[2] = name_lst[0] + else: + name_lst[0] = name_lst[2] + name_bytes = name_lst[0].encode() + team_bytes = name_lst[2].encode() + self.name = name_lst[0] + self.team = name_lst[2] + self.name_len = len(name_lst[0]) + self.team_len = len(name_lst[2]) + for i in range(self.name_len): + self.name_str[i + 1] = name_bytes[i] + for i in range(self.team_len): + self.team_str[i + 1] = team_bytes[i] + self.name_len += 1 + self.team_len += 1 + + s = 0 + for i in range(256): + s += self.team_str[i % self.team_len] + self.val[i] + s %= 256 + self.val[i], self.val[s] = self.val[s], self.val[i] + + for i in range(2): + s = 0 + for j in range(256): + s += self.name_str[j % self.name_len] + self.val[j] + s %= 256 + self.val[j], self.val[s] = self.val[s], self.val[j] + s = 0 + for i in range(256): + m = ((self.val[i] * 181) + 160) % 256 + if m >= 89 and m < 217: + self.name_base[s] = m & 63 + s += 1 + + propcnt = 0 + r = self.name_base[0:32] + for i in range(10, 31, 3): + r[i : i + 3] = sorted(r[i : i + 3]) + self.name_prop[propcnt] = r[i + 1] + propcnt += 1 + r[0:10] = sorted(r[0:10]) + self.name_prop[propcnt] = 154 + propcnt += 1 + for i in range(3, 7): + self.name_prop[propcnt - 1] += r[i] + for i in range(7): + self.name_prop[i] += 36 + + self.skl_id = list(range(0, 40)) + self.skl_freq = [0] * 40 + a = b = 0 + randbase = [] + randbase[:] = self.val[:] + + def randgen(): + def m(): + nonlocal a, b, randbase + a = (a + 1) % 256 + b = (b + randbase[a]) % 256 + randbase[a], randbase[b] = randbase[b], randbase[a] + return randbase[(randbase[a] + randbase[b]) & 255] + + return ((m() << 8) | m()) % 40 + + s = 0 + for i in range(2): + for j in range(40): + rand = randgen() + s = (s + rand + self.skl_id[j]) % 40 + self.skl_id[j], self.skl_id[s] = self.skl_id[s], self.skl_id[j] + last = -1 + j = 0 + for i in range(64, 128, 4): + p = ( + min( + self.name_base[i], + self.name_base[i + 1], + self.name_base[i + 2], + self.name_base[i + 3], + ) + % 256 + ) + if p > 10 and self.skl_id[j] < 35: + self.skl_freq[j] = p - 10 + if self.skl_id[j] < 25: + last = j + j += 1 + if last != -1: + self.skl_freq[last] *= 2 + if self.skl_freq[14] > 0 and last != 14: + self.skl_freq[14] += min( + self.name_base[60], self.name_base[61], self.skl_freq[14] + ) + if self.skl_freq[15] > 0 and last != 15: + self.skl_freq[15] += min( + self.name_base[62], self.name_base[63], self.skl_freq[15] + ) + return True + + def display(self) -> str: + cache = io.StringIO() + cache.write(f"{self.name}@{self.team}|") + full = sum(self.name_prop[0:7]) + round(self.name_prop[7] / 3) + datas = [ + self.name_prop[7], + *self.name_prop[0:7], + full + ] + cache.write( + "|".join( + [ + f"{prop_names[index]}:{value}" + for index, value in enumerate(datas) + ] + ) + ) + cache.write("\n") + cache.write( + "|".join( + [ + f"{sklname[self.skl_id[index]]}:{self.skl_freq[index]}" + for index, value in enumerate(self.skl_freq) + if value > 0 + ] + ) + ) + return cache.getvalue() diff --git a/ica-rs/plugins/namerena.py b/ica-rs/plugins/namerena.py index e34a1d2..9c55f19 100644 --- a/ica-rs/plugins/namerena.py +++ b/ica-rs/plugins/namerena.py @@ -1,3 +1,5 @@ +import io +import sys import time import traceback import subprocess @@ -6,31 +8,67 @@ from pathlib import Path from typing import TYPE_CHECKING, TypeVar +if str(Path(__file__).parent.absolute()) not in sys.path: + sys.path.append(str(Path(__file__).parent.absolute())) + +import name_utils + if TYPE_CHECKING: - from ica_typing import IcaNewMessage, IcaClient, ConfigData + from ica_typing import IcaNewMessage, IcaClient, ConfigData, ReciveMessage, TailchatReciveMessage CONFIG_DATA: ConfigData else: CONFIG_DATA = None # type: ignore IcaNewMessage = TypeVar("NewMessage") IcaClient = TypeVar("IcaClient") + ReciveMessage = TypeVar("ReciveMessage") + TailchatReciveMessage = TypeVar("TailchatReciveMessage") -_version_ = "0.4.2" -COMMAND = "/namerena" +_version_ = "0.5.0" -def on_ica_message(msg: IcaNewMessage, client: IcaClient) -> None: - if not msg.content.startswith("/namerena") or msg.is_reply: - return +EVAL_PREFIX = "/namerena" +CONVERT_PREFIX = "/namer-peek" + +def convert_name(msg: ReciveMessage, client) -> None: + # 也是多行 if msg.content.find("\n") == -1: client.send_message( msg.reply_with( - f"请使用 {COMMAND} 命令,然后换行输入名字,例如:\n{COMMAND}\n张三\n李四\n王五\n" + f"请使用 {CONVERT_PREFIX} 命令,然后换行输入名字,例如:\n{CONVERT_PREFIX}\n张三\n李四\n王五\n" ) ) return - # 去掉 /name - names = msg.content[len(COMMAND) :] + # 去掉 prefix + names = msg.content[len(CONVERT_PREFIX) :] + # 去掉第一个 \n + names = names[names.find("\n") + 1 :] + cache = io.StringIO() + raw_players = [x for x in names.split("\n") if x != ""] + players = [name_utils.Player() for _ in raw_players] + for i, player in enumerate(players): + if not player.load(raw_players[i]): + cache.write(f"{i+1} {raw_players[i]} 无法解析\n") + raw_players[i] = "" + for i, player in enumerate(players): + if raw_players[i] == "": + continue + cache.write(player.display()) + cache.write("\n") + reply = msg.reply_with(f"{cache.getvalue()}版本:{_version_}") + client.send_message(reply) + + +def eval_fight(msg: ReciveMessage, client) -> None: + if msg.content.find("\n") == -1: + client.send_message( + msg.reply_with( + f"请使用 {EVAL_PREFIX} 命令,然后换行输入名字,例如:\n{EVAL_PREFIX}\n张三\n李四\n王五\n" + ) + ) + return + # 去掉 prefix + names = msg.content[len(EVAL_PREFIX) :] # 去掉第一个 \n names = names[names.find("\n") + 1 :] @@ -64,6 +102,18 @@ def on_ica_message(msg: IcaNewMessage, client: IcaClient) -> None: reply = msg.reply_with(f"发生错误:{e}\n{traceback.format_exc()}") client.send_message(reply) +def dispatch_msg(msg: ReciveMessage, client) -> None: + if msg.is_reply or msg.is_from_self: + return + if msg.content.startswith(EVAL_PREFIX): + eval_fight(msg, client) + elif msg.content.startswith(CONVERT_PREFIX): + convert_name(msg, client) -def on_tailchat_message(msg, client) -> None: - on_ica_message(msg, client) + +def on_ica_message(msg: IcaNewMessage, client: IcaClient) -> None: + dispatch_msg(msg, client) # type: ignore + + +def on_tailchat_message(msg: TailchatReciveMessage, client) -> None: + dispatch_msg(msg, client) # type: ignore diff --git a/ica-rs/src/py/call.rs b/ica-rs/src/py/call.rs index 0fa34ef..cd2fad6 100644 --- a/ica-rs/src/py/call.rs +++ b/ica-rs/src/py/call.rs @@ -1,11 +1,9 @@ use std::path::PathBuf; -use std::sync::Arc; use pyo3::prelude::*; use rust_socketio::asynchronous::Client; use tracing::{event, info, warn, Level}; -use crate::data_struct::tailchat::status::BotStatus; use crate::data_struct::{ica, tailchat}; use crate::error::PyPluginError; use crate::py::{class, PyPlugin, PyStatus}; diff --git a/ica-rs/src/py/mod.rs b/ica-rs/src/py/mod.rs index 7a65b86..bf5fb0e 100644 --- a/ica-rs/src/py/mod.rs +++ b/ica-rs/src/py/mod.rs @@ -79,12 +79,13 @@ impl TryFrom for PyPlugin { type Error = PyErr; fn try_from(value: RawPyPlugin) -> Result { let (path, changed_time, content) = value; - let py_module = py_module_from_code(&content, &path); - if let Err(e) = py_module { - warn!("加载 Python 插件: {:?} 失败", e); - return Err(e); + let py_module = match py_module_from_code(&content, &path) { + Ok(module) => module, + Err(e) => { + warn!("加载 Python 插件: {:?} 失败", e); + return Err(e); + } }; - let py_module = py_module.unwrap(); Python::with_gil(|py| { let module = py_module.bind(py); if let Ok(config_func) = call::get_func(module, "on_config") {