Compare commits

...

4 Commits

Author SHA1 Message Date
linyuchen
5044d24ee1 feat: go-cqhttp api get_stranger_info
feat: api send_like
fix: some image of message use base64 instead of http url
2024-02-21 22:19:02 +08:00
linyuchen
7664e746b4 feat: some go-cqhttp feature 2024-02-21 17:17:15 +08:00
linyuchen
ebea755731 perf: log long string 2024-02-21 16:43:10 +08:00
linyuchen
e4508ea5c7 feat: go-cqhttp api send_private_forward_msg & send_group_forward_msg 2024-02-21 16:36:40 +08:00
25 changed files with 293 additions and 137 deletions

View File

@@ -36,6 +36,7 @@ LiteLoaderQQNT的OneBot11协议插件
- [x] 上报好友、群消息撤回
消息格式支持:
- [x] cq码
- [x] 文字
- [x] 表情
- [x] 图片
@@ -64,6 +65,10 @@ LiteLoaderQQNT的OneBot11协议插件
- [x] can_send_image
- [x] can_send_record
支持的go-cqhtp api:
- [x] send_private_forward_msg
- [x] send_group_forward_msg
## 示例
![](doc/image/example.jpg)
@@ -91,13 +96,6 @@ LiteLoaderQQNT的OneBot11协议插件
</details>
<br/>
<details>
<summary>不支持cq码</summary>
<br/>
cq码已经过时了没有支持的打算(主要是我不用这玩意儿,加上我懒)
</details>
<br/>
<details>
<summary>QQ变得很卡</summary>
<br/>

View File

@@ -4,7 +4,7 @@
"name": "LLOneBot",
"slug": "LLOneBot",
"description": "LiteLoaderQQNT的OneBotApi",
"version": "3.4.0",
"version": "3.6.0",
"thumbnail": "./icon.png",
"authors": [
{

View File

@@ -87,4 +87,4 @@ export function getUidByUin(uin: string) {
}
}
export const version = "v3.4.0"
export const version = "v3.6.0"

View File

@@ -91,6 +91,7 @@ export abstract class HttpServerBase {
if (method == "get"){
payload = req.query
}
log("收到http请求", url, payload);
try{
res.send(await handler(res, payload))
}catch (e) {

View File

@@ -13,6 +13,23 @@ export function getConfigUtil() {
return new ConfigUtil(configFilePath)
}
function truncateString(obj: any, maxLength = 500) {
if (obj !== null && typeof obj === 'object') {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'string') {
// 如果是字符串且超过指定长度,则截断
if (obj[key].length > maxLength) {
obj[key] = obj[key].substring(0, maxLength) + '...';
}
} else if (typeof obj[key] === 'object') {
// 如果是对象或数组,则递归调用
truncateString(obj[key], maxLength);
}
});
}
return obj;
}
export function log(...msg: any[]) {
if (!getConfigUtil().getConfig().log) {
return
@@ -28,7 +45,8 @@ export function log(...msg: any[]) {
for (let msgItem of msg) {
// 判断是否是对象
if (typeof msgItem === "object") {
logMsg += JSON.stringify(msgItem) + " ";
let obj = JSON.parse(JSON.stringify(msgItem));
logMsg += JSON.stringify(truncateString(obj)) + " ";
continue;
}
logMsg += msgItem + " ";

View File

@@ -16,6 +16,7 @@ import {OB11FriendRecallNoticeEvent} from "../onebot11/event/notice/OB11FriendRe
import {OB11GroupRecallNoticeEvent} from "../onebot11/event/notice/OB11GroupRecallNoticeEvent";
import {postEvent} from "../onebot11/server/postevent";
import {ob11ReverseWebsockets} from "../onebot11/server/ws/ReverseWebsocket";
import {EventType} from "../onebot11/event/OB11BaseEvent";
let running = false;
@@ -94,7 +95,8 @@ function onLoad() {
if (debug) {
msg.raw = message;
}
if (msg.user_id.toString() == selfInfo.uin && !reportSelfMessage) {
const isSelfMsg = msg.user_id.toString() == selfInfo.uin
if (isSelfMsg && !reportSelfMessage) {
return
}
postEvent(msg);

View File

@@ -17,7 +17,8 @@ export enum ReceiveCmd {
USER_INFO = "nodeIKernelProfileListener/onProfileSimpleChanged",
GROUPS = "nodeIKernelGroupListener/onGroupListUpdate",
GROUPS_UNIX = "onGroupListUpdate",
FRIENDS = "onBuddyListChange"
FRIENDS = "onBuddyListChange",
MEDIA_DOWNLOAD_COMPLETE = "nodeIKernelMsgListener/onRichMediaDownloadComplete"
}
interface NTQQApiReturnData<PayloadType = unknown> extends Array<any> {

View File

@@ -3,7 +3,7 @@ import {hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook} fr
import {log} from "../common/utils";
import {ChatType, Friend, Group, GroupMember, RawMessage, SelfInfo, SendMessageElement, User} from "./types";
import * as fs from "fs";
import {addHistoryMsg, msgHistory, selfInfo, uidMaps} from "../common/data";
import {addHistoryMsg, msgHistory, selfInfo} from "../common/data";
import {v4 as uuidv4} from "uuid"
interface IPCReceiveEvent {
@@ -66,14 +66,16 @@ interface NTQQApiParams {
className?: NTQQApiClass,
channel?: NTQQApiChannel,
args?: unknown[],
cbCmd?: ReceiveCmd | null
cbCmd?: ReceiveCmd | null,
cmdCB?: (payload: any) => boolean;
timeoutSecond?: number,
}
function callNTQQApi<ReturnType>(params: NTQQApiParams) {
let {
className, methodName, channel, args,
cbCmd, timeoutSecond: timeout
cbCmd, timeoutSecond: timeout,
cmdCB
} = params;
className = className ?? NTQQApiClass.NT_API;
channel = channel ?? NTQQApiChannel.IPC_UP_2;
@@ -95,12 +97,20 @@ function callNTQQApi<ReturnType>(params: NTQQApiParams) {
// 这里的callback比较特殊QQ后端先返回是否调用成功再返回一条结果数据
hookApiCallbacks[uuid] = (result: GeneralCallResult) => {
log(`${methodName} callback`, result)
if (result.result == 0) {
if (result?.result == 0 || result === undefined) {
const hookId = registerReceiveHook<ReturnType>(cbCmd, (payload) => {
log(methodName, "second callback", cbCmd, payload);
removeReceiveHook(hookId);
success = true
resolve(payload);
if (cmdCB) {
if (cmdCB(payload)) {
removeReceiveHook(hookId);
success = true
resolve(payload);
}
} else {
removeReceiveHook(hookId);
success = true
resolve(payload);
}
})
} else {
success = true
@@ -138,7 +148,7 @@ interface GeneralCallResult {
export class NTQQApi {
// static likeFriend = defineNTQQApi<void>(NTQQApiChannel.IPC_UP_2, NTQQApiClass.NT_API, NTQQApiMethod.LIKE_FRIEND)
static likeFriend(uid: string, count = 1) {
return callNTQQApi({
return callNTQQApi<GeneralCallResult>({
methodName: NTQQApiMethod.LIKE_FRIEND,
args: [{
doLikeUserInfo: {
@@ -225,10 +235,10 @@ export class NTQQApi {
let values = result.result.infos.values()
let members = Array.from(values) as GroupMember[]
for(const member of members){
uidMaps[member.uid] = member.uin;
for (const member of members) {
// uidMaps[member.uid] = member.uin;
}
log(uidMaps);
// log(uidMaps);
// log("members info", values);
return members
} catch (e) {
@@ -328,7 +338,15 @@ export class NTQQApi {
},
undefined,
]
await callNTQQApi({methodName: NTQQApiMethod.DOWNLOAD_MEDIA, args: apiParams})
// log("需要下载media", sourcePath);
await callNTQQApi({
methodName: NTQQApiMethod.DOWNLOAD_MEDIA,
args: apiParams,
cbCmd: ReceiveCmd.MEDIA_DOWNLOAD_COMPLETE,
cmdCB:(payload: {notifyInfo: {filePath: string}})=>{
// log("media 下载完成判断", payload.notifyInfo.filePath, sourcePath);
return payload.notifyInfo.filePath == sourcePath;
}})
return sourcePath
}
@@ -341,8 +359,8 @@ export class NTQQApi {
})
}
static sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = false) {
const sendTimeout = 10 * 1000
static sendMsg(peer: Peer, msgElements: SendMessageElement[], waitComplete = false, timeout = 10000) {
const sendTimeout = timeout
return new Promise<RawMessage>((resolve, reject) => {
const peerUid = peer.peerUid;

View File

@@ -1,6 +1,6 @@
import {ActionName, BaseCheckResult} from "./types"
import {OB11Response, OB11WebsocketResponse} from "./utils"
import {OB11Return, OB11WebsocketReturn} from "../types";
import {OB11Response} from "./utils"
import {OB11Return} from "../types";
class BaseAction<PayloadType, ReturnDataType> {
actionName: ActionName
@@ -23,16 +23,16 @@ class BaseAction<PayloadType, ReturnDataType> {
}
}
public async websocketHandle(payload: PayloadType, echo: string): Promise<OB11WebsocketReturn<ReturnDataType | null>> {
public async websocketHandle(payload: PayloadType, echo: string): Promise<OB11Return<ReturnDataType | null>> {
const result = await this.check(payload)
if (!result.valid) {
return OB11WebsocketResponse.error(result.message, 1400)
return OB11Response.error(result.message, 1400)
}
try {
const resData = await this._handle(payload)
return OB11WebsocketResponse.ok(resData, echo);
return OB11Response.ok(resData, echo);
} catch (e) {
return OB11WebsocketResponse.error(e.toString(), 1200)
return OB11Response.error(e.toString(), 1200, echo)
}
}

View File

@@ -0,0 +1,31 @@
import BaseAction from "./BaseAction";
import {getFriend} from "../../common/data";
import {NTQQApi} from "../../ntqqapi/ntcall";
import {ActionName} from "./types";
import { log } from "../../common/utils";
interface Payload {
user_id: number,
times: number
}
export default class SendLike extends BaseAction<Payload, null> {
actionName = ActionName.SendLike
protected async _handle(payload: Payload): Promise<null> {
const qq = payload.user_id.toString();
const friend = await getFriend(qq)
if (!friend) {
throw (`点赞失败,${qq}不是好友`)
}
try {
let result = await NTQQApi.likeFriend(friend.uid, parseInt(payload.times.toString()) || 1);
if (result.result !== 0){
throw result.errMsg
}
} catch (e) {
throw `点赞失败 ${e}`
}
return null
}
}

View File

@@ -9,6 +9,7 @@ import {ActionName, BaseCheckResult} from "./types";
import * as fs from "fs";
import {log} from "../../common/utils";
import {v4 as uuidv4} from "uuid"
import {parseCQCode} from "../cqcode";
function checkSendMessage(sendMsgList: OB11MessageData[]) {
function checkUri(uri: string): boolean {
@@ -49,7 +50,7 @@ export interface ReturnDataType {
message_id: number
}
class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
actionName = ActionName.SendMsg
protected async check(payload: OB11PostSendMsg): Promise<BaseCheckResult> {
@@ -115,14 +116,15 @@ class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
}
}
private convertMessage2List(message: OB11MessageMixType) {
protected convertMessage2List(message: OB11MessageMixType) {
if (typeof message === "string") {
message = [{
type: OB11MessageDataType.text,
data: {
text: message
}
}] as OB11MessageData[]
// message = [{
// type: OB11MessageDataType.text,
// data: {
// text: message
// }
// }] as OB11MessageData[]
message = parseCQCode(message.toString())
} else if (!Array.isArray(message)) {
message = [message]
}
@@ -143,24 +145,27 @@ class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
peerUid: selfInfo.uid
}
let nodeIds: string[] = []
for (const messageNode of messageNodes) {
for (const messageNode of messageNodes){
// 一个node表示一个人的消息
let nodeId = messageNode.data.id;
// 有nodeId表示一个子转发消息卡片
if (nodeId) {
nodeIds.push(nodeId)
let nodeMsg = getHistoryMsgByShortId(nodeId);
if (nodeMsg){
nodeIds.push(nodeMsg.msgId);
}
} else {
// 自定义的消息
// 提取消息段发给自己生成消息id
const {
sendElements,
deleteAfterSentFiles
} = await this.createSendElements(this.convertMessage2List(messageNode.data.content), group)
try {
const {
sendElements,
deleteAfterSentFiles
} = await this.createSendElements(this.convertMessage2List(messageNode.data.content), group);
log("开始生成转发节点", sendElements);
const nodeMsg = await this.send(selfPeer, sendElements, deleteAfterSentFiles, true);
nodeIds.push(nodeMsg.msgId)
log("转发节点生成成功", nodeMsg.msgId);
} catch (e) {
log("生效转发消息节点失败", e)
}
@@ -240,7 +245,8 @@ class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
}
}
}
} break;
}
break;
}
}
@@ -255,7 +261,7 @@ class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
if (!sendElements.length) {
throw ("消息体无法解析")
}
const returnMsg = await NTQQApi.sendMsg(peer, sendElements, waitComplete)
const returnMsg = await NTQQApi.sendMsg(peer, sendElements, waitComplete, 20000);
addHistoryMsg(returnMsg)
deleteAfterSentFiles.map(f => fs.unlink(f, () => {
}))

View File

@@ -0,0 +1,24 @@
import BaseAction from "../BaseAction";
import {OB11GroupMember, OB11User} from "../../types";
import {friends, getFriend, getGroupMember, groups} from "../../../common/data";
import {OB11Constructor} from "../../constructor";
import {ActionName} from "../types";
export default class GoCQHTTPGetStrangerInfo extends BaseAction<{user_id: number}, OB11User>{
actionName = ActionName.GoCQHTTP_GetStrangerInfo
protected async _handle(payload: { user_id: number }): Promise<OB11User> {
const user_id = payload.user_id.toString()
const friend = await getFriend(user_id)
if (friend){
return OB11Constructor.friend(friend);
}
for(const group of groups){
const member = await getGroupMember(group.groupCode, user_id)
if (member){
return OB11Constructor.groupMember(group.groupCode, member) as OB11User
}
}
throw ("查无此人")
}
}

View File

@@ -0,0 +1,15 @@
import SendMsg, {ReturnDataType} from "../SendMsg";
import {OB11MessageMixType, OB11PostSendMsg} from "../../types";
import {ActionName, BaseCheckResult} from "../types";
export class GoCQHTTPSendGroupForwardMsg extends SendMsg{
actionName = ActionName.GoCQHTTP_SendGroupForwardMsg;
protected async check(payload: OB11PostSendMsg){
payload.message = this.convertMessage2List(payload.messages);
return super.check(payload);
}
}
export class GoCQHTTPSendPrivateForwardMsg extends GoCQHTTPSendGroupForwardMsg{
actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg;
}

View File

@@ -14,8 +14,12 @@ import GetVersionInfo from "./GetVersionInfo";
import CanSendRecord from "./CanSendRecord";
import CanSendImage from "./CanSendImage";
import GetStatus from "./GetStatus";
import {GoCQHTTPSendGroupForwardMsg, GoCQHTTPSendPrivateForwardMsg} from "./go-cqhttp/SendForwardMsg";
import GoCQHTTPGetStrangerInfo from "./go-cqhttp/GetStrangerInfo";
import SendLike from "./SendLike";
export const actionHandlers = [
new SendLike(),
new GetMsg(),
new GetLoginInfo(),
new GetFriendList(),
@@ -25,7 +29,13 @@ export const actionHandlers = [
new GetVersionInfo(),
new CanSendRecord(),
new CanSendImage(),
new GetStatus()
new GetStatus(),
//以下为go-cqhttp api
new GoCQHTTPSendGroupForwardMsg(),
new GoCQHTTPSendPrivateForwardMsg(),
new GoCQHTTPGetStrangerInfo()
]
function initActionMap() {

View File

@@ -13,6 +13,7 @@ export interface InvalidCheckResult {
export enum ActionName {
TestForwardMsg = "test_forward_msg",
SendLike = "send_like",
GetLoginInfo = "get_login_info",
GetFriendList = "get_friend_list",
GetGroupInfo = "get_group_info",
@@ -28,4 +29,8 @@ export enum ActionName {
GetStatus = "get_status",
CanSendRecord = "can_send_record",
CanSendImage = "can_send_image",
// 以下为go-cqhttp api
GoCQHTTP_SendGroupForwardMsg = "send_group_forward_msg",
GoCQHTTP_SendPrivateForwardMsg = "send_private_forward_msg",
GoCQHTTP_GetStrangerInfo = "get_stranger_info"
}

View File

@@ -1,4 +1,4 @@
import {OB11Return, OB11WebsocketReturn} from '../types';
import {OB11Return} from '../types';
export class OB11Response {
static res<T>(data: T, status: string, retcode: number, message: string = ""): OB11Return<T> {
@@ -6,31 +6,25 @@ export class OB11Response {
status: status,
retcode: retcode,
data: data,
message: message
message: message,
wording: message,
echo: ""
}
}
static ok<T>(data: T) {
return OB11Response.res<T>(data, "ok", 0)
}
static error(err: string, retcode: number) {
return OB11Response.res(null, "failed", retcode, err)
}
}
export class OB11WebsocketResponse {
static res<T>(data: T, status: string, retcode: number, echo: string, message: string = ""): OB11WebsocketReturn<T> {
return {
status: status,
retcode: retcode,
data: data,
echo: echo,
message: message
}
}
static ok<T>(data: T, echo: string = "") {
return OB11WebsocketResponse.res<T>(data, "ok", 0, echo)
let res = OB11Response.res<T>(data, "ok", 0)
if (echo) {
res.echo = echo;
}
return res;
}
static error(err: string, retcode: number, echo: string = "") {
return OB11WebsocketResponse.res(null, "failed", retcode, echo, err)
let res = OB11Response.res(null, "failed", retcode, err)
if (echo) {
res.echo = echo;
}
return res;
}
}

View File

@@ -7,10 +7,21 @@ import {
OB11MessageDataType,
OB11User
} from "./types";
import {AtType, ChatType, Group, GroupMember, IMAGE_HTTP_HOST, RawMessage, SelfInfo, User} from '../ntqqapi/types';
import {
AtType,
ChatType,
Friend,
Group,
GroupMember,
IMAGE_HTTP_HOST,
RawMessage,
SelfInfo,
User
} from '../ntqqapi/types';
import {getFriend, getGroupMember, getHistoryMsgBySeq, selfInfo} from '../common/data';
import {file2base64, getConfigUtil, log} from "../common/utils";
import {NTQQApi} from "../ntqqapi/ntcall";
import {EventType} from "./event/OB11BaseEvent";
export class OB11Constructor {
@@ -34,10 +45,10 @@ export class OB11Constructor {
font: 14,
sub_type: "friend",
message: [],
post_type: "message",
post_type: selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE,
}
if (msg.chatType == ChatType.group) {
resMsg.sub_type = "normal"
resMsg.sub_type = "normal" // 这里go-cqhttp是group而onebot11标准是normal, 蛋疼
resMsg.group_id = parseInt(msg.peerUin)
const member = await getGroupMember(msg.peerUin, msg.senderUin);
if (member) {
@@ -80,7 +91,14 @@ export class OB11Constructor {
}
} else if (element.textElement) {
message_data["type"] = "text"
resMsg.raw_message += message_data["data"]["text"] = element.textElement.content
let text= element.textElement.content
if (!text.trim()){
continue;
}
message_data["data"]["text"] = text
if (text){
resMsg.raw_message += text
}
} else if (element.picElement) {
message_data["type"] = "image"
message_data["data"]["file_id"] = element.picElement.fileUuid
@@ -123,7 +141,7 @@ export class OB11Constructor {
if (!enableLocalFile2Url) {
message_data.data.file = "file://" + filePath
} else { // 不使用本地路径
if (message_data.data.http_file) {
if (message_data.data.http_file && !message_data.data.http_file.startsWith(IMAGE_HTTP_HOST + "/download")) {
message_data.data.file = message_data.data.http_file
} else {
let {err, data} = await file2base64(filePath);
@@ -139,6 +157,7 @@ export class OB11Constructor {
resMsg.message.push(message_data);
}
}
resMsg.raw_message = resMsg.raw_message.trim();
return resMsg;
}

49
src/onebot11/cqcode.ts Normal file
View File

@@ -0,0 +1,49 @@
import {OB11MessageData} from "./types";
const pattern = /\[CQ:(\w+)((,\w+=[^,\]]*)*)\]/
function unescape(source: string) {
return String(source)
.replace(/&#91;/g, '[')
.replace(/&#93;/g, ']')
.replace(/&#44;/g, ',')
.replace(/&amp;/g, '&')
}
function from(source: string) {
const capture = pattern.exec(source)
if (!capture) return null
const [, type, attrs] = capture
const data: Record<string, any> = {}
attrs && attrs.slice(1).split(',').forEach((str) => {
const index = str.indexOf('=')
data[str.slice(0, index)] = unescape(str.slice(index + 1))
})
return {type, data, capture}
}
function h(type: string, data: any) {
return {
type,
data,
}
}
export function parseCQCode(source: string): OB11MessageData[] {
const elements: any[] = []
let result: ReturnType<typeof from>
while ((result = from(source))) {
const {type, data, capture} = result
if (capture.index) {
elements.push(h('text', {text: unescape(source.slice(0, capture.index))}))
}
elements.push(h(type, data))
source = source.slice(capture.index + capture[0].length)
}
if (source) elements.push(h('text', {text: unescape(source)}))
return elements
}
// const result = parseCQCode("[CQ:at,qq=114514]早上好啊[CQ:image,file=http://baidu.com/1.jpg,type=show,id=40004]")
// const result = parseCQCode("好好好")
// console.log(JSON.stringify(result))

View File

@@ -4,7 +4,8 @@ export enum EventType {
META = "meta_event",
REQUEST = "request",
NOTICE = "notice",
MESSAGE = "message"
MESSAGE = "message",
MESSAGE_SENT = "message_sent",
}

View File

@@ -4,7 +4,7 @@ import * as WebSocket from "ws";
import {selfInfo} from "../../../common/data";
import {LifeCycleSubType, OB11LifeCycleEvent} from "../../event/meta/OB11LifeCycleEvent";
import {ActionName} from "../../action/types";
import {OB11WebsocketResponse} from "../../action/utils";
import {OB11Response} from "../../action/utils";
import BaseAction from "../../action/BaseAction";
import {actionMap} from "../../action";
import {registerWsEventSender, unregisterWsEventSender} from "../postevent";
@@ -35,22 +35,22 @@ export class ReverseWebsocket {
public async onmessage(msg: string) {
let receiveData: { action: ActionName, params: any, echo?: string } = {action: null, params: {}}
let echo = ""
log("收到反向Websocket消息", msg.toString())
try {
receiveData = JSON.parse(msg.toString())
echo = receiveData.echo
log("收到反向Websocket消息", receiveData)
} catch (e) {
return wsReply(this.websocket, OB11WebsocketResponse.error("json解析失败请检查数据格式", 1400, echo))
return wsReply(this.websocket, OB11Response.error("json解析失败请检查数据格式", 1400, echo))
}
const action: BaseAction<any, any> = actionMap.get(receiveData.action);
if (!action) {
return wsReply(this.websocket, OB11WebsocketResponse.error("不支持的api " + receiveData.action, 1404, echo))
return wsReply(this.websocket, OB11Response.error("不支持的api " + receiveData.action, 1404, echo))
}
try {
let handleResult = await action.websocketHandle(receiveData.params, echo);
wsReply(this.websocket, handleResult)
} catch (e) {
wsReply(this.websocket, OB11WebsocketResponse.error(`api处理出错:${e}`, 1200, echo))
wsReply(this.websocket, OB11Response.error(`api处理出错:${e}`, 1200, echo))
}
}
@@ -89,18 +89,18 @@ export class ReverseWebsocket {
log("Trying to connect to the websocket server: " + this.url);
this.websocket.on("open", ()=> {
this.websocket.on("open", () => {
log("Connected to the websocket server: " + this.url);
this.onopen();
});
this.websocket.on("message", async (data)=>{
this.websocket.on("message", async (data) => {
await this.onmessage(data.toString());
});
this.websocket.on("error", log);
this.websocket.on("close", ()=> {
this.websocket.on("close", () => {
log("The websocket connection: " + this.url + " closed, trying reconnecting...");
this.onclose();
});

View File

@@ -1,7 +1,7 @@
import {WebSocket} from "ws";
import {getConfigUtil, log} from "../../../common/utils";
import {actionMap} from "../../action";
import {OB11WebsocketResponse} from "../../action/utils";
import {OB11Response} from "../../action/utils";
import {postWsEvent, registerWsEventSender, unregisterWsEventSender} from "../postevent";
import {ActionName} from "../../action/types";
import BaseAction from "../../action/BaseAction";
@@ -15,19 +15,19 @@ let heartbeatRunning = false;
class OB11WebsocketServer extends WebsocketServerBase {
authorizeFailed(wsClient: WebSocket) {
wsClient.send(JSON.stringify(OB11WebsocketResponse.res(null, "failed", 1403, "token验证失败")))
wsClient.send(JSON.stringify(OB11Response.res(null, "failed", 1403, "token验证失败")))
}
async handleAction(wsClient: WebSocket, actionName: string, params: any, echo?: string) {
const action: BaseAction<any, any> = actionMap.get(actionName);
if (!action) {
return wsReply(wsClient, OB11WebsocketResponse.error("不支持的api " + actionName, 1404, echo))
return wsReply(wsClient, OB11Response.error("不支持的api " + actionName, 1404, echo))
}
try {
let handleResult = await action.websocketHandle(params, echo);
wsReply(wsClient, handleResult)
} catch (e) {
wsReply(wsClient, OB11WebsocketResponse.error(`api处理出错:${e}`, 1200, echo))
wsReply(wsClient, OB11Response.error(`api处理出错:${e}`, 1200, echo))
}
}
@@ -36,12 +36,12 @@ class OB11WebsocketServer extends WebsocketServerBase {
wsClient.on("message", async (msg) => {
let receiveData: { action: ActionName, params: any, echo?: string } = {action: null, params: {}}
let echo = ""
log("收到正向Websocket消息", msg.toString())
try {
receiveData = JSON.parse(msg.toString())
echo = receiveData.echo
log("收到正向Websocket消息", receiveData);
} catch (e) {
return wsReply(wsClient, OB11WebsocketResponse.error("json解析失败请检查数据格式", 1400, echo))
return wsReply(wsClient, OB11Response.error("json解析失败请检查数据格式", 1400, echo))
}
this.handleAction(wsClient, receiveData.action, receiveData.params, receiveData.echo).then()
})

View File

@@ -1,9 +1,9 @@
import * as websocket from "ws";
import {OB11WebsocketResponse} from "../../action/utils";
import {OB11Response} from "../../action/utils";
import {PostEventType} from "../postevent";
import {log} from "../../../common/utils";
export function wsReply(wsClient: websocket.WebSocket, data: OB11WebsocketResponse | PostEventType) {
export function wsReply(wsClient: websocket.WebSocket, data: OB11Response | PostEventType) {
try {
let packet = Object.assign({
}, data);
@@ -11,7 +11,7 @@ export function wsReply(wsClient: websocket.WebSocket, data: OB11WebsocketRespon
delete packet["echo"];
}
wsClient.send(JSON.stringify(packet))
log("ws 消息上报", wsClient.url, data)
log("ws 消息上报", wsClient.url || "", data)
} catch (e) {
log("websocket 回复失败", e)
}

View File

@@ -1,4 +1,5 @@
import {AtType, RawMessage} from "../ntqqapi/types";
import {EventType} from "./event/OB11BaseEvent";
export interface OB11User {
user_id: number;
@@ -67,7 +68,7 @@ export interface OB11Message {
message: OB11MessageData[],
raw_message: string,
font: number,
post_type?: "message",
post_type?: EventType,
raw?: RawMessage
}
@@ -76,10 +77,8 @@ export interface OB11Return<DataType> {
retcode: number
data: DataType
message: string,
}
export interface OB11WebsocketReturn<DataType> extends OB11Return<DataType>{
echo: string
echo?: string, // ws调用api才有此字段
wording?: string, // go-cqhttp字段错误信息
}
export enum OB11MessageDataType {
@@ -160,6 +159,7 @@ export interface OB11PostSendMsg {
user_id: string,
group_id?: string,
message: OB11MessageMixType;
messages?: OB11MessageMixType; // 兼容 go-cqhttp
}
export interface OB11Version {

View File

@@ -60,40 +60,4 @@ export async function uri2local(fileName: string, uri: string){
res.success = true
res.path = filePath
return res
}
function checkSendMessage(sendMsgList: OB11MessageData[]) {
function checkUri(uri: string): boolean {
const pattern = /^(file:\/\/|http:\/\/|https:\/\/|base64:\/\/)/;
return pattern.test(uri);
}
for (let msg of sendMsgList) {
if (msg["type"] && msg["data"]) {
let type = msg["type"];
let data = msg["data"];
if (type === "text" && !data["text"]) {
return 400;
} else if (["image", "voice", "record"].includes(type)) {
if (!data["file"]) {
return 400;
} else {
if (checkUri(data["file"])) {
return 200;
} else {
return 400;
}
}
} else if (type === "at" && !data["qq"]) {
return 400;
} else if (type === "reply" && !data["id"]) {
return 400;
}
} else {
return 400
}
}
return 200;
}
}

View File

@@ -109,7 +109,7 @@ async function onSettingWindowCreated(view: Element) {
<setting-item data-direction="row" class="hostItem vertical-list-item">
<div>
<div>上报文件不采用本地路径</div>
<div class="tips">开启后,上报图片为http连接语音为base64编码</div>
<div class="tips">开启后,上报文件(图片语音等)为http链接或base64编码</div>
</div>
<setting-switch id="switchFileUrl" ${config.enableLocalFile2Url ? "is-active" : ""}></setting-switch>
</setting-item>