Loading... ## 一、前言 这个插件的作用是把羊毛插件里的关于变量(CK)授权的操作抽离出来。这样就不需要在每个羊毛插件中各自维护着一套授权系统,能减少很多插件的开发难度,并且积分系统也是直接使用的盖亚,直接就把羊毛变量授权系统和积分系统统一了。当然,有优点就有缺点,为了适用更多的场景,插件内只能做到对授权数据的集中管理,无法真正做到变量全覆盖通用,还是需要单独开发羊毛插件进行接入,比如一些登录逻辑,变量的有效性检测,这些都需要其他插件来实现,具体可以看最后那部分的接入指南,其他的缺点还有在接入的青龙容器里,备注被插件占用,无法单独修改等。 ## 二、食用教程 ### 准备工作 `autMan版本:>=3.3.5` ### 下载云插件 云市场订阅我的市场`sirhexs`,安装`通用变量授权(目前名字是通用变量授权Demo,相当于内测版的意思,很多地方还需要测试完善)` `通用变量授权RT`两个插件 上面两个插件就是这套授权系统的主体插件。 ### 安装web插件 <button class=" btn m-b-xs btn-primary " onclick="window.open('https://wwgn.lanzoul.com/i9x002nipjtc','_blank')">蓝奏云:envAuthorize.zip</button> 下载这个压缩包,到`/autman/plugin/web`下解压。效果如下,注意要与图中的路径一致  ### 设置插件命令(可跳过) 插件中定义的命令都是一些英文组合,这样做是为了让你能通过aut自带的命令转换来自定义成你需要的命令,具体设置教程,可以看我前面写的文章。 <div class="preview"> <div class="post-inser post box-shadow-wrap-normal"> <a href="https://www.dwblog.cn/archives/78/" target="_blank" class="post_inser_a no-external-link no-underline-link"> <div class="inner-image bg" style="background-image: url(https://t.mwm.moe/fj/);background-size: cover;"></div> <div class="inner-content" > <p class="inser-title">【AutMan】如何自定义指令</p> <div class="inster-summary text-muted"> 一、前言目前AutMan插件自定义触发指令的方式大致分为2种,全触发规则插件,通过配参设置触发指令。这种方式非常简... </div> </div> </a> <!-- .inner-content #####--> </div> <!-- .post-inser ####--> </div> #### ^sm:envAuthorize:record$ 作用:发送这条指令,机器人进入变量记录(绑定)程序,机器人会先把已上架的项目列表展示出来,用户需要先回复序号选择对应的项目,然后发送对应项目的变量。机器人会根据’检测接口‘的返回数据,绑定这条变量到发送指令的用户社交媒体账号上。 后话:没办法,因为是通用插件,没办法一个个羊毛去适配全部变量,只能通过这种选择项目,然后发送变量的方式进行绑定。当然,如果你会自己写插件,可以自己写一个插件来接入,后面有一个指令就是这个作用。 #### ^sm:envAuthorize:authorization$ 作用:发送这条指令,机器人进入变量授权程序,程序流程:列出项目->选择项目->列出已绑定账号->选择账号->输入授权数量->盖亚支付->授权成功,变量提交青龙->根据管理员设置的授权回复语文本,发送授权文本。 #### ^sm:envAuthorize:query.*$ 作用:这条指令的作用是调用插件内置的简易查询程序,通过设置的’查询接口‘,获取对应的数据变量,然后根据管理员设置的项目‘查询回复语文本’,给用户发送相应项目的查询信息。 后话:这条指令的指令转换和上面的不一样,要看你对应项目设置的‘项目标识’,比如你有一个顺丰项目,‘项目标识’设置的`shunfeng`,那么你想调用插件内置的查询程序,那么转换的指令应该是`^sm:envAuthorize:query:shunfeng$`,意思就是`^sm:envAuthorize:query:这里填你项目的项目标识$` #### ^sm:envAuthorize:breakIn.*$ 作用:这条指令是提供给外部插件接入本插件的,还是用顺丰做例子,现在除了直接抓包获取对应变量,还能通过扫码获取,那就可以把扫码相关的流程写成一个插件,然后在最后获取到对应变量,可以通过aut提供的`breakIn()`函数(aut支持的各语言都有这个函数,可以自己看中间件)使用这条指令,把变量提交给本授权系统进行账号的绑定操作,格式:`sm:envAuthorize:breakIn:{identifier}:{env}` identifier 是管理员设置的项目标识,env 是对应的变量,文章最后有接入示例,可以看一下。 后话:这指令基本就是插件作者的事,你单纯就是使用授权系统的,可以无视这条指令。 #### 更多指令 TODO(构思暂未实现的指令,没错,就是在画大饼) * `^sm:envAuthorize:generateCDK$` 管理员可以通过这条指令,选择项目生成特定的项目卡密。 * `^(ENVCDK)[A-Z0-9]{14}$` 就是上面生成的卡密,用户可以发送项目卡密,进行变量的授权。 * `^sm:envAuthorize:authorization.*$` 单独项目的授权,不用触发项目选择选项,就和查询那条指令差不多的设置。 ### 设置项目  这里主要是对项目的每一个设置进行更详细的说明,方便你们设置项目,这里贴一个我的某个项目设置,有红点的就是必填项。 **项目名称**:在机器人的交互中,都会使用这个名称,来展示,就是项目的名字嘛,没什么好说的。 **项目标识**:这个字段就是项目的ID、sfz,非常重要,在插件的逻辑中,就靠这个字段来寻找项目的配置。 **项目变量名称**:这个字段的作用是用来存储变量数据的,多个项目可以用同一个项目变量名称,比如说,很多羊毛不是有呆瓜+抢兑的吗,那这不是要设置两个项目嘛,但呆瓜和抢兑用的变量不是一样嘛,通过设置同一个项目变量名称,就可以获取到一样的绑定数据,不用重新绑定一次。 **是否上架**:上架后,上面记录和授权指令才会显示对应的项目。 **类型**:就是字面意思 **价格**:接入了盖亚,1r=100积分。 **礼币占比**:盖亚的积分系统中积分分两类 1.余额-用户通过各种渠道充值的积分 2.礼币-通过签到等营销活动发放的积分。这里可以设置该项目的积分中最多可以用多少比例的礼币进行抵扣。范围0-1,例如填0.5,相当于最多可以用礼币抵扣50%的积分,剩下的用余额扣除。你填0,就是不能用礼币白嫖。 **检测接口**:上面说过,因为是通用授权系统,没办法做到全羊毛适配,只能通过这个检测接口来完成变量的有效性检测和变量的特征值获取(就是主键的意思,例如jdck的pin值),而且是强制性的,必填,不需要检测,那为什么不直接用aut自带的变量整理呢,是吧。那这检测接口怎么来?从你的羊毛脚本中扣一个请求出来,能请求通不就是有效了吗,把这个请求写成aut的微服务插件,不就行了。ok,你说你不会写插件,那没法,你只能找或者等其他插件作者写,然后被圈一下了。当然,你懂事也不是不可以找我看看能不能给你写,手动滑稽。**具体的响应体结构,我放最后的示例那里,需要的记得看** **查询接口**:和上面需要检测接口的原因一样,没办法内置各羊毛的查询,只能通过这个接口来获取对应的数据,然后通过文本替换,替换你设置的查询文本中的变量值。当然,本插件内置的查询程序是非常简陋的,你完全可以在自己插件中实现查询逻辑,所以这里也就不是必填项。 **对接容器**:web页面会自动把你在aut中添加的对接容器获取出来,你选择就行了,变量授权后会自动往这里设置的容器提交变量,注意一个点:还是老原因,所以本插件没办法像 某东变量 那样,自动根据pin去更新变量,只能通过变量的备注去识别,所以你别动你容器中的环境变量备注。 **环境变量名称**:字面意思嘛,正常设置就行 然后就是剩下的回复语设置,这里就不一个个说了,全部回复语文本都支持`【用户名】【授权时间】`这两个变量。除此之外,`记录成功回复`和`更新成功回复`还会请求检测接口,所以也支持检测接口带来的变量,`授权成功回复`不会请求接口,所以就只支持上面两个变量,`变量查询回复`就多了,你看查询接口带来的变量就行了。 ## 三、插件接入指南 ### 检测接口响应结构 ```json { code: 200, // 响应码 200为成功,其他根据你习惯来就行 message: "success", // 提示信息,响应码非200 插件会用这个字段回复 data:{ valid: true, // 变量是否有效 primaryKey: 'xxxxx', // 变量主键,用来区别变量,最好使用这个变量的唯一标识,比如id,手机号等 variable: { // 可供替换的文本变量,插件会用这里提供的变量进行文本替换 '【手机号】': 'xxxxxx' // 我习惯使用【】包裹文本做变量,这里看你喜欢吧 } } } ``` ### 查询接口响应结构 你会发现这里的结构和上面是一样的,如果资产查询比较少,是可以只写一个接口,但我还是推荐做成两个接口,因为检测有效性并不需要所有资产查询信息,用查询接口做检测,会产生很多不必要的请求。 ```json { code: 200, // 响应码 200为成功,其他根据你习惯来就行 message: "success", // 提示信息,响应码非200 插件会用这个字段回复 data:{ valid: true, // 变量是否有效 primaryKey: 'xxxxx', // 变量主键,用来区别变量,最好使用这个变量的唯一标识,比如id,手机号等 variable: { // 可供替换的文本变量,插件会用这里提供的变量进行文本替换 '【手机号】': 'xxxxxx' // 我习惯使用【】包裹文本做变量,这里看你喜欢吧 } } } ``` ### 接入Demo ```javascript // ==================云市场元数据================== //[title: EA_顺丰] //[author: sirhexs] //[language: es5] //[class: 工具类] //[service: 46883409] 售后联系方式 //[version: 1.0.0]版本号 //[public:true] 是否发布?值为true或false,不设置则上传aut云时会自动设置为true,false时上传后不显示在市场中,但是搜索能搜索到,方便开发者测试 //[price: 8888.88] 上架价格 //[platform: rt] 适用的平台 //[description: 介绍:本插件无法单独使用,需配合【通用变量授权】使用。本插件实现了顺丰的登录流程,开放顺丰的资产查询接口、有效性接口] 使用方法尽量写具体 //[open_source: false]是否开源 // ==================功能元数据================== //[priority: 100] 优先级,数字越大表示优先级越高 //[router: /sm_ea_shunfeng] 路由路径 //[method: get]微服务方法,我们只需要get请求 //[rule: ^sm:ea:shunfeng:login$] 变量记录 // ==================配参数据================== //[param: {"required":true,"key":"sm_ea_shunfeng_config.token","bool":false,"placeholder":"","name":"鉴权token","desc":"这个token非常重要,相当于一个密码,按你的想法设置就行,别让别人知道,避免接口外露,被恶意请求"}] //[param: {"required":true,"key":"sm_ea_shunfeng_config.identifier","bool":false,"placeholder":"","name":"项目ID","desc":"登录成功会回调给【变量通用授权】插件,请正确填写对应项目的ID"}] // 自动安装模块 function requireWithInstall(moduleName) { try { return require(moduleName); } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { console.log(`模块 ${moduleName} 未找到,正在安装...`); const execSync = require('child_process').execSync; execSync(`npm install ${moduleName}`, { stdio: 'inherit' }); return require(moduleName); } else { throw e; } } } // 使用 requireWithInstall 函数 const middleware = require('./middleware.js'); const axios = requireWithInstall('axios'); const uuidv4 = requireWithInstall('uuid').v4; const senderID = middleware.getSenderID(); const sender = new middleware.Sender(senderID); (async () => { const imtype = await sender.getImtype() const message = await sender.getMessage() // 路由请求 检测接口+查询接口 if (imtype == 'rt') { // 获取路由数据 let routerMethod = await sender.getRouterMethod() let routerParams = await sender.getRouterParams() // 最基础的请求检查 if (!routerMethod == 'get' || !routerParams.hasOwnProperty('token') || !routerParams.hasOwnProperty('env')) { return await sender.response({ code: 403, message: '出错了,你这代码有问题,快修一下', valid: false, primaryKey: '', variable: {} }) } // 先验证token,防止恶意请求 let token = await sender.bucketGet('sm_ea_shunfeng_config', 'token') if (routerParams.token !== token) { return await sender.response({ code: 403, message: 'token错误', valid: false, primaryKey: '', variable: {} }) } // 检测接口 ,实质用登录接口,能正常请求则有效 if (routerParams.hasOwnProperty('fun') && routerParams.fun == 'valid') { try { // 获取url const url = decodeURI(routerParams.env) // 实例化接口 const sf = new ShunFeng(url) // 登录操作 const res = await sf.login(url) if (!res) { // 登录失败,返回数据 return await sender.response({ code: 401, message: "错误url", valid: false, primaryKey: '', variable: {} }) } // 成功登录,返回数据 return await sender.response({ code: 200, message: "", valid: true, primaryKey: res.loginMobile, variable: { '【手机号】': res.loginMobile } }) } catch (e) { return await sender.response({ code: 404, message: "出错了", valid: false, primaryKey: '', variable: {} }) } } // 查询接口 查询资产返回 if (routerParams.hasOwnProperty('fun') && routerParams.fun == 'query') { try { // 获取url const url = decodeURI(routerParams.env) // 实例化接口 const sf = new ShunFeng(url) // 登录操作 const res = await sf.login(url) if (!res) { // 登录失败,返回数据 return await sender.response({ code: 401, message: "错误url", valid: false, primaryKey: '', variable: {} }) } // 获取蜜罐情况 const capacity = await sf.quearHoneypot(); console.log(`Capacity: ${capacity}`); // 获取今日积分情况 const { todayIntegral, allIntegral } = await sf.queryIntergral(); console.log(`Today Integral: ${todayIntegral}, ALL Integral: ${allIntegral}`); // 获取今日蜂蜜情况 const { todayHoney, allHoney } = await sf.queryHoney(); console.log(`Today Honey: ${todayHoney}, All Honey: ${allHoney}`); // 你会发现 我这里得响应和上面检测的一样,如果资产查询接口查的资产比较少,其实可以写成一个接口 return await sender.response({ code: 200, message: "", valid: true, primaryKey: res.loginMobile, variable: { '【今日积分】': todayIntegral, '【当前积分】': allIntegral, '【今日蜂蜜】': todayHoney, '【当前蜂蜜】': allHoney, '【蜜罐容量】': capacity, '【手机号】': res.loginMobile } }) } catch (e) { return await sender.response({ code: 404, message: "出错了", valid: false, primaryKey: '', variable: {} }) } } return await sender.response({ code: 404, message: '没有这个服务,检查你的请求', valid: false, primaryKey: '', variable: {} }) } // 登录逻辑 if (message == 'sm:ea:shunfeng:login') { const identifier = await sender.bucketGet('sm_ea_shunfeng_config', 'identifier') if (!identifier) { return await sender.reply('管理员未配置参数,无法正常使用') } try { // 登录逻辑,这里省略了 const env = 'xxxxx' // 假设这里获取到了变量 // 成功登录,获取到相关变量,使用下面这个breakIn()函数丢给授权插件处理即可 sender.breakIn(`sm:envAuthorize:breakIn:${identifier}:${env}`) return } catch (error) { await sender.reply(`❌ 登录失败!报错:${error.message}`); return; } } // 除了登录逻辑,你还能继续写其他,比如你不想用授权系统中内置的查询程序,那么你完全可以自己实现一个查询。 })() /** * @description: 顺丰相关接口,比较长,我省略了,就是一些接口内容 */ class ShunFeng {} ``` 最后修改:2025 年 02 月 24 日 © 允许规范转载 赞 3 如果觉得我的文章对你有用,请随意赞赏
1 条评论
对权力结构的解构充满勇气与智慧。