mirror of
https://github.com/silenceper/wechat.git
synced 2026-02-06 21:52:27 +08:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83e223999b | ||
|
|
e068d53dcb | ||
|
|
8c87c49f2a | ||
|
|
74053fe6ef | ||
|
|
566c3c27cb | ||
|
|
dc24ad4262 | ||
|
|
05ec6a42ae | ||
|
|
efad41bcda | ||
|
|
3fb288d932 | ||
|
|
f5f401e76c | ||
|
|
ab4f427647 | ||
|
|
a9ae64ef63 | ||
|
|
83621a38c6 | ||
|
|
4937f019a0 | ||
|
|
e9489625c6 | ||
|
|
f74869e61c | ||
|
|
fd96154231 | ||
|
|
8621e06a01 | ||
|
|
1e2f909f34 | ||
|
|
00b13cda0d |
29
.github/workflows/release.yml
vendored
Normal file
29
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: goreleaser
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
-
|
||||||
|
name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.15
|
||||||
|
-
|
||||||
|
name: Run GoReleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
args: release --rm-dist
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
29
.goreleaser.yml
Normal file
29
.goreleaser.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# This is an example goreleaser.yaml file with some sane defaults.
|
||||||
|
# Make sure to check the documentation at http://goreleaser.com
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
# You may remove this if you don't use go modules.
|
||||||
|
- go mod download
|
||||||
|
# you may remove this if you don't need go generate
|
||||||
|
- go generate ./...
|
||||||
|
builds:
|
||||||
|
- skip: true
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- replacements:
|
||||||
|
darwin: Darwin
|
||||||
|
linux: Linux
|
||||||
|
windows: Windows
|
||||||
|
386: i386
|
||||||
|
amd64: x86_64
|
||||||
|
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ .Tag }}-next"
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- '^docs:'
|
||||||
|
- '^test:'
|
||||||
@@ -2,16 +2,20 @@
|
|||||||

|

|
||||||
[](https://goreportcard.com/report/github.com/silenceper/wechat)
|
[](https://goreportcard.com/report/github.com/silenceper/wechat)
|
||||||
[](https://pkg.go.dev/github.com/silenceper/wechat/v2?tab=doc)
|
[](https://pkg.go.dev/github.com/silenceper/wechat/v2?tab=doc)
|
||||||
|

|
||||||
|
|
||||||
使用Golang开发的微信SDK,简单、易用。
|
使用Golang开发的微信SDK,简单、易用。
|
||||||
>当前版本为v2版本
|
>注意:当前版本为v2版本,v1版本已废弃
|
||||||
|
|
||||||
|
|
||||||
## 文档 && 例子
|
## 文档 && 例子
|
||||||
|
[API列表](https://github.com/silenceper/wechat/tree/v2/doc/api)
|
||||||
|
|
||||||
[Wechat SDK 2.0 文档](https://silenceper.com/wechat)
|
[Wechat SDK 2.0 文档](https://silenceper.com/wechat)
|
||||||
|
|
||||||
[Wechat SDK 2.0 例子](https://github.com/gowechat/example)
|
[Wechat SDK 2.0 例子](https://github.com/gowechat/example)
|
||||||
|
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
```
|
```
|
||||||
import "github.com/silenceper/wechat/v2"
|
import "github.com/silenceper/wechat/v2"
|
||||||
@@ -61,8 +65,10 @@ server.Send()
|
|||||||
- openplatform:开放平台API
|
- openplatform:开放平台API
|
||||||
- work:企业微信
|
- work:企业微信
|
||||||
- aispeech:智能对话
|
- aispeech:智能对话
|
||||||
|
- doc: api文档
|
||||||
|
|
||||||
## 贡献
|
## 贡献
|
||||||
|
- 在[API列表](https://github.com/silenceper/wechat/tree/v2/doc/api)中查看哪些API未实现
|
||||||
- 提交issue,描述需要贡献的内容
|
- 提交issue,描述需要贡献的内容
|
||||||
- 完成更改后,提交PR
|
- 完成更改后,提交PR
|
||||||
|
|
||||||
|
|||||||
18
doc/api/README.md
Normal file
18
doc/api/README.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# API 文档
|
||||||
|
已完成以及未完成API列表汇总
|
||||||
|
|
||||||
|
如果有兴趣参与贡献,可以在具体的API表格后面标识自己为贡献者以及完成时间,例如:
|
||||||
|
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者|完成时间|
|
||||||
|
| :---------------------: | -------- | :------------------------- | ---------- | -------- |-------- |-------- |
|
||||||
|
| 获取公众号类目 | GET | /wxaapi/newtmpl/getcategory | NO | |silenceper| 2021-12-20|
|
||||||
|
|
||||||
|
|
||||||
|
- [微信公众号](./officialaccount.md)
|
||||||
|
- [小程序](./miniprogram.md)
|
||||||
|
- [小游戏](./minigame.md)
|
||||||
|
- [开放平台](./oplatform.md)
|
||||||
|
- [微信支付](./wxpay.md)
|
||||||
|
- [企业微信](./work.md)
|
||||||
|
- [智能对话](./aispeech.md)
|
||||||
2
doc/api/aispeech.md
Normal file
2
doc/api/aispeech.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# 智能对话
|
||||||
|
TODO
|
||||||
2
doc/api/minigame.md
Normal file
2
doc/api/minigame.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# 小游戏
|
||||||
|
TODO
|
||||||
2
doc/api/miniprogram.md
Normal file
2
doc/api/miniprogram.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# 小程序
|
||||||
|
TODO
|
||||||
180
doc/api/officialaccount.md
Normal file
180
doc/api/officialaccount.md
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# 微信公众号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 | |
|
||||||
|
|
||||||
|
## 订阅通知
|
||||||
|
|
||||||
|
[官方文档](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 | NO | |
|
||||||
|
| 获取模板中的关键词 | GET | /wxaapi/newtmpl/getpubtemplatekeywords | NO | |
|
||||||
|
| 获取类目下的公共模板 | GET | /wxaapi/newtmpl/getpubtemplatetitles | NO | |
|
||||||
|
| 获取私有模板列表 | GET | /wxaapi/newtmpl/gettemplate | YES | (tpl *Subscribe) List() |
|
||||||
|
| 发送订阅通知 | POST | /cgi-bin/message/subscribe/bizsend | YES | (tpl *Subscribe) Send |
|
||||||
|
|
||||||
|
## 客服消息
|
||||||
|
|
||||||
|
### PC 客服能力
|
||||||
|
|
||||||
|
#### 客服管理
|
||||||
|
|
||||||
|
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Customer_Service_Management.html)
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
|
| ---------------- | --------- | -------------------------------------- | ---------- | -------- |
|
||||||
|
| 获取客服基本信息 | GET | /cgi-bin/customservice/getkflist | NO | |
|
||||||
|
| 添加客服帐号 | POST | /customservice/kfaccount/add | NO | |
|
||||||
|
| 邀请绑定客服帐号 | POST | /customservice/kfaccount/inviteworker | NO | |
|
||||||
|
| 设置客服信息 | POST | /customservice/kfaccount/update | NO | |
|
||||||
|
| 上传客服头像 | POST/FORM | /customservice/kfaccount/uploadheadimg | NO | |
|
||||||
|
| 删除客服帐号 | GET | /customservice/kfaccount/del | NO | |
|
||||||
|
|
||||||
|
#### 会话控制
|
||||||
|
|
||||||
|
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Session_control.html)
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
|
| ------------------ | -------- | --------------------------------------- | ---------- | -------- |
|
||||||
|
| 创建会话 | POST | /customservice/kfsession/create | NO | |
|
||||||
|
| 获取客户会话状态 | GET | /customservice/kfsession/getsession | NO | |
|
||||||
|
| 获取客服会话列表 | GET | /customservice/kfsession/getsessionlist | NO | |
|
||||||
|
| 获取未接入会话列表 | POST | /customservice/kfsession/getwaitcase | NO | |
|
||||||
|
|
||||||
|
#### 获取聊天记录
|
||||||
|
|
||||||
|
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Obtain_chat_transcript.html)
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
|
| ------------ | -------- | ----------------------------------- | ---------- | -------- |
|
||||||
|
| 获取聊天记录 | POST | /customservice/msgrecord/getmsglist | NO | |
|
||||||
|
|
||||||
|
### 对话能力
|
||||||
|
|
||||||
|
[官方文档](https://developers.weixin.qq.com/doc/offiaccount/Shopping_Guide/guide.html)
|
||||||
|
|
||||||
|
#### 顾问管理
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
|
| ------------------------------ | -------- | -------------------------------------- | ---------- | -------- |
|
||||||
|
| 添加顾问 | POST | /cgi-bin/guide/addguideacct | NO | |
|
||||||
|
| 获取顾问信息 | POST | /cgi-bin/guide/getguideacct | NO | |
|
||||||
|
| 修改顾问信息 | POST | /cgi-bin/guide/updateguideacct | NO | |
|
||||||
|
| 删除顾问 | POST | /cgi-bin/guide/delguideacct | NO | |
|
||||||
|
| 获取服务号顾问列表 | POST | /cgi-bin/guide/getguideacctlist | NO | |
|
||||||
|
| 生成顾问二维码 | POST | /cgi-bin/guide/guidecreateqrcode | NO | |
|
||||||
|
| 获取顾问聊天记录 | POST | /cgi-bin/guide/getguidebuyerchatrecord | NO | |
|
||||||
|
| 设置快捷回复与关注自动回复 | POST | /cgi-bin/guide/setguideconfig | NO | |
|
||||||
|
| 获取快捷回复与关注自动回复 | POST | /cgi-bin/guide/getguideconfig | NO | |
|
||||||
|
| 设置敏感词与离线自动回复 | POST | /cgi-bin/guide/setguideacctconfig | NO | |
|
||||||
|
| 获取离线自动回复与敏感词 | POST | /cgi-bin/guide/getguideacctconfig | NO | |
|
||||||
|
| 允许微信用户复制小程序页面路径 | POST | /cgi-bin/guide/pushshowwxapathmenu | NO | |
|
||||||
|
| 新建顾问分组 | POST | /cgi-bin/guide/newguidegroup | NO | |
|
||||||
|
| 获取顾问分组列表 | POST | /cgi-bin/guide/getguidegrouplist | NO | |
|
||||||
|
| 获取顾问分组信息 | POST | /cgi-bin/guide/getgroupinfo | NO | |
|
||||||
|
| 分组内添加顾问 | POST | /cgi-bin/guide/addguide2guidegroup | NO | |
|
||||||
|
| 分组内删除顾问 | POST | /cgi-bin/guide/delguide2guidegroup | NO | |
|
||||||
|
| 获取顾问所在分组 | POST | /cgi-bin/guide/getgroupbyguide | NO | |
|
||||||
|
| 删除指定顾问分组 | POST | /cgi-bin/guide/delguidegroup | NO | |
|
||||||
|
|
||||||
|
#### 客户管理
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
|
| ------------------------ | -------- | ------------------------------------------- | ---------- | -------- |
|
||||||
|
| 为顾问分配客户 | POST | /cgi-bin/guide/addguidebuyerrelation | NO | |
|
||||||
|
| 为顾问移除客户 | POST | /cgi-bin/guide/delguidebuyerrelation | NO | |
|
||||||
|
| 获取顾问的客户列表 | POST | /cgi-bin/guide/getguidebuyerrelationlist | NO | |
|
||||||
|
| 为客户更换顾问 | POST | /cgi-bin/guide/rebindguideacctforbuyer | NO | |
|
||||||
|
| 修改客户昵称 | POST | /cgi-bin/guide/updateguidebuyerrelation | NO | |
|
||||||
|
| 查询客户所属顾问 | POST | /cgi-bin/guide/getguidebuyerrelationbybuyer | NO | |
|
||||||
|
| 查询指定顾问和客户的关系 | POST | /cgi-bin/guide/getguidebuyerrelation | NO | |
|
||||||
|
|
||||||
|
#### 标签管理
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
|
| ------------------ | -------- | -------------------------------------- | ---------- | -------- |
|
||||||
|
| 新建标签类型 | POST | /cgi-bin/guide/newguidetagoption | NO | |
|
||||||
|
| 删除标签类型 | POST | /cgi-bin/guide/delguidetagoption | NO | |
|
||||||
|
| 为标签添加可选值 | POST | /cgi-bin/guide/addguidetagoption | NO | |
|
||||||
|
| 获取标签和可选值 | POST | /cgi-bin/guide/getguidetagoption | NO | |
|
||||||
|
| 为客户设置标签 | POST | /cgi-bin/guide/addguidebuyertag | NO | |
|
||||||
|
| 查询客户标签 | POST | /cgi-bin/guide/getguidebuyertag | NO | |
|
||||||
|
| 根据标签值筛选客户 | POST | /cgi-bin/guide/queryguidebuyerbytag | NO | |
|
||||||
|
| 删除客户标签 | POST | /cgi-bin/guide/delguidebuyertag | NO | |
|
||||||
|
| 设置自定义客户信息 | POST | /cgi-bin/guide/addguidebuyerdisplaytag | NO | |
|
||||||
|
| 获取自定义客户信息 | POST | /cgi-bin/guide/getguidebuyerdisplaytag | NO | |
|
||||||
|
|
||||||
|
#### 素材管理
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
|
| ------------------ | -------- | ------------------------------------ | ---------- | -------- |
|
||||||
|
| 添加小程序卡片素材 | POST | /cgi-bin/guide/setguidecardmaterial | NO | |
|
||||||
|
| 查询小程序卡片素材 | POST | /cgi-bin/guide/getguidecardmaterial | NO | |
|
||||||
|
| 删除小程序卡片素材 | POST | /cgi-bin/guide/delguidecardmaterial | NO | |
|
||||||
|
| 添加图片素材 | POST | /cgi-bin/guide/setguideimagematerial | NO | |
|
||||||
|
| 查询图片素材 | POST | /cgi-bin/guide/getguideimagematerial | NO | |
|
||||||
|
| 删除图片素材 | POST | /cgi-bin/guide/delguideimagematerial | NO | |
|
||||||
|
| 添加文字素材 | POST | /cgi-bin/guide/setguidewordmaterial | NO | |
|
||||||
|
| 查询文字素材 | POST | /cgi-bin/guide/getguidewordmaterial | NO | |
|
||||||
|
| 删除文字素材 | POST | /cgi-bin/guide/delguidewordmaterial | NO | |
|
||||||
|
|
||||||
|
#### 群发任务管理
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |
|
||||||
|
| -------------------- | -------- | ------------------------------------- | ---------- | -------- |
|
||||||
|
| 添加群发任务 | POST | /cgi-bin/guide/addguidemassendjob | NO | |
|
||||||
|
| 获取群发任务列表 | POST | /cgi-bin/guide/getguidemassendjoblist | NO | |
|
||||||
|
| 获取指定群发任务信息 | POST | /cgi-bin/guide/getguidemassendjob | NO | |
|
||||||
|
| 修改群发任务 | POST | /cgi-bin/guide/updateguidemassendjob | NO | |
|
||||||
|
| 取消群发任务 | POST | /cgi-bin/guide/cancelguidemassendjob | NO | |
|
||||||
|
|
||||||
|
## 微信网页开发
|
||||||
|
|
||||||
|
[官方文档](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 |
|
||||||
|
|
||||||
|
## 素材管理
|
||||||
|
|
||||||
|
## 图文消息留言管理
|
||||||
|
|
||||||
|
## 用户管理
|
||||||
|
|
||||||
|
## 账号管理
|
||||||
|
|
||||||
|
## 数据统计
|
||||||
|
|
||||||
|
## 微信卡券
|
||||||
|
|
||||||
|
## 微信门店
|
||||||
|
|
||||||
|
## 智能接口
|
||||||
|
|
||||||
|
## 微信设备功能
|
||||||
|
|
||||||
|
## 微信“一物一码”
|
||||||
|
|
||||||
|
## 微信发票
|
||||||
|
|
||||||
|
## 微信非税缴费
|
||||||
|
|
||||||
1
doc/api/oplatform.md
Normal file
1
doc/api/oplatform.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# 开放平台
|
||||||
65
doc/api/work.md
Normal file
65
doc/api/work.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# 企业微信
|
||||||
|
|
||||||
|
host: https://qyapi.weixin.qq.com/
|
||||||
|
|
||||||
|
## 微信客服
|
||||||
|
|
||||||
|
[官方文档](https://work.weixin.qq.com/api/doc/90000/90135/94638)
|
||||||
|
|
||||||
|
### 客服账号管理
|
||||||
|
|
||||||
|
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94684)
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
|
||||||
|
| :--------------: | -------- | :-------------------------- | ---------- | -------------------------------|------------|
|
||||||
|
| 添加客服帐号 | POST | /cgi-bin/kf/account/add | YES | (r *Client) AccountAdd | NICEXAI |
|
||||||
|
| 删除客服帐号 | POST | /cgi-bin/kf/account/del | YES | (r *Client) AccountDel | NICEXAI |
|
||||||
|
| 修改客服帐号 | POST | /cgi-bin/kf/account/update | YES | (r *Client) AccountUpdate | NICEXAI |
|
||||||
|
| 获取客服帐号列表 | GET | /cgi-bin/kf/account/list | YES | (r *Client) AccountList | NICEXAI |
|
||||||
|
| 获取客服帐号链接 | GET | /cgi-bin/kf/add_contact_way | YES | (r *Client) AddContactWay | NICEXAI |
|
||||||
|
|
||||||
|
### 接待人员列表
|
||||||
|
|
||||||
|
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94693)
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
|
||||||
|
| :--------------: | -------- | :-------------------------- | ---------- | -------------------------------|------------|
|
||||||
|
| 添加接待人员 | POST | /cgi-bin/kf/servicer/add | YES | (r *Client) ReceptionistAdd | NICEXAI |
|
||||||
|
| 删除接待人员 | POST | /cgi-bin/kf/servicer/del | YES | (r *Client) ReceptionistDel | NICEXAI |
|
||||||
|
| 获取接待人员列表 | GET | /cgi-bin/kf/servicer/list | YES | (r *Client) ReceptionistList | NICEXAI |
|
||||||
|
|
||||||
|
### 会话分配与消息收发
|
||||||
|
|
||||||
|
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94694)
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
|
||||||
|
| :--------------: | -------- | :-------------------------------| ---------- | ------------------------------- |------------|
|
||||||
|
| 获取会话状态 | POST | /cgi-bin/kf/service_state/get | YES | (r *Client) ServiceStateGet | NICEXAI |
|
||||||
|
| 变更会话状态 | POST | /cgi-bin/kf/service_state/trans | YES | (r *Client) ServiceStateTrans | NICEXAI |
|
||||||
|
| 读取消息 | POST | /cgi-bin/kf/sync_msg | YES | (r *Client) SyncMsg | NICEXAI |
|
||||||
|
| 发送消息 | POST | /cgi-bin/kf/send_msg | YES | (r *Client) SendMsg | NICEXAI |
|
||||||
|
| 发送事件响应消息 | POST | /cgi-bin/kf/send_msg_on_event | YES | (r *Client) SendMsgOnEvent | NICEXAI |
|
||||||
|
|
||||||
|
### 升级服务配置
|
||||||
|
|
||||||
|
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/94702)
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 |贡献者 |
|
||||||
|
| :--------------: | -------- | :-------------------------------------------------| ---------- | ------------------------------- |------------|
|
||||||
|
| 获取配置的专员与客户群 | POST | /cgi-bin/kf/customer/get_upgrade_service_config | YES | (r *Client) UpgradeServiceConfig | NICEXAI |
|
||||||
|
| 为客户升级为专员或客户群服务 | POST | /cgi-bin/kf/customer/upgrade_service | YES | (r *Client) UpgradeService | NICEXAI |
|
||||||
|
| 为客户取消推荐 | POST | /cgi-bin/kf/customer/cancel_upgrade_service | YES | (r *Client) UpgradeServiceCancel | NICEXAI |
|
||||||
|
|
||||||
|
### 其他基础信息获取
|
||||||
|
|
||||||
|
[官方文档](https://open.work.weixin.qq.com/api/doc/90001/90143/95148)
|
||||||
|
|
||||||
|
| 名称 | 请求方式 | URL | 是否已实现 | 使用方法 | 贡献者 |
|
||||||
|
| :--------------: | -------- | :---------------------------------------| ---------- | ------------------------------- |------------|
|
||||||
|
| 获取客户基础信息 | POST | /cgi-bin/kf/customer/batchget | YES | (r *Client) CustomerBatchGet | NICEXAI |
|
||||||
|
| 获取视频号绑定状态 | GET | /cgi-bin/kf/get_corp_qualification | YES | (r *Client) GetCorpQualification | NICEXAI |
|
||||||
|
|
||||||
|
## 应用管理
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
2
doc/api/wxpay.md
Normal file
2
doc/api/wxpay.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# 微信支付
|
||||||
|
TODO
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
context2 "context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@@ -43,8 +44,13 @@ type RspCheckEncryptedData struct {
|
|||||||
|
|
||||||
// Code2Session 登录凭证校验。
|
// Code2Session 登录凭证校验。
|
||||||
func (auth *Auth) Code2Session(jsCode string) (result ResCode2Session, err error) {
|
func (auth *Auth) Code2Session(jsCode string) (result ResCode2Session, err error) {
|
||||||
|
return auth.Code2SessionContext(context2.Background(), jsCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code2SessionContext 登录凭证校验。
|
||||||
|
func (auth *Auth) Code2SessionContext(ctx context2.Context, jsCode string) (result ResCode2Session, err error) {
|
||||||
var response []byte
|
var response []byte
|
||||||
if response, err = util.HTTPGet(fmt.Sprintf(code2SessionURL, auth.AppID, auth.AppSecret, jsCode)); err != nil {
|
if response, err = util.HTTPGetContext(ctx, fmt.Sprintf(code2SessionURL, auth.AppID, auth.AppSecret, jsCode)); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = json.Unmarshal(response, &result); err != nil {
|
if err = json.Unmarshal(response, &result); err != nil {
|
||||||
@@ -64,6 +70,11 @@ func (auth *Auth) GetPaidUnionID() {
|
|||||||
|
|
||||||
// CheckEncryptedData .检查加密信息是否由微信生成(当前只支持手机号加密数据),只能检测最近3天生成的加密数据
|
// CheckEncryptedData .检查加密信息是否由微信生成(当前只支持手机号加密数据),只能检测最近3天生成的加密数据
|
||||||
func (auth *Auth) CheckEncryptedData(encryptedMsgHash string) (result RspCheckEncryptedData, err error) {
|
func (auth *Auth) CheckEncryptedData(encryptedMsgHash string) (result RspCheckEncryptedData, err error) {
|
||||||
|
return auth.CheckEncryptedDataContext(context2.Background(), encryptedMsgHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckEncryptedDataContext .检查加密信息是否由微信生成(当前只支持手机号加密数据),只能检测最近3天生成的加密数据
|
||||||
|
func (auth *Auth) CheckEncryptedDataContext(ctx context2.Context, encryptedMsgHash string) (result RspCheckEncryptedData, err error) {
|
||||||
var response []byte
|
var response []byte
|
||||||
var (
|
var (
|
||||||
at string
|
at string
|
||||||
@@ -71,7 +82,7 @@ func (auth *Auth) CheckEncryptedData(encryptedMsgHash string) (result RspCheckEn
|
|||||||
if at, err = auth.GetAccessToken(); err != nil {
|
if at, err = auth.GetAccessToken(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if response, err = util.HTTPPost(fmt.Sprintf(checkEncryptedDataURL, at), "encrypted_msg_hash="+encryptedMsgHash); err != nil {
|
if response, err = util.HTTPPostContext(ctx, fmt.Sprintf(checkEncryptedDataURL, at), "encrypted_msg_hash="+encryptedMsgHash); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = util.DecodeWithError(response, &result, "CheckEncryptedDataAuth"); err != nil {
|
if err = util.DecodeWithError(response, &result, "CheckEncryptedDataAuth"); err != nil {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/silenceper/wechat/v2/miniprogram/context"
|
"github.com/silenceper/wechat/v2/miniprogram/context"
|
||||||
)
|
)
|
||||||
@@ -90,6 +91,9 @@ func GetCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if len(ivBytes) != aes.BlockSize {
|
||||||
|
return nil, fmt.Errorf("bad iv length %d", len(ivBytes))
|
||||||
|
}
|
||||||
block, err := aes.NewCipher(aesKey)
|
block, err := aes.NewCipher(aesKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
15
miniprogram/encryptor/encryptor_test.go
Normal file
15
miniprogram/encryptor/encryptor_test.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package encryptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetCipherText_BadIV(t *testing.T) {
|
||||||
|
keyData := base64.StdEncoding.EncodeToString([]byte("1234567890123456"))
|
||||||
|
badData := base64.StdEncoding.EncodeToString([]byte("1"))
|
||||||
|
_, err := GetCipherText(keyData, badData, badData)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
@@ -19,60 +19,62 @@ const (
|
|||||||
// MsgTypeText 表示文本消息
|
// MsgTypeText 表示文本消息
|
||||||
MsgTypeText MsgType = "text"
|
MsgTypeText MsgType = "text"
|
||||||
// MsgTypeImage 表示图片消息
|
// MsgTypeImage 表示图片消息
|
||||||
MsgTypeImage = "image"
|
MsgTypeImage MsgType = "image"
|
||||||
// MsgTypeVoice 表示语音消息
|
// MsgTypeVoice 表示语音消息
|
||||||
MsgTypeVoice = "voice"
|
MsgTypeVoice MsgType = "voice"
|
||||||
// MsgTypeVideo 表示视频消息
|
// MsgTypeVideo 表示视频消息
|
||||||
MsgTypeVideo = "video"
|
MsgTypeVideo MsgType = "video"
|
||||||
// MsgTypeMiniprogrampage 表示小程序卡片消息
|
// MsgTypeMiniprogrampage 表示小程序卡片消息
|
||||||
MsgTypeMiniprogrampage = "miniprogrampage"
|
MsgTypeMiniprogrampage MsgType = "miniprogrampage"
|
||||||
// MsgTypeShortVideo 表示短视频消息[限接收]
|
// MsgTypeShortVideo 表示短视频消息[限接收]
|
||||||
MsgTypeShortVideo = "shortvideo"
|
MsgTypeShortVideo MsgType = "shortvideo"
|
||||||
// MsgTypeLocation 表示坐标消息[限接收]
|
// MsgTypeLocation 表示坐标消息[限接收]
|
||||||
MsgTypeLocation = "location"
|
MsgTypeLocation MsgType = "location"
|
||||||
// MsgTypeLink 表示链接消息[限接收]
|
// MsgTypeLink 表示链接消息[限接收]
|
||||||
MsgTypeLink = "link"
|
MsgTypeLink MsgType = "link"
|
||||||
// MsgTypeMusic 表示音乐消息[限回复]
|
// MsgTypeMusic 表示音乐消息[限回复]
|
||||||
MsgTypeMusic = "music"
|
MsgTypeMusic MsgType = "music"
|
||||||
// MsgTypeNews 表示图文消息[限回复]
|
// MsgTypeNews 表示图文消息[限回复]
|
||||||
MsgTypeNews = "news"
|
MsgTypeNews MsgType = "news"
|
||||||
// MsgTypeTransfer 表示消息消息转发到客服
|
// MsgTypeTransfer 表示消息消息转发到客服
|
||||||
MsgTypeTransfer = "transfer_customer_service"
|
MsgTypeTransfer MsgType = "transfer_customer_service"
|
||||||
// MsgTypeEvent 表示事件推送消息
|
// MsgTypeEvent 表示事件推送消息
|
||||||
MsgTypeEvent = "event"
|
MsgTypeEvent MsgType = "event"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// EventSubscribe 订阅
|
// EventSubscribe 订阅
|
||||||
EventSubscribe EventType = "subscribe"
|
EventSubscribe EventType = "subscribe"
|
||||||
// EventUnsubscribe 取消订阅
|
// EventUnsubscribe 取消订阅
|
||||||
EventUnsubscribe = "unsubscribe"
|
EventUnsubscribe EventType = "unsubscribe"
|
||||||
// EventScan 用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者
|
// EventScan 用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者
|
||||||
EventScan = "SCAN"
|
EventScan EventType = "SCAN"
|
||||||
// EventLocation 上报地理位置事件
|
// EventLocation 上报地理位置事件
|
||||||
EventLocation = "LOCATION"
|
EventLocation EventType = "LOCATION"
|
||||||
// EventClick 点击菜单拉取消息时的事件推送
|
// EventClick 点击菜单拉取消息时的事件推送
|
||||||
EventClick = "CLICK"
|
EventClick EventType = "CLICK"
|
||||||
// EventView 点击菜单跳转链接时的事件推送
|
// EventView 点击菜单跳转链接时的事件推送
|
||||||
EventView = "VIEW"
|
EventView EventType = "VIEW"
|
||||||
// EventScancodePush 扫码推事件的事件推送
|
// EventScancodePush 扫码推事件的事件推送
|
||||||
EventScancodePush = "scancode_push"
|
EventScancodePush EventType = "scancode_push"
|
||||||
// EventScancodeWaitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送
|
// EventScancodeWaitmsg 扫码推事件且弹出“消息接收中”提示框的事件推送
|
||||||
EventScancodeWaitmsg = "scancode_waitmsg"
|
EventScancodeWaitmsg EventType = "scancode_waitmsg"
|
||||||
// EventPicSysphoto 弹出系统拍照发图的事件推送
|
// EventPicSysphoto 弹出系统拍照发图的事件推送
|
||||||
EventPicSysphoto = "pic_sysphoto"
|
EventPicSysphoto EventType = "pic_sysphoto"
|
||||||
// EventPicPhotoOrAlbum 弹出拍照或者相册发图的事件推送
|
// EventPicPhotoOrAlbum 弹出拍照或者相册发图的事件推送
|
||||||
EventPicPhotoOrAlbum = "pic_photo_or_album"
|
EventPicPhotoOrAlbum EventType = "pic_photo_or_album"
|
||||||
// EventPicWeixin 弹出微信相册发图器的事件推送
|
// EventPicWeixin 弹出微信相册发图器的事件推送
|
||||||
EventPicWeixin = "pic_weixin"
|
EventPicWeixin EventType = "pic_weixin"
|
||||||
// EventLocationSelect 弹出地理位置选择器的事件推送
|
// EventLocationSelect 弹出地理位置选择器的事件推送
|
||||||
EventLocationSelect = "location_select"
|
EventLocationSelect EventType = "location_select"
|
||||||
// EventTemplateSendJobFinish 发送模板消息推送通知
|
// EventTemplateSendJobFinish 发送模板消息推送通知
|
||||||
EventTemplateSendJobFinish = "TEMPLATESENDJOBFINISH"
|
EventTemplateSendJobFinish EventType = "TEMPLATESENDJOBFINISH"
|
||||||
// EventMassSendJobFinish 群发消息推送通知
|
// EventMassSendJobFinish 群发消息推送通知
|
||||||
EventMassSendJobFinish = "MASSSENDJOBFINISH"
|
EventMassSendJobFinish EventType = "MASSSENDJOBFINISH"
|
||||||
// EventWxaMediaCheck 异步校验图片/音频是否含有违法违规内容推送事件
|
// EventWxaMediaCheck 异步校验图片/音频是否含有违法违规内容推送事件
|
||||||
EventWxaMediaCheck = "wxa_media_check"
|
EventWxaMediaCheck EventType = "wxa_media_check"
|
||||||
|
// EventSubscribeMsgPopupEvent 订阅通知事件推送
|
||||||
|
EventSubscribeMsgPopupEvent EventType = "subscribe_msg_popup_event"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -81,11 +83,13 @@ const (
|
|||||||
// InfoTypeVerifyTicket 返回ticket
|
// InfoTypeVerifyTicket 返回ticket
|
||||||
InfoTypeVerifyTicket InfoType = "component_verify_ticket"
|
InfoTypeVerifyTicket InfoType = "component_verify_ticket"
|
||||||
// InfoTypeAuthorized 授权
|
// InfoTypeAuthorized 授权
|
||||||
InfoTypeAuthorized = "authorized"
|
InfoTypeAuthorized InfoType = "authorized"
|
||||||
// InfoTypeUnauthorized 取消授权
|
// InfoTypeUnauthorized 取消授权
|
||||||
InfoTypeUnauthorized = "unauthorized"
|
InfoTypeUnauthorized InfoType = "unauthorized"
|
||||||
// InfoTypeUpdateAuthorized 更新授权
|
// InfoTypeUpdateAuthorized 更新授权
|
||||||
InfoTypeUpdateAuthorized = "updateauthorized"
|
InfoTypeUpdateAuthorized InfoType = "updateauthorized"
|
||||||
|
// InfoTypeNotifyThirdFasterRegister 注册审核事件推送
|
||||||
|
InfoTypeNotifyThirdFasterRegister InfoType = "notify_third_fasteregister"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MixMessage 存放所有微信发送过来的消息和事件
|
// MixMessage 存放所有微信发送过来的消息和事件
|
||||||
@@ -142,6 +146,10 @@ type MixMessage struct {
|
|||||||
Poiname string `xml:"Poiname"`
|
Poiname string `xml:"Poiname"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SubscribeMsgPopupEvent []struct {
|
||||||
|
List SubscribeMsgPopupEvent `xml:"List"`
|
||||||
|
} `xml:"SubscribeMsgPopupEvent"`
|
||||||
|
|
||||||
// 第三方平台相关
|
// 第三方平台相关
|
||||||
InfoType InfoType `xml:"InfoType"`
|
InfoType InfoType `xml:"InfoType"`
|
||||||
AppID string `xml:"AppId"`
|
AppID string `xml:"AppId"`
|
||||||
@@ -150,6 +158,15 @@ type MixMessage struct {
|
|||||||
AuthorizationCode string `xml:"AuthorizationCode"`
|
AuthorizationCode string `xml:"AuthorizationCode"`
|
||||||
AuthorizationCodeExpiredTime int64 `xml:"AuthorizationCodeExpiredTime"`
|
AuthorizationCodeExpiredTime int64 `xml:"AuthorizationCodeExpiredTime"`
|
||||||
PreAuthCode string `xml:"PreAuthCode"`
|
PreAuthCode string `xml:"PreAuthCode"`
|
||||||
|
AuthCode string `xml:"auth_code"`
|
||||||
|
Info struct {
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Code string `xml:"code"`
|
||||||
|
CodeType int `xml:"code_type"`
|
||||||
|
LegalPersonaWechat string `xml:"legal_persona_wechat"`
|
||||||
|
LegalPersonaName string `xml:"legal_persona_name"`
|
||||||
|
ComponentPhone string `xml:"component_phone"`
|
||||||
|
} `xml:"info"`
|
||||||
|
|
||||||
// 卡券相关
|
// 卡券相关
|
||||||
CardID string `xml:"CardId"`
|
CardID string `xml:"CardId"`
|
||||||
@@ -172,6 +189,13 @@ type MixMessage struct {
|
|||||||
device.MsgDevice
|
device.MsgDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubscribeMsgPopupEvent 订阅通知事件推送的消息体
|
||||||
|
type SubscribeMsgPopupEvent struct {
|
||||||
|
TemplateID string `xml:"TemplateId"`
|
||||||
|
SubscribeStatusString string `xml:"SubscribeStatusString"`
|
||||||
|
PopupScene int `xml:"PopupScene"`
|
||||||
|
}
|
||||||
|
|
||||||
// EventPic 发图事件推送
|
// EventPic 发图事件推送
|
||||||
type EventPic struct {
|
type EventPic struct {
|
||||||
PicMd5Sum string `xml:"PicMd5Sum"`
|
PicMd5Sum string `xml:"PicMd5Sum"`
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ type Response struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WalletTransfer 付款到零钱
|
// WalletTransfer 付款到零钱
|
||||||
func (transfer *Transfer) WalletTransfer(p *Params) (rsp Response, err error) {
|
func (transfer *Transfer) WalletTransfer(p *Params) (rsp *Response, err error) {
|
||||||
nonceStr := util.RandomStr(32)
|
nonceStr := util.RandomStr(32)
|
||||||
param := make(map[string]string)
|
param := make(map[string]string)
|
||||||
param["mch_appid"] = transfer.AppID
|
param["mch_appid"] = transfer.AppID
|
||||||
@@ -83,11 +83,10 @@ func (transfer *Transfer) WalletTransfer(p *Params) (rsp Response, err error) {
|
|||||||
if p.DeviceInfo != "" {
|
if p.DeviceInfo != "" {
|
||||||
param["device_info"] = p.DeviceInfo
|
param["device_info"] = p.DeviceInfo
|
||||||
}
|
}
|
||||||
|
param["check_name"] = "NO_CHECK"
|
||||||
if p.CheckName {
|
if p.CheckName {
|
||||||
param["check_name"] = "FORCE_CHECK"
|
param["check_name"] = "FORCE_CHECK"
|
||||||
param["re_user_name"] = p.ReUserName
|
param["re_user_name"] = p.ReUserName
|
||||||
} else {
|
|
||||||
param["check_name"] = "NO_CHECK"
|
|
||||||
}
|
}
|
||||||
if p.SpbillCreateIP != "" {
|
if p.SpbillCreateIP != "" {
|
||||||
param["spbill_create_ip"] = p.SpbillCreateIP
|
param["spbill_create_ip"] = p.SpbillCreateIP
|
||||||
@@ -110,13 +109,11 @@ func (transfer *Transfer) WalletTransfer(p *Params) (rsp Response, err error) {
|
|||||||
Desc: p.Desc,
|
Desc: p.Desc,
|
||||||
SpbillCreateIP: p.SpbillCreateIP,
|
SpbillCreateIP: p.SpbillCreateIP,
|
||||||
}
|
}
|
||||||
|
req.CheckName = "NO_CHECK"
|
||||||
if p.CheckName {
|
if p.CheckName {
|
||||||
req.CheckName = "FORCE_CHECK"
|
req.CheckName = "FORCE_CHECK"
|
||||||
req.ReUserName = p.ReUserName
|
req.ReUserName = p.ReUserName
|
||||||
} else {
|
|
||||||
req.CheckName = "NO_CHECK"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rawRet, err := util.PostXMLWithTLS(walletTransferGateway, req, p.RootCa, transfer.MchID)
|
rawRet, err := util.PostXMLWithTLS(walletTransferGateway, req, p.RootCa, transfer.MchID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
23
util/http.go
23
util/http.go
@@ -2,6 +2,7 @@ package util
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
@@ -19,7 +20,16 @@ import (
|
|||||||
|
|
||||||
// HTTPGet get 请求
|
// HTTPGet get 请求
|
||||||
func HTTPGet(uri string) ([]byte, error) {
|
func HTTPGet(uri string) ([]byte, error) {
|
||||||
response, err := http.Get(uri)
|
return HTTPGetContext(context.Background(), uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPGetContext get 请求
|
||||||
|
func HTTPGetContext(ctx context.Context, uri string) ([]byte, error) {
|
||||||
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response, err := http.DefaultClient.Do(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -33,8 +43,17 @@ func HTTPGet(uri string) ([]byte, error) {
|
|||||||
|
|
||||||
// HTTPPost post 请求
|
// HTTPPost post 请求
|
||||||
func HTTPPost(uri string, data string) ([]byte, error) {
|
func HTTPPost(uri string, data string) ([]byte, error) {
|
||||||
|
return HTTPPostContext(context.Background(), uri, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPPostContext post 请求
|
||||||
|
func HTTPPostContext(ctx context.Context, uri string, data string) ([]byte, error) {
|
||||||
body := bytes.NewBuffer([]byte(data))
|
body := bytes.NewBuffer([]byte(data))
|
||||||
response, err := http.Post(uri, "", body)
|
request, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response, err := http.DefaultClient.Do(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ const (
|
|||||||
SDKUnknownError Error = "未知错误"
|
SDKUnknownError Error = "未知错误"
|
||||||
// SDKInvalidCredential 错误码:40001
|
// SDKInvalidCredential 错误码:40001
|
||||||
SDKInvalidCredential Error = "不合法的secret参数"
|
SDKInvalidCredential Error = "不合法的secret参数"
|
||||||
|
// SDKInvalidImageSize 错误码:40009
|
||||||
|
SDKInvalidImageSize Error = "无效的图片大小"
|
||||||
// SDKInvalidCorpID 错误码:40013
|
// SDKInvalidCorpID 错误码:40013
|
||||||
SDKInvalidCorpID Error = "无效的 CorpID"
|
SDKInvalidCorpID Error = "无效的 CorpID"
|
||||||
// SDKAccessTokenInvalid 错误码:40014
|
// SDKAccessTokenInvalid 错误码:40014
|
||||||
@@ -25,6 +27,10 @@ const (
|
|||||||
SDKValidateSignatureFailed Error = "校验签名错误"
|
SDKValidateSignatureFailed Error = "校验签名错误"
|
||||||
// SDKDecryptMSGFailed 错误码:40016
|
// SDKDecryptMSGFailed 错误码:40016
|
||||||
SDKDecryptMSGFailed Error = "消息解密失败"
|
SDKDecryptMSGFailed Error = "消息解密失败"
|
||||||
|
// SDKMediaIDExceedMinLength 错误码:40058
|
||||||
|
SDKMediaIDExceedMinLength Error = "不合法的参数, 请参照具体 API 接口说明进行传参"
|
||||||
|
// SDKContentContainsSensitiveInformation 错误码:40201
|
||||||
|
SDKContentContainsSensitiveInformation Error = "当前客服账号由于涉及敏感信息,已被封禁,请联系企业微信客服处理"
|
||||||
// SDKAccessTokenMissing 错误码:41001
|
// SDKAccessTokenMissing 错误码:41001
|
||||||
SDKAccessTokenMissing Error = "缺少AccessToken参数"
|
SDKAccessTokenMissing Error = "缺少AccessToken参数"
|
||||||
// SDKAccessTokenExpired 错误码:42001
|
// SDKAccessTokenExpired 错误码:42001
|
||||||
@@ -50,46 +56,38 @@ func (r Error) Error() string {
|
|||||||
return reflect.ValueOf(r).String()
|
return reflect.ValueOf(r).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSDKErr 初始化SDK实例错误信息
|
var codeDic = map[int64]error{
|
||||||
func NewSDKErr(code int64, msgList ...string) Error {
|
50001: SDKInitFailed,
|
||||||
switch code {
|
50002: SDKCacheUnavailable,
|
||||||
case 50001:
|
50003: SDKUnknownError,
|
||||||
return SDKInitFailed
|
40001: SDKInvalidCredential,
|
||||||
case 50002:
|
40009: SDKInvalidImageSize,
|
||||||
return SDKCacheUnavailable
|
40013: SDKInvalidCorpID,
|
||||||
case 40001:
|
40014: SDKAccessTokenInvalid,
|
||||||
return SDKInvalidCredential
|
40015: SDKValidateSignatureFailed,
|
||||||
case 41001:
|
40016: SDKDecryptMSGFailed,
|
||||||
return SDKAccessTokenMissing
|
40058: SDKMediaIDExceedMinLength,
|
||||||
case 42001:
|
40201: SDKContentContainsSensitiveInformation,
|
||||||
return SDKAccessTokenExpired
|
41001: SDKAccessTokenMissing,
|
||||||
case 40013:
|
42001: SDKAccessTokenExpired,
|
||||||
return SDKInvalidCorpID
|
45009: SDKApiFreqOutOfLimit,
|
||||||
case 40014:
|
48002: SDKApiForbidden,
|
||||||
return SDKAccessTokenInvalid
|
95000: SDKInvalidOpenKFID,
|
||||||
case 40015:
|
95004: SDKOpenKFIDNotExist,
|
||||||
return SDKValidateSignatureFailed
|
95011: SDKWeWorkAlready,
|
||||||
case 40016:
|
95012: SDKNotUseInWeCom,
|
||||||
return SDKDecryptMSGFailed
|
95017: SDKApiNotOpen,
|
||||||
case 45009:
|
}
|
||||||
return SDKApiFreqOutOfLimit
|
|
||||||
case 48002:
|
// NewSDKErr 初始化SDK实例错误信息
|
||||||
return SDKApiForbidden
|
func NewSDKErr(code int64, msgList ...string) error {
|
||||||
case 95000:
|
if err := codeDic[code]; err != nil {
|
||||||
return SDKInvalidOpenKFID
|
return err
|
||||||
case 95004:
|
}
|
||||||
return SDKOpenKFIDNotExist
|
|
||||||
case 95011:
|
//返回未知的自定义错误
|
||||||
return SDKWeWorkAlready
|
if len(msgList) > 0 {
|
||||||
case 95012:
|
return Error(strings.Join(msgList, ","))
|
||||||
return SDKNotUseInWeCom
|
}
|
||||||
case 95017:
|
return SDKUnknownError
|
||||||
return SDKApiNotOpen
|
|
||||||
default:
|
|
||||||
//返回未知的自定义错误
|
|
||||||
if len(msgList) > 0 {
|
|
||||||
return Error(strings.Join(msgList, ","))
|
|
||||||
}
|
|
||||||
return SDKUnknownError
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
49
work/kf/other.go
Normal file
49
work/kf/other.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package kf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/silenceper/wechat/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
//获取视频号绑定状态
|
||||||
|
corpQualification = "https://qyapi.weixin.qq.com/cgi-bin/kf/get_corp_qualification?access_token=%s"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CorpQualificationSchema 获取视频号绑定状态响应内容
|
||||||
|
type CorpQualificationSchema struct {
|
||||||
|
util.CommonError
|
||||||
|
WechatChannelsBinding bool `json:"wechat_channels_binding"` // 当企业具有绑定成功的视频号时,返回true,否则返回false。 1. 企业申请绑定视频号且由视频号管理员确认后,才为绑定成功状态 2. 至少有一个绑定成功的视频号就会返回true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCorpQualification 获取视频号绑定状态
|
||||||
|
// 微信客服可接待的客户数,和企业是否已完成主体验证、是否绑定视频号相关。
|
||||||
|
//
|
||||||
|
// 企业未完成主体验证时,微信客服仅可累计接待100位客户
|
||||||
|
// 企业已验证但未绑定视频号时,微信客服仅可累计接待10000位客户
|
||||||
|
// 企业已验证且已绑定视频号时,微信客服可接待的客户数不受限制
|
||||||
|
//
|
||||||
|
// 开发者可获取状态后,在应用等地方提示企业去完成主体验证或绑定视频号。
|
||||||
|
func (r *Client) GetCorpQualification() (info CorpQualificationSchema, err error) {
|
||||||
|
var (
|
||||||
|
accessToken string
|
||||||
|
data []byte
|
||||||
|
)
|
||||||
|
accessToken, err = r.ctx.GetAccessToken()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err = util.HTTPGet(fmt.Sprintf(corpQualification, accessToken))
|
||||||
|
if err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(data, &info); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if info.ErrCode != 0 {
|
||||||
|
return info, NewSDKErr(info.ErrCode, info.ErrMsg)
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
@@ -18,7 +18,14 @@ type SendMsgSchema struct {
|
|||||||
MsgID string `json:"msgid"` // 消息ID。如果请求参数指定了msgid,则原样返回,否则系统自动生成并返回。不多于32字节, 字符串取值范围(正则表达式):[0-9a-zA-Z_-]*
|
MsgID string `json:"msgid"` // 消息ID。如果请求参数指定了msgid,则原样返回,否则系统自动生成并返回。不多于32字节, 字符串取值范围(正则表达式):[0-9a-zA-Z_-]*
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMsg 获取消息
|
// SendMsg 发送消息
|
||||||
|
// 当微信客户处于“新接入待处理”或“由智能助手接待”状态下,可调用该接口给用户发送消息。
|
||||||
|
// 注意仅当微信客户在主动发送消息给客服后的48小时内,企业可发送消息给客户,最多可发送5条消息;若用户继续发送消息,企业可再次下发消息。
|
||||||
|
// 支持发送消息类型:文本、图片、语音、视频、文件、图文、小程序、菜单消息、地理位置。
|
||||||
|
// 目前该接口允许下发消息条数和下发时限如下:
|
||||||
|
//
|
||||||
|
// 用户动作 允许下发条数限制 下发时限
|
||||||
|
// 用户发送消息 5条 48 小时
|
||||||
func (r *Client) SendMsg(options interface{}) (info SendMsgSchema, err error) {
|
func (r *Client) SendMsg(options interface{}) (info SendMsgSchema, err error) {
|
||||||
var (
|
var (
|
||||||
accessToken string
|
accessToken string
|
||||||
|
|||||||
@@ -19,9 +19,20 @@ type SendMsgOnEventSchema struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SendMsgOnEvent 发送事件响应消息
|
// SendMsgOnEvent 发送事件响应消息
|
||||||
|
// 当特定的事件回调消息包含code字段,或通过接口变更到特定的会话状态,会返回code字段。
|
||||||
|
// 开发者可以此code为凭证,调用该接口给用户发送相应事件场景下的消息,如客服欢迎语、客服提示语和会话结束语等。
|
||||||
|
// 除”用户进入会话事件”以外,响应消息仅支持会话处于获取该code的会话状态时发送,如将会话转入待接入池时获得的code仅能在会话状态为”待接入池排队中“时发送。
|
||||||
|
//
|
||||||
|
// 目前支持的事件场景和相关约束如下:
|
||||||
|
//
|
||||||
|
// 事件场景 允许下发条数 code有效期 支持的消息类型 获取code途径
|
||||||
|
// 用户进入会话,用于发送客服欢迎语 1条 20秒 文本、菜单 事件回调
|
||||||
|
// 进入接待池,用于发送排队提示语等 1条 48小时 文本 转接会话接口
|
||||||
|
// 从接待池接入会话,用于发送非工作时间的提示语或超时未回复的提示语等 1条 48小时 文本 事件回调、转接会话接口
|
||||||
|
// 结束会话,用于发送结束会话提示语或满意度评价等 1条 20秒 文本、菜单 事件回调、转接会话接口
|
||||||
|
//
|
||||||
//「进入会话事件」响应消息:
|
//「进入会话事件」响应消息:
|
||||||
// 如果满足通过API下发欢迎语条件(条件为:1. 企业没有在管理端配置了原生欢迎语;2. 用户在过去48小时里未收过欢迎语,且未向该用户发过消息),则用户进入会话事件会额外返回一个welcome_code,开发者以此为凭据调用接口(填到该接口code参数),即可向客户发送客服欢迎语。
|
// 如果满足通过API下发欢迎语条件(条件为:1. 企业没有在管理端配置了原生欢迎语;2. 用户在过去48小时里未收过欢迎语,且未向该用户发过消息),则用户进入会话事件会额外返回一个welcome_code,开发者以此为凭据调用接口(填到该接口code参数),即可向客户发送客服欢迎语。
|
||||||
// 为了保证用户体验以及避免滥用,开发者仅可在收到相关事件后20秒内调用,且只可调用一次。
|
|
||||||
func (r *Client) SendMsgOnEvent(options interface{}) (info SendMsgOnEventSchema, err error) {
|
func (r *Client) SendMsgOnEvent(options interface{}) (info SendMsgOnEventSchema, err error) {
|
||||||
var (
|
var (
|
||||||
accessToken string
|
accessToken string
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ type ServiceStateGetSchema struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ServiceStateGet 获取会话状态
|
// ServiceStateGet 获取会话状态
|
||||||
// 0 未处理 新会话接入。可选择:1.直接用API自动回复消息。2.放进待接入池等待接待人员接待。3.指定接待人员进行接待
|
// 0 未处理 新会话接入(客户发信咨询)。可选择:1.直接用API自动回复消息。2.放进待接入池等待接待人员接待。3.指定接待人员(接待人员须处于“正在接待”中,下同)进行接待
|
||||||
// 1 由智能助手接待 可使用API回复消息。可选择转入待接入池或者指定接待人员处理。
|
// 1 由智能助手接待 可使用API回复消息。可选择转入待接入池或者指定接待人员处理
|
||||||
// 2 待接入池排队中 在待接入池中排队等待接待人员接入。可选择转为指定人员接待
|
// 2 待接入池排队中 在待接入池中排队等待接待人员接入。可选择转为指定人员接待
|
||||||
// 3 由人工接待 人工接待中。可选择结束会话
|
// 3 由人工接待 人工接待中。可选择转接给其他接待人员处理或者结束会话
|
||||||
// 4 已结束 会话已经结束或未开始。不允许变更会话状态,等待用户发起咨询
|
// 4 已结束 会话已经结束或未开始。不允许变更会话状态,客户重新发信咨询后会话状态变为“未处理”
|
||||||
// 注:一个微信用户向一个客服帐号发起咨询后,在48h内,或主动结束会话前(包括接待人员手动结束,或企业通过API结束会话),都算是一次会话
|
// 注:一个微信用户向一个客服帐号发起咨询后,在48h内,或主动结束会话前(包括接待人员手动结束,或企业通过API结束会话),都算是一次会话
|
||||||
func (r *Client) ServiceStateGet(options ServiceStateGetOptions) (info ServiceStateGetSchema, err error) {
|
func (r *Client) ServiceStateGet(options ServiceStateGetOptions) (info ServiceStateGetSchema, err error) {
|
||||||
var (
|
var (
|
||||||
@@ -64,8 +64,14 @@ type ServiceStateTransOptions struct {
|
|||||||
ServicerUserID string `json:"servicer_userid"` // 接待人员的userid,当state=3时要求必填,接待人员须处于“正在接待”中
|
ServicerUserID string `json:"servicer_userid"` // 接待人员的userid,当state=3时要求必填,接待人员须处于“正在接待”中
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServiceStateTransSchema 变更会话状态响应内容
|
||||||
|
type ServiceStateTransSchema struct {
|
||||||
|
util.CommonError
|
||||||
|
MsgCode string `json:"msg_code"` // 用于发送响应事件消息的code,将会话初次变更为service_state为2和3时,返回回复语code,service_state为4时,返回结束语code。可用该code调用发送事件响应消息接口给客户发送事件响应消息
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceStateTrans 变更会话状态
|
// ServiceStateTrans 变更会话状态
|
||||||
func (r *Client) ServiceStateTrans(options ServiceStateTransOptions) (info util.CommonError, err error) {
|
func (r *Client) ServiceStateTrans(options ServiceStateTransOptions) (info ServiceStateTransSchema, err error) {
|
||||||
var (
|
var (
|
||||||
accessToken string
|
accessToken string
|
||||||
data []byte
|
data []byte
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ type ReceptionistStatusChangeEvent struct {
|
|||||||
Event struct {
|
Event struct {
|
||||||
EventType string `json:"event_type"` // 事件类型。此处固定为:servicer_status_change
|
EventType string `json:"event_type"` // 事件类型。此处固定为:servicer_status_change
|
||||||
ReceptionistUserID string `json:"servicer_userid"` // 客服人员userid
|
ReceptionistUserID string `json:"servicer_userid"` // 客服人员userid
|
||||||
|
OpenKFID string `json:"open_kfid"` // 客服帐号ID
|
||||||
Status uint32 `json:"status"` // 状态类型。1-接待中 2-停止接待
|
Status uint32 `json:"status"` // 状态类型。1-接待中 2-停止接待
|
||||||
} `json:"event"`
|
} `json:"event"`
|
||||||
}
|
}
|
||||||
@@ -159,5 +160,6 @@ type SessionStatusChangeEvent struct {
|
|||||||
ChangeType uint32 `json:"change_type"` // 变更类型。1-从接待池接入会话 2-转接会话 3-结束会话
|
ChangeType uint32 `json:"change_type"` // 变更类型。1-从接待池接入会话 2-转接会话 3-结束会话
|
||||||
OldReceptionistUserID string `json:"old_servicer_userid"` // 老的客服人员userid。仅change_type为2和3有值
|
OldReceptionistUserID string `json:"old_servicer_userid"` // 老的客服人员userid。仅change_type为2和3有值
|
||||||
NewReceptionistUserID string `json:"new_servicer_userid"` // 新的客服人员userid。仅change_type为1和2有值
|
NewReceptionistUserID string `json:"new_servicer_userid"` // 新的客服人员userid。仅change_type为1和2有值
|
||||||
|
MsgCode string `json:"msg_code"` // 用于发送事件响应消息的code,仅change_type为1和3时,会返回该字段。可用该msg_code调用发送事件响应消息接口给客户发送回复语或结束语。
|
||||||
} `json:"event"` // 事件消息
|
} `json:"event"` // 事件消息
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user