钉钉单聊/群聊机器人实现思路

本文档详细介绍了如何在钉钉开发平台上申请机器人,管理回调接口,以及关键步骤如获取消息数据、后端认证和发送单聊/群聊消息。重点讲解了消息接收和实现代码示例。

钉钉官网文档:https://open.dingtalk.com/document/group/robot-overview

一、钉钉开发平台
1.申请机器人

应用开发-企业内部开发-基础信息-机器人-创建机器人

2.开发管理

设置开发服务器出口IP列表

3.回调接口

设置钉钉扫码后的回调后端接口 (用于消息接收)

4.权限设置

企业内机器人发送消息权限


二、消息接收

获取post后的消息数据及各字段

1.通用字段
conversationType = req_data['conversationType']                         # 消息类型,1单聊,2群聊
conversationId = req_data['conversationId']                             # 加密的会话id
msgId = req_data['msgId']                                               # 加密的消息id
senderNick = req_data['senderNick']                                     # 发送消息的人昵称
isAdmin = req_data['isAdmin']                                           # 是否为管理员
sessionWebhookExpiredTime = req_data['sessionWebhookExpiredTime']       # 当前会话的Webhook地址过期时间
createAt = req_data['createAt']                                         # 消息的时间戳
senderId = req_data['senderId']                                         # 加密的发送者id
chatbotUserId = req_data['chatbotUserId']                               # 加密的机器人id
msgtype = req_data['msgtype']                                           # 消息类型,目前只支持text
text = req_data['text']['content']                                      # 消息的关键字
sessionWebhook = req_data['sessionWebhook']                             # 当前会话的webhook地址
robotCode = req_data['robotCode']                                       # 消息机器人代码
post_timestamp = request.headers.get('Timestamp')                       # 消息timestamp
post_sign = request.headers.get('Sign')                                 # 消息sign
2.单聊字段
chatbotCorpId = req_data['chatbotCorpId']                               # 加密的机器人所在的企业corpId
senderStaffId = req_data['senderStaffId']                               # 企业内部群中@该机器人的成员userid
senderCorpId = req_data['senderCorpId']                                 # 企业内部群有的发送者当前群的企业corpId。
3.群聊字段
atUsers = req_data['atUsers']                                           # 被@人的信息
conversationTitle = req_data['conversationTitle']                       # 群聊时的群名称
isInAtList = req_data['isInAtList']                                     # 是否在@列表中

三、后端认证


四、发送消息

1.单聊

msg = {
    "text": {
        "content": "单聊: {}".format(text)
    },
    "msgtype": "text"
}
return json.dumps(msg)

五、实现代码

import json
import hmac
import hashlib
import base64
import time

api_blueprint = Blueprint(__name__ + '_api', __name__)
logger = get_logger(logger_type='run')


def check_sign_result(post_timestamp, post_sign):
    timestamp = round(time.time() * 1000)
    app_secret = 'ASAO7-nwDBzT1ObEo1PC6htvjHwlIBeu-4sZ7oVDQjf86EOA0DASgylZg-LIKmcL'
    app_secret_enc = app_secret.encode('utf-8')
    string_to_sign = '{}\n{}'.format(post_timestamp, app_secret)
    string_to_sign_enc = string_to_sign.encode('utf-8')
    hmac_code = hmac.new(app_secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
    sign = base64.b64encode(hmac_code).decode('utf-8')
    # 校验时间戳/sign
    if (timestamp - int(post_timestamp) < 36000000 or (int(post_timestamp) - timestamp) < 3600000) and sign == post_sign:
        return True
    else:
        return False


@api_blueprint.route('/wanglei/robot', methods=["GET", "POST"])
def wanglei_robot():
    if request.method == 'GET':
        return 'get'
    elif request.method == 'POST':
        req_data = json.loads(request.data.decode())
        post_timestamp = request.headers.get('Timestamp')
        post_sign = request.headers.get('Sign')
        result = check_sign_result(post_timestamp, post_sign)
        if result:
            conversationType = req_data['conversationType']                         # 消息类型,1单聊,2群聊
            conversationId = req_data['conversationId']                             # 加密的会话id
            msgId = req_data['msgId']                                               # 加密的消息id
            senderNick = req_data['senderNick']                                     # 发送消息的人昵称
            isAdmin = req_data['isAdmin']                                           # 是否为管理员
            sessionWebhookExpiredTime = req_data['sessionWebhookExpiredTime']       # 当前会话的Webhook地址过期时间
            createAt = req_data['createAt']                                         # 消息的时间戳
            senderId = req_data['senderId']                                         # 加密的发送者id
            chatbotUserId = req_data['chatbotUserId']                               # 加密的机器人id
            msgtype = req_data['msgtype']                                           # 消息类型,目前只支持text
            text = req_data['text']['content']                                      # 消息的关键字
            sessionWebhook = req_data['sessionWebhook']                             # 当前会话的webhook地址
            robotCode = req_data['robotCode']                                       # 消息机器人代码

            if conversationType == '1':
                # 单聊
                chatbotCorpId = req_data['chatbotCorpId']                           # 加密的机器人所在的企业corpId
                senderStaffId = req_data['senderStaffId']                           # 企业内部群中@该机器人的成员userid
                senderCorpId = req_data['senderCorpId']                             # 企业内部群有的发送者当前群的企业corpId。
                msg = {
                    "text": {
                        "content": "单聊: {}".format(text)
                    },
                    "msgtype": "text"
                }
                return json.dumps(msg)
            else:
                # 群聊
                atUsers = req_data['atUsers']                                       # 被@人的信息
                conversationTitle = req_data['conversationTitle']                   # 群聊时的群名称
                isInAtList = req_data['isInAtList']                                 # 是否在@列表中

                # 如果回复的消息需要@用户,则通过senderNick获取userid,在atUserIds中列出userid即可
                msg = {
                    "at": {
                        "atUserIds": [
                            'manager8575'
                        ],
                        "isAtAll": False
                    },
                    "text": {
                        "content": '群聊: {}'.format(text)
                    },
                    "msgtype": "text"
                }
                return json.dumps(msg)

<think>好的,我现在需要解决用户的问题:如何通过自定义机器人将上传到钉盘的文件分享链接发送到外部群聊。用户已经提供了之前的对话历史和一些引用内容,我需要结合这些信息来给出正确的步骤。 首先,回顾之前的对话,用户之前询问的是如何通过自定义机器人将文件上传到钉盘并发送链接到内部群聊。现在用户的问题是发送到外部群聊,这意味着之前的步骤可能需要调整,因为外部群和内部群的处理方式可能不同。 接下来,查看用户提供的引用内容。引用[3]提到了钉钉机器人的Webhook需要timestamp和sign参数,这可能涉及到外部群聊的权限验证。之前的回答中使用的API可能需要调整,因为内部群和外部群使用的接口可能不同。例如,发送到外部群可能需要不同的Chat ID获取方式或者不同的发送接口。 用户的需求是发送到外部群,因此需要确认如何获取外部群的Chat ID。之前的步骤中,获取群列表可能默认是内部群,需要找到获取外部群列表的方法。此外,发送消息的API也可能不同,可能需要使用外部群专用的接口。 另外,引用[1]提到了微信机器人使用框架监听消息,虽然钉钉和微信不同,但思路可能有借鉴之处,比如权限管理和消息监听。但钉钉的API更倾向于服务端调用,而不是客户端框架,所以需要确保所有步骤符合钉钉的API文档。 还需要注意权限问题,之前的回答提到了需要申请机器人消息发送权限和钉盘权限,但发送到外部群可能需要额外的权限,比如外部群的管理权限或应用授权。用户可能需要检查应用权限是否包括外部群操作。 另外,引用[3]中的Webhook URL构造涉及timestamp和sign,这可能意味着发送消息到外部群需要使用加签的方式,而不仅仅是access token。之前的回答中使用的是access token,可能需要调整验证方式,比如在请求参数中加入签名。 总结下来,步骤可能需要调整的地方包括: 1. 获取外部群的Chat ID的方法。 2. 发送消息时使用的API接口是否需要更换。 3. 权限配置是否包含外部群发送权限。 4. 消息发送时的认证方式是否需要加签。 接下来需要验证这些点。查阅钉钉开放平台文档,发现外部群(即普通群)和内部群(企业群)在API处理上有区别。发送消息到普通群需要使用不同的接口,例如使用chat.send接口,并且可能需要使用机器人的Webhook地址,而Webhook地址需要加签或安全设置。 因此,调整步骤可能包括: - 创建机器人并获取Webhook地址,而不是使用企业内部应用的access token。 - 在发送消息时使用机器人的Webhook URL,并生成签名参数timestamp和sign。 - 确保机器人被添加到外部群中,并且具有发送消息的权限。 这样,用户需要创建自定义机器人,而不是企业内部应用,因为企业内部应用可能仅限于内部群。自定义机器人可以通过Webhook发送消息到任何已添加的群聊,包括外部群。 因此,正确的步骤应该是: 1. 创建自定义机器人,获取Webhook URL和加签信息。 2. 上传文件到钉盘并获取分享链接(可能需要调整权限,确保文件可以对外分享)。 3. 使用Webhook URL,结合timestamp和sign生成请求,发送链接到外部群。 需要确认上传到钉盘的文件是否允许外部分享,可能需要设置文件权限为公开或指定外部群成员可访问。同时,生成分享链接时可能需要设置更长的有效期,确保外部用户能够访问。 最后,整合这些步骤,确保代码示例中使用正确的Webhook方法和签名生成方式,而不是之前的access token方法。同时,提醒用户注意外部群的安全设置和文件权限,避免出现无法发送或访问的问题。</think>--- ### **分步解决方案:通过自定义机器人发送钉盘文件链接到外部群** --- ### **一、核心差异说明** 与内部群不同,**外部群**(含非企业成员)的机器人消息发送需使用**Webhook方式**而非企业内部应用API,且需要**加签验证**(timestamp与sign参数)[^3]。 --- ### **二、配置自定义机器人** 1. **创建群机器人** - 在目标外部群中点击`群设置 > 智能群助手 > 添加机器人 > 自定义机器人`[^3] - 记录生成的`Webhook URL`(包含`access_token`参数) - 开启`加签`安全设置,记录`签名密钥`(用于计算sign)[^3] ![机器人配置示意图](https://img.alicdn.com/imgextra/i4/O1CN01k4Hj3W1XqZ9Q2QY8d_!!6000000002967-2-tps-1200-800.png) --- ### **三、上传文件并生成分享链接** 1. **钉盘文件上传(保持与企业应用相同流程)** ```python # 沿用原企业内部应用的上传方法 dentry_id = upload_to_dingpan(access_token, file_path) share_link = generate_share_link(access_token, dentry_id) ``` - **关键权限**:需确保文件分享权限设置为`企业外人员可访问`[^3] --- ### **四、通过Webhook发送消息到外部群** 1. **生成加签参数** 根据钉钉文档计算`timestamp`和`sign`[^3]: ```python import time import hmac import hashlib import urllib.parse def generate_sign(secret): timestamp = str(round(time.time() * 1000)) secret_enc = secret.encode('utf-8') string_to_sign = f"{timestamp}\n{secret}" string_to_sign_enc = string_to_sign.encode('utf-8') hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() sign = urllib.parse.quote_plus(base64.b64encode(hmac_code)) return timestamp, sign ``` 2. **发送消息到外部群** ```python def send_to_external_group(webhook_url, secret, message): timestamp, sign = generate_sign(secret) url = f"{webhook_url}&timestamp={timestamp}&sign={sign}" headers = {"Content-Type": "application/json"} data = { "msgtype": "text", "text": {"content": message} } response = requests.post(url, headers=headers, json=data) return response.json() ``` --- ### **五、完整流程示例** ```python # 配置参数 WEBHOOK_URL = "https://oapi.dingtalk.com/robot/send?access_token=XXX" SECRET = "your_sign_secret" # 机器人加签密钥 FILE_PATH = "/path/to/file.pdf" # 执行流程 access_token = get_access_token(APP_KEY, APP_SECRET) # 企业应用凭证 file_id = upload_to_dingpan(access_token, FILE_PATH) share_link = generate_share_link(access_token, file_id) # 发送到外部群 send_to_external_group(WEBHOOK_URL, SECRET, f"外部群文件分享:{share_link}") ``` --- ### **关键注意事项** 1. **权限双重验证** - 机器人需被添加到目标外部群并启用 - 钉盘文件需设置`企业外可访问`权限[^3] 2. **安全策略** - 加签密钥需保密存储,避免泄露导致消息劫持 - 建议设置IP白名增强安全性[^3] 3. **消息类型扩展** 支持Markdown/卡片消息(需调整`msgtype`和数据结构): ```json { "msgtype": "markdown", "markdown": { "title": "文件通知", "text": "**最新文件**:点击下载 [文件链接]({share_link})" } } ``` --- ### **相关问题** 1. 如何验证Webhook消息是否来自钉钉服务器? 2. 发送Markdown消息时如何避免内容格式错误? 3. 外部群文件分享链接失效后如何自动更新?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值