From 88fc6465bb8a06dc5b4659ce3a19895f7b49740d Mon Sep 17 00:00:00 2001
From: gzylg <40072122+gzylg@users.noreply.github.com>
Date: Wed, 31 Aug 2022 22:50:51 +0800
Subject: [PATCH] =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7=E7=94=A8=E6=88=B7?=
=?UTF-8?q?=E9=BB=91=E5=90=8D=E5=8D=95=E7=AE=A1=E7=90=86=20(#609)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
doc/api/officialaccount.md | 131 +++++++++++++++++-------------
officialaccount/user/blacklist.go | 116 ++++++++++++++++++++++++++
2 files changed, 190 insertions(+), 57 deletions(-)
create mode 100644 officialaccount/user/blacklist.go
diff --git a/doc/api/officialaccount.md b/doc/api/officialaccount.md
index 46d0bb6..5b81b4e 100644
--- a/doc/api/officialaccount.md
+++ b/doc/api/officialaccount.md
@@ -1,29 +1,29 @@
-# 微信公众号API列表
+# 微信公众号 API 列表
## 基础接口
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html)
-| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
-| :---------------------: | -------- | :------------------------- | ---------- | -------- |
-| 获取Access token | GET | /cgi-bin/token | YES | |
-| 获取微信服务器IP地址 | GET | /cgi-bin/get_api_domain_ip | YES | |
-| 获取微信callback IP地址 | GET | /cgi-bin/getcallbackip | YES | |
-| 清理接口调用次数 | POST | /cgi-bin/clear_quota | YES | |
+| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
+| :-----------------------: | -------- | :------------------------- | ---------- | -------- |
+| 获取 Access token | GET | /cgi-bin/token | YES | |
+| 获取微信服务器 IP 地址 | GET | /cgi-bin/get_api_domain_ip | YES | |
+| 获取微信 callback IP 地址 | GET | /cgi-bin/getcallbackip | YES | |
+| 清理接口调用次数 | POST | /cgi-bin/clear_quota | YES | |
## 订阅通知
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Subscription_Messages/api.html)
-| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
-| -------------------- | -------- | -------------------------------------- | ---------- | ----------------------- |
-| 选用模板 | POST | /wxaapi/newtmpl/addtemplate | YES | (tpl *Subscribe) Add |
-| 删除模板 | POST | /wxaapi/newtmpl/deltemplate | YES | (tpl *Subscribe) Delete |
-| 获取公众号类目 | GET | /wxaapi/newtmpl/getcategory | YES | (tpl *Subscribe) GetCategory |
-| 获取模板中的关键词 | GET | /wxaapi/newtmpl/getpubtemplatekeywords | YES | (tpl *Subscribe) GetPubTplKeyWordsByID |
-| 获取类目下的公共模板 | GET | /wxaapi/newtmpl/getpubtemplatetitles | YES | (tpl *Subscribe) GetPublicTemplateTitleList |
-| 获取私有模板列表 | GET | /wxaapi/newtmpl/gettemplate | YES | (tpl *Subscribe) List() |
-| 发送订阅通知 | POST | /cgi-bin/message/subscribe/bizsend | YES | (tpl *Subscribe) Send |
+| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
+| -------------------- | -------- | -------------------------------------- | ---------- | -------------------------------------------- |
+| 选用模板 | POST | /wxaapi/newtmpl/addtemplate | YES | (tpl \*Subscribe) Add |
+| 删除模板 | POST | /wxaapi/newtmpl/deltemplate | YES | (tpl \*Subscribe) Delete |
+| 获取公众号类目 | GET | /wxaapi/newtmpl/getcategory | YES | (tpl \*Subscribe) GetCategory |
+| 获取模板中的关键词 | GET | /wxaapi/newtmpl/getpubtemplatekeywords | YES | (tpl \*Subscribe) GetPubTplKeyWordsByID |
+| 获取类目下的公共模板 | GET | /wxaapi/newtmpl/getpubtemplatetitles | YES | (tpl \*Subscribe) GetPublicTemplateTitleList |
+| 获取私有模板列表 | GET | /wxaapi/newtmpl/gettemplate | YES | (tpl \*Subscribe) List() |
+| 发送订阅通知 | POST | /cgi-bin/message/subscribe/bizsend | YES | (tpl \*Subscribe) Send |
## 客服消息
@@ -33,16 +33,16 @@
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Customer_Service_Management.html)
-| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
-| ---------------- | --------- | -------------------------------------- | ---------- | -------- |
-| 获取客服基本信息 | GET | /cgi-bin/customservice/getkflist | YES | (csm *Manager) List |
-| 添加客服帐号 | POST | /customservice/kfaccount/add | YES | (csm *Manager) Add |
-| 邀请绑定客服帐号 | POST | /customservice/kfaccount/inviteworker | YES | (csm *Manager) InviteBind |
-| 设置客服信息 | POST | /customservice/kfaccount/update | YES | (csm *Manager) Update |
-| 上传客服头像 | POST/FORM | /customservice/kfaccount/uploadheadimg | YES | (csm *Manager) UploadHeadImg |
-| 删除客服帐号 | POST | /customservice/kfaccount/del | YES | (csm *Manager) Delete |
-| 获取在线客服 | POST | /cgi-bin/customservice/getonlinekflist| YES | (csm *Manager) OnlineList |
-| 下发客服输入状态 | POST | /cgi-bin/message/custom/typing | YES | (csm *Manager) SendTypingStatus |
+| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
+| ---------------- | --------- | -------------------------------------- | ---------- | -------------------------------- |
+| 获取客服基本信息 | GET | /cgi-bin/customservice/getkflist | YES | (csm \*Manager) List |
+| 添加客服帐号 | POST | /customservice/kfaccount/add | YES | (csm \*Manager) Add |
+| 邀请绑定客服帐号 | POST | /customservice/kfaccount/inviteworker | YES | (csm \*Manager) InviteBind |
+| 设置客服信息 | POST | /customservice/kfaccount/update | YES | (csm \*Manager) Update |
+| 上传客服头像 | POST/FORM | /customservice/kfaccount/uploadheadimg | YES | (csm \*Manager) UploadHeadImg |
+| 删除客服帐号 | POST | /customservice/kfaccount/del | YES | (csm \*Manager) Delete |
+| 获取在线客服 | POST | /cgi-bin/customservice/getonlinekflist | YES | (csm \*Manager) OnlineList |
+| 下发客服输入状态 | POST | /cgi-bin/message/custom/typing | YES | (csm \*Manager) SendTypingStatus |
#### 会话控制
@@ -148,15 +148,15 @@
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html)
-| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
-| ------------------------------------------------------------ | -------- | --------------------------------------------------- | ---------- | ----------------------------------- |
-| 获取跳转的url地址 | GET | https://open.weixin.qq.com/connect/oauth2/authorize | YES | (oauth *Oauth) GetRedirectURL |
-| 获取网页应用跳转的url地址 | GET | https://open.weixin.qq.com/connect/qrconnect | YES | (oauth *Oauth) GetWebAppRedirectURL |
-| 通过网页授权的code 换取access_token(区别于context中的access_token) | GET | /sns/oauth2/access_token | YES | (oauth *Oauth) GetUserAccessToken |
-| 刷新access_token | GET | /sns/oauth2/refresh_token? | YES | (oauth *Oauth) RefreshAccessToken |
-| 检验access_token是否有效 | GET | /sns/auth | YES | (oauth *Oauth) CheckAccessToken( |
-| 拉取用户信息(需scope为 snsapi_userinfo) | GET | /sns/userinfo | YES | (oauth *Oauth) GetUserInfo |
-| 获取jssdk需要的配置参数 | GET | /cgi-bin/ticket/getticket | YES | (js *Js) GetConfig |
+| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
+| ----------------------------------------------------------------------- | -------- | --------------------------------------------------- | ---------- | ------------------------------------ |
+| 获取跳转的 url 地址 | GET | https://open.weixin.qq.com/connect/oauth2/authorize | YES | (oauth \*Oauth) GetRedirectURL |
+| 获取网页应用跳转的 url 地址 | GET | https://open.weixin.qq.com/connect/qrconnect | YES | (oauth \*Oauth) GetWebAppRedirectURL |
+| 通过网页授权的 code 换取 access_token(区别于 context 中的 access_token) | GET | /sns/oauth2/access_token | YES | (oauth \*Oauth) GetUserAccessToken |
+| 刷新 access_token | GET | /sns/oauth2/refresh_token? | YES | (oauth \*Oauth) RefreshAccessToken |
+| 检验 access_token 是否有效 | GET | /sns/auth | YES | (oauth \*Oauth) CheckAccessToken( |
+| 拉取用户信息(需 scope 为 snsapi_userinfo) | GET | /sns/userinfo | YES | (oauth \*Oauth) GetUserInfo |
+| 获取 jssdk 需要的配置参数 | GET | /cgi-bin/ticket/getticket | YES | (js \*Js) GetConfig |
## 素材管理
@@ -164,17 +164,15 @@
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Draft_Box/Add_draft.html)
-| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
-| -------------------------- | -------- | ------------------------------------------------------------ | ---------- | ---------------------------- |
-| 新建草稿 | POST | /cgi-bin/draft/add | YES | (draft *Draft) AddDraft |
-| 获取草稿 | POST | /cgi-bin/draft/get | YES | (draft *Draft) GetDraft |
-| 删除草稿 | POST | /cgi-bin/draft/delete | YES | (draft *Draft) DeleteDraft |
-| 修改草稿 | POST | /cgi-bin/draft/update | YES | (draft *Draft) UpdateDraft |
-| 获取草稿总数 | GET | /cgi-bin/draft/count | YES | (draft *Draft) CountDraft |
-| 获取草稿列表 | POST | /cgi-bin/draft/batchget | YES | (draft *Draft) PaginateDraft |
-| MP端开关(仅内测期间使用) | POST | /cgi-bin/draft/switch
/cgi-bin/draft/switch?checkonly=1 | NO | |
-
-
+| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
+| --------------------------- | -------- | ------------------------------------------------------------ | ---------- | ----------------------------- |
+| 新建草稿 | POST | /cgi-bin/draft/add | YES | (draft \*Draft) AddDraft |
+| 获取草稿 | POST | /cgi-bin/draft/get | YES | (draft \*Draft) GetDraft |
+| 删除草稿 | POST | /cgi-bin/draft/delete | YES | (draft \*Draft) DeleteDraft |
+| 修改草稿 | POST | /cgi-bin/draft/update | YES | (draft \*Draft) UpdateDraft |
+| 获取草稿总数 | GET | /cgi-bin/draft/count | YES | (draft \*Draft) CountDraft |
+| 获取草稿列表 | POST | /cgi-bin/draft/batchget | YES | (draft \*Draft) PaginateDraft |
+| MP 端开关(仅内测期间使用) | POST | /cgi-bin/draft/switch
/cgi-bin/draft/switch?checkonly=1 | NO | |
## 发布能力
@@ -187,20 +185,40 @@
- 群发:主动推送给粉丝,历史消息可看,被搜一搜收录,可以限定部分的粉丝接收到。
- 发布:不会主动推给粉丝,历史消息列表看不到,但是是公开给所有人的文章。也不会占用群发的次数。每天可以发布多篇内容。可以用于自动回复、自定义菜单、页面模板和话题中,发布成功时会生成一个永久链接。
-| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
-| ------------------------------ | -------- | ------------------------------- | ---------- | --------------------------------------- |
-| 发布接口 | POST | /cgi-bin/freepublish/submit | YES | (freePublish *FreePublish) Publish |
-| 发布状态轮询接口 | POST | /cgi-bin/freepublish/get | YES | (freePublish *FreePublish) SelectStatus |
-| 事件推送发布结果 | | | YES | EventPublishJobFinish |
-| 删除发布 | POST | /cgi-bin/freepublish/delete | YES | (freePublish *FreePublish) Delete |
-| 通过 article_id 获取已发布文章 | POST | /cgi-bin/freepublish/getarticle | YES | (freePublish *FreePublish) First |
-| 获取成功发布列表 | POST | /cgi-bin/freepublish/batchget | YES | (freePublish *FreePublish) Paginate |
-
+| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
+| ------------------------------ | -------- | ------------------------------- | ---------- | ---------------------------------------- |
+| 发布接口 | POST | /cgi-bin/freepublish/submit | YES | (freePublish \*FreePublish) Publish |
+| 发布状态轮询接口 | POST | /cgi-bin/freepublish/get | YES | (freePublish \*FreePublish) SelectStatus |
+| 事件推送发布结果 | | | YES | EventPublishJobFinish |
+| 删除发布 | POST | /cgi-bin/freepublish/delete | YES | (freePublish \*FreePublish) Delete |
+| 通过 article_id 获取已发布文章 | POST | /cgi-bin/freepublish/getarticle | YES | (freePublish \*FreePublish) First |
+| 获取成功发布列表 | POST | /cgi-bin/freepublish/batchget | YES | (freePublish \*FreePublish) Paginate |
## 图文消息留言管理
## 用户管理
+| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
+| ------------------------------------------ | -------- | -------------------------------------- | ---------- | ---------------------------------- |
+| 获取指定 OpenID 变化列表(公众号账号迁移) | POST | /cgi-bin/changeopenid | YES | (user \*User) ListChangeOpenIDs |
+| 获取所有用户 OpenID 列表(公众号账号迁移) | | | YES | (user \*User) ListAllChangeOpenIDs |
+| 获取用户基本信息 | GET | /cgi-bin/user/info | YES | (user \*User) GetUserInfo |
+| 设置用户备注名 | POST | /cgi-bin/user/info/updateremark | YES | (user \*User) UpdateRemark |
+| 获取用户列表 | GET | /cgi-bin/user/get | YES | (user \*User) ListUserOpenIDs |
+| 获取所有用户 OpenID 列表 | | | YES | (user \*User) ListAllUserOpenIDs |
+| 获取公众号的黑名单列表 | POST | /cgi-bin/tags/members/getblacklist | YES | (user \*User) GetBlackList |
+| 获取公众号的所有黑名单列表 | | | YES | (user \*User) GetAllBlackList |
+| 拉黑用户 | POST | /cgi-bin/tags/members/batchblacklist | YES | (user \*User) BatchBlackList |
+| 取消拉黑用户 | POST | /cgi-bin/tags/members/batchunblacklist | YES | (user \*User) BatchUnBlackList |
+| 创建标签 | POST | /cgi-bin/tags/create | YES | (user \*User) CreateTag |
+| 删除标签 | POST | /cgi-bin/tags/delete | YES | (user \*User) DeleteTag |
+| 编辑标签 | POST | /cgi-bin/tags/update | YES | (user \*User) UpdateTag |
+| 获取公众号已创建的标签 | GET | /cgi-bin/tags/get | YES | (user \*User) GetTag |
+| 获取标签下粉丝列表 | POST | /cgi-bin/user/tag/get | YES | (user \*User) OpenIDListByTag |
+| 批量为用户打标签 | POST | /cgi-bin/tags/members/batchtagging | YES | (user \*User) BatchTag |
+| 批量为用户取消标签 | POST | /cgi-bin/tags/members/batchuntagging | YES | (user \*User) BatchUntag |
+| 获取用户身上的标签列表 | POST | /cgi-bin/tags/getidlist | YES | (user \*User) UserTidList |
+
## 账号管理
## 数据统计
@@ -217,5 +235,4 @@
## 微信发票
-## 微信非税缴费
-
+## 微信非税缴费
diff --git a/officialaccount/user/blacklist.go b/officialaccount/user/blacklist.go
new file mode 100644
index 0000000..b957b09
--- /dev/null
+++ b/officialaccount/user/blacklist.go
@@ -0,0 +1,116 @@
+// Package user blacklist 公众号用户黑名单管理
+// 参考文档:https://developers.weixin.qq.com/doc/offiaccount/User_Management/Manage_blacklist.html
+package user
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/silenceper/wechat/v2/util"
+)
+
+const (
+ // 获取公众号的黑名单列表
+ getblacklistURL = "https://api.weixin.qq.com/cgi-bin/tags/members/getblacklist?access_token=%s"
+ // 拉黑用户
+ batchblacklistURL = "https://api.weixin.qq.com/cgi-bin/tags/members/batchblacklist?access_token=%s"
+ // 取消拉黑用户
+ batchunblacklistURL = "https://api.weixin.qq.com/cgi-bin/tags/members/batchunblacklist?access_token=%s"
+)
+
+// GetBlackList 获取公众号的黑名单列表
+// 该接口每次调用最多可拉取 1000 个OpenID,当列表数较多时,可以通过多次拉取的方式来满足需求。
+// 参数 beginOpenid:当 begin_openid 为空时,默认从开头拉取。
+func (user *User) GetBlackList(beginOpenid ...string) (userlist *OpenidList, err error) {
+ if len(beginOpenid) > 1 {
+ return nil, errors.New("参数 beginOpenid 错误:请传递 1 个openID,若需要从头开始拉取列表请留空。")
+ }
+ // 获取 AccessToken
+ var accessToken string
+ if accessToken, err = user.GetAccessToken(); err != nil {
+ return
+ }
+
+ // 处理 request 内容
+ request := map[string]string{"begin_openid": ""}
+ if len(beginOpenid) == 1 {
+ request["begin_openid"] = beginOpenid[0]
+ }
+
+ // 调用接口
+ var resp []byte
+ url := fmt.Sprintf(getblacklistURL, accessToken)
+ if resp, err = util.PostJSON(url, &request); err != nil {
+ return nil, err
+ }
+
+ // 处理返回
+ userlist = &OpenidList{}
+ if err = util.DecodeWithError(resp, userlist, "GetBlackList"); err != nil {
+ return nil, err
+ }
+
+ return
+}
+
+// GetAllBlackList 获取公众号的所有黑名单列表
+func (user *User) GetAllBlackList() (openIDList []string, err error) {
+ var (
+ beginOpenid string
+ count int
+ userlist *OpenidList
+ )
+
+ for {
+ // 获取列表(每次1k条)
+ if userlist, err = user.GetBlackList(beginOpenid); err != nil {
+ return nil, err
+ }
+ openIDList = append(openIDList, userlist.Data.OpenIDs...) // 存储本次获得的OpenIDs
+ count += userlist.Count // 记录获得的总数量
+ beginOpenid = userlist.NextOpenID // 记录下次循环的起始openID
+ if count >= userlist.Total {
+ break // 获得的数量=total,结束循环
+ }
+ }
+
+ return
+}
+
+// BatchBlackList 拉黑用户
+// 参数 openidList:需要拉入黑名单的用户的openid,每次拉黑最多允许20个
+func (user *User) BatchBlackList(openidList ...string) (err error) {
+ return user.batch(batchblacklistURL, "BatchBlackList", openidList...)
+}
+
+// BatchUnBlackList 取消拉黑用户
+// 参数 openidList:需要取消拉入黑名单的用户的openid,每次拉黑最多允许20个
+func (user *User) BatchUnBlackList(openidList ...string) (err error) {
+ return user.batch(batchunblacklistURL, "BatchUnBlackList", openidList...)
+}
+
+// batch 公共方法
+func (user *User) batch(url, apiName string, openidList ...string) (err error) {
+ // 检查参数
+ if len(openidList) == 0 || len(openidList) > 20 {
+ return errors.New("参数 openidList 错误:每次操作黑名单用户数量为1-20个。")
+ }
+
+ // 获取 AccessToken
+ var accessToken string
+ if accessToken, err = user.GetAccessToken(); err != nil {
+ return
+ }
+
+ // 处理 request 内容
+ request := map[string][]string{"openid_list": openidList}
+
+ // 调用接口
+ var resp []byte
+ url = fmt.Sprintf(url, accessToken)
+ if resp, err = util.PostJSON(url, &request); err != nil {
+ return
+ }
+
+ return util.DecodeWithCommonError(resp, apiName)
+}