mirror of
https://github.com/AIsouler/GKD_subscription.git
synced 2025-12-19 00:05:19 +08:00
chore: @gkd-kit/api
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import url from 'node:url';
|
||||
import type { RawApp } from '../src/types';
|
||||
import type { RawApp } from '@gkd-kit/api';
|
||||
import { tryRun } from '../src/utils';
|
||||
|
||||
// 使用命令更新内存订阅
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RawCategory } from './types';
|
||||
import type { RawCategory } from '@gkd-kit/api';
|
||||
|
||||
const categories: RawCategory[] = [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import categories from './categories';
|
||||
import globalGroups from './globalGroups';
|
||||
import apps from './rawApps';
|
||||
import type { RawSubscription } from './types';
|
||||
import type { RawSubscription } from '@gkd-kit/api';
|
||||
|
||||
const subsConfig: RawSubscription = {
|
||||
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 type PkgT from '../package.json';
|
||||
import { parseSelector } from './selector';
|
||||
import type { RawAppAddProp } from './types';
|
||||
import type {
|
||||
RawApp,
|
||||
RawAppGroup,
|
||||
RawGlobalGroup,
|
||||
IArray,
|
||||
RawSubscription,
|
||||
} from './types';
|
||||
} from '@gkd-kit/api';
|
||||
import JSON5 from 'json5';
|
||||
|
||||
// 定义一个将 IArray<T> 类型转换为 T[] 类型的函数
|
||||
@@ -206,7 +206,7 @@ export const checkConfig = (newConfig: RawSubscription) => {
|
||||
|
||||
// 检查组和规则的重复键
|
||||
const apps = newConfig.apps || [];
|
||||
apps.forEach((app) => {
|
||||
apps.forEach((app: RawAppAddProp) => {
|
||||
const deprecatedKeys = app.deprecatedKeys || [];
|
||||
const keys = new Set<number>();
|
||||
const oldGroups = oldConfig.apps?.find((a) => a.id == app.id)?.groups || [];
|
||||
@@ -377,7 +377,7 @@ export const checkAndDeleteFiles = async (): Promise<void> => {
|
||||
};
|
||||
|
||||
// 导出一个异步函数,用于更新应用的 Markdown 文件
|
||||
export const updateAppMd = async (app: RawApp) => {
|
||||
export const updateAppMd = async (app: RawAppAddProp) => {
|
||||
// 生成应用的 Markdown 文本内容
|
||||
const appHeadMdText = [
|
||||
`# ${app.name}`,
|
||||
@@ -509,7 +509,7 @@ const getGlobalDiffLog = (
|
||||
|
||||
// 定义一个类型,表示应用的变更日志
|
||||
type AppDiff = {
|
||||
app: RawApp;
|
||||
app: RawAppAddProp;
|
||||
addGroups: RawAppGroup[];
|
||||
changeGroups: RawAppGroup[];
|
||||
removeGroups: RawAppGroup[];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import apps from './rawApps';
|
||||
import type { RawGlobalGroup } from './types';
|
||||
import type { RawGlobalGroup } from '@gkd-kit/api';
|
||||
import { OPEN_AD_ORDER } from './utils';
|
||||
|
||||
const diabledAppIds = [
|
||||
@@ -13,6 +13,14 @@ const diabledAppIds = [
|
||||
'com.android.phone.recorder', // 录音
|
||||
'com.android.soundrecorder', // 录音机
|
||||
'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', // 微信
|
||||
'li.songe.gkd', // GKD
|
||||
|
||||
@@ -4,15 +4,17 @@ import url from 'node:url';
|
||||
import picocolors from 'picocolors';
|
||||
import { pinyin } from 'pinyin-pro';
|
||||
import { walk } from './file';
|
||||
import type { RawApp } from './types';
|
||||
import type { RawAppAddProp } from './types';
|
||||
import { OPEN_AD_ORDER } from './utils';
|
||||
|
||||
const rawApps: RawApp[] = [];
|
||||
const rawApps: RawAppAddProp[] = [];
|
||||
for await (const tsFp of walk(process.cwd() + '/src/apps')) {
|
||||
if (!tsFp.endsWith('.ts')) {
|
||||
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;
|
||||
if (path.basename(tsFp, '.ts') != appConfig.id) {
|
||||
throw new Error(
|
||||
|
||||
618
src/types.ts
618
src/types.ts
@@ -1,626 +1,16 @@
|
||||
/**
|
||||
* 一个或者多个值类型
|
||||
* @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[];
|
||||
import * as api from '@gkd-kit/api';
|
||||
|
||||
export type RawAppAddProp = api.RawApp & {
|
||||
/**
|
||||
* 某些规则组被移除不使用时, 为了避免 key 在后续被复用, 需要将已经删除的规则组的 key 填入此数组做校验使用
|
||||
*/
|
||||
deprecatedKeys?: number[];
|
||||
};
|
||||
// --APP规则相关-->
|
||||
|
||||
export type 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) => {
|
||||
export const defineSubsConfig = (config: api.RawSubscription) => {
|
||||
return JSON.stringify(config, undefined, 2);
|
||||
};
|
||||
|
||||
export const defineAppConfig = (config: RawApp) => {
|
||||
export const defineAppConfig = (config: RawAppAddProp) => {
|
||||
return config;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user