钉钉企业内部机器人开发——绑定群聊信息到机器人

这篇具有很好参考价值的文章主要介绍了钉钉企业内部机器人开发——绑定群聊信息到机器人。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

开发机器人的过程中,需要将用户添加的机器人存储在数据库中,对于企业内部机器人,官方只提供一个token,其他的机器人信息都没有提供。 对于自定义webhook机器人,还多了一个secret,其实,一个机器人应该有如下的信息

type DingRobot struct {
    RobotId            string         `gorm:"primaryKey;foreignKey:RobotId" json:"robot_id"` //机器人的token
    Deleted            gorm.DeletedAt `json:"deleted"` //软删除字段
    Type               string         `json:"type"` //机器人类型,1为企业内部机器人,2为自定义webhook机器人
    TypeDetail         string         `json:"type_detail"`  //具体机器人类型
    ChatBotUserId      string         `json:"chat_bot_user_id"` //加密的机器人id,该字段无用
    Secret             string         `json:"secret"` //如果是自定义成机器人, 则存在此字段
    DingUsers          []DingUser     `json:"ding_users" gorm:"many2many:user_robot"` //机器人@多个人,一个人可以被多个机器人@
    ChatId             string         `json:"chat_id"` //机器人所在的群聊chatId
    OpenConversationID string         `json:"open_conversation_id"`//机器人所在的群聊openConversationID
    Tasks              []Task         `gorm:"foreignKey:RobotId;references:RobotId"` //机器人拥有多个任务
    Name               string         `json:"name"` //机器人的名称
    DingToken          `json:"ding_token" gorm:"-"`
}

其中,DingToken字段中存储的是token,也就是访问钉钉接口的凭证,Task字段是钉钉机器人拥有的定时任务,是我自己封装的,还有一个字段是DingUsers ,也就是说一个钉钉里面存了好多个用户,这些用户就是群成员,存这些用户的目的是为了能够让机器人@群成员。

其中,RobotId就是token,是唯一的,可以当做主键。 而 ChatBotUserId DingUsers ChatId OpenConversationID Name 如何获取获取呢?

上面的问题只针对于企业内部机器人,如果是自定义机器人,则不需存这些字段。

解决方案:

首先,我们知道RobotId,也就是机器人的token,此token是调用机器人的关键信息,我们先把RobotId存储起来

然后我们使用钉钉的调试器

https://open-dev.dingtalk.com/apiExplorer?spm=ding_open_doc.document.0.0.20bf4063FEGqWg#/jsapi?api=biz.chat.chooseConversationByCorpId

输入cropId之后,我们可以在手机上面选择群聊信息,然后可以获取到chatId(chatId必须通过手机扫描二维码授权)和Title(群聊名称,可以作为机器人的名称),然后我们拿着chatId可以获取到openconversationId,然后通过openConverstaionId可以到该群的所有群成员信息(用来实现机器人@群成员)。

上面是大体的思路,经过上面一番操作后,就可以把原来只有一个robotId机企业内部机器人给绑定上其所在的群成员信息和群聊基本信息。

但是,如果想要让用户使用,首先用户肯定不能手动打开钉钉调试器,其次是用户也不知道企业的cropId,所以我们需要使用程序,让该功能简单化,理想的情况是,用户输入机器人RobotId,然后加载二维码,然后用户使用手机选择机器人所在的群聊,之后机器人就和群聊信息已经该群的群成员绑定在一起了。

难点一:

如何让用户扫描二维码?此二维码实时更新,而且被隐藏在一个canvas中,无法获取到。 解决办法是,我们使用chromedp来模拟浏览器操作,直接把cropId放在程序,自动输入即可,至于二维码,直接使用chromedp进行截图,然后存储在数据库中,然后渲染给前端,等待用户扫描即可

难点二:

如何获取所有群成员,钉钉开放的有接口,我们通过二维码扫码获取chatId,然后获取openconversationId,然后在钉钉开放的小程序上面添加一个酷应用,然后把酷应用添加到群聊中,才可以获取到群成员信息,不然,就会出现系统错误(该问题是我请教钉钉技术支持解决的)

部分源代码展示

//获取二维码buf,chatId, title
func (u *DingUser) GetQRCode(c *gin.Context) (buf []byte, chatId, title string, err error) {
    d := data{}
    opts := append(
        chromedp.DefaultExecAllocatorOptions[:],
        chromedp.NoDefaultBrowserCheck, //不检查默认浏览器
        chromedp.Flag("headless", false),
        chromedp.Flag("blink-settings", "imagesEnabled=true"), //开启图像界面,重点是开启这个
        chromedp.Flag("ignore-certificate-errors", true),      //忽略错误
        chromedp.Flag("disable-web-security", true),           //禁用网络安全标志
        chromedp.Flag("disable-extensions", true),             //开启插件支持
        chromedp.Flag("disable-default-apps", true),
        chromedp.NoFirstRun, //设置网站不是首次运行
        chromedp.WindowSize(1921, 1024),
        chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36"), //设置UserAgent
    )
    allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
    //defer cancel()
    print(cancel)

    // 创建上下文实例
    ctx, cancel := chromedp.NewContext(
        allocCtx,
        chromedp.WithLogf(log.Printf),
    )
    //defer cancel()

    // 创建超时上下文
    ctx, cancel = context.WithTimeout(ctx, 10*time.Minute)
    //defer cancel()

    // navigate to a page, wait for an element, click

    // capture screenshot of an element

    // capture entire browser viewport, returning png with quality=90
    var html string

    if err := chromedp.Run(ctx,
        //打开网页
        chromedp.Navigate(`https://open-dev.dingtalk.com/apiExplorer?spm=ding_open_doc.document.0.0.20bf4063FEGqWg#/jsapi?api=biz.chat.chooseConversationByCorpId`),
        //定位登录按钮
        chromedp.Click(`document.querySelector(".ant-btn.ant-btn-primary")`, chromedp.ByJSPath),
        //等二维码出现
        chromedp.WaitVisible(`document.querySelector(".ant-modal")`, chromedp.ByJSPath),
        //截图
        chromedp.ActionFunc(func(ctx context.Context) error {
            // get layout metrics
            _, _, _, _, _, contentSize, err := page.GetLayoutMetrics().Do(ctx)
            if err != nil {
                return err
            }

            width, height := int64(math.Ceil(contentSize.Width)), int64(math.Ceil(contentSize.Height))

            // force viewport emulation
            err = emulation.SetDeviceMetricsOverride(width, height, 1, false).
                WithScreenOrientation(&emulation.ScreenOrientation{
                    Type:  emulation.OrientationTypePortraitPrimary,
                    Angle: 0,
                }).
                Do(ctx)
            if err != nil {
                return err
            }

            // capture screenshot
            buf, err = page.CaptureScreenshot().
                WithQuality(90).
                WithClip(&page.Viewport{
                    X:      contentSize.X,
                    Y:      contentSize.Y,
                    Width:  contentSize.Width,
                    Height: contentSize.Height,
                    Scale:  1,
                }).Do(ctx)
            username, _ := c.Get(global.CtxUserNameKey)
            err = ioutil.WriteFile(fmt.Sprintf("./Screenshot_%s.png", username), buf, 0644)
            if err != nil {
                zap.L().Error("二维码写入失败", zap.Error(err))
            }
            return nil
        }),
        //等待用户扫码连接成功
        chromedp.WaitVisible(`document.querySelector(".connect-info")`, chromedp.ByJSPath),
        //chromedp.SendKeys(`document.querySelector("#corpId")`, "caonima",chromedp.ByJSPath),
        //设置输入框中的值为空
        chromedp.SetValue(`document.querySelector("#corpId")`, "", chromedp.ByJSPath),
        //chromedp.Click(`document.querySelector(".ant-btn.ant-btn-primary")`, chromedp.ByJSPath),
        //chromedp.Clear(`#corpId`,chromedp.ByID),
        //输入正确的值
        chromedp.SendKeys(`document.querySelector("#corpId")`, "输入自己企业的cropId", chromedp.ByJSPath),
        //点击发起调用按钮
        chromedp.Click(`document.querySelector(".ant-btn.ant-btn-primary")`, chromedp.ByJSPath),

        chromedp.WaitVisible(`document.querySelector("#dingapp > div > div > div.api-explorer-wrap > div.api-info > div > div.ant-tabs-content.ant-tabs-content-animated.ant-tabs-top-content > div.ant-tabs-tabpane.ant-tabs-tabpane-active > div.debug-result > div.code-mirror > div.code-content > div > div > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div:nth-child(2) > pre > span > span.cm-tab")`, chromedp.ByJSPath),
        //自定义函数进行爬虫
        chromedp.ActionFunc(func(ctx context.Context) error {
            //b := chromedp.WaitEnabled(`document.querySelector("#dingapp > div > div > div.api-explorer-wrap > div.api-info > div > div.ant-tabs-content.ant-tabs-content-animated.ant-tabs-top-content > div.ant-tabs-tabpane.ant-tabs-tabpane-active > div.debug-result > div.code-mirror > div.code-content > div > div > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre")`, chromedp.ByJSPath)
            //b.Do(ctx)
            a := chromedp.OuterHTML(`document.querySelector("body")`, &html, chromedp.ByJSPath)
            a.Do(ctx)
            dom, err := goquery.NewDocumentFromReader(strings.NewReader(html))
            if err != nil {
                fmt.Println("123", err.Error())
                return err
            }
            var data string
            dom.Find("#dingapp > div > div > div.api-explorer-wrap > div.api-info > div > div.ant-tabs-content.ant-tabs-content-animated.ant-tabs-top-content > div.ant-tabs-tabpane.ant-tabs-tabpane-active > div.debug-result > div.code-mirror > div.code-content > div > div > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre").Each(func(i int, selection *goquery.Selection) {
                data = data + selection.Text()
                selection.Next()
            })
            data = strings.ReplaceAll(data, " ", "")
            data = strings.ReplaceAll(data, "\n", "")
            reader := strings.NewReader(data)
            bytearr, err := ioutil.ReadAll(reader)

            err1 := json.Unmarshal(bytearr, &d)
            if err1 != nil {

            }
            return nil
        }),
    ); err != nil {
        zap.L().Error("使用chromedp失败")
        return nil, "", "", err
    }
    if &d == nil {
        return nil, "", "", err
    }
    return buf, d.Result.ChatId, d.Result.Title, err
}

把图片返回前端代码

User.GET("showQRCode", func(c *gin.Context) {
            username, _ := c.Get(global.CtxUserNameKey)
            c.File(fmt.Sprintf("Screenshot_%s.png", username))
        })

获取openConversationId

type DingGroup struct {
    OpenConversationID string
    ChatID             string
    Name               string
    Token              DingToken
}
func (g *DingGroup) GetOpenConversationID() string {
    client, _err := createClient()
    if _err != nil {
        return g.OpenConversationID
    }

    chatIdToOpenConversationIdHeaders := &dingtalkim_1_0.ChatIdToOpenConversationIdHeaders{}
    chatIdToOpenConversationIdHeaders.XAcsDingtalkAccessToken = tea.String(g.Token.Token)
    tryErr := func() (_e error) {
        defer func() {
            if r := tea.Recover(recover()); r != nil {
                _e = r
            }
        }()
        result, _err := client.ChatIdToOpenConversationIdWithOptions(tea.String(g.ChatID), chatIdToOpenConversationIdHeaders, &util.RuntimeOptions{})
        if _err != nil {
            return _err
        }
        g.OpenConversationID = *(result.Body.OpenConversationId)
        return nil
    }()

    if tryErr != nil {
        var err = &tea.SDKError{}
        if _t, ok := tryErr.(*tea.SDKError); ok {
            err = _t
        } else {
            err.Message = tea.String(tryErr.Error())
        }
        if !tea.BoolValue(util.Empty(err.Code)) && !tea.BoolValue(util.Empty(err.Message)) {
            // err 中含有 code 和 message 属性,可帮助开发定位问题
        }

    }
    return g.OpenConversationID
}

获取机器人所在的群成员文章来源地址https://www.toymoban.com/news/detail-564121.html

type DingRobot struct {
    RobotId            string         `gorm:"primaryKey;foreignKey:RobotId" json:"robot_id"` //机器人的token
    Deleted            gorm.DeletedAt `json:"deleted"`                                       //软删除字段
    Type               string         `json:"type"`                                          //机器人类型,1为企业内部机器人,2为自定义webhook机器人
    TypeDetail         string         `json:"type_detail"`                                   //具体机器人类型
    ChatBotUserId      string         `json:"chat_bot_user_id"`                              //加密的机器人id,该字段无用
    Secret             string         `json:"secret"`                                        //如果是自定义成机器人, 则存在此字段
    DingUserID         string         `json:"ding_user_id"`                                  // 机器人所属用户id
    UserName           string         `json:"user_name"`                                     //机器人所属用户名
    DingUsers          []DingUser     `json:"ding_users" gorm:"many2many:user_robot"`        //机器人@多个人,一个人可以被多个机器人@
    ChatId             string         `json:"chat_id"`                                       //机器人所在的群聊chatId
    OpenConversationID string         `json:"open_conversation_id"`                          //机器人所在的群聊openConversationID
    Tasks              []Task         `gorm:"foreignKey:RobotId;references:RobotId"`         //机器人拥有多个任务
    Name               string         `json:"name"`                                          //机器人的名称
    DingToken          `json:"ding_token" gorm:"-"`
}
//获取机器人所在的群聊的userIdList ,前提是获取到OpenConversationId,获取到OpenConverstaionId的前提是获取到二维码

func (r *DingRobot) GetGroupUserIds() (userIds []string, _err error) {
    //所需参数access_token, OpenConversationId string
    olduserIds := []*string{}
    client, _err := createClient()
    if _err != nil {
        return
    }

    batchQueryGroupMemberHeaders := &dingtalkim_1_0.BatchQueryGroupMemberHeaders{}
    batchQueryGroupMemberHeaders.XAcsDingtalkAccessToken = tea.String(r.DingToken.Token)
    batchQueryGroupMemberRequest := &dingtalkim_1_0.BatchQueryGroupMemberRequest{
        OpenConversationId: tea.String(r.OpenConversationID),
        CoolAppCode:        tea.String("小程序下面的酷应用编码"),
        MaxResults:         tea.Int64(300),
        NextToken:          tea.String("XXXXX"),
    }
    tryErr := func() (_e error) {
        defer func() {
            if r := tea.Recover(recover()); r != nil {
                _e = r
            }
        }()
        result, _err := client.BatchQueryGroupMemberWithOptions(batchQueryGroupMemberRequest, batchQueryGroupMemberHeaders, &util.RuntimeOptions{})
        if _err != nil {
            return _err
        }
        olduserIds = result.Body.MemberUserIds
        return
    }()

    if tryErr != nil {
        var err = &tea.SDKError{}
        if _t, ok := tryErr.(*tea.SDKError); ok {
            err = _t
        } else {
            err.Message = tea.String(tryErr.Error())
        }
        if !tea.BoolValue(util.Empty(err.Code)) && !tea.BoolValue(util.Empty(err.Message)) {
            // err 中含有 code 和 message 属性,可帮助开发定位问题
        }

    }
    userIds = make([]string, len(olduserIds))
    for i, id := range olduserIds {
        userIds[i] = *id
    }
    return
}

到了这里,关于钉钉企业内部机器人开发——绑定群聊信息到机器人的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 企业微信、飞书、钉钉机器人消息发送工具类

    其实你也可以使用RestTemplate,我这里主要是用到了webflux框架,所以需要实例化客户端请求对象

    2024年02月10日
    浏览(15)
  • 钉钉企业机器人单聊消息发送实践-大数据平台(XSailboat)消息中心消息推送

    钉钉企业机器人单聊消息发送实践-大数据平台(XSailboat)消息中心消息推送

    在笔者开发的 大数据平台XSailboat 中有 消息中心 模块,用来全平台的消息收集,整理分拆、订阅发送等功能。消息推送方式支持钉钉群聊、钉钉单聊、短信通知。现记录一下企业机器人消息单聊推送的实现过程。 这是官方的开发文档地址:《机器人发送、查询和撤回单聊消

    2024年01月25日
    浏览(9)
  • Outlook无需API开发连接钉钉群机器人,实现新增会议日程自动发送群消息通知

    Outlook无需API开发连接钉钉群机器人,实现新增会议日程自动发送群消息通知

    Outlook用户使用场景: 在企业中,会议和活动的顺利举行对于业务运转和团队协作至关重要。然而,计划的变动总是无法避免,这可能会导致其他人的计划受到影响,打乱原有的安排。为了解决这个问题,许多企业开始使用各种工具和技术来确保信息的及时传递和更新。其中

    2024年02月09日
    浏览(9)
  • 【钉钉】通过钉钉机器人抓取群消息

    【钉钉】通过钉钉机器人抓取群消息

    一、进入钉钉开放平台中的开发者后台  二、进入应用开发的企业内部开发   三、选择机器人创建应用  四、进入开发管理点击修改  五、配置公网的服务器出口IP以及消息接收地址  六、消息接收代码部分 七、钉钉群@机器人发送消息  后端即可获取到消息,因为已经和我的

    2024年02月12日
    浏览(13)
  • 钉钉机器人推送

     自定义类型机器人文档 自定义机器人发送群消息 - 钉钉开放平台 验证签名文档 自定义机器人的创建和安装 - 钉钉开放平台 php代码实现方式

    2024年01月24日
    浏览(20)
  • java实现钉钉群机器人@机器人获取信息后,机器人回复(机器人接收消息)

    java实现钉钉群机器人@机器人获取信息后,机器人回复(机器人接收消息)

    1.需求 鉴于需要使用钉钉群机器人回复,人们提出的问题,需要识别提出的问题中的,后端进行处理实现对应的业务逻辑 2.实现方式 用户@群机器人,附带提出的问题,后端接收消息后识别消息内容,读取到进行对应的业务逻辑操作后,机器人获取返回的信息后

    2024年02月14日
    浏览(16)
  • 对接钉钉机器人发送钉钉通知

    对接钉钉机器人发送钉钉通知

    实现效果 话不多说 直接上代码 http工具类

    2024年01月25日
    浏览(15)
  • 钉钉机器人推送消息

    钉钉机器人推送消息

    https://open.dingtalk.com/document/ https://open.dingtalk.com/document/resourcedownload/download-server-sdk 就拿java的服务端SDK来说分为两个大的版本:老版本SDK和新版本SDK. https://s01.oss.sonatype.org/?spm=ding_open_doc.document.0.0.4564722fJDmwRj#nexus-search;quick~dingtalk 这种方式只需要在钉钉中拉一个群,人员超过三

    2024年02月09日
    浏览(13)
  • python使用钉钉机器人给钉钉发送消息
  • 钉钉stream机器人-实操详细教程

    钉钉stream机器人-实操详细教程

    支持事件订阅、机器人收消息、卡片回调等功能 优点: 配置简单,不依赖也不需要暴露公网IP,无需向公网开放端口 github官方链接:GitHub - open-dingtalk/dingtalk-stream-sdk-python: Python SDK for DingTalk Stream Mode API, Compared with the webhook mode, it is easier to access the DingTalk chatbot 安装 pip insta

    2024年02月06日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包