Compare commits

..

2 Commits

Author SHA1 Message Date
184b9c92ef
up to 0.2.3! 2024-01-25 22:08:02 +08:00
0ed685b6ec
稍微重新实现了一下配置这边的内容 2024-01-24 18:14:50 +08:00
12 changed files with 409 additions and 207 deletions

View File

@ -1,64 +0,0 @@
from typing import Optional
from lib_not_dr.types import Options
class SocketData(Options):
name = 'SocketData'
def init(self, **kwargs) -> bool:
self.from_json(kwargs)
return False
def from_json(self, data: dict):
...
def to_json(self) -> dict:
return self.option()
class Message(SocketData):
name = 'icalingua socket message'
# 消息 id
message_id: str
# 发送者 id
sender_id: int
# 发送者昵称
sender_name: str
# 消息内容
content: str
# 消息时间戳
# 13:32:46
time_stamp: str
# 消息日期
# 2023/10/05
date: str
# unix 时间戳
# 1633395166
unix_time_stamp: int
# 发送者身份
role: str
# 发送者群昵称/备注
title: str
# 匿名 id
anonymous_id: Optional[int] = None
# 是否匿名
is_anonymous: bool = False
# 啊 ?
bubble_id: int
# 啊 ?
sub_id: int
file: Optional[dict] = None
files: Optional[list] = None
class AddMessage(SocketData):
name = 'icalingua socket add message event'
room_id: int

View File

@ -47,6 +47,10 @@ class Message(Options):
'sticker': self.sticker, 'sticker': self.sticker,
'messageType': self.message_type 'messageType': self.message_type
} }
def to_content(self, content: str) -> "Message":
self.content = content
return self
class BotConfig(Options): class BotConfig(Options):

141
ica-rs/Cargo.lock generated
View File

@ -463,6 +463,12 @@ version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.3" version = "0.3.3"
@ -554,7 +560,9 @@ dependencies = [
"ed25519", "ed25519",
"ed25519-dalek", "ed25519-dalek",
"hex", "hex",
"pyo3",
"rust_socketio", "rust_socketio",
"serde",
"serde_json", "serde_json",
"toml", "toml",
] ]
@ -579,6 +587,12 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "indoc"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.12" version = "0.1.12"
@ -627,6 +641,16 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.20" version = "0.4.20"
@ -639,6 +663,15 @@ version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@ -752,6 +785,29 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.48.5",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
@ -807,6 +863,67 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "pyo3"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a89dc7a5850d0e983be1ec2a463a171d20990487c3cfcd68b5363f1ee3d6fe0"
dependencies = [
"cfg-if",
"indoc",
"libc",
"memoffset",
"parking_lot",
"pyo3-build-config",
"pyo3-ffi",
"pyo3-macros",
"unindent",
]
[[package]]
name = "pyo3-build-config"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07426f0d8fe5a601f26293f300afd1a7b1ed5e78b2a705870c5f30893c5163be"
dependencies = [
"once_cell",
"target-lexicon",
]
[[package]]
name = "pyo3-ffi"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb7dec17e17766b46bca4f1a4215a85006b4c2ecde122076c562dd058da6cf1"
dependencies = [
"libc",
"pyo3-build-config",
]
[[package]]
name = "pyo3-macros"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f738b4e40d50b5711957f142878cfa0f28e054aa0ebdfc3fd137a843f74ed3"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
"quote",
"syn",
]
[[package]]
name = "pyo3-macros-backend"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc910d4851847827daf9d6cdd4a823fbdaab5b8818325c5e97a86da79e8881f"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.33"
@ -978,6 +1095,12 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "security-framework" name = "security-framework"
version = "2.9.2" version = "2.9.2"
@ -1099,6 +1222,12 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.4.10" version = "0.4.10"
@ -1167,6 +1296,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "target-lexicon"
version = "0.12.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.8.1" version = "3.8.1"
@ -1381,6 +1516,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unindent"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.0" version = "2.5.0"

View File

@ -11,9 +11,17 @@ ed25519 = "2.2.3"
ed25519-dalek = "2.1.0" ed25519-dalek = "2.1.0"
hex = "0.4.3" hex = "0.4.3"
rust_socketio = "0.4.4" rust_socketio = "0.4.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.108" serde_json = "1.0.108"
toml = "0.8.8" toml = "0.8.8"
colored = "2.1.0" colored = "2.1.0"
# inline-python = "0.12.0"
[dependencies.pyo3]
version = "0.20.2"
# features = ["auto-initialize"]
[patch.crates-io] [patch.crates-io]
rust_socketio = { path = "../../../rust-socketio/socketio" } rust_socketio = { path = "../../../rust-socketio/socketio" }

View File

@ -10,10 +10,11 @@ fn pyo3_config() {
// wsl // wsl
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
env::set_var("PYO3_PYTHON", "python3.10") env::set_var("PYO3_PRINT_CONFIG", "1");
env::set_var("PYO3_PYTHON", "/usr/bin/python3.11");
} }
} }
fn main() { fn main() {
pyo3_config(); pyo3_config();
} }

View File

@ -1,6 +1,8 @@
use serde_json::Value; use crate::config::IcaConfig;
use ed25519_dalek::{Signature, Signer, SigningKey}; use ed25519_dalek::{Signature, Signer, SigningKey};
use rust_socketio::{ClientBuilder, Event, Payload, RawClient}; use rust_socketio::{Payload, RawClient};
use serde_json::Value;
pub struct IcalinguaSinger { pub struct IcalinguaSinger {
pub host: String, pub host: String,
@ -8,24 +10,17 @@ pub struct IcalinguaSinger {
} }
impl IcalinguaSinger { impl IcalinguaSinger {
pub fn new_from_config(config_file_path: String) -> Self { pub fn new_from_config(config: IcaConfig) -> Self {
// try read config from file let host = config.host;
let config = std::fs::read_to_string(config_file_path).expect("Failed to read config file"); let pub_key = config.private_key;
let config: toml::Value = toml::from_str(&config).expect("Failed to parse config file");
let host = config["host"]
.as_str()
.expect("host should be string")
.to_string();
let pub_key = config["private_key"]
.as_str()
.expect("private_key should be string")
.to_string();
Self::new_from_raw(host, pub_key) Self::new_from_raw(host, pub_key)
} }
pub fn new_from_raw(host: String, pub_key: String) -> Self { pub fn new_from_raw(host: String, pub_key: String) -> Self {
let array_key: [u8; 32] = hex::decode(pub_key).unwrap().try_into().unwrap(); let array_key: [u8; 32] = hex::decode(pub_key)
.expect("Not a vaild pub key")
.try_into()
.expect("Not a vaild pub key");
let signing_key: SigningKey = SigningKey::from_bytes(&array_key); let signing_key: SigningKey = SigningKey::from_bytes(&array_key);
Self { Self {
@ -34,14 +29,10 @@ impl IcalinguaSinger {
} }
} }
pub fn sign_for_salt(&self, salt: String) -> Vec<u8> { /// 最痛苦的一集
let salt: Vec<u8> = hex::decode(salt).unwrap(); ///
let signature: Signature = self.private_key.sign(salt.as_slice()); /// 签名的回调函数
pub fn sign_callback(&self, payload: Payload, client: RawClient) {
signature.to_bytes().to_vec()
}
pub fn sign_callback(&self, payload: Payload, client: RawClient, _id: Option<i32>) {
let require_data = match payload { let require_data = match payload {
Payload::Text(json_value) => Some(json_value), Payload::Text(json_value) => Some(json_value),
_ => None, _ => None,
@ -49,7 +40,7 @@ impl IcalinguaSinger {
.expect("Payload should be Json data"); .expect("Payload should be Json data");
let (auth_key, version) = (&require_data[0], &require_data[1]); let (auth_key, version) = (&require_data[0], &require_data[1]);
// println!("auth_key: {:?}, version: {:?}", auth_key, version); println!("auth_key: {:?}, version: {:?}", auth_key, version);
let auth_key = match &require_data.get(0) { let auth_key = match &require_data.get(0) {
Some(Value::String(auth_key)) => Some(auth_key), Some(Value::String(auth_key)) => Some(auth_key),
_ => None, _ => None,
@ -60,6 +51,8 @@ impl IcalinguaSinger {
let signature: Signature = self.private_key.sign(salt.as_slice()); let signature: Signature = self.private_key.sign(salt.as_slice());
let sign = signature.to_bytes().to_vec(); let sign = signature.to_bytes().to_vec();
client.emit("auth", sign).unwrap(); client
.emit("auth", sign)
.expect("Faild to send signin data");
} }
} }

35
ica-rs/src/config.rs Normal file
View File

@ -0,0 +1,35 @@
use std::env;
use std::fs;
use serde::Deserialize;
use toml;
#[derive(Debug, Deserialize)]
pub struct IcaConfig {
/// icalingua 私钥
pub private_key: String,
/// icalingua 服务器地址
pub host: String,
/// bot 的 qq
pub self_id: u64,
/// 提醒的房间
pub notice_room: Vec<i64>,
/// 是否提醒
pub notice_start: Option<bool>,
/// Python 插件路径
pub py_plugin_path: Option<String>,
}
impl IcaConfig {
pub fn new_from_path(config_file_path: String) -> Self {
// try read config from file
let config = fs::read_to_string(&config_file_path).expect("Failed to read config file");
let ret: Self = toml::from_str(&config)
.expect(format!("Failed to parse config file {}", &config_file_path).as_str());
ret
}
pub fn new_from_cli() -> Self {
let config_file_path = env::args().nth(1).expect("No config path given");
Self::new_from_path(config_file_path)
}
}

View File

@ -1,23 +1,22 @@
mod client; mod client;
mod config;
mod py;
use colored::*; use colored::Colorize;
use ed25519_dalek::{Signature, Signer, SigningKey};
use rust_socketio::{ClientBuilder, Event, Payload, RawClient}; use rust_socketio::{ClientBuilder, Event, Payload, RawClient};
use serde_json::Value;
use std::time::Duration; use std::time::Duration;
#[allow(unused)] #[allow(unused)]
fn any_event(event: Event, payload: Payload, _client: RawClient, id: Option<i32>) { fn any_event(event: Event, payload: Payload, _client: RawClient) {
match payload { match payload {
Payload::Binary(ref data) => { Payload::Binary(ref data) => {
println!("event: {} |{:?}|id{:?}", event, data, id) println!("event: {} |{:?}", event, data)
} }
Payload::Text(ref data) => { Payload::Text(ref data) => {
print!("\x1b[35mevent: {event}\x1b[0m"); print!("event: {}", event.as_str().purple());
for value in data { for value in data {
print!("|{}", value.to_string()); println!("|{}", value.to_string());
} }
println!("|id:{:?}|", id);
} }
_ => (), _ => (),
} }
@ -28,11 +27,20 @@ fn ws_main() {
// define a callback which is called when a payload is received // define a callback which is called when a payload is received
// this callback gets the payload as well as an instance of the // this callback gets the payload as well as an instance of the
// socket to communicate with the server // socket to communicate with the server
let connect_call_back = |payload: Payload, _client: RawClient, _id| match payload { let connect_call_back = |payload: Payload, _client: RawClient| match payload {
Payload::Text(values) => { Payload::Text(values) => {
if let Some(value) = values.first() { if let Some(value) = values.first() {
if let Some("authSucceed") = value.as_str() { // if let Some("authSucceed") = value.as_str() {
println!("\x1b[32m已经登录到 icalingua!\x1b[0m"); // println!("{}", "已经登录到 icalingua!".green());
// }
match value.as_str() {
Some("authSucceed") => println!("{}", "已经登录到 icalingua!".green()),
Some("authFailed") => {
println!("{}", "登录到 icalingua 失败!".red());
panic!("登录失败")
}
Some("authRequired") => println!("{}", "需要登录到 icalingua!".yellow()),
_ => (),
} }
} }
} }
@ -40,8 +48,8 @@ fn ws_main() {
}; };
// 从命令行获取 host 和 key // 从命令行获取 host 和 key
// 从命令行获取配置文件路径 // 从命令行获取配置文件路径
let config_path = std::env::args().nth(1).expect("No config path given"); let ica_config = config::IcaConfig::new_from_cli();
let ica_singer = client::IcalinguaSinger::new_from_config(config_path); let ica_singer = client::IcalinguaSinger::new_from_config(ica_config);
// get a socket that is connected to the admin namespace // get a socket that is connected to the admin namespace
@ -49,7 +57,7 @@ fn ws_main() {
// .namespace("/admin") // .namespace("/admin")
.on_any(any_event) .on_any(any_event)
.on("message", connect_call_back) .on("message", connect_call_back)
.on("requireAuth", move |a, b, c| ica_singer.sign_callback(a, b, c)) .on("requireAuth", move |a, b| ica_singer.sign_callback(a, b))
.connect() .connect()
.expect("Connection failed"); .expect("Connection failed");

18
ica-rs/src/py.rs Normal file
View File

@ -0,0 +1,18 @@
// use inline_python::{python, Context};
use pyo3::prelude::*;
#[pyclass]
#[pyo3(name = "BotStatus")]
pub struct BotStatusPy {}
pub fn run() {
//
}
// pub fn run() {
// let con: Context = python! {
// print("Hello, world!");
// };
// }

249
main.py
View File

@ -1,35 +1,36 @@
import time import time
import json
import random import random
import asyncio import asyncio
import argparse import argparse
import traceback import traceback
from typing import Dict, List, Tuple, Any from typing import Dict, List, Tuple, Any
# import qtoml import aiohttp
import socketio import socketio
from colorama import Fore from colorama import Fore
from nacl.signing import SigningKey from nacl.signing import SigningKey
# from lib_not_dr.types import Options # from lib_not_dr.types import Options
from lib_not_dr.loggers import config from lib_not_dr.loggers import config
from data_struct import Message, ReplyMessage, get_config, BotConfig, BotStatus from data_struct import Message, ReplyMessage, get_config, BotConfig, BotStatus
_version_ = "0.2.2" _version_ = "0.2.3"
logger = config.get_logger('icalingua') logger = config.get_logger("icalingua")
BOTCONFIG: BotConfig = get_config() BOTCONFIG: BotConfig = get_config()
if __name__ == '__main__': if __name__ == "__main__":
# --debug # --debug
# --config=config.toml # --config=config.toml
# -n --no-notice # -n --no-notice
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('-d', '--debug', action='store_true') parser.add_argument("-d", "--debug", action="store_true")
parser.add_argument('-n', '--no-notice', action='store_true') parser.add_argument("-n", "--no-notice", action="store_true")
parser.add_argument('-c', '--config', type=str) parser.add_argument("-c", "--config", type=str)
args = parser.parse_args() args = parser.parse_args()
if args.debug: if args.debug:
logger.global_level = 0 logger.global_level = 0
@ -44,49 +45,50 @@ BotStatus = BotStatus()
sio: socketio.AsyncClient = socketio.AsyncClient() sio: socketio.AsyncClient = socketio.AsyncClient()
@sio.on('connect') # type: ignore @sio.on("connect") # type: ignore
def connect(): def connect():
logger.info(f'{Fore.GREEN}icalingua 已连接') logger.info(f"{Fore.GREEN}icalingua 已连接")
@sio.on('requireAuth') # type: ignore @sio.on("requireAuth") # type: ignore
async def require_auth(salt: str, versions: Dict[str, str]): async def require_auth(salt: str, versions: Dict[str, str]):
logger.info(f"{Fore.BLUE}versions: {versions}\n{type(salt)}|{salt=}") logger.info(f"{Fore.BLUE}versions: {versions}\n{type(salt)}|{salt=}")
# 准备数据 # 准备数据
sign = SigningKey(bytes.fromhex(BOTCONFIG.private_key)) sign = SigningKey(bytes.fromhex(BOTCONFIG.private_key))
signature = sign.sign(bytes.fromhex(salt)) signature = sign.sign(bytes.fromhex(salt))
# 发送数据 # 发送数据
await sio.emit('auth', signature.signature) await sio.emit("auth", signature.signature)
logger.info(f"{Fore.BLUE}send auth emit") logger.info(f"{Fore.BLUE}send auth emit")
# @sio.on('requireAuth') # @sio.on('requireAuth')
# def require_auth(*data: Dict[str, Any]): # def require_auth(*data: Dict[str, Any]):
# logger.info(f"{Fore.BLUE}requireAuth: {data}") # logger.info(f"{Fore.BLUE}requireAuth: {data}")
@sio.on('auth') # type: ignore @sio.on("auth") # type: ignore
def auth(data: Dict[str, Any]): def auth(data: Dict[str, Any]):
logger.info(f"auth: {data}") logger.info(f"auth: {data}")
@sio.on('authFailed') # type: ignore @sio.on("authFailed") # type: ignore
async def auth_failed(): async def auth_failed():
logger.info(f"{Fore.RED}authFailed") logger.info(f"{Fore.RED}authFailed")
await sio.disconnect() await sio.disconnect()
@sio.on('authSucceed') # type: ignore @sio.on("authSucceed") # type: ignore
def auth_succeed(): def auth_succeed():
logger.info(f"{Fore.GREEN}authSucceed") logger.info(f"{Fore.GREEN}authSucceed")
@sio.on('connect_error') # type: ignore @sio.on("connect_error") # type: ignore
def connect_error(*args, **kwargs): def connect_error(*args, **kwargs):
logger.info(f"连接错误 {args}, {kwargs}") logger.info(f"连接错误 {args}, {kwargs}")
@sio.on('updateRoom') # type: ignore @sio.on("updateRoom") # type: ignore
def update_room(data: Dict[str, Any]): def update_room(data: Dict[str, Any]):
logger.info(f"{Fore.CYAN}update_room: {data}") logger.info(f"{Fore.CYAN}update_room: {data}")
@ -99,35 +101,38 @@ def safe_eval(code: str) -> str:
# code = code.replace('decode', '坏东西!\n') # code = code.replace('decode', '坏东西!\n')
# code = code.replace('compile', '屑的!\n') # code = code.replace('compile', '屑的!\n')
# code = code.replace('globals', '拿不到!\n') # code = code.replace('globals', '拿不到!\n')
code = code.replace('os', '坏东西!\n') code = code.replace("os", "坏东西!\n")
code = code.replace('sys', '坏东西!\n') code = code.replace("sys", "坏东西!\n")
# code = code.replace('input', '坏东西!\n') # code = code.replace('input', '坏东西!\n')
# code = code.replace('__', '啊哈!\n') # code = code.replace('__', '啊哈!\n')
# code = code.replace('import', '很坏!\n') # code = code.replace('import', '很坏!\n')
code = code.replace(' kill', '别跑!\n') code = code.replace(" kill", "别跑!\n")
code = code.replace(' rm ', '别跑!\n') code = code.replace(" rm ", "别跑!\n")
code = code.replace('exit', '好坏!\n') code = code.replace("exit", "好坏!\n")
code = code.replace('eval', '啊哈!\n') code = code.replace("eval", "啊哈!\n")
code = code.replace('exec', '抓住!\n') code = code.replace("exec", "抓住!\n")
start_time = time.time() start_time = time.time()
try: try:
import os import os
import math import math
import decimal import decimal
global_val = {'time': time,
'math': math, global_val = {
'decimal': decimal, "time": time,
'random': random, "math": math,
'__import__': '<built-in function __import__>', "decimal": decimal,
'globals': '也别惦记你那个 globals 了', "random": random,
'compile': '想得美', "__import__": "<built-in function __import__>",
'help': '虽然但是 help 也不行', "globals": "也别惦记你那个 globals 了",
'exit': '不许 exit', "compile": "想得美",
'input': '你想干嘛', "help": "虽然但是 help 也不行",
'return': '别惦记你那个 return 了', "exit": "不许 exit",
'getattr': '<built-in function getattr>', "input": "你想干嘛",
'setattr': '<built-in function setattr>'} "return": "别惦记你那个 return 了",
os.system = '不许' "getattr": "<built-in function getattr>",
"setattr": "<built-in function setattr>",
}
os.system = "不许"
result = str(eval(code, global_val, {})) result = str(eval(code, global_val, {}))
limit = 500 limit = 500
if len(result) > limit: if len(result) > limit:
@ -135,40 +140,39 @@ def safe_eval(code: str) -> str:
except: except:
result = traceback.format_exc() result = traceback.format_exc()
end_time = time.time() end_time = time.time()
result = result.replace(BOTCONFIG.private_key, '***') result = result.replace(BOTCONFIG.private_key, "***")
result = result.replace(BOTCONFIG.host, '***') result = result.replace(BOTCONFIG.host, "***")
logger.info(f"{Fore.MAGENTA}safe_eval: {result}") logger.info(f"{Fore.MAGENTA}safe_eval: {result}")
if result == '6' or result == 6: if result == "6" or result == 6:
result = '他确实等于 6' result = "他确实等于 6"
result = f'{code}\neval result:\n{result}\n耗时: {end_time - start_time} s' result = f"{code}\neval result:\n{result}\n耗时: {end_time - start_time} s"
return result return result
except: except:
error = traceback.format_exc() error = traceback.format_exc()
result = f'error:\n{error}' result = f"error:\n{error}"
return result return result
@sio.on('addMessage') # type: ignore @sio.on("addMessage") # type: ignore
async def add_message(data: Dict[str, Any]): async def add_message(data: Dict[str, Any]):
logger.info(f"{Fore.MAGENTA}add_message: {data}") logger.info(f"{Fore.MAGENTA}add_message: {data}")
is_self = data['message']['senderId'] == BOTCONFIG.self_id is_self = data["message"]["senderId"] == BOTCONFIG.self_id
sender_name = data['message']['username'] sender_name = data["message"]["username"]
sender_id = data['message']['senderId'] sender_id = data["message"]["senderId"]
content = data['message']['content'] content = data["message"]["content"]
room_id = data['roomId'] room_id = data["roomId"]
msg_id = data["message"]["_id"]
reply_msg = Message(content="", room_id=room_id, reply_to=ReplyMessage(id=msg_id))
if not is_self: if not is_self:
if content == '/bot': if content == "/bot":
message = Message(content=f'icalingua bot test v{_version_}', message = reply_msg.to_content(f"icalingua bot pong v{_version_}")
room_id=data['roomId'], await sio.emit("sendMessage", message.to_json())
reply_to=ReplyMessage(id=data['message']['_id'])) elif content.startswith("=="):
await sio.emit('sendMessage', message.to_json())
elif content.startswith('=='):
evals: str = content[2:] evals: str = content[2:]
result = safe_eval(evals) result = safe_eval(evals)
@ -183,97 +187,140 @@ async def add_message(data: Dict[str, Any]):
# result = '你在干嘛' # result = '你在干嘛'
# else: # else:
# result = str(eval(express)) # result = str(eval(express))
message = reply_msg.to_content(result)
reply = ReplyMessage(id=data['message']['_id'])
message = Message(content=result,
reply_to=reply,
room_id=room_id)
await asyncio.sleep(random.random() * 2) await asyncio.sleep(random.random() * 2)
await sio.emit('sendMessage', message.to_json()) await sio.emit("sendMessage", message.to_json())
elif content == '!!jrrp': elif content == "!!jrrp":
randomer = random.Random(f'{sender_id}-{data["message"]["date"]}-jrrp-{_version_}') randomer = random.Random(
f'{sender_id}-{data["message"]["date"]}-jrrp-{_version_}'
)
result = randomer.randint(0, 50) + randomer.randint(0, 50) result = randomer.randint(0, 50) + randomer.randint(0, 50)
logger.info(f'{sender_name} 今日人品值为 {result}') logger.info(f"{sender_name} 今日人品值为 {result}")
reply = ReplyMessage(id=data['message']['_id']) message = reply_msg.to_content(f"{sender_name} 今日人品为 {result}")
message = Message(content=f'{sender_name} 今日人品值为 {result}',
reply_to=reply,
room_id=room_id)
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
await sio.emit('sendMessage', message.to_json()) await sio.emit("sendMessage", message.to_json())
# 如果只包括一个或多个 6 elif content == "/bmcl":
# elif data['message']['content'].replace(' ', '') in ('6', '666', '六', '3+3', '5+1', '4+2', '2+4', '1+5'): await asyncio.sleep(0.1)
# reply = ReplyMessage(id=data['message']['_id']) await sio.emit(
# message = Message(content='你 6 nm 呢', "sendMessage", reply_msg.to_content("请求数据中……").to_json()
# reply_to=reply, )
# room_id=room_id) async with aiohttp.ClientSession() as session:
# await asyncio.sleep(0.5) async with session.get(
# await sio.emit('sendMessage', message.to_json()) "https://bd.bangbang93.com/openbmclapi/metric/dashboard"
) as response:
if not response.status == "200":
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: int = 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"]
report_msg = (
"OpenBMCLAPI 状态:\n"
f"在线节点: {online_node} 带宽: {online_bandwidth}Mbps\n"
f"实时负载带宽: {data_bandwidth}Mbps 负载: {load_str}%\n"
f"当日 总请求: {data_hits} 总数据量: {data_bytes}"
)
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")
"""
async with aiohttp.ClientSession() as session:
async with session.get('http://httpbin.org/get') as resp:
print(resp.status)
print(await resp.text())"""
@sio.on('deleteMessage') # type: ignore @sio.on("deleteMessage") # type: ignore
def delete_message(message_id: str): def delete_message(message_id: str):
logger.info(f"{Fore.MAGENTA}delete_message: {message_id}") logger.info(f"{Fore.MAGENTA}delete_message: {message_id}")
@sio.on('setMessages') # type: ignore @sio.on("setMessages") # type: ignore
def set_messages(data: Dict[str, Any]): def set_messages(data: Dict[str, Any]):
logger.info(f"{Fore.YELLOW}set_messages: {data}\nmessage_len: {len(data['messages'])}") logger.info(
f"{Fore.YELLOW}set_messages: {data}\nmessage_len: {len(data['messages'])}"
)
async def notice_startup(room_list: List[int]): async def notice_startup(room_list: List[int]):
for notice_room in BOTCONFIG.notice_room: for notice_room in BOTCONFIG.notice_room:
if notice_room in room_list: if notice_room in room_list:
notice_message = Message(content=f'ica bot v{_version_}', room_id=notice_room) notice_message = Message(
await sio.emit('sendMessage', notice_message.to_json()) content=f"ica bot v{_version_}", room_id=notice_room
)
await sio.emit("sendMessage", notice_message.to_json())
BotStatus.inited = True BotStatus.inited = True
logger.info("inited", tag='notice room') logger.info("inited", tag="notice room")
else: else:
logger.warn(f"未找到通知房间: {notice_room}", tag='notice room') logger.warn(f"未找到通知房间: {notice_room}", tag="notice room")
await asyncio.sleep(random.randint(2, 5)) await asyncio.sleep(random.randint(2, 5))
@sio.on('setAllRooms') # type: ignore @sio.on("setAllRooms") # type: ignore
async def set_all_rooms(rooms: List[Dict[str, Any]]): async def set_all_rooms(rooms: List[Dict[str, Any]]):
BotStatus.running = True BotStatus.running = True
room_list: List[int] = [room.get('roomId') for room in rooms] # type: ignore room_list: List[int] = [room.get("roomId") for room in rooms] # type: ignore
if not BotStatus.inited: if not BotStatus.inited:
logger.info("initing...", tag='setAllRooms') logger.info("initing...", tag="setAllRooms")
logger.debug(f"room_list: {room_list}", tag='setAllRooms') logger.debug(f"room_list: {room_list}", tag="setAllRooms")
if BOTCONFIG.notice_start: if BOTCONFIG.notice_start:
await notice_startup(room_list) await notice_startup(room_list)
if room_list != BotStatus.rooms: if room_list != BotStatus.rooms:
logger.info(f"{Fore.YELLOW}set_all_rooms: {rooms}\nlen: {len(rooms)}\n") logger.info(f"{Fore.YELLOW}set_all_rooms: {rooms}\nlen: {len(rooms)}\n")
BotStatus.rooms = room_list BotStatus.rooms = room_list
logger.info(f"更新房间: {room_list}", tag='setAllRooms') logger.info(f"更新房间: {room_list}", tag="setAllRooms")
@sio.on('setAllChatGroups') # type: ignore @sio.on("setAllChatGroups") # type: ignore
def set_all_chat_groups(groups: List[Dict[str, Any]]): def set_all_chat_groups(groups: List[Dict[str, Any]]):
logger.info(f"{Fore.YELLOW}set_all_chat_groups: {groups}\nlen: {len(groups)}\n") logger.info(f"{Fore.YELLOW}set_all_chat_groups: {groups}\nlen: {len(groups)}\n")
@sio.on('notify') # type: ignore @sio.on("notify") # type: ignore
def notify(data: List[Tuple[str, Any]]): def notify(data: List[Tuple[str, Any]]):
logger.info(f"notify: {data}") logger.info(f"notify: {data}")
@sio.on('closeLoading') # type: ignore @sio.on("closeLoading") # type: ignore
def close_loading(_): def close_loading(_):
logger.info(f"{Fore.GREEN}close_loading") logger.info(f"{Fore.GREEN}close_loading")
@sio.on('onlineData') # type: ignore @sio.on("onlineData") # type: ignore
def online_data(data: Dict[str, Any]): def online_data(data: Dict[str, Any]):
logger.info(f"{Fore.GREEN}online_data: {data}") logger.info(f"{Fore.GREEN}online_data: {data}")
@sio.on('*') # type: ignore @sio.on("*") # type: ignore
def catch_all(event, data): def catch_all(event, data):
logger.info(f"{Fore.RED}catch_all: {event}|{data}") logger.info(f"{Fore.RED}catch_all: {event}|{data}")
async def main(): async def main():
""" """
while True: while True:
@ -288,11 +335,11 @@ async def main():
await sio.connect(BOTCONFIG.host) await sio.connect(BOTCONFIG.host)
await sio.wait() await sio.wait()
if __name__ == '__main__':
if __name__ == "__main__":
try: try:
asyncio.run(main()) asyncio.run(main())
except KeyboardInterrupt: except KeyboardInterrupt:
logger.info("KeyboardInterrupt") logger.info("KeyboardInterrupt")
except Exception: except Exception:
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())

9
news.md Normal file
View File

@ -0,0 +1,9 @@
# 更新日志
## 0.2.3
添加了 `/bmcl` 请求 bmclapi 状态
## 0.2.2
重构了一波整体代码

View File

@ -2,4 +2,6 @@ lib-not-dr >= 0.3.13
colorama colorama
qtoml qtoml
pynacl pynacl
python-socketio[asyncio_client] python-socketio[asyncio_client]
aiohttp