一键同步远程工作信息到TG,豆瓣小组,知识星球
在之前的文章自动化:小书签已经不够用了,上油猴脚本!中我有预告过,虽然油猴脚本已经极大的减少了我发布远程工作的重复劳动,但是依然不是我理想的自动化工作流。
理想的工作流是,利用扩展助手发布到远程工作者网站,然后再自动发布到豆瓣和知识星球,从其他渠道引流到网站。
这几日,我终于走通了豆瓣小组发布文章的自动化流程,同步远程工作信息的效率大大提升了。
先来看看我之前的操作流程,需要来来回回复制粘贴,尤其标签页多的时候,很容易信息错配,耗时费力,虽然比之以往纯手工格式化信息已经进步很多,但是还是不够。
于是,狗哥我几经雕琢,改进版的发布同步工具终于出炉了,目前已经能自动发布到TG频道,豆瓣小组以及知识星球了,先看视频。
除了 TG 使用了官方提供的 API 以外,其它的两个渠道均是采用了模拟网页操作的方式(国内的产品相对还是比较闭塞),借助的是 Chrome Extension 的脚本执行能力。
Chrome 扩展的这个能力是一把双刃剑,既强大,又危险,我在 GDG 西安 2022 年的 DevFest 活动中做过技术分享。
https://www.bilibili.com/video/BV1jK411D7hJ/
技术分享演示用到的相关代码有开源在 github,感兴趣的可以交流学习一下。这里多说一句,对于非 Webstore 渠道,来源不明的扩展,除非你对它的执行过程有清楚的认识,还是谨慎使用,毕竟安全大于天。
https://github.com/greatghoul/chrome-extension-risks-demo
其实知识星球的分享这里有一点瑕疵,就是必须是标签页激活的状态,不然它的编辑器识别不到输入区域文本的变化,提交按钮无法点击,不能像豆瓣小组那样静默执行。但这一定是可以想办法绕过的,只是狗哥我急着发文,暂时搁置了这个问题,凑活用就得了。
目前的自动化工具中,并未包含发布内容到微信公众号的部分,不过狗哥我已经有了规划,准备做成类似 [自动化] 为了方便发今日头条,我做了一个浏览器扩展 的样子,留到下次再做分享。
Demo 看完了,这里以豆瓣小组为例,再简单说明一下这个扩展的“核心技术”。
Chrome 支持在指定标签页的上下文中动态调用一个方法,需要注意的是,该方法内不能引用任何该方法定义之外的其他方法或者变量,否则会访问错误,但是它支持动态设置方法参数,这提供了一定的灵活性。
function scriptPostingTopic () {
return async (title, message) => {
// 等待指定时间
const wait = (timeout) => new Promise(resolve => setTimeout(resolve, timeout));
// 等待指定元素出现
const waitElement = (selector, timeout = 5000, interval = 300) => {
return new Promise((resolve, reject) => {
let elapsed = 0;
const check = () => {
const el = document.querySelector(selector);
if (el) {
resolve(el);
} else if (elapsed >= timeout) {
reject(new Error(`Element ${selector} not found within ${timeout}ms`));
} else {
elapsed += interval;
setTimeout(check, interval);
}
};
check();
});
}
// 模拟点击
const clickElement = (element) => {
const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, view: window });
element.dispatchEvent(clickEvent);
}
// 粘贴内容
const pasteElement = (element, text) => {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(element);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
const clipboardData = new DataTransfer();
clipboardData.setData('text/plain', text);
const pasteEvent = new ClipboardEvent('paste', { clipboardData: clipboardData, bubbles: true, cancelable: true });
element.dispatchEvent(pasteEvent);
}
// 模拟触发输入事件,不然表单识别不到输入的内容
const inputElement = (element, text) => {
element.focus();
element.value = text;
const inputEvent = new Event('input', { bubbles: true });
element.dispatchEvent(inputEvent);
}
// 填写标题
const titleElement = await waitElement('.DRE-topic-editor-title-inputor');
clickElement(titleElement);
await wait(500);
inputElement(titleElement, `分享远程工作:${title}`);
await wait(1000);
// 填写正文
const contentElement = await waitElement('.DRE-topic-editor-main [contenteditable="true"]');
clickElement(contentElement);
await wait(500);
pasteElement(contentElement, message);
await wait(1000);
const submitButton = await waitElement('.DRE-topic-editor-header .DRE-primary-button');
submitButton.click();
}
}
async function postMessage () {
const tab = await chrome.tabs.create({ url: GROUP_POST_URL, active: false });
await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: scriptPostingTopic(),
args: [title, message], // 调用 scriptPostingTopic 传递的参数
});
}
这是一个简单的调用场景,打开标签页,动态执行脚本,填写标题和正文,并提交表单。如果你的脚本的逻辑更加复杂,可以使用独立的脚本文件的方式去调用执行,不过执行脚本文件的方式就不支持例子中便捷的传参方式了,需要使用消息通信的 API 来实现数据传递,本文不多做扩展。感兴趣的朋友可以参考 Scripting 和 Message Passing 文档。
- https://developer.chrome.com/docs/extensions/reference/api/scripting
- https://developer.chrome.com/docs/extensions/develop/concepts/messaging
文章同步发表于微信公众号老狗拾光,欢迎关注。