Memos同步至Mastodon(长毛象)
2025-04-12
我一直把Memos当作微博来用,偶尔也会手动同步到长毛象,但也只是偶尔。原因是即便在用梯子的情况下长毛象打开也太慢了,毕竟大多数实例都在墙外,且长毛象挺吃服务器资源的。
目前又不太想自建实例,虽然现在弄了NAS,理论上可以搭在上面,但是Ghost似乎在6月份要推出联邦宇宙服务了,到时候可以直接迁移到那个上面去,也就一直没下手。
之前看到过蜗牛哥出过一期长毛象同步到Memos的方案,和我的需求是相反的,因为今天周末恰好有空,所以就捣鼓了一下,弄了一版。
因为Memos可以设置WebHook调用,所以我的思路是:
Memos发布 -> 触发WebHook调用 -> 数据转发给Cloudflare Worker -> Worker里用Mastodon API发布嘟文
实现
先去长毛象上申请Access token ,路径为 :
偏好设置 -> 开发 -> 创建新应用 -> 起个名字,勾选 write:statuses、write:media 权限
然后就是Cloudflare Worker的实现了,我用DeepseekR1跑了一版,稍加改动就能初步使用了。
💡
如果你的Memos服务器在国内,那可能需要给Cloudflare worker绑定一个域名,不然会无法访问。
// cloudflare-worker.js
const MASTODON_INSTANCE = ""; // 实例地址
const ACCESS_TOKEN = ""; // 访问token
async function uploadMediaFromUrl(imageUrl, mimeType) {
try {
// 从 URL 获取图片数据
const imageResponse = await fetch(imageUrl);
if (!imageResponse.ok) throw new Error(`下载图片失败: ${imageResponse.status}`);
// 转换为可上传的格式
const blob = await imageResponse.blob();
const formData = new FormData();
formData.append("file", blob); // 文件名按需处理
// 上传到 Mastodon
const uploadRes = await fetch(`${MASTODON_INSTANCE}/api/v2/media`, {
method: "POST",
headers: { Authorization: `Bearer ${ACCESS_TOKEN}` },
body: formData
});
if (!uploadRes.ok) throw new Error(`上传失败: ${await uploadRes.text()}`);
return uploadRes.json();
} catch (error) {
console.error("媒体上传错误:", error);
throw new Error(`图片处理失败: ${error.message}`);
}
}
async function handlePost(request) {
try {
const { memo } = await request.json();
const { content, visibility, resourceList = [] } = memo;
// 验证内容
if (!content) return new Response(JSON.stringify({ error: "内容不能为空" }), { status: 400 });
// 处理最多 4 张图片
const validResources = resourceList
.filter(res => res.type?.startsWith("image/"))
.slice(0, 4);
// 并行上传所有图片
const mediaUploads = validResources.map(async res => {
const media = await uploadMediaFromUrl(res.externalLink, res.type);
return media.id;
});
const mediaIds = await Promise.all(mediaUploads);
// 构建嘟文参数
const params = new URLSearchParams({
status: content,
visibility: visibility.toLowerCase() || "public" // 默认公开
});
mediaIds.forEach(id => params.append("media_ids[]", id));
// 发布嘟文
const postRes = await fetch(`${MASTODON_INSTANCE}/api/v1/statuses`, {
method: "POST",
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`,
"Content-Type": "application/x-www-form-urlencoded"
},
body: params
});
if (!postRes.ok) throw new Error(await postRes.text());
return new Response(JSON.stringify(await postRes.json()), { status: 200 });
} catch (error) {
console.error("处理错误:", error);
return new Response(JSON.stringify({ error: error.message }), { status: 500 });
}
}
// Worker 入口
export default { fetch: handlePost };
CloudFlare Worker代码
给Memos启用Webhook
设置 -> 偏好设置 -> Webhook -> 创建 -> 起个名字,填入Cloudflare Worker的地址
一些不足
这种方式同步速度应该会稍快,但是还是有一些限制,比如
- 图片太大超过Worker可运行时间会上传失败。
- Mastodon API好像最大只能传4张图片。
- 向Memos的TG机器人发的消息不会触发Webhook。
- 长毛象好像不支持 Markdown。
- 我使用的Memos后端版本为 v0.18.1
- 等等,目前只发现上面2个,应该不止。
加入评论