通过Telegram的DOI URL自动导入研究论文到Zotero
高级
这是一个AI Summarization, Multimodal AI领域的自动化工作流,包含 25 个节点。主要使用 If, Set, Code, Merge, Telegram 等节点。 从Telegram导入研究论文到Zotero,含AI摘要总结
前置要求
- •Telegram Bot Token
- •可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "njoCDW50H4hQEGIY",
"meta": {
"instanceId": "0d045f8fe3802ff2be0bb9a9ea445ee6c9ed61973377effe00767e483681e2f4",
"templateCredsSetupCompleted": true
},
"name": "通过 Telegram 的 DOI URL 自动导入研究论文到 Zotero",
"tags": [],
"nodes": [
{
"id": "474334ec-ff15-4a12-a200-50eb928f11db",
"name": "Telegram 触发器",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
9.007782644689968,
0
],
"webhookId": "d5ec43d4-76ab-4e79-9822-f42d522b605b",
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"typeVersion": 1.2
},
{
"id": "80605785-8fd8-4d86-893d-e33d9cff315e",
"name": "代码",
"type": "n8n-nodes-base.code",
"position": [
220,
0
],
"parameters": {
"jsCode": "// Node: Code (Parse DOI)\nconst txt = $json.message?.text?.trim() || \"\";\n\nfunction extractDOI(s) {\n // 1) doi.org/...\n let m = s.match(/doi\\.org\\/(10\\.\\d{4,9}\\/\\S+)/i);\n if (m) return m[1].replace(/[)\\].,;]*$/, \"\");\n // 2) raw DOI\n m = s.match(/(10\\.\\d{4,9}\\/\\S+)/i);\n if (m) return m[1].replace(/[)\\].,;]*$/, \"\");\n // 3) arXiv link\n m = s.match(/arxiv\\.org\\/abs\\/([\\w\\-.]+)/i);\n if (m) return `10.48550/arXiv.${m[1]}`;\n return null;\n}\n\nconst doi = extractDOI(txt);\nif (!doi) {\n return [{ error: true, message: \"DOI not found. Please send a DOI or arXiv/doi.org link.\" }];\n}\n\nreturn [{ doi }];\n"
},
"typeVersion": 2
},
{
"id": "7e96135b-4bfb-4a05-a7fe-4936f771e7e5",
"name": "HTTP 请求2",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
960,
0
],
"parameters": {
"url": "=https://api.datacite.org/dois/{{ $('Code').item.json.doi }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "6562fd43-ad5a-4172-bf40-e627adec9b65",
"name": "HTTP请求3",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
960,
-260
],
"parameters": {
"url": "=https://api.crossref.org/works/{{ $('Code').item.json.doi }}",
"options": {}
},
"typeVersion": 4.2,
"alwaysOutputData": false
},
{
"id": "44f8af5d-d0c5-4fbc-9717-9bdcc80f6389",
"name": "HTTP 请求4",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
960,
260
],
"parameters": {
"url": "=https://api.unpaywall.org/v2/{{ $('Code').item.json.doi }}?email=your-email@example.com",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "b63893ad-f200-4c1b-ba07-7f429f38f2ff",
"name": "HTTP 请求",
"type": "n8n-nodes-base.httpRequest",
"position": [
640,
0
],
"parameters": {
"url": "=https://api.zotero.org/keys/{{ $json['Zotero API Key'] }}",
"options": {},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Zotero-API-Key",
"value": "={{ $json['Zotero API Key'] }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "9f834057-efc6-4f75-b114-4db5d39bd498",
"name": "编辑字段",
"type": "n8n-nodes-base.set",
"position": [
420,
0
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "2530066f-becf-4a83-b96c-a91ddd4aceb1",
"name": "Zotero API Key",
"type": "string",
"value": "={{ $credentials.zoteroApi.key }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d7b1e0b4-dee1-4359-8da6-b9f3df1854f6",
"name": "代码4",
"type": "n8n-nodes-base.code",
"position": [
1680,
0
],
"parameters": {
"jsCode": "// Code4 — Build Zotero parent metadata + pick best PDF URL\n// Input from Merge: $json.crossref, $json.datacite, $json.unpay, and $json.doi\n\nconst doi = $json.doi;\n\n// Unwrap API results if not error\nconst crossrefBody = ($json.crossref && !$json.crossref.error) ? $json.crossref.message : null;\nconst dataciteAttr = ($json.datacite && !$json.datacite.error) ? $json.datacite.data?.attributes : null;\nconst unpayBody = ($json.unpay && !$json.unpay.error) ? $json.unpay : null;\n\nconst pick = (...vals) => vals.find(v => {\n if (v === undefined || v === null) return false;\n if (typeof v === 'string') return v.trim().length > 0;\n return true;\n}) ?? null;\n\n/* ---------- PDF pickers ---------- */\nfunction fromCrossref(m) {\n if (!m) return null;\n const links = m.link || [];\n for (const l of links) {\n const ct = (l['content-type'] || '').toLowerCase();\n const url = l.URL || '';\n if (ct.includes('pdf') || /\\.pdf(\\?|$)/i.test(url)) return url;\n }\n return null;\n}\n\nfunction fromDataCite(attr) {\n if (!attr) return null;\n // try identifiers -> arXiv\n const ids = attr.identifiers || [];\n const arxiv = ids.find(i => /arxiv/i.test(i.identifierType || ''))?.identifier;\n if (arxiv) {\n const id = String(arxiv).replace(/^arXiv:/i,'');\n return `https://arxiv.org/pdf/${id}.pdf`;\n }\n return null;\n}\n\nfunction fromUnpay(u) {\n if (!u) return null;\n if (u.best_oa_location?.url_for_pdf) return u.best_oa_location.url_for_pdf;\n if (/\\.pdf(\\?|$)/i.test(u.best_oa_location?.url || '')) return u.best_oa_location.url;\n for (const loc of (u.oa_locations || [])) {\n if (loc.url_for_pdf) return loc.url_for_pdf;\n if (/\\.pdf(\\?|$)/i.test(loc.url || '')) return loc.url;\n }\n return null;\n}\n\n// Priority: Crossref → Unpaywall → DataCite → arXiv fallback (from DOI)\nlet pdfUrl = fromCrossref(crossrefBody);\nif (!pdfUrl) pdfUrl = fromUnpay(unpayBody);\nif (!pdfUrl) pdfUrl = fromDataCite(dataciteAttr);\nif (!pdfUrl && /^10\\.48550\\/arxiv\\./i.test(doi || '')) {\n const arxivId = doi.replace(/^10\\.48550\\/arxiv\\./i, '');\n pdfUrl = `https://arxiv.org/pdf/${arxivId}.pdf`;\n}\n\n/* ---------- Parent metadata (Zotero item) ---------- */\n// Title\nconst title = pick(\n crossrefBody?.title?.[0],\n dataciteAttr?.titles?.[0]?.title,\n doi\n);\n\n// Date (YYYY, YYYY-MM, or YYYY-MM-DD)\nconst issued = crossrefBody?.issued?.['date-parts']?.[0] || [];\nconst dateStr = issued.length\n ? issued.join('-')\n : pick(\n dataciteAttr?.published,\n dataciteAttr?.publishedOnline,\n dataciteAttr?.publishedPrint,\n dataciteAttr?.created\n ) || \"\";\n\n// Journal / container\nconst publicationTitle = pick(\n crossrefBody?.['container-title']?.[0],\n dataciteAttr?.container?.title\n) || \"\";\n\n// Landing page (not PDF)\nconst landingFromCR = crossrefBody?.URL;\nconst landingFromUP = unpayBody?.best_oa_location?.url || unpayBody?.oa_locations?.[0]?.url;\nconst landingFromDC = dataciteAttr?.url;\nconst url = pick(landingFromCR, landingFromUP, landingFromDC) || \"\";\n\n// Abstract\nlet abstractNote = \"\";\nif (Array.isArray(dataciteAttr?.descriptions) && dataciteAttr.descriptions.length) {\n const absExact = dataciteAttr.descriptions.find(d => /abstract/i.test(d.descriptionType || ''));\n abstractNote = pick(absExact?.description, dataciteAttr.descriptions[0]?.description) || \"\";\n}\n\n// Creators\nlet creators = [];\nif (Array.isArray(crossrefBody?.author)) {\n creators = crossrefBody.author.map(a => ({\n creatorType: \"author\",\n firstName: a.given || \"\",\n lastName: a.family || (a.name || \"\")\n }));\n} else if (Array.isArray(dataciteAttr?.creators)) {\n creators = dataciteAttr.creators.map(c => ({\n creatorType: \"author\",\n firstName: c.givenName || \"\",\n lastName: c.familyName || (c.name || \"\")\n }));\n}\n\n// Tags\nconst tags = [];\nif (Array.isArray(crossrefBody?.subject)) {\n for (const s of crossrefBody.subject.slice(0, 5)) tags.push({ tag: String(s) });\n}\ntags.push({ tag: \"from-telegram\" });\n\n// Item type heuristic\nconst itemType = publicationTitle ? \"journalArticle\" : \"preprint\";\n\n// Build meta object for POST /items (parent)\nconst meta = {\n itemType,\n title,\n DOI: doi || \"\",\n url,\n date: dateStr,\n publicationTitle,\n abstractNote,\n creators,\n tags\n};\n\n// Unified output for next step:\n// - meta: used for \"Zotero - Create Parent\"\n// - pdfUrl + hasPdf: for IF & attachment creation (linked_url)\nreturn [{\n doi,\n title,\n meta,\n pdfUrl,\n hasPdf: Boolean(pdfUrl)\n}];\n"
},
"typeVersion": 2
},
{
"id": "c7aa9f0e-d58a-48d8-8353-f2c60feb42fc",
"name": "合并",
"type": "n8n-nodes-base.merge",
"position": [
1520,
0
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition",
"numberInputs": 3
},
"executeOnce": true,
"typeVersion": 3.2
},
{
"id": "5f26c1c8-f21f-4aca-b4ae-84ff86c970f4",
"name": "合并二进制项",
"type": "n8n-nodes-base.if",
"position": [
1920,
0
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "520e9b80-afa5-45e1-b572-88a855298b2c",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.hasPdf }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "987447af-6fc0-4ef7-afa6-cf43066fd476",
"name": "HTTP 请求9",
"type": "n8n-nodes-base.httpRequest",
"position": [
2160,
0
],
"parameters": {
"url": "=https://api.zotero.org/users/{{ $('HTTP Request').item.json.userID }}/items",
"method": "POST",
"options": {},
"jsonBody": "={{ [ $('Code4').item.json.meta ] }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "ZOTERO-API-KEY",
"value": "={{ $('HTTP Request').item.json.key }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "d9369d30-23f6-4425-8064-160a40189650",
"name": "编辑字段 1",
"type": "n8n-nodes-base.set",
"position": [
1180,
-260
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "0df8889d-a22d-4512-a2dc-3e4700f37fe4",
"name": "crossref",
"type": "object",
"value": "={{ $json }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a50771b9-b2a1-4ed4-8c7e-1b0b1d629285",
"name": "编辑字段2",
"type": "n8n-nodes-base.set",
"position": [
1180,
0
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "0df8889d-a22d-4512-a2dc-3e4700f37fe4",
"name": "datacite",
"type": "object",
"value": "={{ $json }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "dd594610-4115-4035-af02-bec335a10212",
"name": "编辑字段3",
"type": "n8n-nodes-base.set",
"position": [
1180,
260
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "0df8889d-a22d-4512-a2dc-3e4700f37fe4",
"name": "unpay",
"type": "object",
"value": "={{ $json }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "0dfa6558-08a4-4a7a-94a2-3d02371aa938",
"name": "HTTP请求1",
"type": "n8n-nodes-base.httpRequest",
"position": [
2380,
0
],
"parameters": {
"url": "=https://api.zotero.org/users/{{ $('HTTP Request').item.json.userID }}/items",
"method": "POST",
"options": {},
"jsonBody": "={{ [\n {\n itemType: \"attachment\",\n parentItem: $json.successful['0'].key,\n linkMode: \"linked_url\",\n title: \"Fulltext PDF\",\n url: $('If3').item.json.pdfUrl,\n contentType: \"application/pdf\"\n }\n] }}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Zotero-API-Key",
"value": "={{ $('HTTP Request').item.json.key }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "594c9d30-853d-4447-a42d-c3eb4068287f",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
-580,
-440
],
"parameters": {
"width": 540,
"height": 1120,
"content": "# 通过 Telegram 的 DOI URL 自动导入研究论文到 Zotero"
},
"typeVersion": 1
},
{
"id": "77de37c2-b6f5-4fba-9932-154f3144a73a",
"name": "基础 LLM 链",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
2600,
0
],
"parameters": {
"text": "=Summarize the following abstract: \n\n{{ $('HTTP Request9').item.json.successful['0'].data.abstractNote }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "=You are an assistant that summarizes academic paper abstracts. Your task is to generate a short and clear summary of the abstract in plain English. Keep the summary concise (2–3 sentences), highlight the main contribution or finding, and avoid technical jargon if possible. "
}
]
},
"promptType": "define"
},
"typeVersion": 1.7
},
{
"id": "f5af851a-9a9e-445c-b9c6-a96fdea644f4",
"name": "OpenRouter Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
2600,
200
],
"parameters": {
"model": "google/gemini-2.0-flash-exp:free",
"options": {}
},
"typeVersion": 1
},
{
"id": "aa28ca2d-8590-431f-b01f-c80494be010c",
"name": "发送短信",
"type": "n8n-nodes-base.telegram",
"position": [
2940,
0
],
"webhookId": "0cb93924-6255-48e4-815f-ef2ecd0a8099",
"parameters": {
"text": "=📄 Research Paper Summary\nTitle: {{ $('HTTP Request9').item.json.successful['0'].data.title }}\nURL: {{ $('HTTP Request1').item.json.successful['0'].data.url }}\n\n📝 Summary:\n{{ $json.text }}\n",
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"additionalFields": {
"appendAttribution": false
}
},
"typeVersion": 1.2
},
{
"id": "74ac8241-a6b5-4229-99f0-c57b2ae36009",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-20,
-180
],
"parameters": {
"color": 2,
"width": 780,
"height": 380,
"content": "- 当用户向您的 Telegram 机器人发送消息时启动工作流。"
},
"typeVersion": 1
},
{
"id": "cc9533ee-5edc-4573-9dfa-1cca98a3c854",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
820,
-500
],
"parameters": {
"color": 4,
"width": 600,
"height": 940,
"content": "- **Crossref API 请求** – 使用提供的 DOI 从 Crossref 获取参考文献数据。"
},
"typeVersion": 1
},
{
"id": "23d15a9f-ebeb-416f-a66d-967cc27b9cf9",
"name": "便签3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1460,
-280
],
"parameters": {
"color": 5,
"width": 360,
"height": 440,
"content": "- 将来自 Crossref、DataCite 和 Unpaywall 的元数据合并为一个对象。"
},
"typeVersion": 1
},
{
"id": "66f99fac-0698-400d-9bb2-1e57ce4c05c0",
"name": "发送文本消息1",
"type": "n8n-nodes-base.telegram",
"position": [
2160,
200
],
"webhookId": "0cb93924-6255-48e4-815f-ef2ecd0a8099",
"parameters": {
"text": "=PDF not found",
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"additionalFields": {
"appendAttribution": false
}
},
"typeVersion": 1.2
},
{
"id": "bd043ae9-6d80-4bf0-b80b-79bb921b3340",
"name": "便签4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1880,
-200
],
"parameters": {
"color": 6,
"width": 640,
"height": 600,
"content": "- 使用 IF 节点检查 PDF URL 是否可用。"
},
"typeVersion": 1
},
{
"id": "60bda7fc-c9dc-457a-8cc1-8f6a2c981a49",
"name": "便签5",
"type": "n8n-nodes-base.stickyNote",
"position": [
2580,
-200
],
"parameters": {
"width": 600,
"height": 600,
"content": "- 使用基础 LLM 链总结论文摘要。"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "f31f5475-f6e7-47c7-afdd-0b9ad3346cf3",
"connections": {
"If3": {
"main": [
[
{
"node": "HTTP Request9",
"type": "main",
"index": 0
}
],
[
{
"node": "Send a text message1",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Code4": {
"main": [
[
{
"node": "If3",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Code4",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields1": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields2": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Edit Fields3": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 2
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "HTTP Request3",
"type": "main",
"index": 0
},
{
"node": "HTTP Request2",
"type": "main",
"index": 0
},
{
"node": "HTTP Request4",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request1": {
"main": [
[
{
"node": "Basic LLM Chain",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request2": {
"main": [
[
{
"node": "Edit Fields2",
"type": "main",
"index": 0
}
],
[]
]
},
"HTTP Request3": {
"main": [
[
{
"node": "Edit Fields1",
"type": "main",
"index": 0
}
],
[]
]
},
"HTTP Request4": {
"main": [
[
{
"node": "Edit Fields3",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request9": {
"main": [
[
{
"node": "HTTP Request1",
"type": "main",
"index": 0
}
]
]
},
"Basic LLM Chain": {
"main": [
[
{
"node": "Send a text message",
"type": "main",
"index": 0
}
]
]
},
"Telegram Trigger": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"OpenRouter Chat Model": {
"ai_languageModel": [
[
{
"node": "Basic LLM Chain",
"type": "ai_languageModel",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - AI 摘要总结, 多模态 AI
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
使用Google Vision OCR、Telegram和Google Sheets的自动化财务报告
使用Google Vision OCR、AI和Telegram处理收据到Google表格
If
Set
Code
+12
31 节点Budi SJ
发票处理
使用AI、Apify和Telegram集成自动化工具评估
通过Telegram、Apify、AI和Google表格自动化网站工具分析
If
Set
Code
+10
20 节点Mirza Ajmal
AI 摘要总结
使用AI和RSS的多平台YouTube内容生成器
使用AI和RSS从YouTube生成多平台内容
If
Set
Code
+10
37 节点Budi SJ
内容创作
基于AI的会议研究与每日议程(Google日历、Attio CRM和Slack)
基于AI的会议研究与每日议程:使用Google日历、Attio CRM和Slack
If
Set
Code
+15
30 节点Harry Siggins
AI 摘要总结
YouTube评论情感和关键词提取器
使用Gemini AI分析YouTube评论情感和关键词并通过Telegram报告
Set
Code
Telegram
+10
20 节点Budi SJ
市场调研
基于 AI、社交媒体和 WhatsApp 链接的自动化本地商业线索生成器
使用 AI、社交媒体和 WhatsApp 链接自动生成本地商业线索
If
Code
Limit
+9
29 节点Budi SJ
潜在客户开发
工作流信息
难度等级
高级
节点数量25
分类2
节点类型10
作者
Budi SJ
@budisjI’m a Product Designer who also works as an Automation Developer. With a background in product design and systems thinking, I build user-centered workflows. My focus is on helping teams and businesses work more productively through impactful automation systems.
外部链接
在 n8n.io 查看 →
分享此工作流