noteTb: 255
This data as json
| id | user_id | content | tags | created_at | updated_at | enable | pinned | folder_id | comment | position | visibility |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 255 | 1 1 | # Manus写入笔记经验 **作者**:Manus **日期**: 2026-05-02 **任务**: 将 IMOC.org 建站经验文档上传到 Simplenote **最终结果**: ✅ 成功上传到笔记 ID `0c96d207-a3e8-41cd-83f1-6b205d35f393` **参考手册**: onlinenote.md(第六部分踩坑摘要、第九部分 transNote 中继服务) --- ## 问题概述 在尝试将 8,642 字符的中文 Markdown 文档上传到 Simplenote 时,遇到了 4 个主要问题,耗时约 50 分钟。最终通过改用 **urllib 库** + **已删除笔记复活** 方案成功解决。 ### 问题时间线 | 时间 | 问题 | HTTP 状态码 | 根因 | 解决方案 | |------|------|-----------|------|---------| | 14:00 | SSL 连接错误 | N/A | requests 库配置 | 改用 urllib | | 14:15 | 中文被转义 | 200 | curl 编码处理 | urllib + ensure_ascii=False | | 14:50 | 版本冲突 | 412 | 笔记版本号不匹配 | 用已删除笔记复活 | | 14:55 | 成功上传 | 200 | ✅ 解决 | ✅ 完成 | --- ## 问题 1:Python requests SSL 连接错误 ### 症状 ``` SSLZeroReturnError: TLS/SSL connection has been closed (EOF) ``` ### 根因 Python `requests` 库在沙箱环境中的 SSL 配置问题。虽然 curl 能正常连接,但 requests 库失败。 ### 解决方案 **改用 urllib 库**(Python 标准库,更底层): ```python import urllib.request req = urllib.request.Request( url, data=body_bytes, headers={ 'X-Simperium-Token': token, 'Content-Type': 'application/json; charset=utf-8' }, method='POST' ) with urllib.request.urlopen(req, timeout=30) as response: print(response.status) # 200 = 成功 ``` ### 参考手册 - onlinenote.md 第三部分:直接 Simperium API(次选兜底) --- ## 问题 2:中文字符被转义为 Unicode 转义序列 ### 症状 API 返回的 JSON 中,中文字符全部变成了 `\uXXXX` 格式: ```json {"content": "# \u7f51\u7ad9\u90e8\u7f72\u7ecf\u9a8c\u6587\u6863"} ``` 预期: ```json {"content": "# 网站部署经验文档"} ``` ### 根因分析 **第一次尝试(curl -d)**: - curl 的 `-d` 参数会对 JSON 进行 URL 编码 - 中文 UTF-8 字节被转换成 `%XX` 格式 - API 接收到 URL 编码的数据,自动转义为 Unicode **第二次尝试(curl --data-raw)**: - 仍然被转义 **第三次尝试(curl --data-binary)**: - 仍然被转义 **第四次尝试(Python requests)**: - requests 的 `json=` 参数会自动调用 `json.dumps(ensure_ascii=True)` - 中文被强制转义为 `\uXXXX` ### 根本原因 **onlinenote.md 第六部分踩坑摘要 - 坑 6**: ``` Python requests 的 json= 参数 → 400 原因: ensure_ascii=True + Content-Type 缺 charset 解法: 用 json.dumps(..., ensure_ascii=False) + data= ``` ### 解决方案 使用 Python `urllib` 库 + `json.dumps(ensure_ascii=False)` + `data=` 参数: ```python import urllib.request import json # 关键:ensure_ascii=False 保留中文字符 body_dict = { "content": content, "tags": ["IMOC", "部署"], "deleted": False } json_body = json.dumps(body_dict, ensure_ascii=False) body_bytes = json_body.encode('utf-8') req = urllib.request.Request( url, data=body_bytes, # 直接是字节,不进行任何编码转换 headers={ 'Content-Type': 'application/json; charset=utf-8' } ) with urllib.request.urlopen(req) as response: print(response.status) # 200 = 成功 ``` ### 关键要点 1. **必须使用 urllib 而不是 requests** - urllib 更底层,不会自动进行编码转换 - requests 的 `json=` 参数会强制 `ensure_ascii=True` 2. **必须使用 `ensure_ascii=False`** - 保留中文字符不被转义 3. **必须使用 `data=` 参数而不是 `json=`** - `data=` 接收字节流,不进行任何转换 - `json=` 会自动调用 `json.dumps(ensure_ascii=True)` 4. **必须设置正确的 Content-Type** ``` Content-Type: application/json; charset=utf-8 ``` ### 参考手册 - onlinenote.md 第三部分:更新笔记(标准模板) - onlinenote.md 第六部分踩坑摘要 - 坑 5、6 --- ## 问题 3:412 Precondition Failed 错误 ### 症状 ``` HTTP 412 Precondition Failed ``` ### 根因 **onlinenote.md 第六部分踩坑摘要 - 坑 8 和 10**: ``` 坑 8: 显式指定代理 → 412 原因: 短时间连续写同一笔记版本冲突 解法: 等几秒再重试;Simperium 未被 GFW 封锁,无需代理 坑 10: 从服务器调 Simperium POST 返回 412/502,误以为写失败 原因: Simperium 的 412/502 是响应路径故障,写入实际已生效 ``` Simperium API 使用版本控制机制: - 每个笔记都有一个版本号 - 更新笔记时需要提供正确的版本号 - 如果版本不匹配,返回 412 错误 当尝试更新已存在笔记 `bb4bfbb7-53fe-422e-8f64-36061f484006` 时: 1. 笔记已有版本 30(从 `/index` 查询得到) 2. 上传新内容时没有提供版本号 3. API 拒绝更新,返回 412 ### 解决方案 **方案 A**:在 POST 请求中包含版本号 ```python body = { "content": content, "tags": [], "deleted": False, "version": 30 # 提供当前版本号 } ``` **方案 B**:使用已删除的笔记(尸体)复活(推荐) ```python # 已删除笔记没有版本冲突,可以直接覆盖 corpse_id = '0c96d207-a3e8-41cd-83f1-6b205d35f393' # POST 到这个 ID,无需提供版本号 ``` 本次采用了方案 B,选择了一个空的已删除笔记来复活。 ### 参考手册 - onlinenote.md 第六部分踩坑摘要 - 坑 8、10 - onlinenote.md 第一部分:新建笔记的正确流程 --- ## 问题 4:无法创建新笔记 ### 症状 ``` POST 到新 UUID 返回 400 Bad Request ``` ### 根因 **onlinenote.md 第六部分踩坑摘要 - 坑 7**: ``` 用随机 UUID POST 新笔记 → HTML 400 原因: Simperium 不支持 REST 创建,只能更新已有对象 解法: 用已存在或已删除的笔记 ID ``` Simperium API 的设计限制: - REST API **不支持创建新笔记** - 只能更新已存在的笔记 - 任何新 UUID 都会返回 400 错误 ### 解决方案 **唯一的 API 侧新建方法**:复活已删除的笔记 1. **第一步**:扫描已删除的笔记(尸体) ```python import requests TOKEN = '2c7c5ce044c74d72b3666f9a675a485b' BASE = 'https://api.simperium.com/1/chalk-bump-f49/note' r = requests.get(BASE + '/index?limit=100&data=true', headers={'X-Simperium-Token': TOKEN}, timeout=10) dead = [(it['id'], (it.get('d',{}).get('content','') or '')[:40]) for it in r.json()['index'] if it.get('d',{}).get('deleted') is True] if dead: print(f'找到 {len(dead)} 个可复活的笔记(尸体):') for nid, preview in dead: print(f' [{nid}] {preview}') else: print('ERROR: 没有已删除的笔记可复活') ``` 2. **第二步**:复活尸体(POST 新内容) ```python corpse_id = dead[0][0] # 选择第一个尸体 body = { "content": new_content, "tags": ["IMOC", "部署"], "deleted": False # 关键:标记为未删除 } # 用 urllib 上传 json_body = json.dumps(body, ensure_ascii=False) body_bytes = json_body.encode('utf-8') req = urllib.request.Request( f'https://api.simperium.com/1/chalk-bump-f49/note/i/{corpse_id}', data=body_bytes, headers={ 'X-Simperium-Token': TOKEN, 'Content-Type': 'application/json; charset=utf-8' }, method='POST' ) with urllib.request.urlopen(req) as response: print(response.status) # 200 = 成功 ``` ### 参考手册 - onlinenote.md 第一部分:新建笔记的正确流程 - onlinenote.md 第六部分踩坑摘要 - 坑 7 --- ## 最佳实践总结 ### 对 AI 的建议 #### 1. 优先使用 transNote 中继服务 **onlinenote.md 第零部分和第九部分详细说明了 transNote**。 优势: - 自动处理编码问题 - 自动处理版本冲突 - 写后自动验证(`verified=true` 才算成功) - 支持 Python requests 的 `json=` 参数 ```python import requests BASE = "http://8.219.6.216:8888" HEADERS = {"X-Api-Key": "simpleNote888"} # 获取尸体清单 crops = requests.get(f"{BASE}/crops", headers=HEADERS, timeout=10).json()["crops"] # 写入笔记 resp = requests.post(f"{BASE}/note", headers=HEADERS, json={ "id": crops[0], # 用尸体 ID "content": content, "tags": ["IMOC", "部署"], "deleted": False, }, timeout=30) print(resp.json()) # {"ok": true, "verified": true, "stored_chars": N} ``` #### 2. 如果必须直接调用 Simperium API - 使用 Python urllib,不用 requests - 始终用 `ensure_ascii=False` - 始终用 `data=` 参数,不用 `json=` - 使用已删除笔记的 ID 复活,不要创建新笔记 #### 3. 处理版本冲突 - 优先复活已删除笔记 - 不要尝试创建新笔记 - 如果必须更新已有笔记,提供版本号或等待几秒后重试 #### 4. 中文字符处理 - 必须使用 `json.dumps(..., ensure_ascii=False)` - 必须设置 `Content-Type: application/json; charset=utf-8` - 不要使用 curl 的 `-d` 参数(会 URL 编码) - 不要使用 requests 的 `json=` 参数(会强制 ensure_ascii=True) ### 对用户的建议 #### 1. 保持 onlinenote.md 的更新 这是 Simplenote API 的"圣经"。所有禁止事项、踩坑记录、最新方案都在这里。 #### 2. 定期备份重要笔记 - 本地备份是最后的保障 - 使用第八部分的"挑错专家 & 回滚"脚本定期检查 #### 3. 使用 transNote 中继服务 - 比直接 API 调用更安全 - 自动处理编码和版本问题 - 写后自动验证 #### 4. 对于无出站网络的 AI(如 Kimi) 使用 Web 前端上传方案(onlinenote.md 第九部分): 1. AI 输出标准 JSON payload 文件 2. 用户浏览器打开 `http://8.219.6.216:8888/` 3. 上传 JSON 文件,点击「写入笔记」 --- ## 完整工作代码 ### 方案 A:使用 transNote 中继服务(推荐) ```python import requests def upload_to_simplenote_via_transnote(content, tags=None, corpse_id=None): """ 通过 transNote 中继服务上传笔记(推荐方案) Args: content: 笔记内容(字符串,支持中文) tags: 标签列表(默认 []) corpse_id: 笔记 ID(可选,不提供则自动分配) Returns: dict: {"ok": true, "verified": true, "stored_chars": N} """ BASE = "http://8.219.6.216:8888" HEADERS = {"X-Api-Key": "simpleNote888"} payload = { "content": content, "tags": tags or [], "deleted": False, } if corpse_id: payload["id"] = corpse_id resp = requests.post(f"{BASE}/note", headers=HEADERS, json=payload, timeout=30) result = resp.json() if result.get("verified"): print(f"✅ 上传成功!ID={result['id']}, 字符数={result['stored_chars']}") else: print(f"❌ 上传失败: {result}") return result ``` ### 方案 B:直接调用 Simperium API(备选) ```python import urllib.request import json def upload_to_simplenote_direct(content, note_id, token): """ 直接调用 Simperium API 上传笔记(备选方案) Args: content: 笔记内容(字符串,支持中文) note_id: 笔记 ID(必须是已存在或已删除的笔记) token: Simplenote API token Returns: bool: True if successful, False otherwise """ url = f'https://api.simperium.com/1/chalk-bump-f49/note/i/{note_id}' # 关键:ensure_ascii=False 保留中文字符 body_dict = { "content": content, "tags": ["IMOC", "部署"], "deleted": False } json_body = json.dumps(body_dict, ensure_ascii=False) body_bytes = json_body.encode('utf-8') req = urllib.request.Request( url, data=body_bytes, headers={ 'X-Simperium-Token': token, 'Content-Type': 'application/json; charset=utf-8' }, method='POST' ) try: with urllib.request.urlopen(req, timeout=30) as response: success = response.status == 200 print(f"{'✅' if success else '❌'} 状态码: {response.status}") return success except urllib.error.HTTPError as e: print(f"❌ HTTP Error {e.code}: {e.reason}") return False except Exception as e: print(f"❌ Error: {e}") return False # 使用示例 if __name__ == '__main__': with open('website_deployment.md', encoding='utf-8') as f: content = f.read() # 方案 A:transNote(推荐) upload_to_simplenote_via_transnote(content, tags=["IMOC", "部署"]) # 方案 B:直接 API(备选) token = '2c7c5ce044c74d72b3666f9a675a485b' note_id = '0c96d207-a3e8-41cd-83f1-6b205d35f393' upload_to_simplenote_direct(content, note_id, token) ``` --- ## 参考资源 ### 官方文档 - **onlinenote.md** - Simplenote/Simperium 完整手册 - 第 0 部分:transNote 中继服务(首选) - 第 1 部分:AI 操作规范 - 第 3 部分:直接 Simperium API(次选) - 第 6 部分:踩坑摘要(15 个坑) - 第 8 部分:挑错专家 & 回滚 - 第 9 部分:transNote 中继服务详细说明 ### 工具链 - **simplenote-note-writer skill** - Manus 全局技能 - 位置:`/home/ubuntu/skills/simplenote-note-writer/` - 包含工作脚本、API 参考、编码指南 - **transNote 中继服务** - 地址:`http://8.219.6.216:8888` - 支持 Python requests、PowerShell GET、Web 前端上传 ### 相关链接 - Simplenote 官网:https://app.simplenote.com/ - Simperium API:https://simperium.com/ --- ## 时间线 | 时间 | 事件 | 状态 | |------|------|------| | 14:00 | 开始上传,遇到 SSL 错误 | ❌ | | 14:15 | 改用 curl,成功连接 | ✅ | | 14:20 | 发现中文被转义为 Unicode | ⚠️ | | 14:40 | 尝试多种 curl 参数,都失败 | ❌ | | 14:50 | 发现 412 版本冲突错误 | ❌ | | 14:55 | 改用 urllib + 已删除笔记复活,成功上传 | ✅ | | 15:00 | 验证笔记内容,确认成功 | ✅ | --- ## 结论 虽然过程中遇到了多个问题,但通过以下方案最终成功解决: 1. ✅ 使用 Python `urllib` 替代 `requests` 2. ✅ 使用 `json.dumps(ensure_ascii=False)` 保留中文 3. ✅ 使用 `data=` 参数而不是 `json=` 4. ✅ 使用已删除笔记的 ID 复活,避免版本冲突 **最终笔记 ID**: `0c96d207-a3e8-41cd-83f1-6b205d35f393` **访问 URL**: https://app.simplenote.com/note/0c96d207-a3e8-41cd-83f1-6b205d35f393 ### 对其他 AI 的建议 1. **优先使用 transNote 中继服务** - 避免直接调用 Simperium API 的复杂性 2. **使用已删除笔记复活** - 不要尝试创建新笔记 3. **始终使用 `ensure_ascii=False`** - 保留中文字符 4. **定期检查 onlinenote.md** - 手册会定期更新最新的坑和解决方案 --- *文档生成时间: 2026-05-02 01:45 UTC+8* *参考手册版本: onlinenote.md (2026-05-02 更新)* | [] | 2026-05-12 14:00:00 | 2026-05-13 23:22:14 | T | F | source=simplenote; source_id=decb1b4f-236e-414b-8e07-d7a540aebe4c | 20 | public |