mirror of
https://github.com/AIsouler/GKD_subscription.git
synced 2025-12-18 15:55:29 +08:00
chore: @gkd-kit/api
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import url from 'node:url';
|
import url from 'node:url';
|
||||||
import type { RawApp } from '../src/types';
|
import type { RawApp } from '@gkd-kit/api';
|
||||||
import { tryRun } from '../src/utils';
|
import { tryRun } from '../src/utils';
|
||||||
|
|
||||||
// 使用命令更新内存订阅
|
// 使用命令更新内存订阅
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { RawCategory } from './types';
|
import type { RawCategory } from '@gkd-kit/api';
|
||||||
|
|
||||||
const categories: RawCategory[] = [
|
const categories: RawCategory[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import categories from './categories';
|
import categories from './categories';
|
||||||
import globalGroups from './globalGroups';
|
import globalGroups from './globalGroups';
|
||||||
import apps from './rawApps';
|
import apps from './rawApps';
|
||||||
import type { RawSubscription } from './types';
|
import type { RawSubscription } from '@gkd-kit/api';
|
||||||
|
|
||||||
const subsConfig: RawSubscription = {
|
const subsConfig: RawSubscription = {
|
||||||
id: 666,
|
id: 666,
|
||||||
|
|||||||
10
src/file.ts
10
src/file.ts
@@ -3,13 +3,13 @@ import fs from 'node:fs/promises';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import type PkgT from '../package.json';
|
import type PkgT from '../package.json';
|
||||||
import { parseSelector } from './selector';
|
import { parseSelector } from './selector';
|
||||||
|
import type { RawAppAddProp } from './types';
|
||||||
import type {
|
import type {
|
||||||
RawApp,
|
|
||||||
RawAppGroup,
|
RawAppGroup,
|
||||||
RawGlobalGroup,
|
RawGlobalGroup,
|
||||||
IArray,
|
IArray,
|
||||||
RawSubscription,
|
RawSubscription,
|
||||||
} from './types';
|
} from '@gkd-kit/api';
|
||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
|
|
||||||
// 定义一个将 IArray<T> 类型转换为 T[] 类型的函数
|
// 定义一个将 IArray<T> 类型转换为 T[] 类型的函数
|
||||||
@@ -206,7 +206,7 @@ export const checkConfig = (newConfig: RawSubscription) => {
|
|||||||
|
|
||||||
// 检查组和规则的重复键
|
// 检查组和规则的重复键
|
||||||
const apps = newConfig.apps || [];
|
const apps = newConfig.apps || [];
|
||||||
apps.forEach((app) => {
|
apps.forEach((app: RawAppAddProp) => {
|
||||||
const deprecatedKeys = app.deprecatedKeys || [];
|
const deprecatedKeys = app.deprecatedKeys || [];
|
||||||
const keys = new Set<number>();
|
const keys = new Set<number>();
|
||||||
const oldGroups = oldConfig.apps?.find((a) => a.id == app.id)?.groups || [];
|
const oldGroups = oldConfig.apps?.find((a) => a.id == app.id)?.groups || [];
|
||||||
@@ -377,7 +377,7 @@ export const checkAndDeleteFiles = async (): Promise<void> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 导出一个异步函数,用于更新应用的 Markdown 文件
|
// 导出一个异步函数,用于更新应用的 Markdown 文件
|
||||||
export const updateAppMd = async (app: RawApp) => {
|
export const updateAppMd = async (app: RawAppAddProp) => {
|
||||||
// 生成应用的 Markdown 文本内容
|
// 生成应用的 Markdown 文本内容
|
||||||
const appHeadMdText = [
|
const appHeadMdText = [
|
||||||
`# ${app.name}`,
|
`# ${app.name}`,
|
||||||
@@ -509,7 +509,7 @@ const getGlobalDiffLog = (
|
|||||||
|
|
||||||
// 定义一个类型,表示应用的变更日志
|
// 定义一个类型,表示应用的变更日志
|
||||||
type AppDiff = {
|
type AppDiff = {
|
||||||
app: RawApp;
|
app: RawAppAddProp;
|
||||||
addGroups: RawAppGroup[];
|
addGroups: RawAppGroup[];
|
||||||
changeGroups: RawAppGroup[];
|
changeGroups: RawAppGroup[];
|
||||||
removeGroups: RawAppGroup[];
|
removeGroups: RawAppGroup[];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import apps from './rawApps';
|
import apps from './rawApps';
|
||||||
import type { RawGlobalGroup } from './types';
|
import type { RawGlobalGroup } from '@gkd-kit/api';
|
||||||
import { OPEN_AD_ORDER } from './utils';
|
import { OPEN_AD_ORDER } from './utils';
|
||||||
|
|
||||||
const diabledAppIds = [
|
const diabledAppIds = [
|
||||||
@@ -13,6 +13,14 @@ const diabledAppIds = [
|
|||||||
'com.android.phone.recorder', // 录音
|
'com.android.phone.recorder', // 录音
|
||||||
'com.android.soundrecorder', // 录音机
|
'com.android.soundrecorder', // 录音机
|
||||||
'com.android.server.telecom', // 来电拒接短信服务
|
'com.android.server.telecom', // 来电拒接短信服务
|
||||||
|
|
||||||
|
// 排除软件包安装程序
|
||||||
|
'com.android.packageinstaller',
|
||||||
|
'com.google.android.packageinstaller', // Google Play
|
||||||
|
'com.miui.packageinstaller', // 小米系
|
||||||
|
'com.samsung.android.packageinstaller', // 三星系
|
||||||
|
'com.oplus.appdetail', // 一加系
|
||||||
|
|
||||||
// 在一些常见的应用中禁用
|
// 在一些常见的应用中禁用
|
||||||
'com.tencent.mm', // 微信
|
'com.tencent.mm', // 微信
|
||||||
'li.songe.gkd', // GKD
|
'li.songe.gkd', // GKD
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ import url from 'node:url';
|
|||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
import { pinyin } from 'pinyin-pro';
|
import { pinyin } from 'pinyin-pro';
|
||||||
import { walk } from './file';
|
import { walk } from './file';
|
||||||
import type { RawApp } from './types';
|
import type { RawAppAddProp } from './types';
|
||||||
import { OPEN_AD_ORDER } from './utils';
|
import { OPEN_AD_ORDER } from './utils';
|
||||||
|
|
||||||
const rawApps: RawApp[] = [];
|
const rawApps: RawAppAddProp[] = [];
|
||||||
for await (const tsFp of walk(process.cwd() + '/src/apps')) {
|
for await (const tsFp of walk(process.cwd() + '/src/apps')) {
|
||||||
if (!tsFp.endsWith('.ts')) {
|
if (!tsFp.endsWith('.ts')) {
|
||||||
throw new Error('invalid typescript app config file: ' + tsFp);
|
throw new Error('invalid typescript app config file: ' + tsFp);
|
||||||
}
|
}
|
||||||
const mod: { default: RawApp } = await import(url.pathToFileURL(tsFp).href);
|
const mod: { default: RawAppAddProp } = await import(
|
||||||
|
url.pathToFileURL(tsFp).href
|
||||||
|
);
|
||||||
const appConfig = mod.default;
|
const appConfig = mod.default;
|
||||||
if (path.basename(tsFp, '.ts') != appConfig.id) {
|
if (path.basename(tsFp, '.ts') != appConfig.id) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|||||||
618
src/types.ts
618
src/types.ts
@@ -1,626 +1,16 @@
|
|||||||
/**
|
import * as api from '@gkd-kit/api';
|
||||||
* 一个或者多个值类型
|
|
||||||
* @example
|
|
||||||
* const n1: IArray<number> = 1; // ✅
|
|
||||||
* const n2: IArray<number> = [1]; // ✅
|
|
||||||
* const s1: IArray<string> = 'hello'; // ✅
|
|
||||||
* const a2: IArray<string> = ['hello']; // ✅
|
|
||||||
*/
|
|
||||||
export type IArray<T> = T | T[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 此类型表示一个整数
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* 114514 // ✅
|
|
||||||
* 2.5 // ❌
|
|
||||||
*/
|
|
||||||
export type Integer = number;
|
|
||||||
|
|
||||||
type RawCommonProps = {
|
|
||||||
/**
|
|
||||||
* 单位: 毫秒
|
|
||||||
*
|
|
||||||
* 当前规则的冷却时间, 或者执行 action 最小间隔
|
|
||||||
*
|
|
||||||
* @default 1000
|
|
||||||
*/
|
|
||||||
actionCd?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 单位: 毫秒
|
|
||||||
*
|
|
||||||
* 延迟执行: 查询到节点->等待一段时间->再次查询到节点则执行对应 action
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
actionDelay?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 如果开启, 此规则下的所有 `末尾属性选择器`的`第一个属性选择表达式`符合下面的结构之一的选择器 将使用快速查找
|
|
||||||
*
|
|
||||||
* - [id='abc']
|
|
||||||
* - [vid='abc']
|
|
||||||
* - [text='abc']
|
|
||||||
* - [text^='abc']
|
|
||||||
* - [text*='abc']
|
|
||||||
* - [text$='abc']
|
|
||||||
*
|
|
||||||
* 比如 `A > B + C[id='x'][childCount=2]` 符合, 但 `A > B + C[childCount=2][id='x']` 不符合
|
|
||||||
*
|
|
||||||
* 它的底层原理是 跳过手动遍历所有节点 直接调用 [findAccessibilityNodeInfosByViewId](https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo#findAccessibilityNodeInfosByViewId(java.lang.String)) / [findAccessibilityNodeInfosByText](https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo#findAccessibilityNodeInfosByText(java.lang.String)) 得到可匹配节点
|
|
||||||
*
|
|
||||||
* 大多数情况下都能查询到, 在少数某些复杂结构下, 即使目标节点存在, 快速查询也不一定查询到
|
|
||||||
*
|
|
||||||
* 比如 [Image < @View + View >2 [text*='广告']](https://github.com/gkd-kit/subscription/blob/1ae87452d287b558f58f9c4e4448a3190e212ca1/src/apps/com.zidongdianji.ts#L26) 虽然符合快速查询的条件但是使用 `findAccessibilityNodeInfosByText("广告")` 并不能查询到节点
|
|
||||||
*
|
|
||||||
* 它是优点是快速, 因为遍历所有节点是一个耗时行为, 虽然多数情况下这种耗时较低
|
|
||||||
*
|
|
||||||
* 但是在某些软件比如 哔哩哔哩 的开屏广告在这种耗时下延迟可达 1-2s, 这也是导致 [gkd-kit/gkd#60](https://github.com/gkd-kit/gkd/issues/60) 的原因
|
|
||||||
*
|
|
||||||
* ---
|
|
||||||
*
|
|
||||||
* v1.4.1 版本后生成的快照时将标识每个节点是否可 quickFind, 网页审查工具属性面板顶部会注明这个标识
|
|
||||||
*
|
|
||||||
* [](https://i.gkd.li/i/13316168)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
quickFind?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 单位: 毫秒
|
|
||||||
*
|
|
||||||
* 匹配延迟
|
|
||||||
*
|
|
||||||
* 规则准备匹配/或被唤醒时, 等待一段时间, 使此规则参与查询屏幕节点
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
matchDelay?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 单位: 毫秒
|
|
||||||
*
|
|
||||||
* 规则匹配时间, 此规则参与查询屏幕节点时, 等待一段时间, 休眠此规则
|
|
||||||
*
|
|
||||||
* 例如某些应用的 开屏广告 的 activityId 容易误触/太广泛, 而开屏广告几乎只在应用切出来时出现, 设置一个有限匹配时间能避免后续的误触
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
matchTime?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最大执行次数
|
|
||||||
*
|
|
||||||
* 规则的 action 被执行的最大次数, 达到最大次数时, 休眠此规则
|
|
||||||
*
|
|
||||||
* 功能类似 matchTime, 适用于只需要执行一次的: 开屏广告/更新弹窗/青少年弹窗 一类规则
|
|
||||||
*
|
|
||||||
* 当规则准备匹配/或被唤醒时, 将重新计算次数
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
actionMaximum?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当规则因为 matchTime/actionMaximum 而休眠时, 如何唤醒此规则
|
|
||||||
*
|
|
||||||
* @default 'activity'
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* 'activity'
|
|
||||||
* // 当 activity 刷新时, 唤醒规则
|
|
||||||
* // 刷新 activity 并不代表 activityId 变化
|
|
||||||
* // 如 哔哩哔哩视频播放页 底部点击推荐视频 进入另一个 视频播放页, 进入了新 activity 但是 activityId 并没有变化
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* 'app'
|
|
||||||
* // 重新进入 app 时, 唤醒规则
|
|
||||||
*/
|
|
||||||
resetMatch?: 'activity' | 'app';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 与这个 key 的 rule 共享 cd
|
|
||||||
*
|
|
||||||
* 比如开屏广告可能需要多个 rule 去匹配, 当一个 rule 触发时, 其它 rule 的触发是无意义的
|
|
||||||
*
|
|
||||||
* 如果你对这个 key 的 rule 设置 actionCd=3000, 那么当这个 rule 和 本 rule 触发任意一个时, 在 3000毫秒 内两个 rule 都将进入 cd
|
|
||||||
*/
|
|
||||||
actionCdKey?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 与这个 key 的 rule 共享次数
|
|
||||||
*
|
|
||||||
* 比如开屏广告可能需要多个 rule 去匹配, 当一个 rule 触发时, 其它 rule 的触发是无意义的
|
|
||||||
*
|
|
||||||
* 如果你对这个 key 的 rule 设置 actionMaximum=1, 那么当这个 rule 和 本 rule 触发任意一个时, 两个 rule 都将进入休眠
|
|
||||||
*/
|
|
||||||
actionMaximumKey?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 规则参与匹配的顺序, 数字越小越先匹配
|
|
||||||
*
|
|
||||||
* 如果两个规则 order 相同, 按照 groups 中的数组顺序匹配, app 类型规则顺序优先于 global 类型规则
|
|
||||||
*
|
|
||||||
* 属于不同订阅的规则按照订阅列表中顺序匹配, 长按订阅卡片可以拖动排序
|
|
||||||
*
|
|
||||||
* @default 0
|
|
||||||
*
|
|
||||||
* @version 1.7.0
|
|
||||||
*/
|
|
||||||
order?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 单位: 毫秒
|
|
||||||
*
|
|
||||||
* 在开始匹配后的一段时间内, 不管界面没有通知变化, 主动使此规则参与屏幕查询
|
|
||||||
*
|
|
||||||
* GKD 借助 [onAccessibilityEvent](https://developer.android.com/reference/android/accessibilityservice/AccessibilityService#onAccessibilityEvent(android.view.accessibility.AccessibilityEvent)) 感知界面变化
|
|
||||||
*
|
|
||||||
* 但是某些基于 flutter/webview 开发的应用/页面在变化时并不会通知系统去触发 onAccessibilityEvent, 但是屏幕上的节点信息确实产生变化
|
|
||||||
*
|
|
||||||
* 唯一的办法是在开始匹配的一定时间内主动查询屏幕节点
|
|
||||||
*
|
|
||||||
* @version 1.7.0
|
|
||||||
*/
|
|
||||||
forcedTime?: Integer;
|
|
||||||
/**
|
|
||||||
* 当前 规则/规则组 的快照链接, 增强订阅可维护性
|
|
||||||
*/
|
|
||||||
snapshotUrls?: IArray<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前 规则/规则组 的规则在手机上的运行示例, 支持 jpg/png/webp/gif
|
|
||||||
*
|
|
||||||
* 如果规则是多个规则组合起来的, 可以更好看懂规则到底在干啥, 比如 点击关闭按钮-选择关闭原因-确认关闭 这种广告用 gif 看着更清楚在干啥
|
|
||||||
*/
|
|
||||||
exampleUrls?: IArray<string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RawRuleProps = RawCommonProps & {
|
|
||||||
/**
|
|
||||||
* 当前规则在列表中的唯一标识
|
|
||||||
*
|
|
||||||
* key 没有顺序大小之分, 可以是任意数字
|
|
||||||
*
|
|
||||||
* 设置后不可更改, 否则造成点击记录错乱
|
|
||||||
*/
|
|
||||||
key?: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 规则组名称
|
|
||||||
*/
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 要求当前列表里某个 key 刚刚执行
|
|
||||||
*
|
|
||||||
* 比如点击关闭按钮-选择关闭原因-确认关闭, key 分别是 1,2,3, preKeys 分别是 [],[1],[2]
|
|
||||||
*
|
|
||||||
* 那么 选择关闭原因 必须要求 比如点击关闭按钮 刚刚点击过才能执行, 确认关闭 也要求 选择关闭原因 刚刚点击过才执行
|
|
||||||
*
|
|
||||||
* 否则后面的规则不会触发, 也就是要求规则按顺序执行, 这是为了防止规则匹配范围太过广泛而误触
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
preKeys?: IArray<Integer>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @example
|
|
||||||
* `click`
|
|
||||||
* // 为默认值, 如果目标节点是 clickable 的, 则使用 `clickNode`, 反之使用 `clickCenter`
|
|
||||||
* // 并且当 `clickNode` 事件没有被应用接收时, 则使用 `clickCenter`
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* `clickNode`
|
|
||||||
* // 向系统发起一个点击无障碍节点事件. 即使节点在屏幕外部/或者被其它节点遮挡,也依然能够正确触发点击目标节点
|
|
||||||
* // 但是如果目标节点不是 clickable 的, 目标 APP 通常不响应这个点击事件, 也就是点击无效果
|
|
||||||
* // 在极少数情况下, 即使节点是 clickable 的, APP 显示接收但是不响应节点点击事件, 此时需要手动设置 `clickCenter`
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* `clickCenter`
|
|
||||||
* // 计算出此控件的中心的坐标并且如果这个坐标在屏幕内部,那么就向系统发起一个点击屏幕坐标事件
|
|
||||||
* // 如果这个坐标不在屏幕内部, 当作未匹配
|
|
||||||
* // 另外如果目标节点的位置被其它节点遮挡覆盖, 则会点击触发最上层的节点(可能不是目标节点)
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* `back`
|
|
||||||
* // 向系统发起一个返回事件, 相当于按下返回键
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* `longClick`
|
|
||||||
* // 如果目标节点是 longClickable 的, 则使用 `longClickNode`, 反之使用 `longClickCenter`
|
|
||||||
* // 并且当 `longClickNode` 事件没有被应用接收时, 则使用 `longClickCenter`
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* `longClickNode`
|
|
||||||
* // 向系统发起一个长按无障碍节点事件,与 clickNode 类似
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* `longClickCenter`
|
|
||||||
* // 与 clickCenter 类似, 长按时间为 400 毫秒
|
|
||||||
*/
|
|
||||||
action?:
|
|
||||||
| 'click'
|
|
||||||
| 'clickNode'
|
|
||||||
| 'clickCenter'
|
|
||||||
| 'back'
|
|
||||||
| 'longClick'
|
|
||||||
| 'longClickNode'
|
|
||||||
| 'longClickCenter';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在使用 clickCenter/longClickCenter 时的自定义点击位置
|
|
||||||
*
|
|
||||||
* 默认坐标为节点中心
|
|
||||||
*
|
|
||||||
* 如果计算出的坐标不在屏幕内部, 当作未匹配
|
|
||||||
*
|
|
||||||
* @version 1.7.0
|
|
||||||
*/
|
|
||||||
position?: Position;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个或者多个合法的 GKD 选择器, 如果每个选择器都能匹配上节点, 那么点击最后一个选择器的目标节点
|
|
||||||
*/
|
|
||||||
matches?: IArray<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个或者多个合法的 GKD 选择器, 如果存在一个选择器匹配上节点, 则停止匹配此规则
|
|
||||||
*/
|
|
||||||
excludeMatches?: IArray<string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RawGroupProps = RawCommonProps & {
|
|
||||||
/**
|
|
||||||
* 当前规则组在列表中的唯一标识
|
|
||||||
*
|
|
||||||
* 也是客户端禁用/启用此规则组的依据
|
|
||||||
*
|
|
||||||
* 设置后不可更改, 否则造成客户端启用/禁用错乱
|
|
||||||
*
|
|
||||||
* key 没有顺序大小之分, 可以是任意数字
|
|
||||||
*/
|
|
||||||
key: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 规则组名称
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 规则组描述
|
|
||||||
*/
|
|
||||||
desc?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 控制规则默认情况下是启用还是禁用, 默认启用
|
|
||||||
*
|
|
||||||
* 仅对于本仓库的规则而言, 除开屏广告外, 其它规则默认禁用
|
|
||||||
*/
|
|
||||||
enable?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 其它 group 的 key, 允许将目标组的所有 rule 添加到当前组的作用域
|
|
||||||
*
|
|
||||||
* 假设 group1{key=1} 有一个 rule1{key=11}, group2{key=2} 有 rule2{key=22}, rule3{key=23}
|
|
||||||
*
|
|
||||||
* 如果 group1 的 scopeKeys=[2] 并且 group2 没有被禁用, 那么 rule1 的 preKeys/actionCdKey/actionMaximumKey 可以是 11/22/23
|
|
||||||
*
|
|
||||||
* 如果存在相同 key 的 rule, 优先使用本组的 rule, 其次按 scopeKeys 的顺序查找其它组的 rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
scopeKeys?: IArray<Integer>;
|
|
||||||
|
|
||||||
// rules: RawRuleProps[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type RawAppRuleProps = {
|
|
||||||
/**
|
|
||||||
* 如果 设备界面Id startWith activityIds 的任意一项, 则匹配
|
|
||||||
*
|
|
||||||
* 如果要匹配所有界面: `undefined` (不填写) 或者 `[]` (避免使用上级属性)
|
|
||||||
*/
|
|
||||||
activityIds?: IArray<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果 设备界面Id startWith excludeActivityIds 的任意一项, 则排除匹配
|
|
||||||
*
|
|
||||||
* 优先级高于 activityIds
|
|
||||||
*/
|
|
||||||
excludeActivityIds?: IArray<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果应用版本名称包含在此列表中, 则匹配
|
|
||||||
*
|
|
||||||
* @version 1.7.0
|
|
||||||
*/
|
|
||||||
versionNames?: IArray<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果应用版本名称包含在此列表中, 则排除匹配, 优先级高于 versionNames
|
|
||||||
*
|
|
||||||
* @version 1.7.0
|
|
||||||
*/
|
|
||||||
excludeVersionNames?: IArray<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果应用版本代码包含在此列表中, 则匹配
|
|
||||||
*
|
|
||||||
* @version 1.7.0
|
|
||||||
*/
|
|
||||||
versionCodes?: IArray<Integer>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果应用版本代码包含在此列表中, 则排除匹配, 优先级高于 versionCodes
|
|
||||||
*
|
|
||||||
* @version 1.7.0
|
|
||||||
*/
|
|
||||||
excludeVersionCodes?: IArray<Integer>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 位置类型, 用以描述自定义点击位置
|
|
||||||
*
|
|
||||||
* 使用 left/top/right/bottom 实现定位, 此对象只能有两个属性
|
|
||||||
*
|
|
||||||
* 合法的定位组合为: left-top, left-bottom, right-top, right-bottom
|
|
||||||
*
|
|
||||||
* 示例1-点击目标节点的中心
|
|
||||||
* ```json5
|
|
||||||
* {
|
|
||||||
* left: 'width/2',
|
|
||||||
* top: 'height/2',
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* 示例2-点击目标节点的左上顶点
|
|
||||||
* ```json5
|
|
||||||
* {
|
|
||||||
* left: 0,
|
|
||||||
* top: 0,
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* 示例2-点击目标节点的右上区域
|
|
||||||
* - https://i.gkd.li/i/14112390
|
|
||||||
* - https://i.gkd.li/i/14319672
|
|
||||||
* - https://github.com/gkd-kit/gkd/assets/38517192/2cac0614-5eba-48a1-9149-4e564cb79945
|
|
||||||
* ```json5
|
|
||||||
* {
|
|
||||||
* right: 'width*0.1352',
|
|
||||||
* top: 'width*0.0852',
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* 相对坐标计算公式,以width属性为例(选择目标节点某一个不变的属性即可,推荐选择width或height)
|
|
||||||
* K为系数,需要计算出具体数值或使用字符串类型填写数学表达式
|
|
||||||
* U/D/L/R 分别为鼠标悬浮在快照截图上时预览图左下角上下左右的值
|
|
||||||
|
|
||||||
* right : 'width * K',
|
|
||||||
* K = R / 目标节点width
|
|
||||||
|
|
||||||
* left : 'width * K',
|
|
||||||
* K = L / 目标节点width
|
|
||||||
|
|
||||||
* top : 'width * K',
|
|
||||||
* K = U / 目标节点width
|
|
||||||
|
|
||||||
* bottom : 'width * K',
|
|
||||||
* K = D / 目标节点width
|
|
||||||
*/
|
|
||||||
export type Position = {
|
|
||||||
/**
|
|
||||||
* 距离目标节点左边的距离
|
|
||||||
*
|
|
||||||
* 方向: 边 -> 节点中心, 负数表示反方向(也可点击节点外部区域)
|
|
||||||
*
|
|
||||||
* 支持两种值类型, 字符串和数字, 数字等价于相同内容的字符串, 如 2.5 等价于 '2.5'
|
|
||||||
*
|
|
||||||
* 字符串类型支持来自快照属性面板上的 left/top/right/bottom/width/height 的数学计算表达式
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* 2.5 // ✅
|
|
||||||
* '2.5' // ✅
|
|
||||||
* '2.5 + 1 - 2 * 3 / 4 ^ 5 % 6' // ✅
|
|
||||||
* '(right + left) / 2' // ✅
|
|
||||||
*/
|
|
||||||
left?: string | number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 距离目标节点上边的距离
|
|
||||||
*/
|
|
||||||
top?: string | number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 距离目标节点右边的距离
|
|
||||||
*/
|
|
||||||
right?: string | number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 距离目标节点下边的距离
|
|
||||||
*/
|
|
||||||
bottom?: string | number;
|
|
||||||
};
|
|
||||||
|
|
||||||
// <--全局规则相关--
|
|
||||||
type RawGlobalApp = RawAppRuleProps & {
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* 默认值: `true`
|
|
||||||
*
|
|
||||||
* true => 在此 APP 启用此规则
|
|
||||||
*
|
|
||||||
* false => 在此 APP 禁用此规则
|
|
||||||
*/
|
|
||||||
enable?: boolean;
|
|
||||||
};
|
|
||||||
type RawGlobalRuleProps = {
|
|
||||||
/**
|
|
||||||
* 默认值: `true`
|
|
||||||
*
|
|
||||||
* true => 匹配任意 APP
|
|
||||||
*
|
|
||||||
* false => 仅匹配 apps 里面的 app
|
|
||||||
*/
|
|
||||||
matchAnyApp?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认值: `false`
|
|
||||||
*
|
|
||||||
* 是否匹配桌面, 仅全局规则可用
|
|
||||||
*
|
|
||||||
* 如果你切换了桌面, 你需要打开 GKD 的界面触发识别新桌面
|
|
||||||
*/
|
|
||||||
matchLauncher?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认值: `false`
|
|
||||||
*
|
|
||||||
* 是否匹配系统应用, 仅全局规则可用
|
|
||||||
*/
|
|
||||||
matchSystemApp?: boolean;
|
|
||||||
|
|
||||||
apps?: RawGlobalApp[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type RawGlobalRule = RawRuleProps & RawGlobalRuleProps;
|
|
||||||
|
|
||||||
export type RawGlobalGroup = RawGroupProps &
|
|
||||||
RawGlobalRuleProps & {
|
|
||||||
apps?: RawGlobalApp[];
|
|
||||||
rules: RawGlobalRule[];
|
|
||||||
};
|
|
||||||
// --全局规则相关-->
|
|
||||||
|
|
||||||
// <--APP规则相关--
|
|
||||||
export type RawCategory = {
|
|
||||||
/**
|
|
||||||
* 当前分类在列表中的唯一标识
|
|
||||||
*
|
|
||||||
* 也是客户端禁用/启用此分类组的依据
|
|
||||||
*/
|
|
||||||
key: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分类名称
|
|
||||||
*
|
|
||||||
* 同时也是分类的依据, 捕获以 name 开头的所有 APP 规则组, 不捕获全局规则组
|
|
||||||
*
|
|
||||||
* 示例: `开屏广告` 将捕获 `开屏广告-1` `开屏广告-2` `开屏广告-233` 这类 APP 规则组
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* null => 跟随捕获的规则组的 enable 的默认值
|
|
||||||
*
|
|
||||||
* true => 全部启用捕获的规则组
|
|
||||||
*
|
|
||||||
* false => 全部禁用捕获的规则组
|
|
||||||
*/
|
|
||||||
enable?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RawAppRule = RawRuleProps & RawAppRuleProps;
|
|
||||||
export type RawAppGroup = RawGroupProps &
|
|
||||||
RawAppRuleProps & {
|
|
||||||
/**
|
|
||||||
* string => { matches: string }
|
|
||||||
*
|
|
||||||
* string[] => { matches: string }[]
|
|
||||||
*/
|
|
||||||
rules: IArray<RawAppRule | string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RawApp = {
|
|
||||||
/**
|
|
||||||
* 应用包名
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果设备没有安装这个 APP, 则使用这个 name 显示
|
|
||||||
*/
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 此应用的规则组列表
|
|
||||||
*/
|
|
||||||
groups: RawAppGroup[];
|
|
||||||
|
|
||||||
|
export type RawAppAddProp = api.RawApp & {
|
||||||
/**
|
/**
|
||||||
* 某些规则组被移除不使用时, 为了避免 key 在后续被复用, 需要将已经删除的规则组的 key 填入此数组做校验使用
|
* 某些规则组被移除不使用时, 为了避免 key 在后续被复用, 需要将已经删除的规则组的 key 填入此数组做校验使用
|
||||||
*/
|
*/
|
||||||
deprecatedKeys?: number[];
|
deprecatedKeys?: number[];
|
||||||
};
|
};
|
||||||
// --APP规则相关-->
|
|
||||||
|
|
||||||
export type RawSubscription = {
|
export const defineSubsConfig = (config: api.RawSubscription) => {
|
||||||
/**
|
|
||||||
* 当前订阅文件的标识, 如果新旧订阅文件id不一致则更新失败\
|
|
||||||
* 范围: `[0, Number.MAX_SAFE_INTEGER]`\
|
|
||||||
* 建议值: `new Date().getTime()`
|
|
||||||
*
|
|
||||||
* GKD默认订阅是 0, 负数 id APP 自己内部使用, APP 不允许用户添加负数 id 的订阅
|
|
||||||
*
|
|
||||||
* 负数订阅由 APP 内部使用, 如本地订阅是 -2, 内存订阅是 -1
|
|
||||||
*/
|
|
||||||
id: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 订阅的名称
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 订阅的版本号, 用于检测更新
|
|
||||||
*
|
|
||||||
* 只有当新订阅的 version 大于本地旧订阅的 version 才执行更新替换本地
|
|
||||||
*/
|
|
||||||
version: Integer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 作者名称
|
|
||||||
*/
|
|
||||||
author?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GKD 会定时或者用户手动刷新请求这个链接, 如果返回的订阅的 version 大于 APP 订阅当前的 version , 则更新
|
|
||||||
*
|
|
||||||
* 如果这个字段不存在, 则使用添加订阅时填写的链接
|
|
||||||
*/
|
|
||||||
updateUrl?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个自定义 uri 链接, 用户点击[用户反馈]时, 打开此链接
|
|
||||||
*
|
|
||||||
* 可以是一个网页链接, 也可以是一个 APP 内部的 uri 链接
|
|
||||||
*/
|
|
||||||
supportUri?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个只需要 id 和 version 的 json 文件链接, 检测更新时, 优先检测此链接, 如果 id 相等并且 version 增加, 则再去请求 updateUrl
|
|
||||||
*
|
|
||||||
* 目的是防止订阅文件过大而消耗过多的流量
|
|
||||||
*/
|
|
||||||
checkUpdateUrl?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 此订阅的全局规则组列表
|
|
||||||
*/
|
|
||||||
globalGroups?: RawGlobalGroup[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 此订阅的应用规则分类列表
|
|
||||||
*/
|
|
||||||
categories?: RawCategory[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 此订阅的应用列表
|
|
||||||
*/
|
|
||||||
apps?: RawApp[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const defineSubsConfig = (config: RawSubscription) => {
|
|
||||||
return JSON.stringify(config, undefined, 2);
|
return JSON.stringify(config, undefined, 2);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defineAppConfig = (config: RawApp) => {
|
export const defineAppConfig = (config: RawAppAddProp) => {
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user