飞书自动化同步:Django 后台一键同步文章到飞书云文档
技术开发 · 工具开发 难度:⭐⭐⭐ 关键词:lark-cli, OAuth, user_access_token, 飞书云文档
故事背景
之前我一直在做"自动同步 Django 文章到飞书云文档"的功能。最痛的点:文档所有者是 App,我自己不能编辑、删除,要"申请权限"才能动。
花了两天研究 OAuth + lark-cli,终于搞定:
- ✅ 文档所有者 = 我(user 身份)
- ✅ 可以编辑、删除、分享
- ✅ Django 后台一键同步
- ✅ token 自动续期
这篇文章复盘整个过程。
一、为什么会卡住?
1.1 两种身份的差别
飞书开放平台有两种调用 API 的方式:
| 模式 | 谁能操作 | 文档所有者 |
|---|---|---|
| 应用身份(tenant_access_token) | App 自己 | App |
| 用户身份(user_access_token) | 模拟你 | 你 |
默认用应用身份,所以文档属于 App,你不是所有者。
1.2 你不能编辑/删除的原因
飞书文档的"权限模型"是: - 所有者(owner):可以编辑、删除、转让 - 协作者:只有"申请权限"才能编辑
App 创建的文档,所有者是 App 这个实体,你只是协作者。所以你不能直接编辑。
二、OAuth 用户授权流程
要让 App "借用你的身份",走 OAuth 授权。
2.1 OAuth 2.0 授权码流程
1. App 引导你到飞书授权页
2. 你登录飞书 → 点"同意授权"
3. 飞书回调 App 带 code
4. App 用 code 换 access_token
5. access_token 有效期 2 小时
6. refresh_token 用于自动续期
2.2 实现方式(手动版)
import requests
# 步骤 1:浏览器跳转飞书授权页
auth_url = (
f"https://open.feishu.cn/open-apis/authen/v1/index"
f"?app_id={APP_ID}"
f"&redirect_uri=http://localhost:8000/callback"
f"&scope=docs:document:create docs:document:write drive:drive offline_access"
)
# 用户在浏览器登录 → 同意 → 跳回带 code=XXX
# 步骤 2:用 code 换 token
resp = requests.post(
"https://open.feishu.cn/open-apis/authen/v1/access_token",
json={
"grant_type": "authorization_code",
"code": "用户复制的code",
"app_id": APP_ID,
"app_secret": APP_SECRET,
}
)
token = resp.json()["data"]
# access_token: u-XXX(用户身份 token)
# refresh_token: ur-XXX(续期用)
坑点:手动写 OAuth 坑很多——字段名错了、scope 没勾全、回调 URL 没配……我光 debug 就花了 1 天。
三、推荐方案:飞书官方 lark-cli
飞书 2026 年出了一个官方 CLI —— lark-cli,所有 OAuth 流程它都帮你搞定。
3.1 安装
# 需要 Node.js(18+)
npm install -g @larksuite/cli
3.2 一键绑定 Hermes
lark-cli config bind --source hermes --app-id cli_xxx --identity user-default
输出:
⚠️ 你正在从应用身份切换到用户身份
⚠️ 请勿将此机器人分享给他人...
配置成功!lark-cli 已可在 Hermes 中使用。
3.3 OAuth 授权(Device Flow)
lark-cli auth login --recommend
自动给你一个验证链接 + 二维码:
在浏览器中打开以下链接进行认证:
https://accounts.feishu.cn/oauth/v1/device/verify?...
user_code: PFFU-YLXN
手机飞书扫码 → 确认授权 → token 自动保存。
3.4 创建文档(一行命令)
lark-cli docs +create \
--doc-format markdown \
--content - \
--title "我的文章" \
--as user
- 表示从 stdin 读 Markdown。lark-cli 自动处理:
- Markdown 转飞书块
- 应用样式(标题/代码/列表/表格/引用/分割线/图片)
- 保存到你的个人云空间
3.5 完整能力
📚 文档:创建/读取/编辑/搜索
📁 云空间:上传/下载/移动/删除/搜索
📊 多维表格:管理/记录/视图/表单/仪表盘
📅 日历:查日程/约会议/查忙闲/推荐时间
📧 邮件:搜索/读取/起草/发送/转发/归档
💬 消息:搜索消息/群聊/发消息/回复话题
📝 任务:创建/更新/拆分子任务
🎬 妙记:搜索妙记/下载音视频/获取总结待办
📎 审批:查询审批实例/处理审批任务
四、集成到 Django 后台
4.1 模型加字段
# blog/models.py
class Article(models.Model):
...
feishu_url = models.URLField('飞书云文档', max_length=500, blank=True)
迁移:
python manage.py makemigrations blog
python manage.py migrate
4.2 Admin 加 action
# blog/admin.py
from django.contrib import admin, messages
from django.utils.html import format_html
import subprocess, json
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ['title', 'sync_status_display', ...]
readonly_fields = [..., 'feishu_url']
actions = ['sync_to_feishu']
def sync_status_display(self, obj):
if obj.feishu_url:
return format_html(
'<a href="{}" target="_blank">'
'<i class="bi bi-cloud-check"></i> 已同步</a>',
obj.feishu_url
)
return '未同步'
sync_status_display.short_description = '飞书状态'
def sync_to_feishu(self, request, queryset):
success = 0
for article in queryset.filter(status='published'):
md = f"# {article.title}\n\n{article.markdown_content}"
result = subprocess.run(
['lark-cli', 'docs', '+create',
'--doc-format', 'markdown',
'--content', '-',
'--title', article.title,
'--as', 'user'],
input=md, capture_output=True, text=True
)
if result.returncode == 0:
data = json.loads(result.stdout)
url = data['data']['document']['url']
article.feishu_url = url
article.save(update_fields=['feishu_url'])
success += 1
self.message_user(request, f'✅ 同步成功 {success} 篇', level=messages.SUCCESS)
sync_to_feishu.short_description = '同步到飞书云文档(以你身份)'
4.3 使用流程
- Django 后台 → 文章列表
- 勾选多篇文章
- 操作 → 选择 "同步到飞书云文档"
- 点击 "执行"
- 几秒后看到 ✅ 提示 + 飞书链接
全程在浏览器里完成,不需要 SSH 进服务器。
五、踩过的坑
5.1 OAuth 字段名
# ❌ OAuth2 标准(飞书不支持)
{"client_id": ..., "client_secret": ...}
# ✅ 飞书 OAuth
{"app_id": ..., "app_secret": ...}
5.2 Scope 必须勾全
常见必需 scope:
docs:document:create
docs:document:write
docs:document:read
drive:drive
drive:file
space:document:retrieve ← 这个容易漏
offline_access ← 必须有,否则没 refresh_token
5.3 用户身份权限 vs 应用身份权限
飞书后台有两套权限: - 应用身份权限:App 自己用 - 用户身份权限:App 模拟用户时用
OAuth 时必须两套都勾。
5.4 lark-cli 安装路径
npx @larksuite/cli@latest install 经常失败(权限问题)。
直接:
sudo npm install -g @larksuite/cli --no-audit --no-fund
六、一键脚本
最终精简版 feishu_sync.py:
import subprocess, json, os, sys
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
import django; django.setup()
from blog.models import Article
def sync_article(slug):
art = Article.objects.get(slug=slug)
md = f"# {art.title}\n\n{art.markdown_content}"
r = subprocess.run(
['lark-cli', 'docs', '+create',
'--doc-format', 'markdown',
'--content', '-',
'--title', art.title,
'--as', 'user'],
input=md, capture_output=True, text=True
)
if r.returncode == 0:
data = json.loads(r.stdout)
url = data['data']['document']['url']
print(f'✅ {url}')
48 行搞定一切,比之前 200+ 行少 75%。
📚 思考题
Q1: 为什么要区分"应用身份"和"用户身份"? A1: 安全考虑。应用身份只能操作 App 自己的数据;用户身份模拟你操作你的数据。这和 OAuth 2.0 的设计哲学一致——不要给 App 过多授权。
Q2: user_access_token 会过期,怎么办? A2: 2 小时过期,但lark-cli 自动用 refresh_token 续期,你不用管。refresh_token 一周有效,过期后再走一遍 OAuth。
Q3: 文档所有者是谁,影响什么? A3: 决定谁能编辑/删除/转让。应用身份文档:你能"申请权限"编辑;用户身份文档:你直接能操作。
🔜 下一篇预告
飞书 + AI:让 lark-cli 接管日程、消息、任务
你以为 lark-cli 只是写文档?太天真了。
- "约明天上午 10 点跟张三开会,30 分钟"
- "找出跟张三的所有未读消息"
- "把今天的会议记录整理成任务,分配给团队"
全部 lark-cli 一行命令搞定。下一篇展示我用 lark-cli 实现"日常 AI 助手"的完整工作流。
📱 关注公众号「网英的日常」,第一时间收到 lark-cli 实战系列。 读完有疑问?在公众号留言,我会逐条回复。