之前的博文中,我提到过本站的友情链接是通过 Vika 维格表管理的,维格表的一大特色是支持通过 API 增、删、改、查表格中的数据,这直接让维格表化身为一个简单的数据库,可玩性大大提升,通过它维护友链数据,并通过自己编写的脚本生成友链页面就是个例子。
然而最近我放弃了维格表,改用 WPS 在线表格,原因有四点:
- Vika 页面经常有奇奇怪怪的 bug,比如拖拽改变数据顺序时,数据会乱跑;
- 性能优化不行,在我仍在服役的那台十年前的笔记本上,用 WPS 还行,用 Vika 一定会卡;
- 每次打开都有一个开通会员的流氓弹窗,这个弹窗的 × 号是个假的,第一次点击会跳转到开通会员的页面,第二次点击才会关闭弹窗,怪难受的;
关于第三点的流氓弹窗,悄悄告诉你一个解决办法——
1 2
| localStorage.setItem("LAST_PAYMENT_REMINDER_TIME", "9999999999999");
|
- Vika 免费空间只支持 20,000 行数据、上传 30 个文件,我已经用了 50% 了,该给数据寻找新家了。
将 Vika 的所有数据迁移至 WPS 在线表格后,最大的问题就是该如何通过 API 查询数据。Vika 中有方便的 @vikadata/vika
SDK,WPS 其实也有方便的 HTTP 接口,通过查阅 金山文档开放平台,我发现在线表格可以通过遍历记录接口获取数据,然而事情真的有这么简单吗?
问题一:想要调用金山文档开放平台的接口,需要认证成为 WPS 开发者,认证需要营业执照,且 不支持个人认证,这不是为难我这个不准开公司的银行小打工人吗?
问题二:即使认证成功,调用接口也需要 access token,一个 token 的有效期仅有一天,刷新 token 需要手动 访问授权页 获取,极其不便。
一番探索后,我便放弃了这个方案。因为我发现了一个更方便的路子:AirScript 脚本。
根据官方介绍,AirScript 是一个基于 JavaScript 的表格应用开发工具,它支持编程完成一些表格操作,下面介绍如何利用 AirScript,实现本站的友链页面自动生成。
登录金山文档,创建一个 智能表格。
在智能表格中,新建一个 数据表。
在数据表中,根据需要增加站名、签名、状态、首页、友链页、RSS、头像、备注等字段,然后录入所有友链数据。
点击 Ribbon 栏中的高级功能 - 高级开发 - AirScript 脚本编辑器。
在 文档共享脚本 中新建一个脚本。
不要放在 我的脚本 中,因为不支持 webhook 调用。
填入脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const sheet = Application.Sheets.Item('友链')
function fetchAllRecords() { const view = sheet.Selection.GetActiveView() let all = [] let offset = null while (all.length === 0 || offset) { let records = sheet.Record.GetRecords({ ViewId: view.viewId, Offset: offset, }) offset = records.offset all = all.concat(records.records) } return all }
return fetchAllRecords()
|
按 Ctrl + S 保存脚本,然后点击工具栏上的 “脚本令牌” 按钮,生成一个脚本令牌,复制这个令牌保存(以后将无法再次查看),令牌有效期是半年,但放心,令牌可以随时延期,延期后令牌不变。
点击脚本名称旁边的菜单,点击 “复制脚本 webhook” 并保存。
接下来只需 POST 这个 webhook 地址,在请求头中带上 AirScript-Token:
脚本令牌,即可获取到 JSON 格式的表格数据。
接下来就可以搭配本地脚本实现自动生成友链页面了!在博客源码目录新建一个 friends-gen.js
,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| const fs = require("fs"); const path = require("path");
function template({ 站名, 首页, RSS, 头像, 签名, 状态, 备注 }) { if (!站名 || 状态 === "已清理") return ""; 站名 = 站名 ? 站名.replace(/(’|‘)/g, "'") : ""; 签名 = 签名 ? 签名.replace(/(’|‘)/g, "'") : ""; const showAvatar = 头像 && 状态 === "正常"; const avatar = showAvatar ? ` style="background-image: url('${头像}')"` : ""; const firstWord = showAvatar ? "" : 站名[0].toUpperCase(); const rss = RSS && RSS !== "无" && 状态 === "正常" ? ` <a class="has-text-grey" target="_blank" href="${RSS}"><i class="fas fa-rss" title="支持 RSS"></i></a>` : ""; const tag = { 正常: "", 无法访问: ' <span class="tag is-warning">无法访问</span>', 单向友链: ' <span class="tag is-warning">单向友链</span>', 已清理: ' <span class="tag is-Danger">已清理</span>', }[状态]; const popList = []; if (签名) { popList.push(`签名: ${签名}`); } if (备注 && 状态 !== "正常") { popList.push(`备注: ${备注}`); } const pop = popList.length > 0 ? ` <div class="friend-pop"><div>${popList.join("<br />")}</div></div>` : ""; const link = 状态 === "正常" ? `<a target="_blank" href="${首页}">${首页.replace( /^https?:\/\//, "" )}</a>` : 首页.replace(/^https?:\/\//, ""); return ` <div class="friend"> <div class="friend-avatar"${avatar}>${firstWord}</div> <div class="friend-detail"> <div>${站名}${tag}${rss}</div> <div>${link}</div> </div>${pop} </div> `; }
async function main() { const res = await fetch( "https://www.kdocs.cn/api/v3/ide/file/******/script/******/sync_task", { method: "POST", headers: { "Content-Type": "application/json", "AirScript-Token": "******", }, body: JSON.stringify({ Context: { argv: {}, }, }), } ); const jsonRes = await res.json(); if (jsonRes && jsonRes.status === "finished") { const records = jsonRes.data.result; let result = ` {% raw %} <div class="friends"> `; let normalCompleted = false; for (const record of records) { if (!normalCompleted && record.fields.状态 !== "正常") { result += ` <div class="friend friend-empty"></div> <div class="friend friend-empty"></div> <div class="friend friend-empty"></div> </div> {% endraw %}
----
异常友链 | 如存在异议请在评论区留言
{% raw %} <div class="friends"> `; normalCompleted = true; } result += template(record.fields); } result += ` <div class="friend friend-empty"></div> <div class="friend friend-empty"></div> <div class="friend friend-empty"></div> </div> {% endraw %} `; const friendsMd = path.resolve(__dirname, "./source/friends/index.md"); const friendsMdContent = fs.readFileSync(friendsMd, { encoding: "utf-8" }); const startStr = "<!-- auto_generation_start -->"; const endStr = "<!-- auto_generation_end -->"; const startPos = friendsMdContent.indexOf(startStr) + startStr.length; const endPos = friendsMdContent.indexOf(endStr, startPos); const newContent = friendsMdContent.slice(0, startPos) + result + friendsMdContent.slice(endPos); fs.writeFileSync(friendsMd, newContent, { encoding: "utf-8" }); } }
main();
|
请按照脚本中 // TODO:
注释标注的要求自己修改脚本。
打开友情链接的 md 文件,在需要自动生成的位置增加起始和结束标记。
执行脚本 node friend-gen.js
,即可测试生成效果了!
通过这种方式完成友链自动化后,你还可以点击友链表格右上角的创建应用 - 表单,生成表单供访客自助提交友链申请,真方便呀!