mirror of
https://github.com/silenceper/wechat.git
synced 2026-03-01 00:35:26 +08:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbda048f62 | |||
| 494082ff4f | |||
| 9c0189340b | |||
| d39615f2fa | |||
| 429219b53f | |||
| 6e80b71cb2 | |||
| ab354c4d03 | |||
| d1d034eb95 | |||
| 1a9dbc493b | |||
| 04559ed4bb | |||
| abd583df01 | |||
| 3cb741ae1a | |||
| da3859261b | |||
| 86ceb6af2f | |||
| 243f8198ae |
+20
-15
@@ -62,21 +62,25 @@ host: https://qyapi.weixin.qq.com/
|
||||
### 客户联系
|
||||
[官方文档](https://developer.work.weixin.qq.com/document/path/92132/92133/92228)
|
||||
|
||||
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
|
||||
|:------------------------:| -------- | :---------------------------------------| ---------- | ------------------------------- |----------|
|
||||
| 获取「联系客户统计」数据 | POST | /cgi-bin/externalcontact/get_user_behavior_data | YES | (r *Client) GetUserBehaviorData | MARKWANG |
|
||||
| 获取「群聊数据统计」数据 (按群主聚合的方式) | POST | /cgi-bin/externalcontact/groupchat/statistic | YES | (r *Client) GetGroupChatStat | MARKWANG |
|
||||
| 获取「群聊数据统计」数据 (按自然日聚合的方式) | POST | /cgi-bin/externalcontact/groupchat/statistic_group_by_day | YES | (r *Client) GetGroupChatStatByDay | MARKWANG |
|
||||
| 配置客户联系「联系我」方式 | POST | /cgi-bin/externalcontact/add_contact_way | YES | (r *Client) AddContactWay | MARKWANG |
|
||||
| 获取企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/get_contact_way | YES | (r *Client) GetContactWay | MARKWANG |
|
||||
| 更新企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/update_contact_way | YES | (r *Client) UpdateContactWay | MARKWANG |
|
||||
| 获取企业已配置的「联系我」列表 | POST | /cgi-bin/externalcontact/list_contact_way | YES | (r *Client) ListContactWay | MARKWANG |
|
||||
| 删除企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/del_contact_way | YES | (r *Client) DelContactWay | MARKWANG |
|
||||
| 创建企业群发 | POST | /cgi-bin/externalcontact/add_msg_template | YES | (r *Client) AddMsgTemplate | MARKWANG |
|
||||
| 获取群发记录列表 | POST | /cgi-bin/externalcontact/get_groupmsg_list_v2 | YES | (r *Client) GetGroupMsgListV2 | MARKWANG |
|
||||
| 获取群发成员发送任务列表 | POST | /cgi-bin/externalcontact/get_groupmsg_task | YES | (r *Client) GetGroupMsgTask | MARKWANG |
|
||||
| 获取企业群发成员执行结果 | POST | /cgi-bin/externalcontact/get_groupmsg_send_result | YES | (r *Client) GetGroupMsgSendResult | MARKWANG |
|
||||
| 发送新客户欢迎语 | POST | /cgi-bin/externalcontact/send_welcome_msg | YES | (r *Client) SendWelcomeMsg | MARKWANG |
|
||||
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
|
||||
|:------------------------:| -------- |:-------------------------------------------------------------| ---------- | ------------------------------- |----------|
|
||||
| 获取「联系客户统计」数据 | POST | /cgi-bin/externalcontact/get_user_behavior_data | YES | (r *Client) GetUserBehaviorData | MARKWANG |
|
||||
| 获取「群聊数据统计」数据 (按群主聚合的方式) | POST | /cgi-bin/externalcontact/groupchat/statistic | YES | (r *Client) GetGroupChatStat | MARKWANG |
|
||||
| 获取「群聊数据统计」数据 (按自然日聚合的方式) | POST | /cgi-bin/externalcontact/groupchat/statistic_group_by_day | YES | (r *Client) GetGroupChatStatByDay | MARKWANG |
|
||||
| 配置客户联系「联系我」方式 | POST | /cgi-bin/externalcontact/add_contact_way | YES | (r *Client) AddContactWay | MARKWANG |
|
||||
| 获取企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/get_contact_way | YES | (r *Client) GetContactWay | MARKWANG |
|
||||
| 更新企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/update_contact_way | YES | (r *Client) UpdateContactWay | MARKWANG |
|
||||
| 获取企业已配置的「联系我」列表 | POST | /cgi-bin/externalcontact/list_contact_way | YES | (r *Client) ListContactWay | MARKWANG |
|
||||
| 删除企业已配置的「联系我」方式 | POST | /cgi-bin/externalcontact/del_contact_way | YES | (r *Client) DelContactWay | MARKWANG |
|
||||
| 创建企业群发 | POST | /cgi-bin/externalcontact/add_msg_template | YES | (r *Client) AddMsgTemplate | MARKWANG |
|
||||
| 获取群发记录列表 | POST | /cgi-bin/externalcontact/get_groupmsg_list_v2 | YES | (r *Client) GetGroupMsgListV2 | MARKWANG |
|
||||
| 获取群发成员发送任务列表 | POST | /cgi-bin/externalcontact/get_groupmsg_task | YES | (r *Client) GetGroupMsgTask | MARKWANG |
|
||||
| 获取企业群发成员执行结果 | POST | /cgi-bin/externalcontact/get_groupmsg_send_result | YES | (r *Client) GetGroupMsgSendResult | MARKWANG |
|
||||
| 发送新客户欢迎语 | POST | /cgi-bin/externalcontact/send_welcome_msg | YES | (r *Client) SendWelcomeMsg | MARKWANG |
|
||||
| 添加入群欢迎语素材 | POST | /cgi-bin/externalcontact/group_welcome_template/add | YES | (r *Client) AddGroupWelcomeTemplate | MARKWANG |
|
||||
| 编辑入群欢迎语素材 | POST | /cgi-bin/externalcontact/group_welcome_template/edit | YES | (r *Client) EditGroupWelcomeTemplate | MARKWANG |
|
||||
| 获取入群欢迎语素材 | POST | /cgi-bin/externalcontact/group_welcome_template/get | YES | (r *Client) GetGroupWelcomeTemplate | MARKWANG |
|
||||
| 删除入群欢迎语素材 | POST | /cgi-bin/externalcontact/group_welcome_template/del | YES | (r *Client) DelGroupWelcomeTemplate | MARKWANG |
|
||||
|
||||
## 通讯录管理
|
||||
[官方文档](https://developer.work.weixin.qq.com/document/path/90193)
|
||||
@@ -87,6 +91,7 @@ host: https://qyapi.weixin.qq.com/
|
||||
|:---------:|------|:----------------------------------------| ---------- | ------------------------------- |----------|
|
||||
| 获取子部门ID列表 | GET | /cgi-bin/department/simplelist | YES | (r *Client) DepartmentSimpleList| MARKWANG |
|
||||
| 获取部门成员 | GET | /cgi-bin/user/simplelist | YES | (r *Client) UserSimpleList | MARKWANG |
|
||||
| 获取成员ID列表 | Post | /cgi-bin/user/list_id | YES | (r *Client) UserListId | MARKWANG |
|
||||
|
||||
|
||||
## 素材管理
|
||||
|
||||
@@ -37,7 +37,7 @@ type ShortLinker struct {
|
||||
// resShortLinker 返回结构体
|
||||
type resShortLinker struct {
|
||||
// 通用错误
|
||||
*util.CommonError
|
||||
util.CommonError
|
||||
|
||||
// 返回的 shortLink
|
||||
Link string `json:"link"`
|
||||
|
||||
@@ -80,6 +80,12 @@ const (
|
||||
EventSubscribeMsgPopupEvent EventType = "subscribe_msg_popup_event"
|
||||
// EventPublishJobFinish 发布任务完成
|
||||
EventPublishJobFinish EventType = "PUBLISHJOBFINISH"
|
||||
// EventWeappAuditSuccess 审核通过
|
||||
EventWeappAuditSuccess EventType = "weapp_audit_success"
|
||||
// EventWeappAuditFail 审核不通过
|
||||
EventWeappAuditFail EventType = "weapp_audit_fail"
|
||||
// EventWeappAuditDelay 审核延后
|
||||
EventWeappAuditDelay EventType = "weapp_audit_delay"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -209,6 +215,13 @@ type MixMessage struct {
|
||||
|
||||
// 设备相关
|
||||
device.MsgDevice
|
||||
|
||||
//小程序审核通知
|
||||
SuccTime int `xml:"SuccTime"` //审核成功时的时间戳
|
||||
FailTime int `xml:"FailTime"` //审核不通过的时间戳
|
||||
DelayTime int `xml:"DelayTime"` //审核延后时的时间戳
|
||||
Reason string `xml:"Reason"` //审核不通过的原因
|
||||
ScreenShot string `xml:"ScreenShot"` //审核不通过的截图示例。用 | 分隔的 media_id 的列表,可通过获取永久素材接口拉取截图内容
|
||||
}
|
||||
|
||||
// SubscribeMsgPopupEvent 订阅通知事件推送的消息体
|
||||
|
||||
@@ -29,11 +29,12 @@ func NewTemplate(context *context.Context) *Template {
|
||||
|
||||
// TemplateMessage 发送的模板消息内容
|
||||
type TemplateMessage struct {
|
||||
ToUser string `json:"touser"` // 必须, 接受者OpenID
|
||||
TemplateID string `json:"template_id"` // 必须, 模版ID
|
||||
URL string `json:"url,omitempty"` // 可选, 用户点击后跳转的URL, 该URL必须处于开发者在公众平台网站中设置的域中
|
||||
Color string `json:"color,omitempty"` // 可选, 整个消息的颜色, 可以不设置
|
||||
Data map[string]*TemplateDataItem `json:"data"` // 必须, 模板数据
|
||||
ToUser string `json:"touser"` // 必须, 接受者OpenID
|
||||
TemplateID string `json:"template_id"` // 必须, 模版ID
|
||||
URL string `json:"url,omitempty"` // 可选, 用户点击后跳转的URL, 该URL必须处于开发者在公众平台网站中设置的域中
|
||||
Color string `json:"color,omitempty"` // 可选, 整个消息的颜色, 可以不设置
|
||||
Data map[string]*TemplateDataItem `json:"data"` // 必须, 模板数据
|
||||
ClientMsgID string `json:"client_msg_id,omitempty"` // 可选, 防重入ID
|
||||
|
||||
MiniProgram struct {
|
||||
AppID string `json:"appid"` // 所需跳转到的小程序appid(该小程序appid必须与发模板消息的公众号是绑定关联关系)
|
||||
|
||||
@@ -64,6 +64,10 @@ type ResAccessToken struct {
|
||||
OpenID string `json:"openid"`
|
||||
Scope string `json:"scope"`
|
||||
|
||||
// IsSnapShotUser 是否为快照页模式虚拟账号,只有当用户是快照页模式虚拟账号时返回,值为1
|
||||
// 公众号文档 https://developers.weixin.qq.com/community/minihome/doc/000c2c34068880629ced91a2f56001
|
||||
IsSnapShotUser int `json:"is_snapshotuser"`
|
||||
|
||||
// UnionID 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。
|
||||
// 公众号文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
|
||||
UnionID string `json:"unionid"`
|
||||
|
||||
+21
-35
@@ -154,134 +154,120 @@ func NewOCR(c *context.Context) *OCR {
|
||||
}
|
||||
|
||||
// IDCard 身份证OCR识别接口
|
||||
func (ocr *OCR) IDCard(path string) (ResIDCard ResIDCard, err error) {
|
||||
func (ocr *OCR) IDCard(path string) (resIDCard ResIDCard, err error) {
|
||||
accessToken, err := ocr.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrIDCardURL, url.QueryEscape(path), accessToken)
|
||||
|
||||
response, err := util.HTTPPost(uri, "")
|
||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrIDCardURL, url.QueryEscape(path), accessToken), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = util.DecodeWithError(response, &ResIDCard, "OCRIDCard")
|
||||
err = util.DecodeWithError(response, &resIDCard, "OCRIDCard")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// BankCard 银行卡OCR识别接口
|
||||
func (ocr *OCR) BankCard(path string) (ResBankCard ResBankCard, err error) {
|
||||
func (ocr *OCR) BankCard(path string) (resBankCard ResBankCard, err error) {
|
||||
accessToken, err := ocr.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrBankCardURL, url.QueryEscape(path), accessToken)
|
||||
|
||||
response, err := util.HTTPPost(uri, "")
|
||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrBankCardURL, url.QueryEscape(path), accessToken), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = util.DecodeWithError(response, &ResBankCard, "OCRBankCard")
|
||||
err = util.DecodeWithError(response, &resBankCard, "OCRBankCard")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Driving 行驶证OCR识别接口
|
||||
func (ocr *OCR) Driving(path string) (ResDriving ResDriving, err error) {
|
||||
func (ocr *OCR) Driving(path string) (resDriving ResDriving, err error) {
|
||||
accessToken, err := ocr.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrDrivingURL, url.QueryEscape(path), accessToken)
|
||||
|
||||
response, err := util.HTTPPost(uri, "")
|
||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrDrivingURL, url.QueryEscape(path), accessToken), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = util.DecodeWithError(response, &ResDriving, "OCRDriving")
|
||||
err = util.DecodeWithError(response, &resDriving, "OCRDriving")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DrivingLicense 驾驶证OCR识别接口
|
||||
func (ocr *OCR) DrivingLicense(path string) (ResDrivingLicense ResDrivingLicense, err error) {
|
||||
func (ocr *OCR) DrivingLicense(path string) (resDrivingLicense ResDrivingLicense, err error) {
|
||||
accessToken, err := ocr.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrDrivingLicenseURL, url.QueryEscape(path), accessToken)
|
||||
|
||||
response, err := util.HTTPPost(uri, "")
|
||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrDrivingLicenseURL, url.QueryEscape(path), accessToken), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = util.DecodeWithError(response, &ResDrivingLicense, "OCRDrivingLicense")
|
||||
err = util.DecodeWithError(response, &resDrivingLicense, "OCRDrivingLicense")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// BizLicense 营业执照OCR识别接口
|
||||
func (ocr *OCR) BizLicense(path string) (ResBizLicense ResBizLicense, err error) {
|
||||
func (ocr *OCR) BizLicense(path string) (resBizLicense ResBizLicense, err error) {
|
||||
accessToken, err := ocr.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrBizLicenseURL, url.QueryEscape(path), accessToken)
|
||||
|
||||
response, err := util.HTTPPost(uri, "")
|
||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrBizLicenseURL, url.QueryEscape(path), accessToken), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = util.DecodeWithError(response, &ResBizLicense, "OCRBizLicense")
|
||||
err = util.DecodeWithError(response, &resBizLicense, "OCRBizLicense")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Common 通用印刷体OCR识别接口
|
||||
func (ocr *OCR) Common(path string) (ResCommon ResCommon, err error) {
|
||||
func (ocr *OCR) Common(path string) (resCommon ResCommon, err error) {
|
||||
accessToken, err := ocr.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrCommonURL, url.QueryEscape(path), accessToken)
|
||||
|
||||
response, err := util.HTTPPost(uri, "")
|
||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrCommonURL, url.QueryEscape(path), accessToken), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = util.DecodeWithError(response, &ResCommon, "OCRCommon")
|
||||
err = util.DecodeWithError(response, &resCommon, "OCRCommon")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// PlateNumber 车牌OCR识别接口
|
||||
func (ocr *OCR) PlateNumber(path string) (ResPlateNumber ResPlateNumber, err error) {
|
||||
func (ocr *OCR) PlateNumber(path string) (resPlateNumber ResPlateNumber, err error) {
|
||||
accessToken, err := ocr.GetAccessToken()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrPlateNumberURL, url.QueryEscape(path), accessToken)
|
||||
|
||||
response, err := util.HTTPPost(uri, "")
|
||||
response, err := util.HTTPPost(fmt.Sprintf("%s?img_url=%s&access_token=%s", ocrPlateNumberURL, url.QueryEscape(path), accessToken), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = util.DecodeWithError(response, &ResPlateNumber, "OCRPlateNumber")
|
||||
err = util.DecodeWithError(response, &resPlateNumber, "OCRPlateNumber")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ func (ctx *Context) RefreshAuthrToken(appid, refreshToken string) (*AuthrAccessT
|
||||
}
|
||||
|
||||
authrTokenKey := "authorizer_access_token_" + appid
|
||||
if err := ctx.Cache.Set(authrTokenKey, ret.AccessToken, time.Minute*80); err != nil {
|
||||
if err := ctx.Cache.Set(authrTokenKey, ret.AccessToken, time.Second*time.Duration(ret.ExpiresIn-30)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package addresslist
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// createTagURL 创建标签
|
||||
createTagURL = "https://qyapi.weixin.qq.com/cgi-bin/tag/create?access_token=%s"
|
||||
// updateTagURL 更新标签名字
|
||||
updateTagURL = "https://qyapi.weixin.qq.com/cgi-bin/tag/update?access_token=%s"
|
||||
// deleteTagURL 删除标签
|
||||
deleteTagURL = "https://qyapi.weixin.qq.com/cgi-bin/tag/delete?access_token=%s&tagid=%d"
|
||||
)
|
||||
|
||||
type (
|
||||
// CreateTagRequest 创建标签请求
|
||||
CreateTagRequest struct {
|
||||
TagName string `json:"tagname"`
|
||||
TagID int `json:"tagid,omitempty"`
|
||||
}
|
||||
// CreateTagResponse 创建标签响应
|
||||
CreateTagResponse struct {
|
||||
util.CommonError
|
||||
TagID int `json:"tagid"`
|
||||
}
|
||||
)
|
||||
|
||||
// CreateTag 创建标签
|
||||
// see https://developer.work.weixin.qq.com/document/path/90210
|
||||
func (r *Client) CreateTag(req *CreateTagRequest) (*CreateTagResponse, error) {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.PostJSON(fmt.Sprintf(createTagURL, accessToken), req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &CreateTagResponse{}
|
||||
if err = util.DecodeWithError(response, result, "CreateTag"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type (
|
||||
// UpdateTagRequest 更新标签名字请求
|
||||
UpdateTagRequest struct {
|
||||
TagID int `json:"tagid"`
|
||||
TagName string `json:"tagname"`
|
||||
}
|
||||
)
|
||||
|
||||
// UpdateTag 更新标签名字
|
||||
// see https://developer.work.weixin.qq.com/document/path/90211
|
||||
func (r *Client) UpdateTag(req *UpdateTagRequest) error {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.PostJSON(fmt.Sprintf(updateTagURL, accessToken), req); err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "UpdateTag")
|
||||
}
|
||||
|
||||
// DeleteTag 删除标签
|
||||
// @see https://developer.work.weixin.qq.com/document/path/90212
|
||||
func (r *Client) DeleteTag(tagID int) error {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.HTTPGet(fmt.Sprintf(deleteTagURL, accessToken, tagID)); err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "DeleteTag")
|
||||
}
|
||||
@@ -11,6 +11,8 @@ const (
|
||||
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"
|
||||
// UserListIDURL 获取成员ID列表
|
||||
UserListIDURL = "https://qyapi.weixin.qq.com/cgi-bin/user/list_id?access_token=%s"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -133,3 +135,43 @@ func (r *Client) UserGet(UserID string) (*UserGetResponse, error) {
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UserListIDRequest 获取成员ID列表请求
|
||||
type UserListIDRequest struct {
|
||||
Cursor string `json:"cursor"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// UserListIDResponse 获取成员ID列表响应
|
||||
type UserListIDResponse struct {
|
||||
util.CommonError
|
||||
NextCursor string `json:"next_cursor"`
|
||||
DeptUser []*DeptUser `json:"dept_user"`
|
||||
}
|
||||
|
||||
// DeptUser 用户-部门关系
|
||||
type DeptUser struct {
|
||||
UserID string `json:"userid"`
|
||||
Department int `json:"department"`
|
||||
}
|
||||
|
||||
// UserListID 获取成员ID列表
|
||||
// see https://developer.work.weixin.qq.com/document/path/96067
|
||||
func (r *Client) UserListID(req *UserListIDRequest) (*UserListIDResponse, error) {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.PostJSON(fmt.Sprintf(UserListIDURL, accessToken), req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &UserListIDResponse{}
|
||||
if err = util.DecodeWithError(response, result, "UserListID"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package externalcontact
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
// 原始回调消息内容
|
||||
type callbackOriginMessage struct {
|
||||
ToUserName string // 企业微信的CorpID,当为第三方套件回调事件时,CorpID的内容为suiteid
|
||||
AgentID string // 接收的应用id,可在应用的设置页面获取
|
||||
Encrypt string // 消息结构体加密后的字符串
|
||||
}
|
||||
|
||||
// EventCallbackMessage 微信客户联系回调消息
|
||||
// https://developer.work.weixin.qq.com/document/path/92130
|
||||
type EventCallbackMessage struct {
|
||||
ToUserName string `json:"to_user_name"`
|
||||
FromUserName string `json:"from_user_name"`
|
||||
CreateTime int64 `json:"create_time"`
|
||||
MsgType string `json:"msg_type"`
|
||||
Event string `json:"event"`
|
||||
ChangeType string `json:"change_type"`
|
||||
UserID string `json:"user_id"`
|
||||
ExternalUserID string `json:"external_user_id"`
|
||||
State string `json:"state"`
|
||||
WelcomeCode string `json:"welcome_code"`
|
||||
}
|
||||
|
||||
// GetCallbackMessage 获取联系客户回调事件中的消息内容
|
||||
func (r *Client) GetCallbackMessage(encryptedMsg []byte) (msg EventCallbackMessage, err error) {
|
||||
var origin callbackOriginMessage
|
||||
if err = xml.Unmarshal(encryptedMsg, &origin); err != nil {
|
||||
return
|
||||
}
|
||||
_, bData, err := util.DecryptMsg(r.CorpID, origin.Encrypt, r.EncodingAESKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = xml.Unmarshal(bData, &msg); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -47,23 +47,23 @@ func (r *Client) GetExternalUserList(userID string) ([]string, error) {
|
||||
// ExternalUserDetailResponse 外部联系人详情响应
|
||||
type ExternalUserDetailResponse struct {
|
||||
util.CommonError
|
||||
ExternalUser
|
||||
ExternalContact ExternalUser `json:"external_contact"`
|
||||
FollowUser []FollowUser `json:"follow_user"`
|
||||
NextCursor string `json:"next_cursor"`
|
||||
}
|
||||
|
||||
// ExternalUser 外部联系人
|
||||
type ExternalUser struct {
|
||||
ExternalUserID string `json:"external_userid"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Type int64 `json:"type"`
|
||||
Gender int64 `json:"gender"`
|
||||
UnionID string `json:"unionid"`
|
||||
Position string `json:"position"`
|
||||
CorpName string `json:"corp_name"`
|
||||
CorpFullName string `json:"corp_full_name"`
|
||||
ExternalProfile string `json:"external_profile"`
|
||||
FollowUser []FollowUser `json:"follow_user"`
|
||||
NextCursor string `json:"next_cursor"`
|
||||
ExternalUserID string `json:"external_userid"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Type int64 `json:"type"`
|
||||
Gender int64 `json:"gender"`
|
||||
UnionID string `json:"unionid"`
|
||||
Position string `json:"position"`
|
||||
CorpName string `json:"corp_name"`
|
||||
CorpFullName string `json:"corp_full_name"`
|
||||
ExternalProfile string `json:"external_profile"`
|
||||
}
|
||||
|
||||
// FollowUser 跟进用户(指企业内部用户)
|
||||
@@ -96,28 +96,34 @@ type WechatChannel struct {
|
||||
}
|
||||
|
||||
// GetExternalUserDetail 获取外部联系人详情
|
||||
func (r *Client) GetExternalUserDetail(externalUserID string, nextCursor ...string) (*ExternalUser, error) {
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92114
|
||||
func (r *Client) GetExternalUserDetail(externalUserID string, nextCursor ...string) (*ExternalUserDetailResponse, error) {
|
||||
accessToken, err := r.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.HTTPGet(fmt.Sprintf("%s?access_token=%v&external_userid=%v&cursor=%v", FetchExternalContactUserDetailURL, accessToken, externalUserID, nextCursor))
|
||||
var cursor string
|
||||
if len(nextCursor) > 0 {
|
||||
cursor = nextCursor[0]
|
||||
}
|
||||
response, err = util.HTTPGet(fmt.Sprintf("%s?access_token=%v&external_userid=%v&cursor=%v", FetchExternalContactUserDetailURL, accessToken, externalUserID, cursor))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result ExternalUserDetailResponse
|
||||
err = util.DecodeWithError(response, &result, "get_external_user_detail")
|
||||
result := &ExternalUserDetailResponse{}
|
||||
err = util.DecodeWithError(response, result, "get_external_user_detail")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result.ExternalUser, nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// BatchGetExternalUserDetailsRequest 批量获取外部联系人详情请求
|
||||
type BatchGetExternalUserDetailsRequest struct {
|
||||
UserIDList []string `json:"userid_list"`
|
||||
Cursor string `json:"cursor"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
// ExternalUserDetailListResponse 批量获取外部联系人详情响应
|
||||
@@ -161,6 +167,7 @@ type FollowInfo struct {
|
||||
}
|
||||
|
||||
// BatchGetExternalUserDetails 批量获取外部联系人详情
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92994
|
||||
func (r *Client) BatchGetExternalUserDetails(request BatchGetExternalUserDetailsRequest) ([]ExternalUserForBatch, error) {
|
||||
accessToken, err := r.GetAccessToken()
|
||||
if err != nil {
|
||||
@@ -195,6 +202,7 @@ type UpdateUserRemarkRequest struct {
|
||||
}
|
||||
|
||||
// UpdateUserRemark 修改客户备注信息
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92115
|
||||
func (r *Client) UpdateUserRemark(request UpdateUserRemarkRequest) error {
|
||||
accessToken, err := r.GetAccessToken()
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package externalcontact
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
// OpengIDToChatIDURL 客户群opengid转换URL
|
||||
const OpengIDToChatIDURL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/opengid_to_chatid"
|
||||
|
||||
type (
|
||||
//GroupChatListRequest 获取客户群列表的请求参数
|
||||
GroupChatListRequest struct {
|
||||
StatusFilter int `json:"status_filter"` // 非必填 客户群跟进状态过滤。0 - 所有列表(即不过滤) 1 - 离职待继承 2 - 离职继承中 3 - 离职继承完成
|
||||
OwnerFilter OwnerFilter `json:"owner_filter"` //非必填 群主过滤。如果不填,表示获取应用可见范围内全部群主的数据(但是不建议这么用,如果可见范围人数超过1000人,为了防止数据包过大,会报错 81017)
|
||||
Cursor string `json:"cursor"` //非必填 用于分页查询的游标,字符串类型,由上一次调用返回,首次调用不填
|
||||
Limit int `json:"limit"` //必填 分页,预期请求的数据量,取值范围 1 ~ 1000
|
||||
}
|
||||
|
||||
//GroupChatList 客户群列表
|
||||
GroupChatList struct {
|
||||
ChatID string `json:"chat_id"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
//GroupChatListResponse 获取客户群列表的返回值
|
||||
GroupChatListResponse struct {
|
||||
util.CommonError
|
||||
GroupChatList []GroupChatList `json:"group_chat_list"`
|
||||
NextCursor string `json:"next_cursor"` //游标
|
||||
}
|
||||
)
|
||||
|
||||
// GetGroupChatList 获取客户群列表
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92120
|
||||
func (r *Client) GetGroupChatList(req *GroupChatListRequest) (*GroupChatListResponse, error) {
|
||||
accessToken, err := r.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.PostJSON(fmt.Sprintf("%s/list?access_token=%s", GroupChatURL, accessToken), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &GroupChatListResponse{}
|
||||
if err = util.DecodeWithError(response, result, "GetGroupChatList"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type (
|
||||
//GroupChatDetailRequest 客户群详情 请求参数
|
||||
GroupChatDetailRequest struct {
|
||||
ChatID string `json:"chat_id"`
|
||||
NeedName int `json:"need_name"`
|
||||
}
|
||||
//Invitor 邀请者
|
||||
Invitor struct {
|
||||
UserID string `json:"userid"` //邀请者的userid
|
||||
}
|
||||
//GroupChatMember 群成员
|
||||
GroupChatMember struct {
|
||||
UserID string `json:"userid"` //群成员id
|
||||
Type int `json:"type"` //成员类型。 1 - 企业成员 2 - 外部联系人
|
||||
JoinTime int `json:"join_time"` //入群时间
|
||||
JoinScene int `json:"join_scene"` //入群方式 1 - 由群成员邀请入群(直接邀请入群) 2 - 由群成员邀请入群(通过邀请链接入群) 3 - 通过扫描群二维码入群
|
||||
Invitor Invitor `json:"invitor,omitempty"` //邀请者。目前仅当是由本企业内部成员邀请入群时会返回该值
|
||||
GroupNickname string `json:"group_nickname"` //在群里的昵称
|
||||
Name string `json:"name"` //名字。仅当 need_name = 1 时返回 如果是微信用户,则返回其在微信中设置的名字 如果是企业微信联系人,则返回其设置对外展示的别名或实名
|
||||
UnionID string `json:"unionid,omitempty"` //外部联系人在微信开放平台的唯一身份标识(微信unionid),通过此字段企业可将外部联系人与公众号/小程序用户关联起来。仅当群成员类型是微信用户(包括企业成员未添加好友),且企业绑定了微信开发者ID有此字段(查看绑定方法)。第三方不可获取,上游企业不可获取下游企业客户的unionid字段
|
||||
}
|
||||
//GroupChatAdmin 群管理员
|
||||
GroupChatAdmin struct {
|
||||
UserID string `json:"userid"` //群管理员userid
|
||||
}
|
||||
//GroupChat 客户群详情
|
||||
GroupChat struct {
|
||||
ChatID string `json:"chat_id"` //客户群ID
|
||||
Name string `json:"name"` //群名
|
||||
Owner string `json:"owner"` //群主ID
|
||||
CreateTime int `json:"create_time"` //群的创建时间
|
||||
Notice string `json:"notice"` //群公告
|
||||
MemberList []GroupChatMember `json:"member_list"` //群成员列表
|
||||
AdminList []GroupChatAdmin `json:"admin_list"` //群管理员列表
|
||||
}
|
||||
//GroupChatDetailResponse 客户群详情 返回值
|
||||
GroupChatDetailResponse struct {
|
||||
util.CommonError
|
||||
GroupChat GroupChat `json:"group_chat"` //客户群详情
|
||||
}
|
||||
)
|
||||
|
||||
// GetGroupChatDetail 获取客户群详情
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92122
|
||||
func (r *Client) GetGroupChatDetail(req *GroupChatDetailRequest) (*GroupChatDetailResponse, error) {
|
||||
accessToken, err := r.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.PostJSON(fmt.Sprintf("%s/get?access_token=%s", GroupChatURL, accessToken), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &GroupChatDetailResponse{}
|
||||
if err = util.DecodeWithError(response, result, "GetGroupChatDetail"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type (
|
||||
//OpengIDToChatIDRequest 客户群opengid转换 请求参数
|
||||
OpengIDToChatIDRequest struct {
|
||||
OpengID string `json:"opengid"`
|
||||
}
|
||||
//OpengIDToChatIDResponse 客户群opengid转换 返回值
|
||||
OpengIDToChatIDResponse struct {
|
||||
util.CommonError
|
||||
ChatID string `json:"chat_id"` //客户群ID
|
||||
}
|
||||
)
|
||||
|
||||
// OpengIDToChatID 客户群opengid转换
|
||||
// @see https://developer.work.weixin.qq.com/document/path/94828
|
||||
func (r *Client) OpengIDToChatID(req *OpengIDToChatIDRequest) (*OpengIDToChatIDResponse, error) {
|
||||
accessToken, err := r.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
response, err = util.PostJSON(fmt.Sprintf("%s?access_token=%s", OpengIDToChatIDURL, accessToken), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &OpengIDToChatIDResponse{}
|
||||
if err = util.DecodeWithError(response, result, "GetGroupChatDetail"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package externalcontact
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
// GroupChatURL 客户群
|
||||
const GroupChatURL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat"
|
||||
|
||||
type (
|
||||
// AddJoinWayRequest 添加群配置请求参数
|
||||
AddJoinWayRequest struct {
|
||||
Scene int `json:"scene"` // 必填 1 - 群的小程序插件,2 - 群的二维码插件
|
||||
Remark string `json:"remark"` //非必填 联系方式的备注信息,用于助记,超过30个字符将被截断
|
||||
AutoCreateRoom int `json:"auto_create_room"` //非必填 当群满了后,是否自动新建群。0-否;1-是。 默认为1
|
||||
RoomBaseName string `json:"room_base_name"` //非必填 自动建群的群名前缀,当auto_create_room为1时有效。最长40个utf8字符
|
||||
RoomBaseID int `json:"room_base_id"` //非必填 自动建群的群起始序号,当auto_create_room为1时有效
|
||||
ChatIDList []string `json:"chat_id_list"` //必填 使用该配置的客户群ID列表,支持5个。见客户群ID获取方法
|
||||
State string `json:"state"` //非必填 企业自定义的state参数,用于区分不同的入群渠道。不超过30个UTF-8字符
|
||||
}
|
||||
|
||||
// AddJoinWayResponse 添加群配置返回值
|
||||
AddJoinWayResponse struct {
|
||||
util.CommonError
|
||||
ConfigID string `json:"config_id"`
|
||||
}
|
||||
)
|
||||
|
||||
// AddJoinWay 加入群聊
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92229
|
||||
func (r *Client) AddJoinWay(req *AddJoinWayRequest) (*AddJoinWayResponse, error) {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
response []byte
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err = util.PostJSON(fmt.Sprintf("%s/add_join_way?access_token=%s", GroupChatURL, accessToken), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &AddJoinWayResponse{}
|
||||
if err = util.DecodeWithError(response, result, "AddJoinWay"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type (
|
||||
//JoinWayConfigRequest 获取或删除群配置的请求参数
|
||||
JoinWayConfigRequest struct {
|
||||
ConfigID string `json:"config_id"`
|
||||
}
|
||||
|
||||
//JoinWay 群配置
|
||||
JoinWay struct {
|
||||
ConfigID string `json:"config_id"`
|
||||
Scene int `json:"scene"`
|
||||
Remark string `json:"remark"`
|
||||
AutoCreateRoom int `json:"auto_create_room"`
|
||||
RoomBaseName string `json:"room_base_name"`
|
||||
RoomBaseID int `json:"room_base_id"`
|
||||
ChatIDList []string `json:"chat_id_list"`
|
||||
QrCode string `json:"qr_code"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
//GetJoinWayResponse 获取群配置的返回值
|
||||
GetJoinWayResponse struct {
|
||||
util.CommonError
|
||||
JoinWay JoinWay `json:"join_way"`
|
||||
}
|
||||
)
|
||||
|
||||
// GetJoinWay 获取客户群进群方式配置
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92229
|
||||
func (r *Client) GetJoinWay(req *JoinWayConfigRequest) (*GetJoinWayResponse, error) {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
response []byte
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err = util.PostJSON(fmt.Sprintf("%s/get_join_way?access_token=%s", GroupChatURL, accessToken), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &GetJoinWayResponse{}
|
||||
if err = util.DecodeWithError(response, result, "GetJoinWay"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdateJoinWayRequest 更新群配置的请求参数
|
||||
type UpdateJoinWayRequest struct {
|
||||
ConfigID string `json:"config_id"`
|
||||
Scene int `json:"scene"` // 必填 1 - 群的小程序插件,2 - 群的二维码插件
|
||||
Remark string `json:"remark"` //非必填 联系方式的备注信息,用于助记,超过30个字符将被截断
|
||||
AutoCreateRoom int `json:"auto_create_room"` //非必填 当群满了后,是否自动新建群。0-否;1-是。 默认为1
|
||||
RoomBaseName string `json:"room_base_name"` //非必填 自动建群的群名前缀,当auto_create_room为1时有效。最长40个utf8字符
|
||||
RoomBaseID int `json:"room_base_id"` //非必填 自动建群的群起始序号,当auto_create_room为1时有效
|
||||
ChatIDList []string `json:"chat_id_list"` //必填 使用该配置的客户群ID列表,支持5个。见客户群ID获取方法
|
||||
State string `json:"state"` //非必填 企业自定义的state参数,用于区分不同的入群渠道。不超过30个UTF-8字符
|
||||
}
|
||||
|
||||
// UpdateJoinWay 更新客户群进群方式配置
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92229
|
||||
func (r *Client) UpdateJoinWay(req *UpdateJoinWayRequest) error {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
response []byte
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
response, err = util.PostJSON(fmt.Sprintf("%s/update_join_way?access_token=%s", GroupChatURL, accessToken), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "UpdateJoinWay")
|
||||
}
|
||||
|
||||
// DelJoinWay 删除客户群进群方式配置
|
||||
// @see https://developer.work.weixin.qq.com/document/path/92229
|
||||
func (r *Client) DelJoinWay(req *JoinWayConfigRequest) error {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
response []byte
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
response, err = util.PostJSON(fmt.Sprintf("%s/del_join_way?access_token=%s", GroupChatURL, accessToken), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return util.DecodeWithCommonError(response, "DelJoinWay")
|
||||
}
|
||||
@@ -17,6 +17,14 @@ const (
|
||||
GetGroupMsgSendResultURL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_groupmsg_send_result?access_token=%s"
|
||||
// SendWelcomeMsgURL 发送新客户欢迎语
|
||||
SendWelcomeMsgURL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/send_welcome_msg?access_token=%s"
|
||||
// AddGroupWelcomeTemplateURL 添加入群欢迎语素材
|
||||
AddGroupWelcomeTemplateURL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/group_welcome_template/add?access_token=%s"
|
||||
// EditGroupWelcomeTemplateURL 编辑入群欢迎语素材
|
||||
EditGroupWelcomeTemplateURL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/group_welcome_template/edit?access_token=%s"
|
||||
// GetGroupWelcomeTemplateURL 获取入群欢迎语素材
|
||||
GetGroupWelcomeTemplateURL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/group_welcome_template/get?access_token=%s"
|
||||
// DelGroupWelcomeTemplateURL 删除入群欢迎语素材
|
||||
DelGroupWelcomeTemplateURL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/group_welcome_template/del?access_token=%s"
|
||||
)
|
||||
|
||||
// AddMsgTemplateRequest 创建企业群发请求
|
||||
@@ -268,3 +276,149 @@ func (r *Client) SendWelcomeMsg(req *SendWelcomeMsgRequest) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddGroupWelcomeTemplateRequest 添加入群欢迎语素材请求
|
||||
type AddGroupWelcomeTemplateRequest struct {
|
||||
Text MsgText `json:"text"`
|
||||
Image AttachmentImg `json:"image"`
|
||||
Link AttachmentLink `json:"link"`
|
||||
MiniProgram AttachmentMiniProgram `json:"miniprogram"`
|
||||
File AttachmentFile `json:"file"`
|
||||
Video AttachmentVideo `json:"video"`
|
||||
AgentID int `json:"agentid"`
|
||||
Notify int `json:"notify"`
|
||||
}
|
||||
|
||||
// AddGroupWelcomeTemplateResponse 添加入群欢迎语素材响应
|
||||
type AddGroupWelcomeTemplateResponse struct {
|
||||
util.CommonError
|
||||
TemplateID string `json:"template_id"`
|
||||
}
|
||||
|
||||
// AddGroupWelcomeTemplate 添加入群欢迎语素材
|
||||
// see https://developer.work.weixin.qq.com/document/path/92366#%E6%B7%BB%E5%8A%A0%E5%85%A5%E7%BE%A4%E6%AC%A2%E8%BF%8E%E8%AF%AD%E7%B4%A0%E6%9D%90
|
||||
func (r *Client) AddGroupWelcomeTemplate(req *AddGroupWelcomeTemplateRequest) (*AddGroupWelcomeTemplateResponse, error) {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.PostJSON(fmt.Sprintf(AddGroupWelcomeTemplateURL, accessToken), req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &AddGroupWelcomeTemplateResponse{}
|
||||
if err = util.DecodeWithError(response, result, "AddGroupWelcomeTemplate"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// EditGroupWelcomeTemplateRequest 编辑入群欢迎语素材请求
|
||||
type EditGroupWelcomeTemplateRequest struct {
|
||||
TemplateID string `json:"template_id"`
|
||||
Text MsgText `json:"text"`
|
||||
Image AttachmentImg `json:"image"`
|
||||
Link AttachmentLink `json:"link"`
|
||||
MiniProgram AttachmentMiniProgram `json:"miniprogram"`
|
||||
File AttachmentFile `json:"file"`
|
||||
Video AttachmentVideo `json:"video"`
|
||||
AgentID int `json:"agentid"`
|
||||
}
|
||||
|
||||
// EditGroupWelcomeTemplateResponse 编辑入群欢迎语素材响应
|
||||
type EditGroupWelcomeTemplateResponse struct {
|
||||
util.CommonError
|
||||
}
|
||||
|
||||
// EditGroupWelcomeTemplate 编辑入群欢迎语素材
|
||||
// see https://developer.work.weixin.qq.com/document/path/92366#%E7%BC%96%E8%BE%91%E5%85%A5%E7%BE%A4%E6%AC%A2%E8%BF%8E%E8%AF%AD%E7%B4%A0%E6%9D%90
|
||||
func (r *Client) EditGroupWelcomeTemplate(req *EditGroupWelcomeTemplateRequest) error {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.PostJSON(fmt.Sprintf(EditGroupWelcomeTemplateURL, accessToken), req); err != nil {
|
||||
return err
|
||||
}
|
||||
result := &EditGroupWelcomeTemplateResponse{}
|
||||
if err = util.DecodeWithError(response, result, "EditGroupWelcomeTemplate"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetGroupWelcomeTemplateRequest 获取入群欢迎语素材请求
|
||||
type GetGroupWelcomeTemplateRequest struct {
|
||||
TemplateID string `json:"template_id"`
|
||||
}
|
||||
|
||||
// GetGroupWelcomeTemplateResponse 获取入群欢迎语素材响应
|
||||
type GetGroupWelcomeTemplateResponse struct {
|
||||
util.CommonError
|
||||
Text MsgText `json:"text"`
|
||||
Image AttachmentImg `json:"image"`
|
||||
Link AttachmentLink `json:"link"`
|
||||
MiniProgram AttachmentMiniProgram `json:"miniprogram"`
|
||||
File AttachmentFile `json:"file"`
|
||||
Video AttachmentVideo `json:"video"`
|
||||
}
|
||||
|
||||
// GetGroupWelcomeTemplate 获取入群欢迎语素材
|
||||
// see https://developer.work.weixin.qq.com/document/path/92366#%E8%8E%B7%E5%8F%96%E5%85%A5%E7%BE%A4%E6%AC%A2%E8%BF%8E%E8%AF%AD%E7%B4%A0%E6%9D%90
|
||||
func (r *Client) GetGroupWelcomeTemplate(req *GetGroupWelcomeTemplateRequest) (*GetGroupWelcomeTemplateResponse, error) {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.PostJSON(fmt.Sprintf(GetGroupWelcomeTemplateURL, accessToken), req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &GetGroupWelcomeTemplateResponse{}
|
||||
if err = util.DecodeWithError(response, result, "GetGroupWelcomeTemplate"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// DelGroupWelcomeTemplateRequest 删除入群欢迎语素材请求
|
||||
type DelGroupWelcomeTemplateRequest struct {
|
||||
TemplateID string `json:"template_id"`
|
||||
AgentID int `json:"agentid"`
|
||||
}
|
||||
|
||||
// DelGroupWelcomeTemplateResponse 删除入群欢迎语素材响应
|
||||
type DelGroupWelcomeTemplateResponse struct {
|
||||
util.CommonError
|
||||
}
|
||||
|
||||
// DelGroupWelcomeTemplate 删除入群欢迎语素材
|
||||
// see https://developer.work.weixin.qq.com/document/path/92366#%E5%88%A0%E9%99%A4%E5%85%A5%E7%BE%A4%E6%AC%A2%E8%BF%8E%E8%AF%AD%E7%B4%A0%E6%9D%90
|
||||
func (r *Client) DelGroupWelcomeTemplate(req *DelGroupWelcomeTemplateRequest) error {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.PostJSON(fmt.Sprintf(DelGroupWelcomeTemplateURL, accessToken), req); err != nil {
|
||||
return err
|
||||
}
|
||||
result := &DelGroupWelcomeTemplateResponse{}
|
||||
if err = util.DecodeWithError(response, result, "DelGroupWelcomeTemplate"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import (
|
||||
const (
|
||||
// UploadImgURL 上传图片
|
||||
UploadImgURL = "https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg?access_token=%s"
|
||||
|
||||
// UploadTempFile 上传临时素材
|
||||
UploadTempFile = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s"
|
||||
)
|
||||
|
||||
// UploadImgResponse 上传图片响应
|
||||
@@ -17,6 +20,14 @@ type UploadImgResponse struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// UploadTempFileResponse 上传临时素材响应
|
||||
type UploadTempFileResponse struct {
|
||||
util.CommonError
|
||||
MediaID string `json:"media_id"`
|
||||
CreateAt string `json:"created_at"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// UploadImg 上传图片
|
||||
// @see https://developer.work.weixin.qq.com/document/path/90256
|
||||
func (r *Client) UploadImg(filename string) (*UploadImgResponse, error) {
|
||||
@@ -37,3 +48,25 @@ func (r *Client) UploadImg(filename string) (*UploadImgResponse, error) {
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UploadTempFile 上传临时素材
|
||||
// @see https://developer.work.weixin.qq.com/document/path/90253
|
||||
// @mediaType 媒体文件类型,分别有图片(image)、语音(voice)、视频(video),普通文件(file)
|
||||
func (r *Client) UploadTempFile(filename string, mediaType string) (*UploadTempFileResponse, error) {
|
||||
var (
|
||||
accessToken string
|
||||
err error
|
||||
)
|
||||
if accessToken, err = r.GetAccessToken(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []byte
|
||||
if response, err = util.PostFile("media", filename, fmt.Sprintf(UploadTempFile, accessToken, mediaType)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &UploadTempFileResponse{}
|
||||
if err = util.DecodeWithError(response, result, "UploadTempFile"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Package message 消息推送,实现企业微信消息推送相关接口:https://developer.work.weixin.qq.com/document/path/90235
|
||||
package message
|
||||
|
||||
import (
|
||||
"github.com/silenceper/wechat/v2/work/context"
|
||||
)
|
||||
|
||||
// Client 消息推送接口实例
|
||||
type Client struct {
|
||||
*context.Context
|
||||
}
|
||||
|
||||
// NewClient 初始化实例
|
||||
func NewClient(ctx *context.Context) *Client {
|
||||
return &Client{ctx}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/silenceper/wechat/v2/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// 发送应用消息的接口地址
|
||||
sendURL = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s"
|
||||
)
|
||||
|
||||
type (
|
||||
// SendRequestCommon 发送应用消息请求公共参数
|
||||
SendRequestCommon struct {
|
||||
// 指定接收消息的成员,成员ID列表(多个接收者用‘|’分隔,最多支持1000个)。 特殊情况:指定为"@all",则向该企业应用的全部成员发送
|
||||
ToUser string `json:"touser"`
|
||||
// 指定接收消息的部门,部门ID列表,多个接收者用‘|’分隔,最多支持100个。 当touser为"@all"时忽略本参数
|
||||
ToParty string `json:"toparty"`
|
||||
// 指定接收消息的标签,标签ID列表,多个接收者用‘|’分隔,最多支持100个。 当touser为"@all"时忽略本参数
|
||||
ToTag string `json:"totag"`
|
||||
// 消息类型,此时固定为:text
|
||||
MsgType string `json:"msgtype"`
|
||||
// 企业应用的id,整型。企业内部开发,可在应用的设置页面查看;第三方服务商,可通过接口 获取企业授权信息 获取该参数值
|
||||
AgentID string `json:"agentid"`
|
||||
// 表示是否是保密消息,0表示可对外分享,1表示不能分享且内容显示水印,默认为0
|
||||
Safe int `json:"safe"`
|
||||
// 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。
|
||||
EnableIDTrans int `json:"enable_id_trans"`
|
||||
// 表示是否开启重复消息检查,0表示否,1表示是,默认0
|
||||
EnableDuplicateCheck int `json:"enable_duplicate_check"`
|
||||
// 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时
|
||||
DuplicateCheckInterval int `json:"duplicate_check_interval"`
|
||||
}
|
||||
// SendResponse 发送应用消息响应参数
|
||||
SendResponse struct {
|
||||
util.CommonError
|
||||
InvalidUser string `json:"invaliduser"` // 不合法的userid,不区分大小写,统一转为小写
|
||||
InvalidParty string `json:"invalidparty"` // 不合法的partyid
|
||||
InvalidTag string `json:"invalidtag"` // 不合法的标签id
|
||||
UnlicensedUser string `json:"unlicenseduser"` // 没有基础接口许可(包含已过期)的userid
|
||||
MsgID string `json:"msgid"` // 消息id
|
||||
ResponseCode string `json:"response_code"`
|
||||
}
|
||||
|
||||
// SendTextRequest 发送文本消息的请求
|
||||
SendTextRequest struct {
|
||||
*SendRequestCommon
|
||||
Text TextField `json:"text"`
|
||||
}
|
||||
// TextField 文本消息参数
|
||||
TextField struct {
|
||||
// 消息内容,最长不超过2048个字节,超过将截断(支持id转译)
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// SendImageRequest 发送图片消息的请求
|
||||
SendImageRequest struct {
|
||||
*SendRequestCommon
|
||||
Image ImageField `json:"image"`
|
||||
}
|
||||
// ImageField 图片消息参数
|
||||
ImageField struct {
|
||||
// 图片媒体文件id,可以调用上传临时素材接口获取
|
||||
MediaID string `json:"media_id"`
|
||||
}
|
||||
|
||||
// SendVoiceRequest 发送语音消息的请求
|
||||
SendVoiceRequest struct {
|
||||
*SendRequestCommon
|
||||
Voice VoiceField `json:"voice"`
|
||||
}
|
||||
// VoiceField 语音消息参数
|
||||
VoiceField struct {
|
||||
// 语音文件id,可以调用上传临时素材接口获取
|
||||
MediaID string `json:"media_id"`
|
||||
}
|
||||
)
|
||||
|
||||
// Send 发送应用消息
|
||||
// @desc 实现企业微信发送应用消息接口:https://developer.work.weixin.qq.com/document/path/90236
|
||||
func (r *Client) Send(apiName string, request interface{}) (*SendResponse, error) {
|
||||
// 获取accessToken
|
||||
accessToken, err := r.GetAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 请求参数转 JSON 格式
|
||||
jsonData, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 发起http请求
|
||||
response, err := util.HTTPPost(fmt.Sprintf(sendURL, accessToken), string(jsonData))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 按照结构体解析返回值
|
||||
result := &SendResponse{}
|
||||
if err = util.DecodeWithError(response, result, apiName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 返回数据
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SendText 发送文本消息
|
||||
func (r *Client) SendText(request SendTextRequest) (*SendResponse, error) {
|
||||
// 发送文本消息MsgType参数固定为:text
|
||||
request.MsgType = "text"
|
||||
return r.Send("MessageSendText", request)
|
||||
}
|
||||
|
||||
// SendImage 发送图片消息
|
||||
func (r *Client) SendImage(request SendImageRequest) (*SendResponse, error) {
|
||||
// 发送图片消息MsgType参数固定为:image
|
||||
request.MsgType = "image"
|
||||
return r.Send("MessageSendImage", request)
|
||||
}
|
||||
|
||||
// SendVoice 发送语音消息
|
||||
func (r *Client) SendVoice(request SendVoiceRequest) (*SendResponse, error) {
|
||||
// 发送语音消息MsgType参数固定为:voice
|
||||
request.MsgType = "voice"
|
||||
return r.Send("MessageSendVoice", request)
|
||||
}
|
||||
|
||||
// 以上实现了部分常用消息推送:SendText 发送文本消息、SendImage 发送图片消息、SendVoice 发送语音消息,
|
||||
// 如需扩展其他消息类型,建议按照以上格式,扩展对应消息类型的参数即可
|
||||
// 也可以直接使用Send方法,按照企业微信消息推送的接口文档传对应消息类型的参数来使用
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/silenceper/wechat/v2/work/externalcontact"
|
||||
"github.com/silenceper/wechat/v2/work/kf"
|
||||
"github.com/silenceper/wechat/v2/work/material"
|
||||
"github.com/silenceper/wechat/v2/work/message"
|
||||
"github.com/silenceper/wechat/v2/work/msgaudit"
|
||||
"github.com/silenceper/wechat/v2/work/oauth"
|
||||
"github.com/silenceper/wechat/v2/work/robot"
|
||||
@@ -67,3 +68,8 @@ func (wk *Work) GetMaterial() *material.Client {
|
||||
func (wk *Work) GetRobot() *robot.Client {
|
||||
return robot.NewClient(wk.ctx)
|
||||
}
|
||||
|
||||
// GetMessage get robot
|
||||
func (wk *Work) GetMessage() *message.Client {
|
||||
return message.NewClient(wk.ctx)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user