NextCloud Talk机器人如何操作

前言

NextCloud生态在私有网盘和个人、小型团队办公上算的是目前开源社区最NB的产品了,其中的Talk应用在功能性上超越了Mattermost和JSTI等众多竞品,虽然在细分功能上有所差异但是算得上是全面且高效的会议应用了。

既然都在使用个人Talk应用了,我们肯定是希望能够集成大多是消息通知到Talk的特定频道中,这个操作在Mattermost里面非常直接且人性化,通过UI界面创建就行了,但是在Talk里面的机器人简直是要命,并且照理来说像Talk这类第一方插件应该是有详细且完善的文档,同时社区也应该有大量的示例参考,再怎么样配置过程对于大多是有开发经验的人来说都不是什么难事才对,但是现实情况是文档极其糟糕且具备高度迷惑性,社区也没有看到一篇文章或者视频完整的描述对应的配置过程,这就是我写这篇博文的目的。

创建Talk机器人

在NextCloud里面是没法直接通过网页界面创建机器人的,必须通过控制台命令创建,这里就事第一个坑,正常的创建逻辑文档是这么描述的:

talk:bot:install [--output [OUTPUT]] [--no-setup] [-f|--feature FEATURE] [--] <name> <secret> <url> [<description>]

然后附带了一个表格对参数进行说明:

ArgumentsDescriptionIs requiredIs arrayDefault
nameThe name under which the messages will be posted (min. 1 char, max. 64 chars)yesnoRequired
secretSecret used to validate API calls (min. 40 chars, max. 128 chars)yesnoRequired
urlWebhook endpoint to post messages to (max. 4000 chars)yesnoRequired
descriptionOptional description shown in the admin settings (max. 4000 chars)nonoNULL

我现在这么说吧,就这个文档谁TM看了不迷,首先name和secret可以理解是干嘛的(推测)那么这个URL是干嘛的通过这两个描述能搞懂吗?

简单来说我一上来就被迷惑了,这个URL真正的意思是当机器人被提及或接收消息的时候回去请求这个URL地址,无论你是否需要机器人传出WebHook你都需要配置这个URL,这么说的话假设我创建一个wordpress的机器人只需要执行如下命令就行了对吧?

occ talk:bot:install wordpress xxxx(秘钥) xxxx(url) 

假设你看文档这样就行了,实际上这个命令执行就会报错:不允许root用户执行occ命令,简直了;

所以只能强行使用www-data用户执行:

su - www-data -s /bin/bash -c '/var/www/html/occ talk:bot:install wordpress xxxx(秘钥) xxxx(url)'

这样一来你会发现还是不行,这波的报错就更加诡异了:PHP内存不足!!!实际上我们运行nextcloud如果是使用容器的话有一个环境变量PHP_MEMORY_LIMIT用于指定PHP内存限制,而这个值是512M,正常情况已经够了,但是一旦你切换为www-data这个用户这个值莫名其妙成了128M,这不仅仅是这个命令无法执行,正常情况下编写在cron.php里面的周期任务也是这个用户在执行,意思就是周期任务始终会失败,且任何直接运行occ的目录都会出现内存不足的问题,实际解决这个问题需要使用我下面这个命令:

su --whitelist-environment=PHP_MEMORY_LIMIT - www-data -s /bin/bash -c 'PHP_MEMORY_LIMIT=2048M /var/www/html/occ talk:bot:install wordpress xxxx(秘钥) xxxx(url)'

原理很简单,那就是给定www-data一个环境变量来处理内存限制,我这里给的是2G,至此安装机器人才算是完成了,我就问你离不离谱。

调用机器人


调用机器人本质就是请求一个http接口实现机器人发言,这里也是非常具有迷惑性的文档描述,具体内容大致如下:

Sending a chat message#

Bots can also send message. On the sending process the same signature/verification method is applied.

  • Required capability: bots-v1
  • Method: POST
  • Endpoint: /bot/{token}/message
  • Header:
NameDescription
X-Nextcloud-Talk-Bot-RandomThe random value used when signing the request
X-Nextcloud-Talk-Bot-SignatureThe signature to validate the request comes from an enabled bot
OCS-APIRequestNeeds to be set to true to access the ocs/vX.php endpoint
  • Data:
fieldtypeDescription
messagestringThe message the user wants to say
replyTointThe message ID this message is a reply to (only allowed for messages from the same conversation and when the message type is not system or command)
referenceIdstringA reference string to be able to identify the message again in a “get messages” request, should be a random sha256
silentboolIf sent silent the message will not create chat notifications even for users
  • Response:
    • Status code:
      • 201 Created When the message was posted successfully
      • 400 Bad Request When the provided replyTo parameter is invalid or the message is empty
      • 401 Unauthenticated When the bot could not be verified for the conversation
      • 404 Not Found When the conversation could not be found
      • 413 Payload Too Large When the message was longer than the allowed limit of 32000 characters (or 1000 until Nextcloud 16.0.1, check the spreed => config => chat => max-length capability for the limit)
      • 429 Too Many Requests When 401 Unauthenticated was triggered too often

看这个描述简单对吧?无非就是计算一个签名吧?没有啥复杂的吧?如果你这么想,你看看/bot/{token}/message里面的token是干嘛的?你想的没有什么错,整个文档就没有说这个token是个啥。这还不是最离谱的,请求头里面的签名X-Nextcloud-Talk-Bot-Signature官方文档的说明如下:

Messages are signed using the shared secret that is specified when installing a bot on the server. Create a HMAC with SHA256 over the RANDOM header and the request body using the shared secret. Only when the SIGNATURE matches the request should be accepted and handled.

Sample PHP code:

$digest = hash_hmac('sha256', $_SERVER['HTTP_X_NEXTCLOUD_TALK_RANDOM'] . file_get_contents('php://input'), $secret);

if (!hash_equals($digest, strtolower($_SERVER['HTTP_X_NEXTCLOUD_TALK_SIGNATURE']))) {
    exit;
}

Create a HMAC with SHA256 over the RANDOM header and the request body using the shared secret,这句话的意思应该是使用hmac运行sha256使用秘钥对随机码+请求数据进行签名对吧?我告诉你当你试过无数次之后你应该清楚一个问题,那就是官方肯定是不希望你使用机器人的,具体这些东西到底是什么意思,我来告诉你。

token是啥?

这个token其实和机器人没有啥关系,这个token其实就是你Talk应用的频道(房间)ID,可以在频道界面的url里面看到(也可以用occ工具查询,这里不细说),它的意思是你希望机器人发送消息到那个频道,同时你的频道在对话设置里面必须添加或启用机器人否则接口会返回授权错误。

签名怎么算?

签名就离大谱了,首先hash_hmac算法很简单,但是你计算错误的问题其实不是你计算签名搞错了,而是它文档在胡说八道,正常通过文档来看你应该是这样算的,伪代码:

request_body = {
	"message":"xxxxxx",
	"referenceId":"xxxx"
}

sign = hash_hmac('sha256', random + request_body, secret)

但是这么算是错的,正确的计算方式真的让人苦笑不得:

request_body = {
	"message":"xxxxxx",
	"referenceId":"xxxx"
}

sign = hash_hmac('sha256', random + request_body["message"], secret)

没错,拿去计算签名的数据是随机数加上请求里面的message字段值,而不是整个请求body😱 😱 😱 😱。

Create a HMAC with SHA256 over the RANDOM header and the request body using the shared secret my ass!!!!

总结

按照我上文给出的逻辑进行你肯定能够走通整个流程,希望大家少踩一下坑吧。但是我必须吐槽一下是谁在维护这个文档,webhookbot这种所有会议都支持的东西,连TM国内的OA软件想钉钉或者企业微信都有的东西你们怎么会搞成这样?确实是太蠢了。

PS:还有一个问题,不知道看到现在有没有人发现一个小问题?这个机器人的头像怎么配置?很好奇对吧,我来告诉你:他不支持配置机器人头像!!!!

留下回复