使用Pipedream自动化实现Twitter多媒体数据到Telegram的自动转发

开车也要一本正经地开。

本文的产出:一个新的、包含激烈的NSFW R18二次元插画内容的、自动发布的、支持媒体组消息的 Telegram频道

未成年的人、自认为未成年的人或对R-18内容没有兴趣的人,千万不要手贱点进去。

该频道中的内容均已署名和给出源链接,图片版权归原作者所有。

简介

Pipedream是一个自动化工具,可以在这篇中文文章中进一步了解其特性。

有了前人的介绍,本文其实无需大书特书,毕竟最终实现的东西并没有什么技术难度,权当记录一下探索的过程。

该工具的相关竞品中,比较出名的有:

根据官方文档的介绍,Pipedream的特性包括:

  • 无服务器运行时和工作流服务
  • 开源触发器和操作数百个集成应用程序
  • 一键式OAuth和基于密钥的身份验证,适用于数百个API(直接在代码中使用令牌或与预构建的操作一起使用)

而且,官方宣称免费用户每个月可以调用10000次(每天333次),每天使用30分钟的计算时间额度。这个限额对于不是太频繁的操作来说,其实挺好的。

发现Pipedream的原因

首先,IFTTT免费版无法满足我。

我曾经构建了几个自动化流程,把Twitter上插图画师的作品全自动转发到一个Telegram频道。IFTTT原生接入的Twitter触发器似乎比较简陋📷,获取不到完整的推文数据。于是只能把默认的数据项喂进触发条件中,用来激活Telegram Bot转发的行为。转发后的图片消息在Telegram里展示的效果勉强过得去(转发的媒体似乎是低清画质,且因为Twitter搜索的限制,不支持敏感内容),因此频道运行了一段时间,在一年多的时间里积累了上万张图片或图链资源。

我的IFTTT工作流的触发逻辑就是“我自己构建的Twitter画师列表里,收到高赞高转发的图片”,实际上这个机制并不严谨,因为粉丝数目非常大的大触画师随便发张午饭的拉面照片,也可能会被点很多赞。Twitter列表机制有点问题,维护Twitter列表也比较浪费时间,前后攒了接近900个画师,现在很少增加了。

何况,众所周知IFTTT后来推出了订阅制方案,免费用户无法无限制构建自己的流程,我即使为此付了一小段时间的费用,但是效果依然差强人意。包括其他自动化,比如根据天气情况自动发推、在Google Sheet里预排期未来十几年的推文等等,也因为IFTTT的限制而停用了。去年我决定把那个图片频道废弃掉,一时间也不再考虑自动化的事。留下的IFTTT自动化只剩下一个从Telegram频道发布内容到Twitter。

但是今年我又开了个Twitter分号,尽可能多地把我知道的画师全部关注起来。并且在这个帐户上,别的什么都不做,只点赞插画;更让Twitter的推荐算法给我的首页时间线疯狂投喂推荐的作品。因此,半年期间已经攒下了7000个点赞内容。

Twitter本身想要批量获取图片是比较困难的,想获取原画质图片的话,要么就手动在网址后加参数,要么用自动化脚本、油猴插件代替你的鼠标完成这件事。而在手机上,上述操作显然就没这么方便。点赞操作也不能自动保存图片,而Twitter经常性封禁R-18画师,一旦画师帐户消失,点赞的那些图也会消失,不及时保存的话就只能在第三方图站上检索到。

我曾想利用IFTTT的RSS触发器,结合RSSHub的功能把我Twitter帐号的点赞时间流给RSS化,然后构建自动化流程,但是试验之后,效果依然差强人意。是时候走出IFTTT这个低门槛的舒适圈了。

打开Google搜索send a photo with telegram bot api on new item in feed from RSS,正所谓得来全不费功夫,仿佛量身定制一般,Pipedream出现在检索结果第一条。

点进去研究了一下,发现根本不需要用RSS触发,Pipedream自身便接入了非常完整的Twitter API📷,令人觉得起步便有了一些信心。

个人工作流的构建

以下为个人从头构建工作流的步骤记录,如果有人看不懂,可以参照官方的Twitter-Telgram整合我的Twitter点赞触发中预设好的工作流进行模仿学习,我相信那样上手速度会更快。

  1. 使用GitHub帐户授权登录(还支持Google授权登录、邮箱注册📷
  2. 在面板中new一个workflow以构建工作流
  3. 在第一个框体📷中选择触发器trigger来开启工作流的第一个步骤step,选择Twitter;也可以继续在这个框中继续增加新的trigger
  4. 在列表中选中选择第一个My Liked Tweet📷作为数据源,并绑定Twitter帐户,需要进行一次授权;其实下面还有个操作,是读取其他用户点赞的数据源,可以访问想要观察的其他用户的点赞操作
    • 默认的两次读取数据间隔是15分钟,是有考虑到API的请求限制;视需求可以改为30分钟或更久,也可以改成以天、星期、月为周期的定期计划,更支持CRON
    • 选时区Timezone对于以天、星期、月份为周期定时执行的是有必要的,这样可以让你选择正确的执行时间
    • 但是我不确定时区服务是否还有点问题,其实这个时区选了也白选,会跳回UTC,可能是临时性问题
  5. 出现了触发器的配置界面📷,可以在其中选择要运行的示例事件;第一次它会自动读取新事件,也可以手动在Twitter上执行一些点赞操作;然后回到触发器界面,点一下Try Now,在列表中把每条被点赞的推文事件显示出来,展示出该推文数据格式化的数组,可以选一个事件来测试
  6. 将鼠标移到数据上方,会出现Copy PathCopy Value的选择,点击前者,就能自动复制变量字段,这个变量可以用来传递到下面的步骤中,比如steps.trigger.event.extended_entities.media[0].media_url_https正是这条推文的第一个图片媒体的传统链接,也是我需要直接获取的数据
  7. 点击"+"号,add a step继续新建下一步骤;前面能够获取到Twitter的数据,实际上已经万事具备了;简单来说,在这一步骤中可以直接选Telegram Bot
  8. 要绑定对应的机器人,如果没有Telegram Bot,找@BotFather/newbot命令新建一个,复制好回传的token进行绑定,并且也需要邀请bot以管理员身份加入需要自动发布消息的Telegram频道或群组
  9. 在接下来的Action行为中选择Send a Photo with Telegram Bot API,在该步骤📷中,选择需要调用的Telegram Bot,并输入对应频道、群组的公开ID或者群号,在数据栏中,将上一步获取的数据传递进去;因为尽可能希望以原图画质进行上传(但是其实存在文件大小限制,也肯定会被Telegram压缩),所以我在之前获取的{{steps.trigger.event.extended_entities.media[0].media_url_https}},也就是默认传统图片链接之后加上?name=orig(其实改成:orig也生效),并输入到相应的数据栏中
  10. 这个Action还支持填入Caption图注的数据,并且根据Telegram Bot API,它支持HTML格式的Caption,更改一下parse mode即可,因此可以为图注加上原图链接、标签等特殊效果
  11. 构建完成后,可以利用事件选择器里的相关推文事件进行几次测试,如果运行正常,那就可以部署了

初步效果:

自定义代码

根据上述工作流的思路,可以构建更复杂的工作流,比如推送yande.re的每日/周/月/年热图。当然,yande.re官方似乎只提供了post的RSS,或者叫做piclens,没有popular的RSS. 所以这里借助需要借助RSSHub来实现。

举个例子,获取五张最近24小时内的热图,RSS地址是https://rsshub.app/yande.re/post/popular_recent/1d?limit=5,具体请参照RSSHub的文档。

然后举一反三,在Pipedream中新建一个工作流,触发器是RSS中的新项目New Item in Feed from RSS API,填入上面的RSS地址,设置成每天运行一次。

接下来,本来也可以进行Telegram Bot发图操作的,但是我一时兴起,希望把yande.re图片的标签数据输入到图片消息的图注中,并给每个标签加上"#"符号。展示太多标签也不行,所以当一张图被打了太多标签时,还要随机挑选5个标签。

Telegram Bot的步骤框中只能填入一点点字符串内容,不支持这么复杂的格式化操作,因此在这之前可以增加一步Run custom code来处理触发器的数据,并把数据传递给下一步,在这个工作流中我选择了比较方便的Python📷

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
import random
def handler(pd: "pipedream"):
# Reference data from previous steps
cat = pd.steps["trigger"]["event"]["categories"]
if len(cat) > 5:
cat = random.sample(cat, 5)
tags = ''
for c in cat:
if '/' in c:
c = '_'.join(c.split('/'))
tags += '#'+c+' '
# Return data for use in future steps
return {"foo": {"test":True}, 'tags': tags}

最后,这段代码返回的tags变量{{steps.python.$return_value.tags}}就是我要传递给Telegram Bot的额外数据了。

注意:yande.re的一些图片体积非常大,根据上面提到的文件大小限制,如果选择Send Photo这个工作流,则执行失败的概率较高。所以,理论上用Send Document可以支持更大的文件;而直接发文字消息,把链接附上,是最稳定的做法。当然,完全可以在自定义代码里执行一段将图片压缩的操作,当然我没有亲自尝试过,而且这样操作很可能会过多消耗每天的计算时间额度,不推荐。

将多张媒体的推文转发到Telegram的MediaGroup

回头审视一开始的Twitter→Telegram工作流。有些推文里带了四张图,而Send Photo操作只能发一张图,难道没有更好一点的方法,把多图塞进一条消息里吗?

当然有,那就是Send an Album,又称为Media Group,媒体组。 可以在Telegram的文档找到这一功能的相关参数。

因此我需要将之前的工作流的最后一步改成Send an Album,随之而来的问题就是:媒体组的API输入参数要求是一段JSON数组,而我输入的推文的图片数量是不固定的(1张到4张,现在甚至可以视频图片混着发),肯定没法直接手动编辑这个数组,所以这里需要再次增加一段自定义代码来处理。

为媒体组增加Caption参照了Stack Overflow上的一个问答,原来只要在第一张图的位置加上caption和parse_mode就好。

但是当我写完Python代码后,我发现这个工作流执行几乎每次都会在Python运行这一步出错,导致大量的自动转发失败,但是偶尔也会成功。报错内容主要是read ECONNRESETread ECONNREFUSED. 相同思路的代码,代码内容也没有错误,在yande.re的工作流上就从来不会失败。令人百思不得其解,查了一堆资料,都说是服务器关闭了连接,但是我的Python代码仅仅是字符串拼接和构建数组而已,并没有任何网络请求。

最终,在消耗了大量额度进行测试后,我猜测Pipedream上执行Python还不够稳定……

而Pipedream对Node.js的支持好像更成熟,因此我随手替换为JavaScript代码。这回终于能100%成功执行了。

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
// To use previous step data, pass the `steps` object to the run() function
export default defineComponent({
async run({ steps, $ }) {
// Return data to use it in future steps
var triggerEvent = steps.trigger.event;
var userName = triggerEvent.user.name;
var screenName = triggerEvent.user.screen_name;
var fullText = triggerEvent.full_text;
var extMedias = triggerEvent.extended_entities.media;
var sauceLink = triggerEvent.url;
var origUrls = [];
var mediaGroup = [];
function getVideo(videoVariants) {
var maxRes = 0;
for (let j = 0; j < videoVariants.length; j++) {
if (videoVariants[j].bitrate >= maxRes) {
var targetUrl = videoVariants[j].url;
maxRes = videoVariants[j].bitrate;
}
}
return targetUrl;
}
for (let i = 0; i < extMedias.length; i++) {
var mediaType = extMedias[i]['type'];
switch (mediaType) {
case 'video':
origUrls[i] = getVideo(extMedias[i].video_info.variants)
break;
case 'animated_gif':
origUrls[i] = getVideo(extMedias[i].video_info.variants)
mediaType = 'video';
break;
default:
origUrls[i] = extMedias[i]['media_url_https']+'?name=orig';
break;
}
mediaGroup[i] = {
"type": mediaType,
"media": origUrls[i]
};
};
var mediaCaption = "<b>" + userName + "</b> ( #" + screenName + " ) " + fullText + "\n\n<a href='"+ sauceLink +"'>⚡Sauce</a>" + " | Load Original: ";
var imgSerial = '';
for (let i = 0; i < origUrls.length; i++) {
var imgNum = (i+1).toString();
imgSerial = imgSerial + "<a href='"+origUrls[i]+"'>▶Media_"+ imgNum +"</a> ";
};
mediaCaption += imgSerial;
mediaGroup[0]['caption'] = mediaCaption;
mediaGroup[0]['parse_mode'] = "html";

return {"mediaGroup": mediaGroup};
},
})

最终返回的album变量{{steps.program.$return_value.mediaGroup}}就是包含了图片和图注信息的JSON数组,可以直接送给下一步动作📷

最终效果:

Pipedream的配额限制

免费用户每个月可以调用10000次(每天333次),每天使用30分钟的计算时间额度。

在帐户的账单界面会显示当天的调用次数以及计算时间的消耗量📷

在每个工作流的设置界面里,可以人为调整📷默认的执行频率和超时时间,以达到防止超过第三方API请求频率限制、节省计算调用预算或增加计算消耗的目的。

每天的额度会在UTC时间的0点重置,对于东八区来说还是挺友好的,毕竟在晚上提前把额度用完了,睡一觉醒来就正好重置。我在最初几天高频率操作,还是很容易被限额,今后只要正常操作的话,作为免费的开发者套餐,这样的额度还是够用的。

如果不想被限额,官方提供了每月19美元的Pro套餐(几乎相当于一个Netflix的UHD大会员),每月99美元的团队套餐,以及自由定价的企业套餐。看了一下,如果有人有极为强烈的个人需求或者商业需求,可以考虑一下他家的套餐。也许是因为他家好像还不是很有名,以后要是用户变得更多了,后续的资费和服务会如何发生变化,我暂且持观望态度。

因此我的建议依然是不付费,以及用其他的自动化工具分摊自己的自动化需求。

展望

我构建的Twitter→Telegram转发的工作流还不算全自动化,因为每个事件都是我在Twitter上手动点赞触发的,当然,这是为了人为筛选图片质量,确保无关的图片不要混进来。

理论上,全自动点赞收图机可以这么设置:

  • 触发器:定时获取某用户、某列表的推文(如日本画师列表、国际媒体列表)
  • 动作:判断推文的数据中是否包含图片(或者其他你想要的数据)
  • 动作:统计推文的转推数和点赞数,如果达到阈值,则继续执行(画师高赞图一般就是插画作品)
  • 动作:顺便点个赞或转发(可选操作)
  • 动作:将想要的推文数据转发到Telegram

其实思路和我当初用IFTTT一致,而Pipedream的优点在于这个过程可以高度自定义,执行步骤也多。

或许有人觉得,这么点任务,搞个Telegram Bot配合Twitter API在服务器上运行就好了。

但是这些现成的自动化工具的卖点正是无服务器,而且整合的应用接口越多,其所能做的任务就比一台服务器上自己辛苦配置的工具能做的要更丰富。因此这些工具还是有值得一试的价值的。未来也可以继续使用这类工具去自动捕捉我所关注的信息资源,趁着互联网还活着的时候,尝试一下也没有什么损失吧。