This commit is contained in:
shenjack 2024-08-02 19:08:49 +08:00
commit 12d4e24a41
Signed by: shenjack
GPG Key ID: 7B1134A979775551
14 changed files with 23402 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
#config/*
*__pycache__*
*.pyc

0
config/your_config_here Normal file
View File

Binary file not shown.

82
plugins/base.py Normal file
View File

@ -0,0 +1,82 @@
import io
import psutil
import platform
from datetime import datetime, timezone
from typing import TYPE_CHECKING, TypeVar
from PIL import (Image, ImageDraw, ImageFont)
if TYPE_CHECKING:
from ica_typing import IcaNewMessage, IcaClient
from ica_typing import TailchatReciveMessage, TailchatClient
else:
IcaNewMessage = TypeVar("NewMessage")
IcaClient = TypeVar("IcaClient")
TailchatReciveMessage = TypeVar("TailchatReciveMessage")
TailchatClient = TypeVar("TailchatClient")
# 生成一张本地信息图
def local_env_info() -> str:
cache = io.StringIO()
# 参考 DR 的 (crash report)
cache.write(f"系统: {platform.platform()}\n")
# 处理器
try:
cache.write("|".join([f"{x}%" for x in psutil.cpu_percent(interval=1, percpu=True)]))
cache.write("\n")
except OSError:
cache.write("CPU: 未知\n")
# Python 版本信息
cache.write(f"{platform.python_implementation()}: {platform.python_version()}-{platform.python_branch()}({platform.python_compiler()})\n")
# 内存信息
try:
memory = psutil.virtual_memory()
cache.write(f"内存: {memory.free / 1024 / 1024 / 1024:.3f}GB/{memory.total / 1024 / 1024 / 1024:.3f}GB\n")
except OSError:
cache.write("内存: 未知\n")
return cache.getvalue()
def local_env_image() -> bytes:
img = Image.new("RGB", (800, 140), (255, 255, 255))
# 往图片上写入一些信息
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("./SMILEYSANS-OBLIQUE.TTF", size=25)
draw.text((10, 10), local_env_info(), fill=(0, 0, 0), font=font)
img_cache = io.BytesIO()
img.save(img_cache, format="PNG")
raw_img = img_cache.getvalue()
img_cache.close()
return raw_img
def on_ica_message(msg: IcaNewMessage, client: IcaClient) -> None:
if not (msg.is_from_self or msg.is_reply):
if msg.content == "/bot":
reply = msg.reply_with(f"ica-async-rs({client.version})-sync-py {client.ica_version}")
client.send_message(reply)
elif msg.content == "/bot-sys":
datas = local_env_info()
reply = msg.reply_with(datas)
reply.set_img(local_env_image(), "image/png", False)
client.send_message(reply)
elif msg.content == "/bot-uptime":
uptime = client.startup_time
up_delta = datetime.now(timezone.utc) - uptime
reply = msg.reply_with(f"Bot 运行时间: {up_delta}")
client.send_message(reply)
def on_tailchat_message(msg: TailchatReciveMessage, client: TailchatClient) -> None:
if not (msg.is_reply or msg.is_from_self):
if msg.content == "/bot":
reply = msg.reply_with(f"tailchat-async-rs({client.version})-sync-py {client.tailchat_version}")
client.send_message(reply)
elif msg.content == "/bot-sys":
datas = local_env_info()
reply = msg.reply_with(datas)
reply.set_img(local_env_image(), "just_img.png")
client.send_message(reply)
elif msg.content == "/bot-uptime":
uptime = client.startup_time
up_delta = datetime.now(timezone.utc) - uptime
reply = msg.reply_with(f"Bot 运行时间: {up_delta}")
client.send_message(reply)

378
plugins/bmcl.py Normal file
View File

@ -0,0 +1,378 @@
import io
import time
import requests
import traceback
import urllib.parse
# import PIL
from typing import TYPE_CHECKING, TypeVar, Optional, Tuple, List
if TYPE_CHECKING:
from ica_typing import IcaNewMessage, IcaClient, ConfigData
CONFIG_DATA: ConfigData
else:
CONFIG_DATA = None # type: ignore
IcaNewMessage = TypeVar("NewMessage")
IcaClient = TypeVar("IcaClient")
_version_ = "2.8.0-rs"
backend_version = "unknown"
def format_data_size(data_bytes: float) -> str:
data_lens = ["B", "KB", "MB", "GB", "TB"]
data_len = "0B"
for i in range(5):
if data_bytes < 1024:
data_bytes = round(data_bytes, 5)
data_len = f"{data_bytes}{data_lens[i]}"
break
else:
data_bytes /= 1024
return data_len
def format_hit_count(count: int) -> str:
"""数据分段, 四位一个下划线
Args:
count (int): 数据
Returns:
str: 格式化后的数据
1 -> 1
1000 -> 1000
10000 -> 1_0000
100000 -> 10_0000
1000000 -> 100_0000
"""
count_str = str(count)
count_len = len(count_str)
if count_len <= 4:
return count_str
else:
# 先倒序
# 再插入
# 最后再倒序
count_str = count_str[::-1]
count_str = "_".join([count_str[i:i+4] for i in range(0, count_len, 4)])
count_str = count_str[::-1]
return count_str
def wrap_request(url: str, msg: IcaNewMessage, client: IcaClient) -> Optional[dict]:
try:
cookie = CONFIG_DATA["cookie"] # type: ignore
if cookie == "填写你的 cookie" or cookie is None:
response = requests.get(url)
else:
response = requests.get(url, cookies={"openbmclapi-jwt": cookie})
except requests.exceptions.RequestException:
warn_msg = f"数据请求失败, 请检查网络\n{traceback.format_exc()}"
reply = msg.reply_with(warn_msg)
client.send_and_warn(reply)
return None
except Exception as _:
warn_msg = f"数据请求中发生未知错误, 请呼叫 shenjack\n{traceback.format_exc()}"
reply = msg.reply_with(warn_msg)
client.send_and_warn(reply)
return None
if not response.status_code == 200 or response.reason != "OK":
warn_msg = f"请求失败, 请检查网络\n{response.status_code} {response.reason}"
reply = msg.reply_with(warn_msg)
client.send_and_warn(reply)
return None
return response.json()
def bmcl_dashboard(msg: IcaNewMessage, client: IcaClient) -> None:
req_time = time.time()
# 记录请求时间
data = wrap_request("https://bd.bangbang93.com/openbmclapi/metric/dashboard", msg, client)
dashboard_status = wrap_request("https://bd.bangbang93.com/openbmclapi/metric/version", msg, client)
if data is None or dashboard_status is None:
return
global backend_version
backend_version = dashboard_status["version"]
backend_commit = dashboard_status["_resolved"].split("#")[1][:7]
data_bytes: float = data["bytes"]
data_hits: int = data["hits"]
data_bandwidth: float = data["currentBandwidth"]
load_str: float = data["load"] * 100
online_node: int = data["currentNodes"]
online_bandwidth: int = data["bandwidth"]
data_len = format_data_size(data_bytes)
hits_count = format_hit_count(data_hits)
report_msg = (
f"OpenBMCLAPI 面板v{_version_}-状态\n"
f"api版本 {backend_version} commit:{backend_commit}\n"
f"实时信息: {online_node} 带宽: {online_bandwidth}Mbps\n"
f"负载: {load_str:.2f}% 带宽: {data_bandwidth:.2f}Mbps\n"
f"当日请求: {hits_count} 数据量: {data_len}\n"
f"请求时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(req_time))}\n"
"数据源: https://bd.bangbang93.com/pages/dashboard"
)
client.debug(report_msg)
reply = msg.reply_with(report_msg)
client.send_message(reply)
def check_is_full_data(data: list) -> bool:
return 'user' in data[0]
def display_rank_min(ranks: list, req_time) -> str:
cache = io.StringIO()
cache.write(f"bmclapi v{_version_}-排名({len(ranks)})")
if check_is_full_data(ranks):
cache.write("完整\n")
for rank in ranks:
cache.write('' if rank['isEnabled'] else '')
if 'fullSize' in rank:
cache.write('🌕' if rank['fullSize'] else '🌘')
if 'version' in rank:
cache.write('🟢' if rank['version'] == backend_version else '🟠')
cache.write(f"-{rank['index']+1:3}")
cache.write(f"|{rank['name']}\n")
else:
cache.write("无cookie\n")
for rank in ranks:
cache.write('' if rank['isEnabled'] else '')
cache.write(f"-{rank['index']+1:3}")
cache.write(f"|{rank['name']}\n")
cache.write(f"请求时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(req_time))}")
return cache.getvalue()
def display_rank_full(ranks: list, req_time) -> str:
cache = io.StringIO()
cache.write(f"bmclapi v{_version_}-排名({len(ranks)})")
if check_is_full_data(ranks):
cache.write("完整\n")
for rank in ranks:
# 基本信息
cache.write('' if rank['isEnabled'] else '')
if 'fullSize' in rank:
cache.write('🌕' if rank['fullSize'] else '🌘')
cache.write(f"|{rank['index']+1:3}|")
cache.write(f"{rank['name']}")
if 'version' in rank:
cache.write(f"|{rank['version']}")
cache.write('🟢' if rank['version'] == backend_version else '🟠')
cache.write('\n')
# 用户/赞助信息
if ('user' in rank) and (rank['user'] is not None):
cache.write(f"所有者:{rank['user']['name']}")
if 'sponsor' in rank:
cache.write(f"|赞助者:{rank['sponsor']['name']}")
if 'sponsor' in rank or ('user' in rank and rank['user'] is not None):
cache.write('\n')
# 数据信息
if 'metric' in rank:
hits = format_hit_count(rank['metric']['hits'])
data = format_data_size(rank['metric']['bytes'])
cache.write(f"hit/data|{hits}|{data}")
cache.write('\n')
else:
cache.write("无cookie\n")
for rank in ranks:
cache.write('' if rank['isEnabled'] else '')
cache.write(f"-{rank['index']+1:3}")
cache.write(f"|{rank['name']}|\n")
if 'sponsor' in rank:
cache.write(f"赞助者: {rank['sponsor']['name']}|")
if 'metric' in rank:
cache.write(f"hit/data|{format_hit_count(rank['metric']['hits'])}|{format_data_size(rank['metric']['bytes'])}\n")
cache.write(f"请求时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(req_time))}")
return cache.getvalue()
def bmcl_rank_general(msg, client):
req_time = time.time()
# 记录请求时间
rank_data = wrap_request("https://bd.bangbang93.com/openbmclapi/metric/rank", msg, client)
if rank_data is None:
return
# 预处理数据
for i, r in enumerate(rank_data):
r['index'] = i
# 显示前3名
ranks = rank_data[:3]
# ranks = rank_data
# image = PIL.Image.new("RGB", (100, 100), (255, 255, 255))
# img_cache = io.BytesIO()
# image.save(img_cache, format="JPEG")
# raw_img = img_cache.getvalue()
# img_cache.close()
report_msg = display_rank_full(ranks, req_time)
client.debug(report_msg)
reply = msg.reply_with(display_rank_full(ranks, req_time))
# reply.set_img(raw_img, "image/jpeg", False)
client.send_message(reply)
def bmcl_rank(msg: IcaNewMessage, client: IcaClient, name: str) -> None:
req_time = time.time()
# 记录请求时间
rank_data = wrap_request("https://bd.bangbang93.com/openbmclapi/metric/rank", msg, client)
if rank_data is None:
return
# 预处理数据
for i, r in enumerate(rank_data):
r['index'] = i
# 搜索是否有这个名字的节点
names: List[str] = [r["name"].lower() for r in rank_data]
# try:
# import regexrs
# pattern = regexrs.compile(name)
# finds = [pattern.match(n) for n in names]
# except Exception as e:
finds = [name.lower() in n for n in names]
if not any(finds):
reply = msg.reply_with(f"未找到名为{name}的节点")
client.send_message(reply)
return
# 如果找到 > 3 个节点, 则提示 不显示
counts = [f for f in finds if f]
ranks = [rank_data[i] for i, f in enumerate(finds) if f]
if len(counts) > 3:
if len(counts) > 10:
reply = msg.reply_with(f"搜索|{name}|到{len(counts)}个节点, 请用更精确的名字")
else:
# 4~10 个节点 只显示名称和次序
report_msg = display_rank_min(ranks, req_time)
reply = msg.reply_with(report_msg)
client.send_message(reply)
return
# 如果找到 <= 3 个节点, 则显示全部信息
report_msg = display_rank_full(ranks, req_time)
client.debug(report_msg)
reply = msg.reply_with(report_msg)
client.send_message(reply)
def bangbang_img(msg: IcaNewMessage, client: IcaClient) -> None:
data = requests.get("https://api.bangbang93.top/api/link")
if data.status_code != 200:
reply = msg.reply_with(f"请求失败 {data.status_code} {data.reason}")
client.send_message(reply)
return
raw_name = data.url.split("/")[-1]
img_suffix = raw_name.split(".")[-1]
# mine 映射一下
if img_suffix.lower() in ("jpeg", "jpg"):
img_suffix = "jpeg"
img_name = raw_name[:-len(img_suffix) - 1]
img_name = urllib.parse.unquote(img_name)
mime_format = f"image/{img_suffix}"
client.info(f"获取到随机怪图: {img_name} {img_suffix}")
reply = msg.reply_with(img_name)
reply.set_img(data.content, mime_format, True)
client.send_message(reply)
help = """/bmcl -> dashboard
/bmcl rank -> all rank
/bmcl rank <name> -> rank of <name>
/bm93 -> 随机怪图
/brrs <name> -> rank of <name>
搜索限制:
1- 3 显示全部信息
4-10 显示状态名称
11+ 不显示
"""
def on_ica_message(msg: IcaNewMessage, client: IcaClient) -> None:
if not (msg.is_from_self or msg.is_reply):
if '\n' in msg.content:
return
try:
if not msg.content.startswith("/b"):
return
global backend_version
if backend_version == "unknown":
dashboard_status = wrap_request("https://bd.bangbang93.com/openbmclapi/metric/version", msg, client)
if dashboard_status is None:
return
backend_version = dashboard_status["version"]
if msg.content.startswith("/bmcl"):
if msg.content == "/bmcl":
bmcl_dashboard(msg, client)
elif msg.content == "/bmcl rank":
bmcl_rank_general(msg, client)
elif msg.content.startswith("/bmcl rank") and len(msg.content) > 11:
name = msg.content[11:]
bmcl_rank(msg, client, name)
else:
reply = msg.reply_with(help)
client.send_message(reply)
elif msg.content.startswith("/brrs"):
if msg.content == "/brrs":
reply = msg.reply_with(help)
client.send_message(reply)
else:
name = msg.content.split(" ")
if len(name) > 1:
name = name[1]
bmcl_rank(msg, client, name)
elif msg.content == "/bm93":
bangbang_img(msg, client)
except: # noqa
report_msg = f"bmcl插件发生错误,请呼叫shenjack\n{traceback.format_exc()}"
if len(report_msg) > 200:
report_msg = report_msg[:200] + "..." # 防止消息过长
reply = msg.reply_with(report_msg)
client.send_and_warn(reply)
def on_tailchat_message(msg, client) -> None:
if not msg.is_reply:
if '\n' in msg.content:
return
try:
if not msg.content.startswith("/b"):
return
global backend_version
if backend_version == "unknown":
dashboard_status = wrap_request("https://bd.bangbang93.com/openbmclapi/metric/version", msg, client)
if dashboard_status is None:
return
backend_version = dashboard_status["version"]
if msg.content.startswith("/bmcl"):
if msg.content == "/bmcl":
bmcl_dashboard(msg, client)
elif msg.content == "/bmcl rank":
bmcl_rank_general(msg, client)
elif msg.content.startswith("/bmcl rank") and len(msg.content) > 11:
name = msg.content[11:]
bmcl_rank(msg, client, name)
else:
reply = msg.reply_with(help)
client.send_message(reply)
elif msg.content.startswith("/brrs"):
if msg.content == "/brrs":
reply = msg.reply_with(help)
client.send_message(reply)
else:
name = msg.content.split(" ")
if len(name) > 1:
name = name[1]
bmcl_rank(msg, client, name)
elif msg.content == "/bm93":
bangbang_img(msg, client)
except: # noqa
report_msg = f"bmcl插件发生错误,请呼叫shenjack\n{traceback.format_exc()}"
if len(report_msg) > 200:
report_msg = report_msg[:200] + "..." # 防止消息过长
reply = msg.reply_with(report_msg)
client.send_and_warn(reply)
def on_config() -> Tuple[str, str]:
return (
"bmcl.toml",
"""cookie = \"填写你的 cookie\""""
)

4
plugins/md5/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
input.txt
node_modules
package.json
package-lock.json

View File

@ -0,0 +1 @@
@@ABMECIGUB@OK@CHBAA@FCMFNBEABCICEG@DJDGBGHMDALBAHHEDIEFB@AACFLCFDC@ABIC@DBLIABFPCFDCTD@B@@ID@@EHDI@GDGFDAH@ABBAGBBF@PKALADHG@AAOC@EJ@@FCHC@MLKBAHBGDAKDGA\C@A@FHE@TTHWCGHJBBAFBSGCAAF@I@D@A@AC@AGIFXHBAEU@@@AMTGFF@AAIBJGAKAAAE@BJIMH@CAAHAABC@DD@L@AABEDFECBCT@BGED@GDF@CFDDGH@ACDBDH@DAFOBBIKD@ICJGCAH@GBADBDGDH@@DIY@BFDEOEAS@G@DIN@GABNHECOCBEAHPBC@AIBDAFBDWB@GCAB@EACD@DE@@FFDB@JBFAENJ@L@JMM@R@JD@@DBCFCDAB@@@EBABCEA@PAEBKB@@@DXBCACFABCDFBIAFDBAEAFCY@LB@EHH@B@BIIDFGDOCH@EB@LFCCCDCE@BF@DG@LNDQKKCC@FACDFCGBCSC@CLI@CHLBHELHA@BCA@AABSBBBNBFI@MBCMB@UB@PNA@DEJ_GOD@DKDBIMC@BMBBADFVEB@DCBA@AG@HE@FDCE@AC@EBBL]@AF@@A@FBHBECJAFDAMBA@EBEICGFDCEBADMC@LC@FH@D@@EEBAFPAHIAAACBBD@BBBCCDCB@GDHCMHAGAGA@HVBFCRCDHCJBERJBBEBEIGOAD@WBDCDKAACK@BAMAOADFE@@EUFAF@NKICDFBFWLAIICBABBKWBGLLZAGHBA@AB@A@DEBG@HER@CDAO@@FCDA@EOFB@DBOJRAAAABCAFFDBAAKDB@ACD@FC@I@@EB@HHCBGCCLCB@BDBA@BFBBHNIA@DDQA@@@EAO@EHIGBOAFSAZCGJRCN@CMJL@CACE@HCIDTBGSFAA@AA@DFBA@J@DJDGEBEFBBHIJ@GCFGDLBA@@DBBD@FA@TBCAIHAJ@CCLHDA@DHFF@HJCF@DEAJE@JGI@ABFJEDKD@DGEICHECPMDBIELA@D[F@HDGDABBND@CAKHBCCDOAJACEE@CIDFVALABQAC@P@HBBBKBA@AM@@RCAC@PKD@HC@@IABD@PCFFA@NQD@DBJFJCAAA@HACFFBAAJ@AM@GDBECBADD@D@@CNK@@FPEJGDATACFJQANKADFA@LKVGEAJQAAB@@KDA@C@DCAEBAADBNHCCE@@@AACGED@JBBFBHANAFMAASHDC@DBIJJHDECAD@FDAFECDGC@GHQB@@BBCEFEXABBA@@EIIJF@@DEC@AAQCA@A@CIBGNACK@CHLEFBCBD@BOIB@BJDEGFD@AL@INECCAUAXHBBEDWBBA@BBLDADD@BHB@D@HHFB@GGAKABAAEGCKFJHJC@@@HECDDVBL@BCLB@DEEE@BDFN[@DPA@DF@AAFF@B@EGEDG@ABBBFCAAA@FPAJBBACBBFGA@@@D@LOOBBQDGDOJRACGABCUCAHGBF@BBKDAA@FCCO[AAIALA@SKHKB@ABRLDDBC@CAEAFBGFCLA@DBFFB@A@EG]DEGEBQWKMB@NBBMEADKI^@@@KHED@P@CC@FACZNADAM@IADC@V@@@A@IO@CKEEKCBLIBCBG@AEBCANA@BJAFBAVCLDGHCKPDEDLIFHBL@@FF@DH@ABDGFDF@HLDIBAEBCF@@BLFBA@AAFBCDBBFA@FBL@AA@AABBCFEEAFCJUICDI@@@I@AA@FBBDFE@C@AKAEBBBC@EGAEKACA@@B@AGHMHHD@ACGABHBEFVCT@FGADC@DADGDADLPNGBAAAEACF@D@PAH@PELHAEJ@CCKEICAAG@DNDE@CDBF@AEJK@O@DEC@FCICLDNHHDBCANBCE@AGLCDGCHCBCCCA@PECE@K@AJJ@QBNBO@@DODCEBNLFBACBEBFABJCFBOBPKRBB@EAFACGGIC@CGG^DDAFVACFHBBACAJDP@GDM@ABB@GG@@@B@K@EBFFAFB@DGB@FANBH@BQDE@FEBBGJ@FBGBAOAI@DQEJA@KE@ATC@JUG@QADH@@HAB@DDA@JCFFB@AAOCL@MPBAC@@CAQCCAA@@DIUHG@EBUEAIHSERCJK@@GTAD@N@AHRCMEL@HCA@@AICAB@HI@AF@ABLAEW@GBZ@CFNKDCHN@@BDO@@CFL@NDBN@L@AUBHBAEFEEQGAKHQ@@@CAB@KIOCA@CF@P@MA@DLBABARQBA@KCAIDGIDG@JCCBBFBFKED@ABEFBBAHGDBDBBMBDFBDBDDABABD@DPCF\DB@G@DD@E@G@CV@EBF@O@@CA@BQFDBLAB@BDGHF@@@@BI@DADEBBCDEGCD@A_@EAKACCA@CICDEPKGCEADNABFDCG@DFCBPE@FEDECBAFGBAGCBH@JDAENADAF@CBHACBA@@BCCQCF@FNNBAD@GABKAEBFACGCBFKCGOEBG@DBI@B@GBAJADIOWA@KKLCGFAABAR@TH@BBDAF@DMDA@AHAABREIABAHCED@@EAFHHEBEDSEABNMBAOREEJ@A@ALACIGHFBNPIEDDFDF@@KBHCGBBBOBCAILBFHDMATBDAD@ADFBWHJ@@BACCRC@FJAB@DCFM@IBHCIAF@ED@HAG@@@ECDHBAAHBEKDC@@@BCENC@C@ACDEBDG@A@A@K@BBCNOK@FBCAFIAPFECBBIOEGHDACGBB@@J@ADEECBEG@AACEADD@@JEGACHJ@BDE@JE@IFAQGEBFDDDKUECEEHFD_@CDCG@BJALEB@B@@QEKE@LKBD@ABW@QBNNAL@B@KCADDADA@BD@QBEQ@DQUC@EC@BOGB@AM@DGBAANEIAGFNPEICA@CHEDGDCD@A@LEP@N@CEMFJ@@BIACB@NIBJG@NG^A@EAIMCDHH@BCFEPOJGC@CSNAAAJ@FCGT@FAAOKHXFVFACJHFMAADAA@LA@MHLKTGSZ@HAAJ@AABD@BCBEPDHCJJB@JAABKEBCCEH@D@DBHIAACABAGBBB@EGCACFUDFC@LB@AAFHDBC@G@E@DQD@DKD@CFEBHABANEQ@CCLDAGCAHBA@@@CKC@DAHCGEMJOBGZ@A@CB@AD@ECQ@DAZNN@ACC@@CECDCJAFNJRGBGG@NCBJ@@BI@DBIDCO@B@CCDAAMTGGGCAKDGG@A@ALBA@@FDQ@BA@AB@AVKDJED@@@CDLFC@@C@PCBMCDK@IEHCEAC@CRCOBCAFAGEFUK@@BADGAUFBFIA@H@AI@DDB@BGAAHLVC@KCKBDCBBACHC@A@NI@BEBDIFVF@AGBCIELDIZABAJAFD@AUKS@DJ@@DD@BMHABFBE@IBBDBADAM@GMDBHD@@EAD@BEADGPGGAAFEEABBECKIBAW@BEEQE@DD@FCCAEHJFB@C@E@BMJCB@ALGD@CBC@F@AFJDECAAA@BEO@EBNACDCUH@@K@A@DB@BFECFMAEHDBAEEADFB@EE@CI@ABDBDCAD@A@@B@JCB@FAGJCK@GG@ABFLQ@BHDEHOAB@DBIDSBDBIVA@FD@@ABCAAAA

176
plugins/md5/assets/zh.json Normal file
View File

@ -0,0 +1,176 @@
{
"recover": "[1]回复体力[2]点",
"sklAbsorb": "[0]发起[吸血攻击]",
"sklAccumulate": "[0]开始[聚气]",
"sklAccumulated": "[1]攻击力上升",
"sklAccumulateCancel": "[1]的[聚气]被打消了",
"sklAssassinate1": "[0][潜行]到[1]身后",
"sklAssassinate2": "[0]发动[背刺]",
"dodge": "[0][回避]了攻击(通用)",
"sklAssassinateFailed": "[0]的[潜行]被识破",
"sklBerserkEnd": "[1]从[狂暴]中解除",
"sklBerserkAttack": "[0]发起[狂暴攻击]",
"sklBerserkHit": "[1]进入[狂暴]状态",
"sklBerserk": "[0]使用[狂暴术]",
"sklCharge": "[0]开始[蓄力]",
"sklChargeCancel": "[1]的[蓄力]被中止了",
"sklCharmEnd": "[1]从[魅惑]中解除",
"sklCharm": "[0]使用[魅惑]",
"sklCharmHit": "[1]被[魅惑]了",
"sklClone": "[0]使用[分身]",
"sklCloned": "出现一个新的[1]",
"sklCritical": "[0]发动[会心一击]",
"sklCurseDamage": "[诅咒]使伤害加倍",
"sklCurseEnd": "[1]从[诅咒]中解除",
"sklCurseHit": "[1]被[诅咒]了",
"sklCurse": "[0]使用[诅咒]",
"sklDisperse": "[0]使用[净化]",
"sklExchange": "[0]使用[生命之轮]",
"sklExchanged": "[1]的体力值与[0]互换",
"sklFire": "[0]使用[火球术]",
"sklHalf": "[0]使用[瘟疫]",
"sklHalfDamage": "[1]体力减少[2]%",
"sklHasteEnd": "[1]从[疾走]中解除",
"sklHaste": "[0]使用[加速术]",
"sklHasteHit": "[1]进入[疾走]状态",
"sklHeal": "[0]使用[治愈魔法]",
"sklIceEnd": "[1]从[冰冻]中解除",
"sklIceHit": "[1]被[冰冻]了",
"sklIce": "[0]使用[冰冻术]",
"sklIron": "[0]发动[铁壁]",
"sklIrond": "[0]防御力大幅上升",
"sklIronCancel": "[1]的[铁壁]被打消了",
"sklIronEnd": "[0]从[铁壁]中解除",
"sklPoisonDamage": "[1][毒性发作]",
"sklPoisonEnd": "[1]从[中毒]中解除",
"sklPoisonHit": "[1][中毒]",
"sklPoison": "[0][投毒]",
"sklQuake": "[0]使用[地裂术]",
"SklRapid": "[0]发起攻击",
"SklRapidNext": "[0][连击]",
"sklRevive": "[0]使用[苏生术]",
"sklRevived": "[1][复活]了",
"sklPossess": "[0]使用[附体]",
"sklShadow": "[0]使用[幻术]",
"sklShadowName": "幻影",
"sklShadowed": "召唤出[1]",
"sklSlowEnd": "[1]从[迟缓]中解除",
"sklSlow": "[0]使用[减速术]",
"sklSlowHit": "[1]进入[迟缓]状态",
"sklExplode": "[0]使用[自爆]",
"sklSummon": "[0]使用[血祭]",
"sklSummonName": "使魔",
"sklSummoned": "召唤出[1]",
"sklThunder": "[0]使用[雷击术]",
"sklThunderEnd": "[0][回避]了攻击(雷击)",
"benchmarking": "实力评估中...[2]%",
"benchmarkRatio": "》 胜率: [2]%",
"benchmarkScore": "》 实力评分: [2]",
"benchmarkSkill": "频率: [2]%",
"searchInvalid": "错误目前最多支持8000人搜索",
"searchStart": "搜索开始...",
"searchEnd": "搜索结束",
"searchFailed": "但是一无所获",
"bossName_aokiji": "青雉",
"sklAokijiDefend": "[0][吸收]所有冰冻伤害",
"sklAokijiIceAge": "[0]使用[冰河时代]",
"bossName_conan": "柯南",
"sklConanKillUnknown": "[0]在一间密室中发现了一具无名尸体",
"sklConanThinking": "[0]正在进行推理",
"sklConanThinkingFinish": "[0]推理完毕",
"sklConanThinkingFinish2": "真相只有一个",
"sklConanThinkingFinish3": "凶手就是你",
"sklConanKillLast": "[1]",
"sklConanKill": "[0]在一间密室中发现了[1]的尸体",
"bossName_covid": "新冠病毒",
"sklCovidDamage": "[1][肺炎]发作",
"sklCovidICU": "[1]在重症监护室无法行动",
"sklCovidStayHome": "[1]在家中自我隔离",
"sklCovidInfect": "[0]和[1]近距离接触",
"sklCovidPrevent": "但[1]没被感染",
"sklAttack": "[0]发起攻击",
"sklCovidMutate": "[1]所感染的病毒发生变异",
"sklCovidHit": "[1]感染了[新冠病毒]",
"bossName_ikaruga": "斑鸠",
"sklIkarugaDefend": "[0][吸收]所有奇数伤害",
"sklIkarugaAttack": "[0]使用[能量释放]",
"bossName_lazy": "懒癌",
"sklLazyDamage": "[1][懒癌]发作",
"sklLazySkipTurn1": "[0]打开了[Steam]",
"sklLazySkipTurn2": "[0]打开了[守望先锋]",
"sklLazySkipTurn3": "[0]打开了[文明6]",
"sklLazySkipTurn4": "[0]打开了[英雄联盟]",
"sklLazySkipTurn5": "[0]打开了[微博]",
"sklLazySkipTurn6": "[0]打开了[朋友圈]",
"sklLazySkipTurn0": "这回合什么也没做",
"sklLazyHit": "[1]感染了[懒癌]",
"bossName_mario": "马里奥",
"bossMarioGrow10": "[0]得到[蘑菇]",
"bossMarioGrow11": "[0]攻击力上升",
"bossMarioGrow20": "[0]得到[火焰花]",
"bossMarioGrow21": "[0]学会[火球术]",
"bossMarioGrow30": "[0]得到[奖命蘑菇]",
"bossMarioLife": "[0]还剩[2]条命",
"bossMarioRevive": "[0]满血复活",
"bossName_mosquito": "蚊",
"bossName_saitama": "一拳超人",
"saitamaHungry": "[0]觉得有点饿",
"saitamaLeave": "[0]离开了战场",
"bossName_slime": "史莱姆",
"sklSlimeSpawn": "[0][分裂]",
"sklSlimeSpawned": "分成了[0] 和 [1]",
"bossName_sonic": "索尼克",
"bossName_yuri": "尤里",
"sklYuriControl": "[0]使用[心灵控制]",
"endMessage": "你已经玩了[0]局了",
"continueGame": "继续游戏",
"navigationLink": "navigation.html",
"errorMaxPlayer": "错误目前最多支持1000人PK",
"errorMinPlayer": "错误,请至少输入两行名字",
"welcome": "名字竞技场",
"welcome2": "(MD5大作战10周年纪念)",
"winnerName": "胜者",
"score": "得分",
"killedCount": "击杀",
"killerName": "致命一击",
"loserName": "败者",
"returnTitle": "返回",
"shareTitle": "分享",
"helpTitle": "帮助",
"HP": "HP",
"detail": " 攻 [] 防 [] 速 [] 敏 [] 魔 [] 抗 [] 智 []",
"inputTitle": "名字竞技场",
"inputPlaceholder": "修改by shenjackyuanjie&超导体元素\n\n版本: latest\n可能会有一些问题, 稳定版请使用根目录下版本",
"startFight": "开 始",
"closeTitle": "关闭",
"fastTitle": "快进",
"challengeLabel": "挑战Boss",
"selectBossHint": "选择Boss",
"win": "[2]获得胜利",
"minionDie": "[1]消失了",
"damage": "[1]受到[2]点伤害",
"die": "[1]被击倒了",
"sklMagicAttack": "[0]发起攻击",
"sklCounter": "[0]发起[反击]",
"defend": "[0][防御]",
"sklHide": "[0]发动[隐匿]",
"sklMerge": "[0][吞噬]了[1]",
"sklMerged": "[0]属性上升",
"sklProtect": "[0][守护][1]",
"sklReflect": "[0]使用[伤害反弹]",
"sklReraise": "[0]使用[护身符]抵挡了一次死亡",
"sklUpgrade": "[0]做出[垂死]抗争",
"sklUpgraded": "[0]所有属性上升",
"sklUpgradeCancel": "[1]的[垂死]属性被打消",
"sklZombieName": "丧尸",
"sklZombie": "[0][召唤亡灵]",
"sklZombied": "[2]变成了[1]",
"weaponDeathNoteAtk": "[0]在[死亡笔记]写下[1]的名字",
"weaponRModifierUse": "[0]使用[属性修改器]",
"weaponS11_0": "[0]在促销日[购买]了武器",
"weaponS11_1": "但是并没有什么用",
"weaponS11_2": "增加了[2]点",
"weaponS11Done1": "[0]信用卡刷爆",
"weaponS11Done3": "[0]砍下了自己的左手",
"weaponS11Done2": "[0]砍下了自己的右手"
}

230
plugins/md5/md5-api.js Normal file
View File

@ -0,0 +1,230 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var md5_module = require("./md5.js");
/**
*
* @param names 原始的输入框输入
* @returns 对战结果
*/
function fight(names) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
// 检查一下输入是否合法
// 比如里面有没有 !test!
if (names.indexOf("!test!") !== -1) {
throw new Error("你怎么在对战输入里加 !test!(恼)\n${names}");
}
return [4 /*yield*/, md5_module.fight(names)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
/**
* 对于胜率/评分的输入检查
* @param names
* @returns
*/
function test_check(names) {
var have_test = names.trim().startsWith("!test!");
return have_test;
}
/**
* 测量胜率
* @param names 原始的输入框输入
* @param round 战斗的回合数
* @returns 胜率结果
*/
function win_rate(names, round) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
// 检查 round 是否合法
if (round <= 0) {
throw new Error("round 必须大于 0");
}
if (!test_check(names)) {
throw new Error("你怎么在胜率输入里丢了 !test!(恼)\n${names}");
}
return [4 /*yield*/, md5_module.win_rate(names, round)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
/**
*
* @param names 原始的输入框输入
* @param callback 用于接收胜率的回调函数
* @returns 胜率结果
*/
function win_rate_callback(names, callback) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!test_check(names)) {
throw new Error("你怎么在胜率输入里丢了 !test!(恼)\n${names}");
}
return [4 /*yield*/, md5_module.win_rate_callback(names, callback)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
function score(names, round) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
// 检查 round 是否合法
if (round <= 0) {
throw new Error("round 必须大于 0");
}
if (!test_check(names)) {
throw new Error("你怎么在分数输入里丢了 !test!(恼)\n${names}");
}
return [4 /*yield*/, md5_module.score(names, round)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
function score_callback(names, callback) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!test_check(names)) {
throw new Error("你怎么在分数输入里加 !test!(恼)\n${names}");
}
return [4 /*yield*/, md5_module.score_callback(names, callback)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
function run_any(names, round) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, md5_module.run_any(names, round)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
}
var out_limit = 1000;
function wrap_any(names, round) {
return __awaiter(this, void 0, void 0, function () {
var result, win_rate_1, win_rate_str, output_str_1, output_datas_1, win_rate_2, output_str_2, output_datas_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, run_any(names, round)];
case 1:
result = _a.sent();
if ('message' in result) {
// 对战结果
return [2 /*return*/, "\u8D62\u5BB6:|".concat(result.source_plr, "|")];
}
else if ('win_count' in result) {
win_rate_1 = result.win_count * 100 / round;
win_rate_str = win_rate_1.toFixed(4);
output_str_1 = "\u6700\u7EC8\u80DC\u7387:|".concat(win_rate_str, "%|(").concat(round, "\u8F6E)");
// 每 500 轮, 输出一次
if (round > out_limit) {
output_datas_1 = [];
result.raw_data.forEach(function (data, index) {
if (data.round % out_limit === 0) {
output_datas_1.push(data);
}
});
output_datas_1.forEach(function (data, index) {
var win_rate = data.win_count * 100 / data.round;
output_str_1 += "\n".concat(win_rate.toFixed(2), "%(").concat(data.round, ")");
});
}
return [2 /*return*/, output_str_1];
// } else if ('score' in result) {
}
else {
win_rate_2 = (result.score * 10000 / round).toFixed(2);
output_str_2 = "\u5206\u6570:|".concat(win_rate_2, "|(").concat(round, "\u8F6E)");
if (round > out_limit) {
output_datas_2 = [];
result.raw_data.forEach(function (data, index) {
if (data.round % out_limit === 0) {
output_datas_2.push(data);
}
});
output_datas_2.forEach(function (data, index) {
var win_rate = (data.score / data.round * 10000).toFixed(2);
output_str_2 += "\n".concat(win_rate, "(").concat(data.round, ")");
});
}
return [2 /*return*/, output_str_2];
}
return [2 /*return*/];
}
});
});
}
function main() {
return __awaiter(this, void 0, void 0, function () {
var fs, path, names, start_time, result, end_time;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
fs = require("fs");
path = require("path");
names = fs.readFileSync(path.resolve(__dirname, "input.txt"), "utf-8");
start_time = Date.now();
return [4 /*yield*/, wrap_any(names, 10000)];
case 1:
result = _a.sent();
end_time = Date.now();
console.log(result);
console.log("Node.js \u8017\u65F6: ".concat(end_time - start_time, " ms"));
return [2 /*return*/];
}
});
});
}
main();

207
plugins/md5/md5-api.ts Normal file
View File

@ -0,0 +1,207 @@
const md5_module = require("./md5.js");
// import * as md5_module from "./md5.js";
/**
*
* source_plr ,
*/
type FightResult = {
message: string;
source_plr: string;
target_plr: string;
affect: string | number;
};
/**
*
*/
type WinRate = {
round: number;
win_count: number;
};
/**
*
*/
type WinRateResult = {
win_count: number;
raw_data: WinRate[];
};
/**
*
* bool, true , false
*/
type WinRateCallback = (run_round: number, win_count: number) => boolean;
/**
*
*/
type Score = {
round: number;
score: number;
};
/**
*
*/
type ScoreResult = {
score: number;
raw_data: Score[];
};
/**
*
* bool, true , false
*/
type ScoreCallback = (run_round: number, score: number) => boolean;
/**
*
* @param names
* @returns
*/
async function fight(names: string): Promise<FightResult> {
// 检查一下输入是否合法
// 比如里面有没有 !test!
if (names.indexOf("!test!") !== -1) {
throw new Error("你怎么在对战输入里加 !test!(恼)\n${names}");
}
return await md5_module.fight(names);
}
/**
* /
* @param names
* @returns
*/
function test_check(names: string): boolean {
const have_test = names.trim().startsWith("!test!");
return have_test;
}
/**
*
* @param names
* @param round
* @returns
*/
async function win_rate(names: string, round: number): Promise<WinRateResult> {
// 检查 round 是否合法
if (round <= 0) {
throw new Error("round 必须大于 0");
}
if (!test_check(names)) {
throw new Error("你怎么在胜率输入里丢了 !test!(恼)\n${names}");
}
return await md5_module.win_rate(names, round);
}
/**
*
* @param names
* @param callback
* @returns
*/
async function win_rate_callback(
names: string,
callback: WinRateCallback,
): Promise<WinRateResult> {
if (!test_check(names)) {
throw new Error("你怎么在胜率输入里丢了 !test!(恼)\n${names}");
}
return await md5_module.win_rate_callback(names, callback);
}
async function score(names: string, round: number): Promise<ScoreResult> {
// 检查 round 是否合法
if (round <= 0) {
throw new Error("round 必须大于 0");
}
if (!test_check(names)) {
throw new Error("你怎么在分数输入里丢了 !test!(恼)\n${names}");
}
return await md5_module.score(names, round);
}
async function score_callback(
names: string,
callback: ScoreCallback,
): Promise<ScoreResult> {
if (!test_check(names)) {
throw new Error("你怎么在分数输入里加 !test!(恼)\n${names}");
}
return await md5_module.score_callback(names, callback);
}
async function run_any(names: string, round: number): Promise<FightResult | WinRateResult | ScoreResult> {
return await md5_module.run_any(names, round);
}
const out_limit: number = 1000;
async function wrap_any(names: string, round: number): Promise<string> {
const result = await run_any(names, round);
if ('message' in result) {
// 对战结果
return `赢家:|${result.source_plr}|`;
} else if ('win_count' in result) {
// 胜率结果
const win_rate = result.win_count * 100 / round;
let win_rate_str = win_rate.toFixed(4);
let output_str = `最终胜率:|${win_rate_str}%|(${round}轮)`;
// 每 500 轮, 输出一次
if (round > out_limit) {
// 把所有要找的数据拿出来
let output_datas: WinRate[] = [];
result.raw_data.forEach((data, index) => {
if (data.round % out_limit === 0) {
output_datas.push(data);
}
});
output_datas.forEach((data, index) => {
const win_rate = data.win_count * 100 / data.round;
output_str += `\n${win_rate.toFixed(2)}%(${data.round})`;
});
}
return output_str;
// } else if ('score' in result) {
} else {
// 分数结果其实还是个胜率, 不过需要 * 100
const win_rate = (result.score * 10000 / round).toFixed(2);
let output_str = `分数:|${win_rate}|(${round}轮)`;
if (round > out_limit) {
// 把所有要找的数据拿出来
let output_datas: Score[] = [];
result.raw_data.forEach((data, index) => {
if (data.round % out_limit === 0) {
output_datas.push(data);
}
});
output_datas.forEach((data, index) => {
const win_rate = (data.score / data.round * 10000).toFixed(2);
output_str += `\n${win_rate}(${data.round})`;
});
}
return output_str;
}
}
async function main() {
// 从相对位置导入内容
const fs = require("fs");
const path = require("path");
const names = fs.readFileSync(path.resolve(__dirname, "input.txt"), "utf-8");
// const result = await fight(names);
// const result = await md5_module.run_any(names, 50000);
// console.log(`赢家:|${result.source_plr}|`);
const start_time = Date.now();
const result = await wrap_any(names, 10000);
const end_time = Date.now();
console.log(result);
console.log(`Node.js 耗时: ${end_time - start_time} ms`);
}
main();

21903
plugins/md5/md5.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,208 @@
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(encoding="utf-8")
team_bytes = name_lst[2].encode(encoding="utf-8")
self.name = name_lst[0]
self.team = name_lst[2]
self.name_len = len(name_bytes)
self.team_len = len(team_bytes)
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 sorted(
enumerate(self.skl_freq), key=lambda x: x[1], reverse=True
)
if value > 0
]
)
)
return cache.getvalue()

129
plugins/namerena.py Normal file
View File

@ -0,0 +1,129 @@
import io
import sys
import time
import traceback
import subprocess
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,
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.5.0"
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"请使用 {CONVERT_PREFIX} 命令,然后换行输入名字,例如:\n{CONVERT_PREFIX}\n张三\n李四\n王五\n"
)
)
return
# 去掉 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:
# 在判断一下是不是 /xxx xxxx
if msg.content.find(" ") != -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 :]
start_time = time.time()
# 开始 try
try:
# 内容写入到 ./md5/input.txt
# 路径是插件文件的相对路径
root_path = Path(__file__).parent
with open(root_path / "md5" / "input.txt", "w") as f:
f.write(names)
# 执行 node md5.js
runner_path = root_path / "md5" / "md5-api.js"
# input_path = root_path / "md5" / "input.txt"
result = subprocess.run(
["node", runner_path.absolute()],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
# 获取结果
out_result = result.stdout.decode("utf-8")
err_result = result.stderr.decode("utf-8")
# 发送结果
end_time = time.time()
reply = msg.reply_with(
f"{out_result}{err_result}外部耗时:{end_time - start_time:.2f}s\n版本:{_version_}"
)
client.send_message(reply)
except Exception as e:
# 发送错误
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_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

80
plugins/save_eval.pyi Normal file
View File

@ -0,0 +1,80 @@
import time
import random
import traceback
from typing import TYPE_CHECKING, TypeVar
if TYPE_CHECKING:
from ica_typing import IcaNewMessage, IcaClient
else:
IcaNewMessage = TypeVar("NewMessage")
IcaClient = TypeVar("IcaClient")
def safe_eval(code: str, msg: IcaNewMessage) -> str:
try:
# code = code.replace('help', '坏东西!\n')
# code = code.replace('bytes', '坏东西!\n')
# code = code.replace('encode', '坏东西!\n')
# code = code.replace('decode', '坏东西!\n')
# code = code.replace('compile', '屑的!\n')
# code = code.replace('globals', '拿不到!\n')
code = code.replace("os", "坏东西!\n")
code = code.replace("sys", "坏东西!\n")
# code = code.replace('input', '坏东西!\n')
# code = code.replace('__', '啊哈!\n')
# code = code.replace('import', '很坏!\n')
code = code.replace(" kill", "别跑!\n")
code = code.replace(" rm ", "别跑!\n")
code = code.replace("exit", "好坏!\n")
code = code.replace("eval", "啊哈!\n")
code = code.replace("exec", "抓住!\n")
start_time = time.time()
try:
import os
import math
import decimal
global_val = {
"time": time,
"math": math,
"decimal": decimal,
"random": random,
"__import__": "<built-in function __import__>",
"globals": "<built-in function globals>",
"compile": "<built-in function compile>",
"open": "<built-in function open>",
"help": "<built-in function help>",
"exit": "<built-in function exit>",
"input": "<built-in function input>",
"return": "别惦记你那个 return 了",
"getattr": "<built-in function getattr>",
"setattr": "<built-in function setattr>",
"msg": msg,
}
os.system = "不许"
result = str(eval(code, global_val, {}))
limit = 500
if len(result) > limit:
result = result[:limit]
except:
result = traceback.format_exc()
end_time = time.time()
if result == "6" or result == 6:
result = "他确实等于 6"
result = f"{code}\neval result:\n{result}\n耗时: {end_time - start_time} s"
return result
except:
error = traceback.format_exc()
result = f"error:\n{error}"
return result
def on_message(message: IcaNewMessage, client: IcaClient) -> None:
if not (message.is_from_self or message.is_reply):
if message.content.startswith("/="):
code = message.content[2:]
result = safe_eval(code, message)
reply = message.reply_with(result)
client.send_message(reply)