Compare commits

...

6 Commits

Author SHA1 Message Date
1fa7267f3e
0.6.10! 2024-08-02 21:50:39 +08:00
4479002b8d
reee 2024-08-02 21:34:28 +08:00
4753df8ded
我的问题( 2024-08-02 19:25:55 +08:00
b8fd3cfc19
ree 2024-08-02 19:25:39 +08:00
c970e6ae45
清理插件 2024-08-02 19:25:35 +08:00
751402d5c6
remove ica-py 2024-08-02 19:03:58 +08:00
25 changed files with 32 additions and 23941 deletions

2
Cargo.lock generated
View File

@ -659,7 +659,7 @@ dependencies = [
[[package]] [[package]]
name = "ica-rs" name = "ica-rs"
version = "0.6.9" version = "0.6.10"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.22.1", "base64 0.22.1",

Binary file not shown.

View File

@ -1,161 +0,0 @@
import time
import json
import random
import asyncio
import argparse
import traceback
from typing import Dict, List, Tuple, Any
import socketio
from colorama import Fore
from nacl.signing import SigningKey
from lib_not_dr.loggers import config
from data_struct import SendMessage, ReplyMessage, get_config, BotConfig, BotStatus
from main import BOTCONFIG, _version_
from router import route
logger = config.get_logger("icalingua")
sio: socketio.AsyncClient = socketio.AsyncClient()
@sio.on("connect") # type: ignore
def connect():
logger.info(f"{Fore.GREEN}icalingua 已连接")
@sio.on("requireAuth") # type: ignore
async def require_auth(salt: str, versions: Dict[str, str]):
logger.info(f"{Fore.BLUE}versions: {versions}\n{type(salt)}|{salt=}")
# 准备数据
sign = SigningKey(bytes.fromhex(BOTCONFIG.private_key))
signature = sign.sign(bytes.fromhex(salt))
# 发送数据
await sio.emit("auth", signature.signature)
logger.info(f"{Fore.BLUE}send auth emit")
@sio.on("auth") # type: ignore
def auth(data: Dict[str, Any]):
logger.info(f"auth: {data}")
@sio.on("authFailed") # type: ignore
async def auth_failed():
logger.info(f"{Fore.RED}authFailed")
await sio.disconnect()
@sio.on("authSucceed") # type: ignore
def auth_succeed():
logger.info(f"{Fore.GREEN}authSucceed")
@sio.on("connect_error") # type: ignore
def connect_error(*args, **kwargs):
logger.info(f"连接错误 {args}, {kwargs}")
@sio.on("updateRoom") # type: ignore
def update_room(data: Dict[str, Any]):
logger.info(f"{Fore.CYAN}update_room: {data}")
@sio.on("addMessage") # type: ignore
async def add_message(data: Dict[str, Any]):
logger.info(f"{Fore.MAGENTA}add_message: {data}")
is_self = data["message"]["senderId"] == BOTCONFIG.self_id
if not is_self:
await route(data, sio)
@sio.on("deleteMessage") # type: ignore
def delete_message(message_id: str):
logger.info(f"{Fore.MAGENTA}delete_message: {message_id}")
@sio.on("setMessages") # type: ignore
def set_messages(data: Dict[str, Any]):
logger.info(
f"{Fore.YELLOW}set_messages: {data}\nmessage_len: {len(data['messages'])}"
)
async def notice_startup(room_list: List[int]):
for notice_room in BOTCONFIG.notice_room:
if notice_room in room_list:
notice_message = SendMessage(
content=f"ica bot v{_version_}", room_id=notice_room
)
await sio.emit("sendMessage", notice_message.to_json())
BotStatus.inited = True
logger.info("inited", tag="notice room")
else:
logger.warn(f"未找到通知房间: {notice_room}", tag="notice room")
await asyncio.sleep(random.randint(2, 5))
@sio.on("setAllRooms") # type: ignore
async def set_all_rooms(rooms: List[Dict[str, Any]]):
BotStatus.running = True
room_list: List[int] = [room.get("roomId") for room in rooms] # type: ignore
if not BotStatus.inited:
logger.info("initing...", tag="setAllRooms")
logger.debug(f"room_list: {room_list}", tag="setAllRooms")
if BOTCONFIG.notice_start:
await notice_startup(room_list)
if room_list != BotStatus.rooms:
logger.info(f"{Fore.YELLOW}set_all_rooms: {rooms}\nlen: {len(rooms)}\n")
BotStatus.rooms = room_list
logger.info(f"更新房间: {room_list}", tag="setAllRooms")
@sio.on("setAllChatGroups") # type: ignore
def set_all_chat_groups(groups: List[Dict[str, Any]]):
logger.info(f"{Fore.YELLOW}set_all_chat_groups: {groups}\nlen: {len(groups)}\n")
@sio.on("notify") # type: ignore
def notify(data: List[Tuple[str, Any]]):
logger.info(f"notify: {data}")
@sio.on("closeLoading") # type: ignore
def close_loading(_):
logger.info(f"{Fore.GREEN}close_loading")
@sio.on("onlineData") # type: ignore
def online_data(data: Dict[str, Any]):
logger.info(f"{Fore.GREEN}online_data: {data}")
@sio.on("*") # type: ignore
def catch_all(event, data):
logger.info(f"{Fore.RED}catch_all: {event}|{data}")
async def main():
"""
while True:
await self.eio.wait()
await self.sleep(1) # give the reconnect task time to start up
if not self._reconnect_task:
break
await self._reconnect_task
if self.eio.state != 'connected':
break
"""
try:
await sio.connect(BOTCONFIG.host)
await sio.wait()
except KeyboardInterrupt:
logger.info("KeyboardInterrupt")
except Exception:
logger.error(traceback.format_exc())

View File

@ -1,102 +0,0 @@
from typing import List, Optional, Union, Literal, Tuple
import qtoml
from lib_not_dr.types import Options
class AtElement(Options):
text: str
id: Union[int, Literal['all']] = 'all'
class ReplyMessage(Options):
id: str
username: str = ''
content: str = ''
files: list = []
def to_json(self) -> dict:
return {
'_id': self.id,
'username': self.username,
'content': self.content,
'files': self.files
}
class SendMessage(Options):
content: str
room_id: Optional[int] = None
room: Optional[int] = None # room id 和 room 二选一 ( 实际上直接填 room id 就行了 )
file: None = None # TODO: 上传文件
reply_to: Optional[ReplyMessage] = None # 源码 给了一个 any TODO: 回复消息
b64_img: Optional[str] = None # TODO: 发送图片
at: Optional[List[AtElement]] = [] # TODO: @某人
sticker: Optional[None] = None # TODO: 发送表情
message_type: Optional[str] = None # TODO: 消息类型
def to_json(self) -> dict:
return {
'content': self.content,
'roomId': self.room_id,
'room': self.room,
'file': self.file,
'replyMessage': self.reply_to.to_json() if self.reply_to else None,
'b64img': self.b64_img,
'at': self.at,
'sticker': self.sticker,
'messageType': self.message_type
}
def to_content(self, content: str) -> "SendMessage":
self.content = content
return self
class NewMessage(Options):
sender_id: int
sender_name: str
room_id: int
content: str
msg_id: str
data: dict
def init(self, **kwargs) -> None:
data = kwargs.pop('data')
self.sender_name = data["message"]["username"]
self.sender_id = data["message"]["senderId"]
self.content = data["message"]["content"]
self.room_id = data["roomId"]
self.msg_id = data["message"]["_id"]
def is_self(self, self_id: int) -> bool:
return self.sender_id == self_id
class BotConfig(Options):
name = 'icalingua bot config'
# _check_filled = True
private_key: str
host: str
self_id: int
notice_room: List[int]
notice_start: bool = False
admin_list: List[int]
py_plugin_path: str
def init(self, **kwargs) -> None:
if self.notice_room is None:
self.notice_start = False
def get_config(config_path: str = 'config.toml') -> BotConfig:
with open(config_path, 'r', encoding='utf-8') as f:
config = qtoml.decoder.load(f)
return BotConfig(**config)
class BotStatus(Options):
inited: bool = False
running: bool = False
rooms: List[int] = []

View File

@ -1,36 +0,0 @@
import asyncio
import argparse
# from lib_not_dr.types import Options
from lib_not_dr.loggers import config
from data_struct import get_config, BotConfig, BotStatus
_version_ = "0.3.3"
logger = config.get_logger("bot")
BOTCONFIG: BotConfig = get_config()
BotStatus = BotStatus()
if __name__ == "__main__":
# --debug
# --config=config.toml
# -n --no-notice
parser = argparse.ArgumentParser(description=f"icalingua bot v{_version_}")
parser.add_argument("-d", "--debug", action="store_true")
parser.add_argument("-n", "--no-notice", action="store_true")
parser.add_argument("-c", "--config", type=str)
args = parser.parse_args()
if args.debug:
logger.global_level = 0
if args.config:
# global BOTCONFIG
BOTCONFIG: BotConfig = get_config(args.config)
if args.no_notice:
BOTCONFIG.notice_start = False
from connect import main
asyncio.run(main())

View File

@ -1,98 +0,0 @@
import time
import json
import aiohttp
from lib_not_dr.loggers import config
from data_struct import NewMessage, SendMessage
logger = config.get_logger("bmcl")
_version_ = "1.1.1"
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:
return "_".join(count_str[i:i + 4] for i in range(0, count_len, 4))
async def bmcl(sio, reply_msg: SendMessage, msg: NewMessage):
req_time = time.time()
# 记录请求时间
async with aiohttp.ClientSession() as session:
async with session.get(
"https://bd.bangbang93.com/openbmclapi/metric/dashboard"
) as response:
if not response.status == 200 or response.reason != "OK":
await sio.emit(
"sendMessage",
reply_msg.to_content(
f"请求数据失败\n{response.status}"
).to_json(),
)
logger.warn(
f"数据请求失败, 请检查网络\n{response.status}",
tag="bmclapi_dashboard",
)
return
raw_data = await response.text()
try:
data = json.loads(raw_data)
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"实时信息: {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"
)
await sio.emit(
"sendMessage",
reply_msg.to_content(report_msg).to_json()
)
except (json.JSONDecodeError, AttributeError, ValueError) as e:
await sio.emit(
"sendMessage",
reply_msg.to_content(f"返回数据解析错误\n{e}").to_json(),
)
logger.warn(f"返回数据解析错误\n{e}", tag="bmclapi_dashboard")

View File

@ -1,72 +0,0 @@
import time
import random
import traceback
from main import BOTCONFIG
from colorama import Fore
from lib_not_dr.loggers import config
logger = config.get_logger("safe_eval")
def safe_eval(code: str) -> 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>",
"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>",
}
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()
result = result.replace(BOTCONFIG.private_key, "***")
result = result.replace(BOTCONFIG.host, "***")
logger.info(f"{Fore.MAGENTA}safe_eval: {result}")
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

View File

@ -1,7 +0,0 @@
lib-not-dr >= 0.3.13
colorama
qtoml
pynacl
python-socketio
aiohttp

View File

@ -1,62 +0,0 @@
import random
import asyncio
from lib_not_dr.loggers import config
from main import BOTCONFIG, _version_
from data_struct import SendMessage, ReplyMessage, NewMessage
from plugins import bmcl, safe_eval
logger = config.get_logger("router")
async def route(data, sio):
is_self = data["message"]["senderId"] == BOTCONFIG.self_id
sender_name = data["message"]["username"]
sender_id = data["message"]["senderId"]
content = data["message"]["content"]
room_id = data["roomId"]
msg_id = data["message"]["_id"]
msg = NewMessage(data=data)
reply_msg = SendMessage(content="", room_id=room_id, reply_to=ReplyMessage(id=msg_id))
if content == "/bot":
message = reply_msg.to_content(f"icalingua bot-python pong v{_version_}")
await sio.emit("sendMessage", message.to_json())
elif content.startswith("=="):
evals: str = content[2:]
result = safe_eval.safe_eval(evals)
# whitelist = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '.', '+', '-', '*', '/', '(', ')', '<',
# '>', '=']
# evals = evals.replace('**', '')
# express = ''
# for text in evals:
# if text in whitelist:
# express += text
# if express == '':
# result = '你在干嘛'
# else:
# result = str(eval(express))
message = reply_msg.to_content(result)
await asyncio.sleep(random.random() * 2)
await sio.emit("sendMessage", message.to_json())
elif content == "!!jrrp":
randomer = random.Random(
f'{sender_id}-{data["message"]["date"]}-jrrp-{_version_}'
)
result = randomer.randint(0, 50) + randomer.randint(0, 50)
logger.info(f"{sender_name} 今日人品值为 {result}")
message = reply_msg.to_content(f"{sender_name} 今日人品为 {result}")
await asyncio.sleep(0.5)
await sio.emit("sendMessage", message.to_json())
elif content == "/bmcl":
await bmcl.bmcl(sio, reply_msg, msg)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ica-rs" name = "ica-rs"
version = "0.6.9" version = "0.6.10"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,82 +0,0 @@
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)

View File

@ -1,378 +0,0 @@
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\""""
)

View File

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

View File

@ -1 +0,0 @@
@@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

View File

@ -1,176 +0,0 @@
{
"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]砍下了自己的右手"
}

View File

@ -1,230 +0,0 @@
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();

View File

@ -1,207 +0,0 @@
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();

File diff suppressed because one or more lines are too long

View File

@ -1,208 +0,0 @@
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()

View File

@ -1,129 +0,0 @@
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

View File

@ -1,80 +0,0 @@
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)

View File

@ -1,6 +1,7 @@
use std::env; use std::env;
use std::fs; use std::fs;
use colored::Colorize;
use serde::Deserialize; use serde::Deserialize;
use toml::from_str; use toml::from_str;
use tracing::warn; use tracing::warn;
@ -75,8 +76,9 @@ impl BotConfig {
pub fn new_from_path(config_file_path: String) -> Self { pub fn new_from_path(config_file_path: String) -> Self {
// try read config from file // try read config from file
let config = fs::read_to_string(&config_file_path).expect("Failed to read config file"); let config = fs::read_to_string(&config_file_path).expect("Failed to read config file");
let ret: Self = from_str(&config) let ret: Self = from_str(&config).unwrap_or_else(|e| {
.unwrap_or_else(|_| panic!("Failed to parse config file {}", &config_file_path)); panic!("Failed to parse config file {}\ne:{:?}", &config_file_path, e)
});
ret ret
} }
pub fn new_from_cli() -> Self { pub fn new_from_cli() -> Self {
@ -86,7 +88,10 @@ impl BotConfig {
let mut args = env::args(); let mut args = env::args();
while let Some(arg) = args.next() { while let Some(arg) = args.next() {
if arg == "-c" { if arg == "-c" {
config_file_path = args.next().expect("No config path given"); config_file_path = args.next().expect(&format!(
"{}",
"No config path given\nUsage: -c <config_file_path>".red()
));
break; break;
} }
} }

View File

@ -52,6 +52,7 @@ pub async fn start_ica(config: &IcaConfig, stop_reciver: StopGetter) -> ClientRe
*room, *room,
None, None,
); );
// 这可是 qq, 要保命
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
event!(Level::INFO, "发送启动消息到房间: {}", room); event!(Level::INFO, "发送启动消息到房间: {}", room);

View File

@ -95,6 +95,23 @@ pub async fn start_tailchat(
event!(Level::INFO, "{}", "tailchat 已经加入房间".green()); event!(Level::INFO, "{}", "tailchat 已经加入房间".green());
if config.notice_start {
event!(Level::INFO, "正在发送启动消息");
for (group, con) in config.notice_room {
event!(Level::INFO, "发送启动消息到: {}|{}", con, group);
let startup_msg =
crate::data_struct::tailchat::messages::SendingMessage::new_without_meta(
"ica-rs 启动成功".to_string(),
con.clone(),
Some(group.clone()),
);
// 反正是 tailchat, 不需要等, 直接发
if let Err(e) = socket.emit("chat.message.sendMessage", startup_msg.as_value()).await {
event!(Level::ERROR, "发送启动消息失败: {}", e);
}
}
}
stop_reciver.await.ok(); stop_reciver.await.ok();
event!(Level::INFO, "socketio client stopping"); event!(Level::INFO, "socketio client stopping");
match socket.disconnect().await { match socket.disconnect().await {

View File

@ -1,5 +1,9 @@
# 更新日志 # 更新日志
## 0.6.10
- 加了点东西 (?)
## 0.6.9 ## 0.6.9
我决定立即发布 0.6.9 我决定立即发布 0.6.9