Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

希望mirai能支持对json消息的ark签名 #2279

Closed
Star-Vk opened this issue Oct 15, 2022 · 24 comments
Closed

希望mirai能支持对json消息的ark签名 #2279

Star-Vk opened this issue Oct 15, 2022 · 24 comments
Labels
t:feature 类型: 新特性

Comments

@Star-Vk
Copy link

Star-Vk commented Oct 15, 2022

关于mirai发送json不显示的问题&分析

关于发送json消息需要进行ark签名后获取token值得问题

114377ab72876a2f7d28902975b78504


一、问题复现

代码编写采用的是mirai-js以及TypeScript语言,实现代码如下:

import { Bot, Message } from "mirai-js";

(async ()=>{

    //创建bot实例
    let bot = new Bot();
    
    //与mah建立连接
    await bot.open({
        baseUrl:"http://localhost:8080",
        verifyKey:"Mykey",
        qq:3054689042
    })

    //要发送的json文本
    let json = `{"app":"com.tencent.bot.task.deblock","desc":"","view":"index","ver":"2.0.4.0","prompt":"XKBot6.0 Menu","appID":"","sourceName":"","actionData":"","actionData_A":"","sourceUrl":"","meta":{"detail":{"appID":"","battleDesc":"","botName":"XKbot","cmdList":[{"cmd":" ","cmdDesc":"欢迎您的使用!!!","cmdTitle":"QQ"}],"cmdTitle":"","content":"121212","guildID":"","iconLeft":[{"num":"10"}],"iconRight":[],"receiverName":"","subGuildID":"SUBGUILDID#","title":"","titleColor":""}}}`;
    
    //发送json消息,只能addApp如果换成addJson的话会报错
    await bot.sendMessage({
        group:1169617978,
        message:new Message().addApp(json)
    })

})()

e99bdf0546bef4026a0cd9545f36288e

发送完的卡片消息如下,但是手机端无法显示(不截图了)


二、问题分析

经过多方求助,根据oicq某某大佬提供,发出去的卡片需要带上token值,而这个token值是和整个token文本相呼应的,整个文本改了token值也要跟着变化,否则就会出现只能发不能收的现象,获取token这个一过程叫做ark签名(小栗子框架是这么定义的)

现在已经知晓的方法除了小栗子框架以外,开源的机器人框架可以使用小程序分享来获取签名后的json,由于我不熟悉Java和Kotlin没办法使用mirai的代码去实现一个签名的函数作为参考,因此采用oicq的core来签名上述的json如下:

Result {
  code: 1,
  msg: '签名成功!',
  data: '{"actionData":"","actionData_A":"","app":"com.tencent.bot.task.deblock","appID":"","config":{"ctime":1665848025,"token":"e0e8a49720c42903dc476783aced2d77"},"desc":"","meta":{"detail":{"appID":"","battleDesc":"","botName":"XKbot","cmdList":[{"cmd":" ","cmdDesc":"欢迎您的使用!!!","cmdTitle":"QQ"}],"cmdTitle":"","content":"121212","guildID":"","iconLeft":[{"num":"10"}],"iconRight":[],"receiverName":"","subGuildID":"SUBGUILDID#","title":"","titleColor":""}},"prompt":"XKBot6.0 Menu","sourceName":"","sourceUrl":"","ver":"2.0.4.0","view":"index"}'
}

可以看到签名后已经拿到了对应的token值“e0e8a49720c42903dc476783aced2d77”,在使用mirai发送试试

fd7f7cb81e833cb892b72bc166b3e749
915976d06b417c13ebe8993fbb338597

发送成功!!!!


三、总结与期盼

根据大佬提供的思路,只要支持音乐分享的框架,仅仅只需要把原来的type类型改成10就是小程序分享,具体代码如何实现的我不清楚,不是我发现的这一签名方法,希望mirai能支持ark签名的功能,让json消息不再是多余的存在!

@Star-Vk Star-Vk added the t:feature 类型: 新特性 label Oct 15, 2022
@Nambers
Copy link
Contributor

Nambers commented Oct 21, 2022

看起来可能好像和 ArkAppMessage, MessageForArkApp , resIDForLongMsg有关系

@Star-Vk
Copy link
Author

Star-Vk commented Oct 22, 2022

是的是的,可惜我比较菜,希望mirai能支持,这样就能填补mirai不能发送视频消息的空缺了

@Xunop
Copy link

Xunop commented Nov 2, 2022

最近我也遇到了这个问题,也在想办法能不能制作token出来,但是能力有限根本不知道怎么计算,不知道你是怎么通过oicq的core去获取token的呢?

@Star-Vk
Copy link
Author

Star-Vk commented Nov 3, 2022

最近我也遇到了这个问题,也在想办法能不能制作token出来,但是能力有限根本不知道怎么计算,不知道你是怎么通过oicq的core去获取token的呢?

在第一条帖子中我已经说明了获取token的方法,这个token值并不是oicq进行计算的,我也试图找到他的算法,但最终以失败告终,通过oicq的底层代码可知,原本的json的文本内容,通过了增加了一些新的东西进去之后进行了两次编码(or 加密?)通过发送消息的方式发送回了腾讯服务器,然后本地监听了message事件,等待腾讯服务器返回签名好的json,然后在返回给ark签名的函数结果。关于oicq实现的代码不方便展现,这一功能实现原理在第一条已经提到过了,可以通过原来音乐分享的入口将type改为10通过小程序的方式”申请“签名好的token值,oicq的ark签名方法无法整个移植到mirai来,以为他需要发送消息的协议。

@LaoLittle
Copy link
Contributor

这个是从服务器动态拉取js代码来计算的吧?我之前找的,可能忘了

@Star-Vk
Copy link
Author

Star-Vk commented Nov 3, 2022

这个是从服务器动态拉取js代码来计算的吧?我之前找的,可能忘了

emmmmm,反正token值不是本地计算的,是要把json文本发送回腾讯服务器然后腾讯服务器计算完,返回带有签名的json回来,然后QQ机器人发出去

@LaoLittle
Copy link
Contributor

emmmmm,反正token值不是本地计算的,是要把json文本发送回腾讯服务器然后腾讯服务器计算完,返回带有签名的json回来,然后QQ机器人发出去

新版macqq查询打开的文件就能看到arkapp的js缓存文件

@Star-Vk
Copy link
Author

Star-Vk commented Nov 3, 2022

emmmmm,反正token值不是本地计算的,是要把json文本发送回腾讯服务器然后腾讯服务器计算完,返回带有签名的json回来,然后QQ机器人发出去

新版macqq查询打开的文件就能看到arkapp的js缓存文件

这样子嘛?有能找到相关的token的算法嘛?望提供下,谢谢!

@LaoLittle
Copy link
Contributor

这样子嘛?有能找到相关的token的算法嘛?望提供下,谢谢!

我还没仔细看过,这玩意可能是算法,可能是界面,而且是从服务器拉取的

@LaoLittle
Copy link
Contributor

this.sendUni是oicq发送”数据包“(应该是这么叫)的方法,再往下追踪就是涉及到收发消息的协议了,同时oicq发送普通消息也是通过this.sendUni实现的。

直接贴链接不好么

@Star-Vk
Copy link
Author

Star-Vk commented Nov 3, 2022

this.sendUni是oicq发送”数据包“(应该是这么叫)的方法,再往下追踪就是涉及到收发消息的协议了,同时oicq发送普通消息也是通过this.sendUni实现的。

直接贴链接不好么

这个方法是根据oicq单独写出来,链接只有oicq通过OidbSvc.0xb77_9分享音乐的方法

@Star-Vk
Copy link
Author

Star-Vk commented Nov 3, 2022

this.sendUni是oicq发送”数据包“(应该是这么叫)的方法,再往下追踪就是涉及到收发消息的协议了,同时oicq发送普通消息也是通过this.sendUni实现的。

直接贴链接不好么

好吧

@Xunop
Copy link

Xunop commented Nov 3, 2022

谢谢!我昨天在好几个地方看见你的id在问这个问题,但是都没人回答,谢谢你的代码!

@Star-Vk
Copy link
Author

Star-Vk commented Nov 3, 2022

谢谢!我昨天在好几个地方看见你的id在问这个问题,但是都没人回答,谢谢你的代码!

好的

@Nambers
Copy link
Contributor

Nambers commented Nov 3, 2022

我根据这个测了下,不知道为什么都是Time Out :(
以及 oicq 的包格式和 mirai 里的有一些差别,我这里以 oicq 为主改了下 mirai 里的包结构
https://github.com/Nambers/mirai/blob/6e09aab2d7c6dfa298e0dc992bddcd088e4d5fed/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/SignArkPacket.kt
测试代码:

val app = """
        {"app":"com.tencent.bot.task.deblock","desc":"","view":"index","ver":"2.0.4.0","prompt":"XKBot6.0 Menu","appID":"","sourceName":"","actionData":"","actionData_A":"","sourceUrl":"","meta":{"detail":{"appID":"1109937557","battleDesc":"","botName":"XKbot","cmdList":[{"cmd":" ","cmdDesc":"欢迎您的使用!!!","cmdTitle":"QQ"}],"cmdTitle":"","content":"121212","guildID":"","iconLeft":[{"num":"10"}],"iconRight":[],"receiverName":"","subGuildID":"SUBGUILDID#","title":"","titleColor":""}}}
    """.trimIndent()
    bot.asQQAndroidBot().network.sendAndExpect(
        SignArkPacket(
            bot.client,
            app
        )
    ).response.printStructure()

@Nekoer
Copy link
Contributor

Nekoer commented Nov 3, 2022

以上的json只有手机端能显示吗

@Nekoer
Copy link
Contributor

Nekoer commented Nov 3, 2022

@Nambers 如果你用的最新的miria源代码的话,应该是QQ版本不支持该OidbSvc,可能需要你重新抓

@Nambers
Copy link
Contributor

Nambers commented Nov 3, 2022

@Nambers 如果你用的最新的miria源代码的话,应该是QQ版本不支持该OidbSvc,可能需要你重新抓

qs 不过我对着 qq 8.8.95里面看了下,结构体和mirai一样的。我晚点可以试一下能不能抓到这个包

@Nambers
Copy link
Contributor

Nambers commented Nov 3, 2022

@Nambers 如果你用的最新的miria源代码的话,应该是QQ版本不支持该OidbSvc,可能需要你重新抓

我看了下,我没抓到 0xb77 这个包, 但是有另外两个可能有关系的包
LightAppSvc.mini_app_share.AdaptShareInfoLightAppSvc.mini_app_usr_time.ReportShare

@Nambers
Copy link
Contributor

Nambers commented Nov 4, 2022

@Nambers 如果你用的最新的miria源代码的话,应该是QQ版本不支持该OidbSvc,可能需要你重新抓

我看了下,我没抓到 0xb77 这个包, 但是有另外两个可能有关系的包 LightAppSvc.mini_app_share.AdaptShareInfoLightAppSvc.mini_app_usr_time.ReportShare

我看了下AdaptShareInfo, 我觉得他比较有可能取到ark,
他的发送包和返回包:

@Serializable
internal class StAdaptShareInfoReq(
    @JvmField @ProtoNumber(1) val extInfo: StCommonExt? = null,
    @JvmField @ProtoNumber(2) val appid: String = "",
    @JvmField @ProtoNumber(3) val title: String = "",
    @JvmField @ProtoNumber(4) val desc: String = "",
    @JvmField @ProtoNumber(5) val time: Int = 0,
    @JvmField @ProtoNumber(6) val scene: Int /* enum */ = 0,
    @JvmField @ProtoNumber(7) val templetType: Int /* enum */ = 0,
    @JvmField @ProtoNumber(8) val businessType: Int /* enum */ = 0,
    @JvmField @ProtoNumber(9) val picUrl: String = "",
    @JvmField @ProtoNumber(10) val vidUrl: String = "",
    @JvmField @ProtoNumber(11) val jumpUrl: String = "",
    @JvmField @ProtoNumber(12) val iconUrl: String = "",
    @JvmField @ProtoNumber(13) val verType: Int = 0,
    @JvmField @ProtoNumber(14) val shareType: Int = 0,
    @JvmField @ProtoNumber(15) val versionId: String = "",
    @JvmField @ProtoNumber(16) val withShareTicket: Int = 0,
    @JvmField @ProtoNumber(17) val webURL: String = "",
    @JvmField @ProtoNumber(18) val appidRich: String = "",
    @JvmField @ProtoNumber(19) val template: StTemplateInfo? = null,
    @JvmField @ProtoNumber(20) val rcvOpenId: String = ""
) : ProtoBuf
@Serializable
internal class StAdaptShareInfoRsp(
    @JvmField @ProtoNumber(1) val extInfo: StCommonExt? = null,
    @JvmField @ProtoNumber(2) val jsonData: String = ""
) : ProtoBuf

但是因为我模拟器上的qq不知道为什么打开不了任何小程序了(比如腾讯文档/qq音乐), 唯一抓到的包还是坏的, 然后我手上没有真机来抓 :(
你们谁要是有时间有兴趣可以去看看这个包,他发送部分是不带 token 的,返回部分的 jsonData 是带 token 的.
PS: extInfo 是设备信息

@ha1c9on
Copy link

ha1c9on commented Jan 21, 2023

结果大佬许可,下面是oicq对ark签名的代码实现

/**
 * 使用须知
 * 1.代码语言为:TypeScript
 * 2.基于的框架为:oicq
 * 3.使用函数ArkSign前,请先执行npm i oicq
 */
import { Client } from "oicq";

/**
 * 通过小程序对json消息进行ark签名 基于oicq的core
 * @param json 要签名的json
 * @param Bot 已经创建好的机器人实例
 * @param core oicq的核心
 * @returns 
 */
 async function ArkSign(json:any,Bot:Client,core:any){
	return new Promise((resolve, reject) => {

        class Result{
            code:number = -1;
            [key:string]:any
        }

		let result = new Result();
		let json_data = null;
		try{
			json_data = JSON.parse(json);
		}catch(err){}
		
		if(!json_data){
			result.code = -1;
			result.msg = '签名失败,不是有效的json!';
			resolve(result);
			return;
		}
		delete json_data['extra'];
		
		//style 改成 10表示小程序发送
		let appid = 100951776, style = 10, appname = 'tv.danmaku.bili', appsign = '7194d531cbe7960a22007b9f6bdaa38b';
		let send_type = 0, recv_uin = Bot.uin, recv_guild_id = 0;
		
		let time = new Date().getTime();

        function random(min:number,max:number){
            const range  = max - min;
            const random = Math.random();
            const result = min + Math.round(random * range);
            return result;
        }
		let msg_seq = BigInt(`${time}${random(100,999)}`);
		
		//拼凑一个发送包
		let body = {
			1: appid,
			2: 1,
			3: style,
			5: {
				1: 1,
				2: "0.0.0",
				3: appname,
				4: appsign,
			},
			7: {
				15: msg_seq
			},
			10: send_type,
			11: recv_uin,
			18: {
				1: 1109937557,
				2: {
					14: 'pages',
				},
				3: 'url',
				4: 'text',
				5: 'text',
				6: 'text',
				10: JSON.stringify(json_data),
			}
		};
		
		//接收到签名好的json消息的处理函数
		let json_handle = function(e:any){
			if(Bot.uin == e.user_id && e?.message[0]?.type == 'json'){
				let json_str = e.message[0].data;
				let json = null;
				let extra = null;
				try{
					json = JSON.parse(json_str);
					extra = typeof(json.extra) == 'object' ? json.extra : JSON.parse(json.extra);
				}catch(err){}
				
				if(extra && extra.msg_seq == msg_seq){
					Bot.off('message',json_handle);
					clearTimeout(timer);
					delete json['extra'];
					result.code = 1;
					result.msg = '签名成功!';
					result.data = JSON.stringify(json);
					resolve(result);
					
				}
				
			}
		}
		
		let timer = setTimeout(function(){
			Bot.off('message',json_handle);
			result.code = -1;
			result.msg = '签名失败,请稍后再试!';
			resolve(result);
		},3000);
		
		//监听TX服务器返回签名好的json文本
		Bot.on('message',json_handle);

		//发送数据包
		Bot.sendOidb("OidbSvc.0xb77_9", core.pb.encode(body));
	});
}

export {ArkSign};

最终把消息进行core.pb.encode(body)得封装后传递进sendOidb方法,继续单步跟踪如下

/** dont use it if not clear the usage */
sendOidb(cmd: string, body: Uint8Array, timeout = 5) {
  	const sp = cmd //OidbSvc.0x568_22
  		.replace("OidbSvc.", "")
  		.replace("oidb_", "")
  		.split("_")
  	const type1 = parseInt(sp[0], 16), type2 = parseInt(sp[1])
  	body = pb.encode({
  		1: type1,
  		2: isNaN(type2) ? 1 : type2,
  		3: 0,
  		4: body,
  		6: "android " + this.apk.ver,
  	})
  	return this.sendUni(cmd, body, timeout)
}

除去其他信息以外,主要是对发送消息就行了两次encode方法

this.sendUni是oicq发送”数据包“(应该是这么叫)的方法,再往下追踪就是涉及到收发消息的协议了,同时oicq发送普通消息也是通过this.sendUni实现的。

您好 请问如何正确调用此函数 本人不太熟悉nodejs 望回复 谢谢!

@Star-Vk
Copy link
Author

Star-Vk commented Jan 21, 2023

结果大佬许可,下面是oicq对ark签名的代码实现

/**
 * 使用须知
 * 1.代码语言为:TypeScript
 * 2.基于的框架为:oicq
 * 3.使用函数ArkSign前,请先执行npm i oicq
 */
import { Client } from "oicq";

/**
 * 通过小程序对json消息进行ark签名 基于oicq的core
 * @param json 要签名的json
 * @param Bot 已经创建好的机器人实例
 * @param core oicq的核心
 * @returns 
 */
 async function ArkSign(json:any,Bot:Client,core:any){
	return new Promise((resolve, reject) => {

        class Result{
            code:number = -1;
            [key:string]:any
        }

		let result = new Result();
		let json_data = null;
		try{
			json_data = JSON.parse(json);
		}catch(err){}
		
		if(!json_data){
			result.code = -1;
			result.msg = '签名失败,不是有效的json!';
			resolve(result);
			return;
		}
		delete json_data['extra'];
		
		//style 改成 10表示小程序发送
		let appid = 100951776, style = 10, appname = 'tv.danmaku.bili', appsign = '7194d531cbe7960a22007b9f6bdaa38b';
		let send_type = 0, recv_uin = Bot.uin, recv_guild_id = 0;
		
		let time = new Date().getTime();

        function random(min:number,max:number){
            const range  = max - min;
            const random = Math.random();
            const result = min + Math.round(random * range);
            return result;
        }
		let msg_seq = BigInt(`${time}${random(100,999)}`);
		
		//拼凑一个发送包
		let body = {
			1: appid,
			2: 1,
			3: style,
			5: {
				1: 1,
				2: "0.0.0",
				3: appname,
				4: appsign,
			},
			7: {
				15: msg_seq
			},
			10: send_type,
			11: recv_uin,
			18: {
				1: 1109937557,
				2: {
					14: 'pages',
				},
				3: 'url',
				4: 'text',
				5: 'text',
				6: 'text',
				10: JSON.stringify(json_data),
			}
		};
		
		//接收到签名好的json消息的处理函数
		let json_handle = function(e:any){
			if(Bot.uin == e.user_id && e?.message[0]?.type == 'json'){
				let json_str = e.message[0].data;
				let json = null;
				let extra = null;
				try{
					json = JSON.parse(json_str);
					extra = typeof(json.extra) == 'object' ? json.extra : JSON.parse(json.extra);
				}catch(err){}
				
				if(extra && extra.msg_seq == msg_seq){
					Bot.off('message',json_handle);
					clearTimeout(timer);
					delete json['extra'];
					result.code = 1;
					result.msg = '签名成功!';
					result.data = JSON.stringify(json);
					resolve(result);
					
				}
				
			}
		}
		
		let timer = setTimeout(function(){
			Bot.off('message',json_handle);
			result.code = -1;
			result.msg = '签名失败,请稍后再试!';
			resolve(result);
		},3000);
		
		//监听TX服务器返回签名好的json文本
		Bot.on('message',json_handle);

		//发送数据包
		Bot.sendOidb("OidbSvc.0xb77_9", core.pb.encode(body));
	});
}

export {ArkSign};

最终把消息进行core.pb.encode(body)得封装后传递进sendOidb方法,继续单步跟踪如下

/** dont use it if not clear the usage */
sendOidb(cmd: string, body: Uint8Array, timeout = 5) {
  	const sp = cmd //OidbSvc.0x568_22
  		.replace("OidbSvc.", "")
  		.replace("oidb_", "")
  		.split("_")
  	const type1 = parseInt(sp[0], 16), type2 = parseInt(sp[1])
  	body = pb.encode({
  		1: type1,
  		2: isNaN(type2) ? 1 : type2,
  		3: 0,
  		4: body,
  		6: "android " + this.apk.ver,
  	})
  	return this.sendUni(cmd, body, timeout)
}

除去其他信息以外,主要是对发送消息就行了两次encode方法
this.sendUni是oicq发送”数据包“(应该是这么叫)的方法,再往下追踪就是涉及到收发消息的协议了,同时oicq发送普通消息也是通过this.sendUni实现的。

您好 请问如何正确调用此函数 本人不太熟悉nodejs 望回复 谢谢!

我很抱歉,如果你不是oicq的开发者无法使用这个函数,以及有人写出了mirai支持的方法,详情可参考https://github.com/Mrs4s/MiraiGo/pull/307,我的疑惑也就此解开,也提醒各位请勿滥用ark签名去签名违法、违规的标签,json卡片最终会因为ark签名滥用而导致和谐,祝您早日替换成图片替换成卡片,再次关闭本issue

@Star-Vk Star-Vk closed this as completed Jan 21, 2023
@ha1c9on
Copy link

ha1c9on commented Jan 21, 2023

结果大佬许可,下面是oicq对ark签名的代码实现

/**
 * 使用须知
 * 1.代码语言为:TypeScript
 * 2.基于的框架为:oicq
 * 3.使用函数ArkSign前,请先执行npm i oicq
 */
import { Client } from "oicq";

/**
 * 通过小程序对json消息进行ark签名 基于oicq的core
 * @param json 要签名的json
 * @param Bot 已经创建好的机器人实例
 * @param core oicq的核心
 * @returns 
 */
 async function ArkSign(json:any,Bot:Client,core:any){
	return new Promise((resolve, reject) => {

        class Result{
            code:number = -1;
            [key:string]:any
        }

		let result = new Result();
		let json_data = null;
		try{
			json_data = JSON.parse(json);
		}catch(err){}
		
		if(!json_data){
			result.code = -1;
			result.msg = '签名失败,不是有效的json!';
			resolve(result);
			return;
		}
		delete json_data['extra'];
		
		//style 改成 10表示小程序发送
		let appid = 100951776, style = 10, appname = 'tv.danmaku.bili', appsign = '7194d531cbe7960a22007b9f6bdaa38b';
		let send_type = 0, recv_uin = Bot.uin, recv_guild_id = 0;
		
		let time = new Date().getTime();

        function random(min:number,max:number){
            const range  = max - min;
            const random = Math.random();
            const result = min + Math.round(random * range);
            return result;
        }
		let msg_seq = BigInt(`${time}${random(100,999)}`);
		
		//拼凑一个发送包
		let body = {
			1: appid,
			2: 1,
			3: style,
			5: {
				1: 1,
				2: "0.0.0",
				3: appname,
				4: appsign,
			},
			7: {
				15: msg_seq
			},
			10: send_type,
			11: recv_uin,
			18: {
				1: 1109937557,
				2: {
					14: 'pages',
				},
				3: 'url',
				4: 'text',
				5: 'text',
				6: 'text',
				10: JSON.stringify(json_data),
			}
		};
		
		//接收到签名好的json消息的处理函数
		let json_handle = function(e:any){
			if(Bot.uin == e.user_id && e?.message[0]?.type == 'json'){
				let json_str = e.message[0].data;
				let json = null;
				let extra = null;
				try{
					json = JSON.parse(json_str);
					extra = typeof(json.extra) == 'object' ? json.extra : JSON.parse(json.extra);
				}catch(err){}
				
				if(extra && extra.msg_seq == msg_seq){
					Bot.off('message',json_handle);
					clearTimeout(timer);
					delete json['extra'];
					result.code = 1;
					result.msg = '签名成功!';
					result.data = JSON.stringify(json);
					resolve(result);
					
				}
				
			}
		}
		
		let timer = setTimeout(function(){
			Bot.off('message',json_handle);
			result.code = -1;
			result.msg = '签名失败,请稍后再试!';
			resolve(result);
		},3000);
		
		//监听TX服务器返回签名好的json文本
		Bot.on('message',json_handle);

		//发送数据包
		Bot.sendOidb("OidbSvc.0xb77_9", core.pb.encode(body));
	});
}

export {ArkSign};

最终把消息进行core.pb.encode(body)得封装后传递进sendOidb方法,继续单步跟踪如下

/** dont use it if not clear the usage */
sendOidb(cmd: string, body: Uint8Array, timeout = 5) {
  	const sp = cmd //OidbSvc.0x568_22
  		.replace("OidbSvc.", "")
  		.replace("oidb_", "")
  		.split("_")
  	const type1 = parseInt(sp[0], 16), type2 = parseInt(sp[1])
  	body = pb.encode({
  		1: type1,
  		2: isNaN(type2) ? 1 : type2,
  		3: 0,
  		4: body,
  		6: "android " + this.apk.ver,
  	})
  	return this.sendUni(cmd, body, timeout)
}

除去其他信息以外,主要是对发送消息就行了两次encode方法
this.sendUni是oicq发送”数据包“(应该是这么叫)的方法,再往下追踪就是涉及到收发消息的协议了,同时oicq发送普通消息也是通过this.sendUni实现的。

您好 请问如何正确调用此函数 本人不太熟悉nodejs 望回复 谢谢!

我很抱歉,如果你不是oicq的开发者无法使用这个函数,以及有人写出了mirai支持的方法,详情可参考https://github.com/Mrs4s/MiraiGo/pull/307,我的疑惑也就此解开,也提醒各位请勿滥用ark签名去签名违法、违规的标签,json卡片最终会因为ark签名滥用而导致和谐,祝您早日替换成图片替换成卡片,再次关闭本issue

了解 因为我是Java开发者 所以对于这两种语言不太熟悉 感谢您的回复 我再学习下

@Star-Vk
Copy link
Author

Star-Vk commented Jan 21, 2023

结果大佬许可,下面是oicq对ark签名的代码实现

/**
 * 使用须知
 * 1.代码语言为:TypeScript
 * 2.基于的框架为:oicq
 * 3.使用函数ArkSign前,请先执行npm i oicq
 */
import { Client } from "oicq";

/**
 * 通过小程序对json消息进行ark签名 基于oicq的core
 * @param json 要签名的json
 * @param Bot 已经创建好的机器人实例
 * @param core oicq的核心
 * @returns 
 */
 async function ArkSign(json:any,Bot:Client,core:any){
	return new Promise((resolve, reject) => {

        class Result{
            code:number = -1;
            [key:string]:any
        }

		let result = new Result();
		let json_data = null;
		try{
			json_data = JSON.parse(json);
		}catch(err){}
		
		if(!json_data){
			result.code = -1;
			result.msg = '签名失败,不是有效的json!';
			resolve(result);
			return;
		}
		delete json_data['extra'];
		
		//style 改成 10表示小程序发送
		let appid = 100951776, style = 10, appname = 'tv.danmaku.bili', appsign = '7194d531cbe7960a22007b9f6bdaa38b';
		let send_type = 0, recv_uin = Bot.uin, recv_guild_id = 0;
		
		let time = new Date().getTime();

        function random(min:number,max:number){
            const range  = max - min;
            const random = Math.random();
            const result = min + Math.round(random * range);
            return result;
        }
		let msg_seq = BigInt(`${time}${random(100,999)}`);
		
		//拼凑一个发送包
		let body = {
			1: appid,
			2: 1,
			3: style,
			5: {
				1: 1,
				2: "0.0.0",
				3: appname,
				4: appsign,
			},
			7: {
				15: msg_seq
			},
			10: send_type,
			11: recv_uin,
			18: {
				1: 1109937557,
				2: {
					14: 'pages',
				},
				3: 'url',
				4: 'text',
				5: 'text',
				6: 'text',
				10: JSON.stringify(json_data),
			}
		};
		
		//接收到签名好的json消息的处理函数
		let json_handle = function(e:any){
			if(Bot.uin == e.user_id && e?.message[0]?.type == 'json'){
				let json_str = e.message[0].data;
				let json = null;
				let extra = null;
				try{
					json = JSON.parse(json_str);
					extra = typeof(json.extra) == 'object' ? json.extra : JSON.parse(json.extra);
				}catch(err){}
				
				if(extra && extra.msg_seq == msg_seq){
					Bot.off('message',json_handle);
					clearTimeout(timer);
					delete json['extra'];
					result.code = 1;
					result.msg = '签名成功!';
					result.data = JSON.stringify(json);
					resolve(result);
					
				}
				
			}
		}
		
		let timer = setTimeout(function(){
			Bot.off('message',json_handle);
			result.code = -1;
			result.msg = '签名失败,请稍后再试!';
			resolve(result);
		},3000);
		
		//监听TX服务器返回签名好的json文本
		Bot.on('message',json_handle);

		//发送数据包
		Bot.sendOidb("OidbSvc.0xb77_9", core.pb.encode(body));
	});
}

export {ArkSign};

最终把消息进行core.pb.encode(body)得封装后传递进sendOidb方法,继续单步跟踪如下

/** dont use it if not clear the usage */
sendOidb(cmd: string, body: Uint8Array, timeout = 5) {
  	const sp = cmd //OidbSvc.0x568_22
  		.replace("OidbSvc.", "")
  		.replace("oidb_", "")
  		.split("_")
  	const type1 = parseInt(sp[0], 16), type2 = parseInt(sp[1])
  	body = pb.encode({
  		1: type1,
  		2: isNaN(type2) ? 1 : type2,
  		3: 0,
  		4: body,
  		6: "android " + this.apk.ver,
  	})
  	return this.sendUni(cmd, body, timeout)
}

除去其他信息以外,主要是对发送消息就行了两次encode方法
this.sendUni是oicq发送”数据包“(应该是这么叫)的方法,再往下追踪就是涉及到收发消息的协议了,同时oicq发送普通消息也是通过this.sendUni实现的。

您好 请问如何正确调用此函数 本人不太熟悉nodejs 望回复 谢谢!

我很抱歉,如果你不是oicq的开发者无法使用这个函数,以及有人写出了mirai支持的方法,详情可参考https://github.com/Mrs4s/MiraiGo/pull/307,我的疑惑也就此解开,也提醒各位请勿滥用ark签名去签名违法、违规的标签,json卡片最终会因为ark签名滥用而导致和谐,祝您早日替换成图片替换成卡片,再次关闭本issue

了解 因为我是Java开发者 所以对于这两种语言不太熟悉 感谢您的回复 我再学习下

好的好的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
t:feature 类型: 新特性
Projects
None yet
Development

No branches or pull requests

6 participants