Browse Source

增加自定义菜单api

wenzl 9 years ago
parent
commit
4a1470dcda
4 changed files with 397 additions and 0 deletions
  1. 128 0
      menu/button.go
  2. 237 0
      menu/menu.go
  3. 24 0
      message/message.go
  4. 8 0
      wechat.go

+ 128 - 0
menu/button.go

@@ -0,0 +1,128 @@
+package menu
+
+//Button 菜单按钮
+type Button struct {
+	Type       string    `json:"type,omitempty"`
+	Name       string    `json:"name,omitempty"`
+	Key        string    `json:"key,omitempty"`
+	URL        string    `json:"url,omitempty"`
+	MediaID    string    `json:"media_id,omitempty"`
+	SubButtons []*Button `json:"sub_button,omitempty"`
+}
+
+//SetSubButton 设置二级菜单
+func (btn *Button) SetSubButton(name string, subButtons []*Button) {
+	btn.Name = name
+	btn.SubButtons = subButtons
+	btn.Type = ""
+	btn.Key = ""
+	btn.URL = ""
+	btn.MediaID = ""
+}
+
+//SetClickButton btn 为click类型
+func (btn *Button) SetClickButton(name, key string) {
+	btn.Type = "click"
+	btn.Name = name
+	btn.Key = key
+	btn.URL = ""
+	btn.MediaID = ""
+	btn.SubButtons = nil
+}
+
+//SetViewButton view类型
+func (btn *Button) SetViewButton(name, url string) {
+	btn.Type = "view"
+	btn.Name = name
+	btn.URL = url
+	btn.Key = ""
+	btn.MediaID = ""
+	btn.SubButtons = nil
+}
+
+// SetScanCodePushButton 扫码推事件
+func (btn *Button) SetScanCodePushButton(name, key string) {
+	btn.Type = "scancode_push"
+	btn.Name = name
+	btn.Key = key
+	btn.URL = ""
+	btn.MediaID = ""
+	btn.SubButtons = nil
+}
+
+//SetScanCodeWaitMsgButton 设置 扫码推事件且弹出"消息接收中"提示框
+func (btn *Button) SetScanCodeWaitMsgButton(name, key string) {
+	btn.Type = "scancode_waitmsg"
+	btn.Name = name
+	btn.Key = key
+
+	btn.URL = ""
+	btn.MediaID = ""
+	btn.SubButtons = nil
+}
+
+//SetPicSysPhotoButton 设置弹出系统拍照发图按钮
+func (btn *Button) SetPicSysPhotoButton(name, key string) {
+	btn.Type = "pic_sysphoto"
+	btn.Name = name
+	btn.Key = key
+
+	btn.URL = ""
+	btn.MediaID = ""
+	btn.SubButtons = nil
+}
+
+//SetPicPhotoOrAlbumButton 设置弹出拍照或者相册发图类型按钮
+func (btn *Button) SetPicPhotoOrAlbumButton(name, key string) {
+	btn.Type = "pic_photo_or_album"
+	btn.Name = name
+	btn.Key = key
+
+	btn.URL = ""
+	btn.MediaID = ""
+	btn.SubButtons = nil
+}
+
+// SetPicWeixinButton 设置弹出微信相册发图器类型按钮
+func (btn *Button) SetPicWeixinButton(name, key string) {
+	btn.Type = "pic_weixin"
+	btn.Name = name
+	btn.Key = key
+
+	btn.URL = ""
+	btn.MediaID = ""
+	btn.SubButtons = nil
+}
+
+// SetLocationSelectButton 设置 弹出地理位置选择器 类型按钮
+func (btn *Button) SetLocationSelectButton(name, key string) {
+	btn.Type = "location_select"
+	btn.Name = name
+	btn.Key = key
+
+	btn.URL = ""
+	btn.MediaID = ""
+	btn.SubButtons = nil
+}
+
+//SetMediaIDButton  设置 下发消息(除文本消息) 类型按钮
+func (btn *Button) SetMediaIDButton(name, mediaID string) {
+	btn.Type = "media_id"
+	btn.Name = name
+	btn.MediaID = mediaID
+
+	btn.Key = ""
+	btn.URL = ""
+	btn.SubButtons = nil
+}
+
+//SetViewLimitedButton  设置 跳转图文消息URL 类型按钮
+func (btn *Button) SetViewLimitedButton(name, mediaID string) {
+	btn.Type = "view_limited"
+	btn.Name = name
+	btn.MediaID = mediaID
+
+	btn.Key = ""
+	btn.URL = ""
+	btn.SubButtons = nil
+}

+ 237 - 0
menu/menu.go

@@ -0,0 +1,237 @@
+package menu
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/silenceper/wechat/context"
+	"github.com/silenceper/wechat/util"
+)
+
+const (
+	menuCreateURL            = "https://api.weixin.qq.com/cgi-bin/menu/create"
+	menuGetURL               = "https://api.weixin.qq.com/cgi-bin/menu/get"
+	menuDeleteURL            = "https://api.weixin.qq.com/cgi-bin/menu/delete"
+	menuAddConditionalURL    = "https://api.weixin.qq.com/cgi-bin/menu/addconditional"
+	menuDeleteConditionalURL = "https://api.weixin.qq.com/cgi-bin/menu/delconditional"
+	menuTryMatchURL          = "https://api.weixin.qq.com/cgi-bin/menu/trymatch"
+)
+
+//Menu struct
+type Menu struct {
+	*context.Context
+}
+
+//reqMenu 设置菜单请求数据
+type reqMenu struct {
+	Button    []*Button  `json:"button,omitempty"`
+	MatchRule *MatchRule `json:"matchrule,omitempty"`
+}
+
+//reqDeleteConditional 删除个性化菜单请求数据
+type reqDeleteConditional struct {
+	MenuID int64 `json:"menuid"`
+}
+
+//reqMenuTryMatch 菜单匹配请求
+type reqMenuTryMatch struct {
+	UserID string `json:"user_id"`
+}
+
+//resConditionalMenu 个性化菜单返回结果
+type resConditionalMenu struct {
+	Button    []Button  `json:"button"`
+	MatchRule MatchRule `json:"matchrule"`
+	MenuID    int64     `json:"menuid"`
+}
+
+//resMenuTryMatch 菜单匹配请求结果
+type resMenuTryMatch struct {
+	util.CommonError
+
+	Button []Button `json:"button"`
+}
+
+//ResMenu 查询菜单的返回数据
+type ResMenu struct {
+	util.CommonError
+
+	Menu struct {
+		Button []Button `json:"button"`
+		MenuID int64    `json:"menuid"`
+	} `json:"menu"`
+	conditionalmenu []resConditionalMenu `json:"conditionalmenu"`
+}
+
+//MatchRule 个性化菜单规则
+type MatchRule struct {
+	GroupID            int32  `json:"group_id,omitempty"`
+	Sex                int32  `json:"sex,omitempty"`
+	Country            string `json:"country,omitempty"`
+	Province           string `json:"province,omitempty"`
+	City               string `json:"city,omitempty"`
+	ClientPlatformType int32  `json:"client_platform_type,omitempty"`
+	Language           string `json:"language,omitempty"`
+}
+
+//NewMenu 实例
+func NewMenu(context *context.Context) *Menu {
+	menu := new(Menu)
+	menu.Context = context
+	return menu
+}
+
+//SetMenu 设置按钮
+func (menu *Menu) SetMenu(buttons []*Button) error {
+	accessToken, err := menu.GetAccessToken()
+	if err != nil {
+		return err
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", menuCreateURL, accessToken)
+	reqMenu := &reqMenu{
+		Button: buttons,
+	}
+
+	response, err := util.PostJSON(uri, reqMenu)
+	if err != nil {
+		return err
+	}
+	var commError util.CommonError
+	err = json.Unmarshal(response, &commError)
+	if err != nil {
+		return err
+	}
+	if commError.ErrCode != 0 {
+		return fmt.Errorf("SetMenu Error , errcode=%d , errmsg=%s", commError.ErrCode, commError.ErrMsg)
+	}
+	return nil
+}
+
+//GetMenu 获取菜单配置
+func (menu *Menu) GetMenu() (resMenu ResMenu, err error) {
+	var accessToken string
+	accessToken, err = menu.GetAccessToken()
+	if err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", menuGetURL, accessToken)
+	var response []byte
+	response, err = util.HTTPGet(uri)
+	if err != nil {
+		return
+	}
+	err = json.Unmarshal(response, &resMenu)
+	if err != nil {
+		return
+	}
+	if resMenu.ErrCode != 0 {
+		err = fmt.Errorf("GetMenu Error , errcode=%d , errmsg=%s", resMenu.ErrCode, resMenu.ErrMsg)
+		return
+	}
+	return
+}
+
+//DeleteMenu 删除菜单
+func (menu *Menu) DeleteMenu() error {
+	accessToken, err := menu.GetAccessToken()
+	if err != nil {
+		return err
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", menuDeleteURL, accessToken)
+	response, err := util.HTTPGet(uri)
+	if err != nil {
+		return err
+	}
+	var commError util.CommonError
+	err = json.Unmarshal(response, &commError)
+	if err != nil {
+		return err
+	}
+	if commError.ErrCode != 0 {
+		return fmt.Errorf("GetMenu Error , errcode=%d , errmsg=%s", commError.ErrCode, commError.ErrMsg)
+	}
+	return nil
+}
+
+//AddConditional 添加个性化菜单
+func (menu *Menu) AddConditional(buttons []*Button, matchRule *MatchRule) error {
+	accessToken, err := menu.GetAccessToken()
+	if err != nil {
+		return err
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", menuAddConditionalURL, accessToken)
+	reqMenu := &reqMenu{
+		Button:    buttons,
+		MatchRule: matchRule,
+	}
+
+	response, err := util.PostJSON(uri, reqMenu)
+	if err != nil {
+		return err
+	}
+	var commError util.CommonError
+	err = json.Unmarshal(response, &commError)
+	if err != nil {
+		return err
+	}
+	if commError.ErrCode != 0 {
+		return fmt.Errorf("AddConditional Error , errcode=%d , errmsg=%s", commError.ErrCode, commError.ErrMsg)
+	}
+	return nil
+}
+
+//DeleteConditional 删除个性化菜单
+func (menu *Menu) DeleteConditional(menuID int64) error {
+	accessToken, err := menu.GetAccessToken()
+	if err != nil {
+		return err
+	}
+
+	uri := fmt.Sprintf("%s?access_token=%s", menuDeleteConditionalURL, accessToken)
+	reqDeleteConditional := &reqDeleteConditional{
+		MenuID: menuID,
+	}
+
+	response, err := util.PostJSON(uri, reqDeleteConditional)
+	if err != nil {
+		return err
+	}
+	var commError util.CommonError
+	err = json.Unmarshal(response, &commError)
+	if err != nil {
+		return err
+	}
+	if commError.ErrCode != 0 {
+		return fmt.Errorf("DeleteConditional Error , errcode=%d , errmsg=%s", commError.ErrCode, commError.ErrMsg)
+	}
+	return nil
+}
+
+//MenuTryMatch 菜单匹配
+func (menu *Menu) MenuTryMatch(userID string) (buttons []Button, err error) {
+	var accessToken string
+	accessToken, err = menu.GetAccessToken()
+	if err != nil {
+		return
+	}
+	uri := fmt.Sprintf("%s?access_token=%s", menuTryMatchURL, accessToken)
+	reqMenuTryMatch := &reqMenuTryMatch{userID}
+	var response []byte
+	response, err = util.PostJSON(uri, reqMenuTryMatch)
+	if err != nil {
+		return
+	}
+	var resMenuTryMatch resMenuTryMatch
+	err = json.Unmarshal(response, &resMenuTryMatch)
+	if err != nil {
+		return
+	}
+	if resMenuTryMatch.ErrCode != 0 {
+		err = fmt.Errorf("MenuTryMatch Error , errcode=%d , errmsg=%s", resMenuTryMatch.ErrCode, resMenuTryMatch.ErrMsg)
+		return
+	}
+	buttons = resMenuTryMatch.Button
+	return
+}

+ 24 - 0
message/message.go

@@ -72,6 +72,30 @@ type MixMessage struct {
 	Latitude  string `xml:"Latitude"`
 	Longitude string `xml:"Longitude"`
 	Precision string `xml:"Precision"`
+	MenuID    string `xml:"MenuId"`
+
+	ScanCodeInfo struct {
+		ScanType   string `xml:"ScanType"`
+		ScanResult string `xml:"ScanResult"`
+	} `xml:"ScanCodeInfo"`
+
+	SendPicsInfo struct {
+		Count   int32      `xml:"Count"`
+		PicList []EventPic `xml:"PicList>item"`
+	} `xml:"SendPicsInfo"`
+
+	SendLocationInfo struct {
+		LocationX float64 `xml:"Location_X"`
+		LocationY float64 `xml:"Location_Y"`
+		Scale     float64 `xml:"Scale"`
+		Label     string  `xml:"Label"`
+		Poiname   string  `xml:"Poiname"`
+	}
+}
+
+//EventPic 发图事件推送
+type EventPic struct {
+	PicMd5Sum string `xml:PicMd5Sum`
 }
 
 //EncryptedXMLMsg 安全模式下的消息体

+ 8 - 0
wechat.go

@@ -8,6 +8,7 @@ import (
 	"github.com/silenceper/wechat/context"
 	"github.com/silenceper/wechat/js"
 	"github.com/silenceper/wechat/material"
+	"github.com/silenceper/wechat/menu"
 	"github.com/silenceper/wechat/oauth"
 	"github.com/silenceper/wechat/server"
 )
@@ -68,3 +69,10 @@ func (wc *Wechat) GetJs(req *http.Request, writer http.ResponseWriter) *js.Js {
 	wc.Context.Writer = writer
 	return js.NewJs(wc.Context)
 }
+
+//GetMenu init
+func (wc *Wechat) GetMenu(req *http.Request, writer http.ResponseWriter) *menu.Menu {
+	wc.Context.Request = req
+	wc.Context.Writer = writer
+	return menu.NewMenu(wc.Context)
+}