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

type DingRobot struct {RobotId            string         `gorm:"primaryKey;foreignKey:RobotId" json:"robot_id"` //机器人的tokenDeleted            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"` //机器人所在的群聊chatIdOpenConversationID string         `json:"open_conversation_id"`//机器人所在的群聊openConversationIDTasks              []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=90var html stringif 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 emulationerr = emulation.SetDeviceMetricsOverride(width, height, 1, false).WithScreenOrientation(&emulation.ScreenOrientation{Type:  emulation.OrientationTypePortraitPrimary,Angle: 0,}).Do(ctx)if err != nil {return err}// capture screenshotbuf, 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 stringdom.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 stringChatID             stringName               stringToken              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
}

获取机器人所在的群成员

type DingRobot struct {RobotId            string         `gorm:"primaryKey;foreignKey:RobotId" json:"robot_id"` //机器人的tokenDeleted            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"`                                  // 机器人所属用户idUserName           string         `json:"user_name"`                                     //机器人所属用户名DingUsers          []DingUser     `json:"ding_users" gorm:"many2many:user_robot"`        //机器人@多个人,一个人可以被多个机器人@ChatId             string         `json:"chat_id"`                                       //机器人所在的群聊chatIdOpenConversationID string         `json:"open_conversation_id"`                          //机器人所在的群聊openConversationIDTasks              []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 stringolduserIds := []*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.MemberUserIdsreturn}()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
}

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

  1. 钉钉通知-调用钉钉发送企业内部消息开发

    首先,我们要明确需求:自己的系统需要发送通知消息到用户,接收消息用户为同一企业内的人员,选用短信可能涉及到费用问题,故可以选用钉钉或者企业微信,在此我使用钉钉进行发送消息. 调用钉钉发送企业内部消息: ...

  2. 钉钉小程序 企业内部应用开发

    综述 什么是钉钉小程序 小程序是一种全新的开发模式,是一种不需要安装即可使用的应用.钉钉小程序以办公社交app钉钉为载体,支持iOS.安卓等多种操作系统部署. 钉钉小程序开发使用AXML(钉钉修改过的 ...

  3. 钉钉机器人调研(群聊机器人)

    钉钉机器人调研(群聊机器人) package demo.test.ding;import com.dingtalk.api.DefaultDingTalkClient; import com.ding ...

  4. chatgpt赋能python:Python微信获取群聊信息:顶级互联的新标准

    Python 微信获取群聊信息:顶级互联的新标准 Python 作为一种开源编程语言,已成为解决各种工程问题的首选方式之一.Python 是一种直观,并且简单易懂的编程语言,同时也非常强大,能够完成各 ...

  5. python itchat库_用python【itchat】库获取群聊信息的正确姿势

    我在<昆明单车骑行群体分析报告(2019版)>中曾用python的itchat模块抓取了我加入的昆明单车群信息,并做了简要分析. 在持续十来天抓取群数据的时候,我发现一个奇怪现象,就是有一 ...

  6. 使用钉钉创建企业内部机器人

    目录 玩转钉钉机器人 创建钉钉机器人: 如何创建钉钉机器人 如何调用机器人api 支持的消息格式 text类型 markdown类型 整体跳转actionCard类型 feedCard类型 使用钉钉机 ...

  7. 钉钉企业内部应用开发php,钉钉企业内部应用开发心得

    基本流程 以企业钉钉管理员身份登陆钉钉开发者平台,进入钉钉开放平台,选择企业内部应用 创建应用,并给应用添加对应logo图片 下载钉钉开放平台提供编译器,在编译器中扫码登陆钉钉账号,选择企业内部应用, ...

  8. 钉钉小程序h5微应用企业内部应用开发

    概述 钉钉在企业应用已经是一件很普遍的事情了,我这讲讲钉钉应用小程序中的h5微应用的开发.本文借鉴了lnexin的文章.主要从两个demo来阐述,第一个是通过依照钉钉官网的描述,html+java实现 ...

  9. 钉钉小程序企业内部应用开发总结

    1,小程序对应的管理后台开发调试 文档链接:https://ding-doc.dingtalk.com/doc#/kn6zg7/qg4y64 需要微应用前端调试工具,直接在钉钉桌面版是没法调试,并且还 ...

最新文章

  1. python工具使用笔记
  2. Matlab中更改fig文件中线宽
  3. 两个python文件怎么联系在一起_【新手求助】怎样把两个程序连接在一起?老师作业,谢谢啦...
  4. Linux下c的进一步学习
  5. 【JavaScript】JS的变量、数组、计算器案例、函数、类、常用对象的方法
  6. Multicast注册中心
  7. 打开word2007总是出现配置进度_实战经验:Word 2007每次打开都弹出正在配置
  8. [振动力学]期中复习
  9. 每日小记2013.3.1
  10. 包括循环和分支的C语言程序,《C语言程序设计》分支和循环的C程序设计.ppt
  11. 【Linux】 C++编程(vim)
  12. connection error mysql_MySQL ConnectionError 安装错误 解决方法
  13. 前缀无歧义编码(PFC)
  14. ps教程|photoshop入门教程|ps高阶段精通教程附百度云资源
  15. python泰坦尼克号数据预测_泰坦尼克号幸存者预测 python 详解
  16. 梦幻西游手游最多人的服务器,梦幻西游手游哪个区人多及区服选择分析
  17. 网站系统开发公司分析
  18. 【ThreeJS基础教程-初识Threejs】1.ThreeJS的HelloWorld
  19. 计算机电源管理最高续航,高效电源管理 vivo TWS Neo最大支持27小时续航
  20. 【论文阅读】NeRF: Representing Scenesas Neural Radiance Fieldsfor View Synthesis

热门文章

  1. javaweb网上订餐系统
  2. 需要一款Mac上好用的音频编辑软件吗?看这里~
  3. 金融银行测试面试题分享
  4. WebGL入门(八)-通过旋转矩阵实现图形(三角形)绕Z轴的旋转
  5. Apache Traffic Server安装配置
  6. Java 报错: Cannot deserialize instance of `com.ruoyi.web.entity.xxxEntity` out of START_ARRAY toke
  7. Go 实现 json 格式定义 http 协议压测脚本
  8. Python3 教程4
  9. 数字经济-新经济指数(2017-2022)31省数字经济测算(2013-2020)两大维度指标
  10. SQL Server with as使用