1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-02-04 12:52:27 +08:00

feat(work): 企业微信接口增加 (#617)

* feat(work): 企业微信接口增加

群机器人 查询成员信息

* 变更

* fix(fix): 修复lint 报错

* fix: 删除注释多余字符
This commit is contained in:
chcthink
2022-09-23 09:14:54 +08:00
committed by GitHub
parent 9ad8981ff0
commit 5e0c31bfa9
7 changed files with 296 additions and 1 deletions

View File

@@ -74,7 +74,9 @@ host: https://qyapi.weixin.qq.com/
| 删除企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/del_contact_way | YES | (r *Client) DelContactWay | MARKWANG | | 删除企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/del_contact_way | YES | (r *Client) DelContactWay | MARKWANG |
## 通讯录管理 ## 通讯录管理
[官方文档](https://developer.work.weixin.qq.com/document/path/95350/90200) [官方文档](https://developer.work.weixin.qq.com/document/path/90193)
### 部门管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 | | 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
|:---------:|------|:----------------------------------------| ---------- | ------------------------------- |----------| |:---------:|------|:----------------------------------------| ---------- | ------------------------------- |----------|
@@ -82,6 +84,22 @@ host: https://qyapi.weixin.qq.com/
| 获取部门成员 | GET | /cgi-bin/user/simplelist | YES | (r *Client) UserSimpleList | MARKWANG | | 获取部门成员 | GET | /cgi-bin/user/simplelist | YES | (r *Client) UserSimpleList | MARKWANG |
======= =======
### 成员管理
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
| -------- | -------- | ----------------- | ---------- | ------------------- | -------- |
| 读取成员 | GET | /cgi-bin/user/get | YES | (r *Client) UserGet | chcthink |
## 群机器人
[官方文档](https://developer.work.weixin.qq.com/document/path/91770)
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
| ---------------- | -------- | --------------------- | ---------- | -------------------------- | -------- |
| 群机器人发送消息 | POST | /cgi-bin/webhook/send | YES | (r *Client) RobotBroadcast | chcthink |
## 应用管理 ## 应用管理
TODO TODO

View File

@@ -9,6 +9,8 @@ import (
const ( const (
// UserSimpleListURL 获取部门成员 // UserSimpleListURL 获取部门成员
UserSimpleListURL = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?access_token=%s&department_id=%d" UserSimpleListURL = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?access_token=%s&department_id=%d"
// UserGetURL 读取成员
UserGetURL = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=%s&userid=%s"
) )
type ( type (
@@ -47,3 +49,87 @@ func (r *Client) UserSimpleList(departmentID int) ([]*UserList, error) {
} }
return result.UserList, nil return result.UserList, nil
} }
// UserGetResponse 获取部门成员响应
type UserGetResponse struct {
util.CommonError
UserID string `json:"userid"` // 成员UserID。对应管理端的帐号企业内必须唯一。不区分大小写长度为1~64个字节第三方应用返回的值为open_userid
Name string `json:"name"` // 成员名称第三方不可获取调用时返回userid以代替name代开发自建应用需要管理员授权才返回对于非第三方创建的成员第三方通讯录应用也不可获取未返回name的情况需要通过通讯录展示组件来展示名字
Department []int `json:"department"` // 成员所属部门id列表仅返回该应用有查看权限的部门id成员授权模式下固定返回根部门id即固定为1。对授权了“组织架构信息”权限的第三方应用返回成员所属的全部部门id
Order []int `json:"order"` // 部门内的排序值默认为0。数量必须和department一致数值越大排序越前面。值范围是[0, 2^32)。成员授权模式下不返回该字段
Position string `json:"position"` // 职务信息;代开发自建应用需要管理员授权才返回;第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取;上游企业不可获取下游企业成员该字段
Mobile string `json:"mobile"` // 手机号码代开发自建应用需要管理员授权且成员oauth2授权获取第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段
Gender string `json:"gender"` // 性别。0表示未定义1表示男性2表示女性。代开发自建应用需要管理员授权且成员oauth2授权获取第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段。注不可获取指返回值0
Email string `json:"email"` // 邮箱代开发自建应用需要管理员授权且成员oauth2授权获取第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段
BizMail string `json:"biz_mail"` // 企业邮箱代开发自建应用需要管理员授权且成员oauth2授权获取第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段
IsLeaderInDept []int `json:"is_leader_in_dept"` // 表示在所在的部门内是否为部门负责人数量与department一致第三方通讯录应用或者授权了“组织架构信息-应用可获取企业的部门组织架构信息-部门负责人”权限的第三方应用可获取;对于非第三方创建的成员,第三方通讯录应用不可获取;上游企业不可获取下游企业成员该字段
DirectLeader []string `json:"direct_leader"` // 直属上级UserID返回在应用可见范围内的直属上级列表最多有五个直属上级第三方通讯录应用或者授权了“组织架构信息-应用可获取可见范围内成员组织架构信息-直属上级”权限的第三方应用可获取;对于非第三方创建的成员,第三方通讯录应用不可获取;上游企业不可获取下游企业成员该字段;代开发自建应用不可获取该字段
Avatar string `json:"avatar"` // 头像url。 代开发自建应用需要管理员授权且成员oauth2授权获取第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段
ThumbAvatar string `json:"thumb_avatar"` // 头像缩略图url。第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段
Telephone string `json:"telephone"` // 座机。代开发自建应用需要管理员授权才返回;第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取;上游企业不可获取下游企业成员该字段
Alias string `json:"alias"` // 别名;第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取;上游企业不可获取下游企业成员该字段
Address string `json:"address"` // 地址。代开发自建应用需要管理员授权且成员oauth2授权获取第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段
OpenUserid string `json:"open_userid"` // 全局唯一。对于同一个服务商不同应用获取到企业内同一个成员的open_userid是相同的最多64个字节。仅第三方应用可获取
MainDepartment int `json:"main_department"` // 主部门,仅当应用对主部门有查看权限时返回。
Extattr struct {
Attrs []struct {
Type int `json:"type"`
Name string `json:"name"`
Text struct {
Value string `json:"value"`
} `json:"text,omitempty"`
Web struct {
URL string `json:"url"`
Title string `json:"title"`
} `json:"web,omitempty"`
} `json:"attrs"`
} `json:"extattr"` // 扩展属性,代开发自建应用需要管理员授权才返回;第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取;上游企业不可获取下游企业成员该字段
Status int `json:"status"` // 激活状态: 1=已激活2=已禁用4=未激活5=退出企业。 已激活代表已激活企业微信或已关注微信插件(原企业号)。未激活代表既未激活企业微信又未关注微信插件(原企业号)。
QrCode string `json:"qr_code"` // 员工个人二维码,扫描可添加为外部联系人(注意返回的是一个url可在浏览器上打开该url以展示二维码)代开发自建应用需要管理员授权且成员oauth2授权获取第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段
ExternalPosition string `json:"external_position"` // 对外职务如果设置了该值则以此作为对外展示的职务否则以position来展示。代开发自建应用需要管理员授权才返回第三方仅通讯录应用可获取对于非第三方创建的成员第三方通讯录应用也不可获取上游企业不可获取下游企业成员该字段
ExternalProfile struct {
ExternalCorpName string `json:"external_corp_name"`
WechatChannels struct {
Nickname string `json:"nickname"`
Status int `json:"status"`
} `json:"wechat_channels"`
ExternalAttr []struct {
Type int `json:"type"`
Name string `json:"name"`
Text struct {
Value string `json:"value"`
} `json:"text,omitempty"`
Web struct {
URL string `json:"url"`
Title string `json:"title"`
} `json:"web,omitempty"`
Miniprogram struct {
Appid string `json:"appid"`
Pagepath string `json:"pagepath"`
Title string `json:"title"`
} `json:"miniprogram,omitempty"`
} `json:"external_attr"`
} `json:"external_profile"` // 成员对外属性,字段详情见对外属性;代开发自建应用需要管理员授权才返回;第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取;上游企业不可获取下游企业成员该字段
}
// UserGet 获取部门成员
// @see https://developer.work.weixin.qq.com/document/path/90196
func (r *Client) UserGet(UserID string) (*UserGetResponse, error) {
var (
accessToken string
err error
)
if accessToken, err = r.GetAccessToken(); err != nil {
return nil, err
}
var response []byte
if response, err = util.HTTPGet(fmt.Sprintf(UserGetURL, accessToken, UserID)); err != nil {
return nil, err
}
result := &UserGetResponse{}
err = util.DecodeWithError(response, result, "UserGet")
if err != nil {
return nil, err
}
return result, nil
}

View File

@@ -17,6 +17,8 @@ type Oauth struct {
var ( var (
// oauthTargetURL 企业微信内跳转地址 // oauthTargetURL 企业微信内跳转地址
oauthTargetURL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect" oauthTargetURL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect"
// oauthTargetURL 企业微信内跳转地址(获取成员的详细信息)
oauthTargetPrivateURL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_privateinfo&agentid=%s&state=STATE#wechat_redirect"
// oauthUserInfoURL 获取用户信息地址 // oauthUserInfoURL 获取用户信息地址
oauthUserInfoURL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=%s&code=%s" oauthUserInfoURL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=%s&code=%s"
// oauthQrContentTargetURL 构造独立窗口登录二维码 // oauthQrContentTargetURL 构造独立窗口登录二维码
@@ -40,6 +42,17 @@ func (ctr *Oauth) GetTargetURL(callbackURL string) string {
) )
} }
// GetTargetPrivateURL 获取个人信息授权地址
func (ctr *Oauth) GetTargetPrivateURL(callbackURL string, agentID string) string {
// url encode
return fmt.Sprintf(
oauthTargetPrivateURL,
ctr.CorpID,
url.QueryEscape(callbackURL),
agentID,
)
}
// GetQrContentTargetURL 构造独立窗口登录二维码 // GetQrContentTargetURL 构造独立窗口登录二维码
func (ctr *Oauth) GetQrContentTargetURL(callbackURL string) string { func (ctr *Oauth) GetQrContentTargetURL(callbackURL string) string {
// url encode // url encode

17
work/robot/client.go Normal file
View File

@@ -0,0 +1,17 @@
package robot
import (
"github.com/silenceper/wechat/v2/work/context"
)
// Client 群聊机器人接口实例
type Client struct {
*context.Context
}
// NewClient 初始化实例
func NewClient(ctx *context.Context) *Client {
return &Client{
ctx,
}
}

29
work/robot/robot.go Normal file
View File

@@ -0,0 +1,29 @@
package robot
import (
"encoding/json"
"fmt"
"github.com/silenceper/wechat/v2/util"
)
const (
// WebhookSendURL 机器人发送群组消息
WebhookSendURL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s"
)
// RobotBroadcast 群机器人消息发送
// @see https://developer.work.weixin.qq.com/document/path/91770
func (r *Client) RobotBroadcast(webhookKey string, options interface{}) (info util.CommonError, err error) {
var data []byte
if data, err = util.PostJSON(fmt.Sprintf(WebhookSendURL, webhookKey), options); err != nil {
return
}
if err = json.Unmarshal(data, &info); err != nil {
return
}
if info.ErrCode != 0 {
return info, err
}
return info, nil
}

126
work/robot/send_option.go Normal file
View File

@@ -0,0 +1,126 @@
package robot
import "github.com/silenceper/wechat/v2/util"
// WebhookSendResponse 机器人发送群组消息响应
type WebhookSendResponse struct {
util.CommonError
}
// WebhookSendTextOption 机器人发送文本消息请求参数
type WebhookSendTextOption struct {
MsgType string `json:"msgtype"` // 消息类型,此时固定为text
Text struct {
Content string `json:"content"` // 文本内容最长不超过2048个字节必须是utf8编码
MentionedList []string `json:"mentioned_list"` // userid的列表提醒群中的指定成员(@某个成员)@all表示提醒所有人如果开发者获取不到userid可以使用mentioned_mobile_list
MentionedMobileList []string `json:"mentioned_mobile_list"` // 手机号列表,提醒手机号对应的群成员(@某个成员)@all表示提醒所有人
} `json:"text"` // 文本消息内容
}
// WebhookSendMarkdownOption 机器人发送markdown消息请求参数
// 支持语法参考 https://developer.work.weixin.qq.com/document/path/91770
type WebhookSendMarkdownOption struct {
MsgType string `json:"msgtype"` // 消息类型,此时固定为markdown
Markdown struct {
Content string `json:"content"` // markdown内容最长不超过4096个字节必须是utf8编码
} `json:"markdown"` // markdown消息内容
}
// WebhookSendImageOption 机器人发送图片消息请求参数
type WebhookSendImageOption struct {
MsgType string `json:"msgtype"` // 消息类型,此时固定为image
Image struct {
Base64 string `json:"base64"` // 图片内容的base64编码
MD5 string `json:"md5"` // 图片内容base64编码前的md5值
} `json:"image"` // 图片消息内容
}
// WebhookSendNewsOption 机器人发送图文消息请求参数
type WebhookSendNewsOption struct {
MsgType string `json:"msgtype"` // 消息类型,此时固定为news
News struct {
Articles []struct {
Title string `json:"title"` // 标题不超过128个字节超过会自动截断
Description string `json:"description"` // 描述不超过512个字节超过会自动截断
URL string `json:"url"` // 点击后跳转的链接
PicURL string `json:"picurl"` // 图文消息的图片链接支持JPG、PNG格式较好的效果为大图 1068*455小图150*150
} `json:"articles"` // 图文消息列表 一个图文消息支持1到8条图文
} `json:"news"` // 图文消息内容
}
// WebhookSendFileOption 机器人发送文件消息请求参数
type WebhookSendFileOption struct {
MsgType string `json:"msgtype"` // 消息类型此时固定为file
File struct {
MediaID string `json:"media_id"` // 文件id通过下文的文件上传接口获取
} `json:"file"` // 文件类型
}
// WebHookSendTempNoticeOption 机器人发送文本通知模版消息请求参数
type WebHookSendTempNoticeOption struct {
MsgType string `json:"msgtype"` // 消息类型此时的消息类型固定为template_card
TemplateCard TemplateCard `json:"template_card"` // 具体的模版卡片参数
}
// TemplateCard 具体的模版卡片参数
type TemplateCard struct {
CardType string `json:"card_type"` // 模版卡片的模版类型文本通知模版卡片的类型为text_notice
Source CardSource `json:"source"` // 卡片来源样式信息,不需要来源样式可不填写
MainTitle CardTitle `json:"main_title"` // 模版卡片的主要内容,包括一级标题和标题辅助信息
EmphasisContent CardTitle `json:"emphasis_content"` // 关键数据样式
QuoteArea CardQuoteArea `json:"quote_area"` // 引用文献样式,建议不与关键数据共用
SubTitleText string `json:"sub_title_text"` // 二级普通文本建议不超过112个字。模版卡片主要内容的一级标题main_title.title和二级普通文本sub_title_text必须有一项填写
HorizontalContentList []CardContent `json:"horizontal_content_list"` // 二级标题+文本列表该字段可为空数组但有数据的话需确认对应字段是否必填列表长度不超过6
JumpList []JumpContent `json:"jump_list"` // 跳转指引样式的列表该字段可为空数组但有数据的话需确认对应字段是否必填列表长度不超过3
CardAction CardAction `json:"card_action"` // 整体卡片的点击跳转事件text_notice模版卡片中该字段为必填项
}
// CardSource 卡片来源样式信息,不需要来源样式可不填写
type CardSource struct {
IconURL string `json:"icon_url"` // 来源图片的url
Desc string `json:"desc"` // 来源图片的描述建议不超过13个字
DescColor int `json:"desc_color"` // 来源文字的颜色目前支持0(默认) 灰色1 黑色2 红色3 绿色
}
// CardTitle 标题和标题辅助信息
type CardTitle struct {
Title string `json:"title"` // 标题建议不超过26个字。模版卡片主要内容的一级标题main_title.title和二级普通文本sub_title_text必须有一项填写
Desc string `json:"desc"` // 标题辅助信息建议不超过30个字
}
// CardQuoteArea 引用文献样式,建议不与关键数据共用
type CardQuoteArea struct {
Type int `json:"type"` // 引用文献样式区域点击事件0或不填代表没有点击事件1 代表跳转url2 代表跳转小程序
URL string `json:"url,omitempty"` // 点击跳转的urlquote_area.type是1时必填
Appid string `json:"appid,omitempty"` // 点击跳转的小程序的appidquote_area.type是2时必填
Pagepath string `json:"pagepath,omitempty"` // 点击跳转的小程序的pagepathquote_area.type是2时选填
Title string `json:"title"` // 引用文献样式的标题
QuoteText string `json:"quote_text"` // 引用文献样式的引用文案
}
// CardContent 二级标题+文本列表该字段可为空数组但有数据的话需确认对应字段是否必填列表长度不超过6
type CardContent struct {
KeyName string `json:"keyname"` // 链接类型0或不填代表是普通文本1 代表跳转url2 代表下载附件3 代表@员工
Value string `json:"value"` // 二级标题建议不超过5个字
Type int `json:"type,omitempty"` // 二级文本如果horizontal_content_list.type是2该字段代表文件名称要包含文件类型建议不超过26个字
URL string `json:"url,omitempty"` // 链接跳转的urlhorizontal_content_list.type是1时必填
MediaID string `json:"media_id,omitempty"` // 附件的media_idhorizontal_content_list.type是2时必填
UserID string `json:"userid,omitempty"` // 被@的成员的useridhorizontal_content_list.type是3时必填
}
// JumpContent 跳转指引样式的列表该字段可为空数组但有数据的话需确认对应字段是否必填列表长度不超过3
type JumpContent struct {
Type int `json:"type"` // 跳转链接类型0或不填代表不是链接1 代表跳转url2 代表跳转小程序
URL string `json:"url,omitempty"` // 跳转链接的urljump_list.type是1时必填
Title string `json:"title"` // 跳转链接样式的文案内容建议不超过13个字
AppID string `json:"appid,omitempty"` // 跳转链接的小程序的appidjump_list.type是2时必填
PagePath string `json:"pagepath,omitempty"` // 跳转链接的小程序的pagepathjump_list.type是2时选填
}
// CardAction 整体卡片的点击跳转事件text_notice模版卡片中该字段为必填项
type CardAction struct {
Type int `json:"type"` // 卡片跳转类型1 代表跳转url2 代表打开小程序。text_notice模版卡片中该字段取值范围为[1,2]
URL string `json:"url,omitempty"` // 跳转事件的urlcard_action.type是1时必填
Appid string `json:"appid,omitempty"` // 跳转事件的小程序的appidcard_action.type是2时必填
PagePath string `json:"pagepath,omitempty"` // 跳转事件的小程序的pagepathcard_action.type是2时选填
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/silenceper/wechat/v2/work/kf" "github.com/silenceper/wechat/v2/work/kf"
"github.com/silenceper/wechat/v2/work/msgaudit" "github.com/silenceper/wechat/v2/work/msgaudit"
"github.com/silenceper/wechat/v2/work/oauth" "github.com/silenceper/wechat/v2/work/oauth"
"github.com/silenceper/wechat/v2/work/robot"
) )
// Work 企业微信 // Work 企业微信
@@ -55,3 +56,8 @@ func (wk *Work) GetExternalContact() *externalcontact.Client {
func (wk *Work) GetAddressList() *addresslist.Client { func (wk *Work) GetAddressList() *addresslist.Client {
return addresslist.NewClient(wk.ctx) return addresslist.NewClient(wk.ctx)
} }
// GetRobot get robot
func (wk *Work) GetRobot() *robot.Client {
return robot.NewClient(wk.ctx)
}