mschabhuettl/betidy-export

GitHub: mschabhuettl/betidy-export

一个非官方的数据导出工具,用于从缺乏导出功能的 BeTidy 家务应用中提取用户数据并转换为开放格式,同时支持迁移至自托管的 Donetick。

Stars: 0 | Forks: 0

# betidy-export **将您的家务从 BeTidy 应用中解放出来 —— 导出为 CSV / JSON / SQLite,并可选择导入至 Donetick。** [BeTidy](https://betidy.io) (`io.betidy.BeTidy`) 是一款 Android 清洁计划/家务应用,它**没有导出功能,也没有官方文档的 API**。`betidy-export` 是一个小型且非官方的工具包,它使用*您自己的凭据*以*您*的身份进行身份验证,从 BeTidy 的云端后端读取*仅属于您自己的*数据,并将其写入由您控制的开放格式中。它还可以根据需要,完整保留重复规则、指派人和优先级,将您的任务导入到自行托管(self-hosted)的 [Donetick](https://github.com/donetick/donetick) 实例中。 本项目完全是为了实现**个人数据的可迁移性**。请参阅[免责声明](#disclaimer)。 ## 功能 - **完整导出**您的 BeTidy 数据 —— 包括任务、项目、完成历史记录以及您的个人资料 —— 汇总到一个 `betidy_export.json` 文件中。 - **整洁的输出**:生成四个 CSV 文件以及一个可查询的 `betidy.sqlite` 数据库。 - **可选的 Donetick 导入**:完整保留重复规则、截止日期、房间标签、指派人、优先级以及工作量/积分 —— 而不仅仅是名称和截止日期。 - **默认安全**:提供 `--dry-run` 预览、`--limit`、`--skip-existing` 以及按房间过滤功能。 - 纯 Python 编写,包含三个独立脚本,仅依赖三个库。 ## 工作原理 BeTidy 是构建在 **AWS Amplify** 后端之上的一个瘦客户端:它通过 AWS Cognito 用户池登录,并从 AppSync GraphQL API 读取数据。`betidy-export` 执行了同样的三个步骤 —— 它使用您的电子邮件和密码**登录** (Cognito SRP),通过查询基于用户的 GraphQL 索引**拉取您的数据**,然后将其**导出或导入**为开放格式。脚本中内置的后端标识符是通过反编译免费下载的 APK 获取的。 了解更多:[工作原理](docs/how-it-works.md) · [数据模型](docs/data-model.md) · [Donetick 导入](docs/donetick-import.md)。 ## 环境要求 - Python **3.9+** - 一个 BeTidy 账号(您自己的电子邮件 + 密码) - *(导入功能可选)* 一个自行托管的 Donetick 实例以及一个 API 访问 token - Python 包:`pycognito`, `boto3`, `requests` ## 安装 ``` git clone https://github.com/mschabhuettl/betidy-export.git cd betidy-export python3 -m venv .venv ./.venv/bin/pip install -r requirements.txt ``` 复制 `.env.example` 为 `.env` 作为参考,或者直接导出下方显示的变量即可。 ## 用法 ### 1. 从 BeTidy 导出 ``` export BETIDY_EMAIL="you@example.com" export BETIDY_PASSWORD="your-betidy-password" python betidy_extract.py # -> betidy_export.json ``` 此操作会登录系统,解析您的 identity id,拉取属于您的所有记录,并将原始数据包写入 `betidy_export.json`。 ### 2. 生成 CSV / SQLite ``` python build_exports.py # -> CSVs + betidy.sqlite ``` 将 `betidy_export.json` 转换为人类可读的 CSV 文件和一个 SQLite 数据库(详见[输出文件](#output-files))。 ### 3. (可选)导入至 Donetick 在 Donetick 的 **Settings → Access Token** 中生成一个访问 token,然后执行: ``` export DONETICK_URL="https://donetick.example.com" export DONETICK_TOKEN="your-donetick-access-token" python donetick_import.py --dry-run # preview, sends nothing python donetick_import.py --discover-labels # list your Donetick label ids python donetick_import.py --labels-map labels.json # real import, with room labels ``` `--labels-map` 接受一个包含 `{"": }` 格式的 JSON 文件。它是可选的 —— 如果不提供,房间名称仍会被记录在每个家务的描述中。 #### 环境变量 | 变量 | 使用者 | 必需 | 默认值 | 用途 | |---|---|---|---|---| | `BETIDY_EMAIL` | `betidy_extract.py` | 是 | — | 您的 BeTidy 账号电子邮件(Cognito 用户名)。 | | `BETIDY_PASSWORD` | `betidy_extract.py` | 是 | — | 您的 BeTidy 密码。 | | `DONETICK_URL` | `donetick_import.py` | 是¹ | — | 您的 Donetick 实例的 Base URL。 | | `DONETICK_TOKEN` | `donetick_import.py` | 是¹ | — | Donetick 访问 token(作为 `secretkey` header 发送)。 | | `BETIDY_TZ` | 所有脚本 | 否 | `UTC` | 用于截止日期的 IANA 时区,例如 `Europe/Vienna`。 | | `BETIDY_DUE_HOUR` | `donetick_import.py` | 否 | `8` | 任务截止时的具体小时(0–23)。 | | `BETIDY_OUTFILE` | 所有脚本 | 否 | `betidy_export.json` | 覆盖默认的数据包文件名。 | ¹ 导入脚本始终需要 `DONETICK_URL`;除非您使用 `--dry-run`,否则 `DONETICK_TOKEN` 也是必需的。 #### 导入标志 | 标志 | 效果 | |---|---| | `--dry-run` | 构建并打印 payload,但不实际 POST 任何内容。 | | `--limit N` | 仅处理前 `N` 个任务。 | | `--include-inactive` | 同时导入非活跃/已完成的任务。 | | `--skip-existing` | 跳过 Donetick 中已存在相同名称的任务。 | | `--room NAME` | 仅导入来自特定 BeTidy 房间的任务。 | | `--labels-map FILE` | 将房间名称映射到 Donetick 标签 id 的 JSON 文件。 | | `--discover-labels` | 打印您 circle 的标签 id(名称 → id)并退出。 | | `--infile FILE` | 输入的数据包文件(默认:`betidy_export.json`)。 | ## 输出文件 | 文件 | 内容 | |---|---| | `betidy_export.json` | 直接从后端获取的原始数据包:`identityId`, `user`, `tasks`, `projects`, `history`。 | | `betidy_tasks.csv` | 每个任务一行。列包括:`id`, `title`, `description`, `room`, `assignee`, `room_id`, `type`, `frequency`, `interval_unit`, `interval_count`, `weekdays`, `todo_date`, `last_todo_date`, `finished_date`, `last_skip_date`, `important`, `effort`, `active`, `creator`, `template_id`, `created_at`, `updated_at`。 | | `betidy_history.csv` | 每次完成记录一行:`id`, `task_id`, `title`, `room_type`, `profile`, `effort`, `is_project`, `time`, `template_id`, `created_at`。 | | `betidy_rooms.csv` | 您的房间:`id`, `name`, `type`, `active`。 | | `betidy_profiles.csv` | 家庭成员档案:`id`, `name`, `active`。 | | `betidy.sqlite` | 包含 `tasks`, `history`, `rooms`, `profiles` 表的 SQLite 数据库(列与 CSV 文件相同)。 | ## BeTidy → Donetick 映射 | BeTidy | Donetick | |---|---| | `type = INTERVAL`,单位 `day`/`week`/`month` × `N` | `frequencyType = interval`,`frequency = N`,`frequencyMetadata.unit = days`/`weeks`/`months` | | `type = DATE`(一次性) | `frequencyType = once` | | `todoDate`(通常为过去时间) | 按间隔时间向前滚动至 ≥ 今天的下一次发生时间;每周任务会自动对齐至其工作日 | | 房间名称 | Donetick 标签(通过 `--labels-map` 设置),并且始终出现在描述中 | | `important` `0` / `1` / `2` | `priority` `0` / P2 / P1 | | `effort` | `points` | | `assigned` 档案名称 | circle 成员的 `userId`s,根据名字进行匹配 | 每个家务的描述中还会添加类似 `[BeTidy] Room: X · Repeats: Y · Assignee: Z · Effort: N` 的一行内容,因此即使某些字段在 Donetick 中没有对应项,数据也不会丢失。 ## 免责声明 这是一个用于**个人数据可迁移性**的**非官方**工具。它与 BeTidy 或 Donetick **没有任何关联、认可或联系**。 - **请仅用于您自己的账号。** 这些脚本使用*您*的凭据进行身份验证,并且只读取*您自己*的记录。 - **这些脚本中的 BeTidy 应用配置并非机密。** 其中的 Cognito、身份池和 AppSync 标识符是公开的客户端配置,嵌入在免费下载的 APK 中 —— 所有用户的配置都是完全相同的。 - **不提供任何担保。** BeTidy 没有官方文档的 API,并且可能随时更改或导致此工具失效。使用风险由您自行承担,并请尊重该应用的服务条款。 ## 许可证 MIT —— 详见 [`LICENSE`](LICENSE)。
标签:AWS Cognito, DFIR, GraphQL, Python, 数据可移植性, 数据导出, 数据迁移, 无后门, 逆向工具