wxapi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a96de275aacbda637fb5d2f52229cbfae165c168f7b657dd5c6608b742805e20
4
+ data.tar.gz: 0e4e7d0888413af25fa4392b9f5a4b7adfabbc8782721eb6fdd900392f3f3ccc
5
+ SHA512:
6
+ metadata.gz: 6050182becc85a57cab38778ae027fe22497a26ee33366c36c95edcb1b4736cf72f5f6ff1f42421d908a4b7d907df1259430cfe1cba38a364d6c98d1816d0b08
7
+ data.tar.gz: e8522ae70f8312f1cc18b039b99e51be5e5c782ff46b7b70cb7ca0a3e16a473993fbd63f248d18e284777acb316a4f93ebbad0c3f7a8976b0322a4975ebd6074
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in wechat_public_api.gemspec
6
+ gemspec
7
+
8
+ gem "rest-client", "~> 2.1"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 zhangS2
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # 简介
2
+
3
+ 基于ruby的微信公众号开发的API,包含对微信公众号菜单栏、客服消息、模板消息、帐号管理等接口的封装。
4
+
5
+ API长期更新维护,建议使用最新版本。
6
+
7
+ ## 安装
8
+
9
+ 添加 Gemfile:
10
+
11
+ ```ruby
12
+ gem 'wxapi'
13
+ ```
14
+
15
+ 执行 bundle:
16
+
17
+ $ bundle install
18
+
19
+ 或者直接通过 gem 安装:
20
+
21
+ $ gem install wxapi
22
+
23
+
24
+
25
+ 项目最早来源于 https://index.ruby-china.com/gems/wechat_public_api
26
+ 我在原项目的基础上进行了一些修改和优化,使其更加符合微信公众号开发的规范,增加了更多的扩展性。
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
3
+
4
+
5
+ task :upload do |t|
6
+ sh "gem build wxapi.gemspec"
7
+ sh "gem push wxapi-#{WxApi::VERSION}.gem"
8
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "wx_api"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/wx_api.rb ADDED
@@ -0,0 +1,41 @@
1
+ require "wxapi/version"
2
+ require "wxapi/menu"
3
+ require "wxapi/kf_message"
4
+ require "wxapi/templet_message"
5
+ require "wxapi/access_token"
6
+ require "wxapi/account"
7
+ require "wxapi/aes"
8
+ require "wxapi/utils"
9
+ require "wxapi/user"
10
+ require "wxapi/material"
11
+
12
+ class WxApi
13
+ include AccessToken
14
+ include Account
15
+ include Aes
16
+ include Kf
17
+ include Material
18
+ include Menu
19
+ include Tp
20
+ include User
21
+ include Utils
22
+
23
+ # 默认不缓存 access_token, access_token_cache = True 缓存
24
+ # @param <hash> aoptions
25
+ # => example
26
+ # wechat_api = WechatPublicApi.new appid: 'xx', app_secret: 'xx', access_token_cache: true
27
+ # wechat_api.app_id -- get appid
28
+ #
29
+ # api.get_access_token
30
+ #
31
+
32
+ attr_accessor :app_id, :app_secret, :access_token_cache
33
+ attr_accessor :prefix
34
+ def initialize(options={})
35
+ @app_id = options[:app_id]
36
+ @app_secret = options[:app_secret]
37
+ @access_token_cache = options[:access_token_cache]
38
+ @prefix = options[:api_prefix]
39
+ @prefix="https://api.weixin.qq.com" if @prefix.nil?
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ #####################################################
2
+ # 获得微信公众号的 access_token (Get wechat public access_token)
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-17
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module AccessToken
10
+
11
+ ###
12
+ # 获取 access_token
13
+ # 判断access_token_cache,决定是否需要缓存数据
14
+ # @return <string> nil or access_token
15
+ def get_access_token()
16
+ appid = @app_id
17
+ secret = @app_secret
18
+ access_token_cache = @access_token_cache
19
+
20
+ unless access_token_cache
21
+ response = HTTParty.get("#{prefix}/cgi-bin/token?grant_type=client_credential&appid=#{appid}&secret=#{secret}").body
22
+ response_body = (JSON.parse response)
23
+
24
+ # 抛出异常
25
+ throw response_body['errmsg'] unless response_body['access_token']
26
+
27
+ return response_body['access_token']
28
+ end
29
+
30
+ _cache_key = "#{appid}_access_token"
31
+ _cached_access_token = $redis.get _cache_key
32
+ if _cached_access_token == nil or _cached_access_token == ''
33
+ response = HTTParty.get("#{prefix}/cgi-bin/token?grant_type=client_credential&appid=#{appid}&secret=#{secret}").body
34
+ response_body = (JSON.parse response)
35
+
36
+ # 抛出异常
37
+ throw response_body['errmsg'] unless response_body['access_token']
38
+
39
+ _cached_access_token = response_body['access_token']
40
+ $redis.set _cache_key, _cached_access_token, ex: 2.minutes
41
+ end
42
+ _cached_access_token
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,112 @@
1
+ #####################################################
2
+ # 微信公众号帐号管理 (带参二维码获得)
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-18
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module Account
10
+ ###
11
+ # 获取临时场景带惨二维码,30天有效
12
+ # @param <int> sceneid -- 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000)
13
+ # @return <string> url -- 二维码网址
14
+ #
15
+ def get_qrscene(sceneid)
16
+ access_token = get_access_token()
17
+
18
+ # 获取ticket
19
+ uri = URI.parse("#{prefix}/cgi-bin/qrcode/create?access_token=#{access_token}")
20
+ post_data = {
21
+ 'expire_seconds' => 2592000,
22
+ 'action_name' => 'QR_SCENE',
23
+ 'action_info' => {'scene' => {'scene_id' => sceneid}}}
24
+ http = Net::HTTP.new(uri.host, uri.port)
25
+ http.use_ssl = true
26
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
27
+ request = Net::HTTP::Post.new("/cgi-bin/qrcode/create?access_token=#{access_token}")
28
+ request.add_field('Content-Type', 'application/json')
29
+ request.body = post_data.to_json
30
+ response = http.request(request)
31
+ content = JSON.parse(response.body)
32
+ ticket = content['ticket']
33
+
34
+ # 通过ticket换取二维码
35
+ "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=#{ticket}"
36
+ end
37
+
38
+ ###
39
+ # 获取永久二维码
40
+ # @param <int> sceneid -- 场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000)
41
+ # @return <string> url -- 二维码网址
42
+ #
43
+ def get_qrsrtscene(sceneid)
44
+ access_token = get_access_token()
45
+
46
+ # 获取ticket
47
+ uri = URI.parse("#{prefix}/cgi-bin/qrcode/create?access_token=#{access_token}")
48
+ post_data = {
49
+ 'action_name' => 'QR_LIMIT_STR_SCENE',
50
+ 'action_info' => {'scene' => {'scene_id' => sceneid}}}
51
+ http = Net::HTTP.new(uri.host, uri.port)
52
+ http.use_ssl = true
53
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
54
+ request = Net::HTTP::Post.new("/cgi-bin/qrcode/create?access_token=#{access_token}")
55
+ request.add_field('Content-Type', 'application/json')
56
+ request.body = post_data.to_json
57
+ response = http.request(request)
58
+ content = JSON.parse(response.body)
59
+ ticket = content['ticket']
60
+
61
+ # 通过ticket换取二维码
62
+ "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=#{ticket}"
63
+ end
64
+
65
+ ###
66
+ # 保存带参数二维码到指定位置
67
+ # @param <string> path -- 例如: "#{Rails.root}/public/qrcode"
68
+ # @param <string> filename -- 文件名,可选参数,默认不填写则使用时间戳+随机数的方式命名
69
+ #
70
+ # @return <string> path -- 二维码的保存路径
71
+ def save_qrcode(path, *filename)
72
+ # 判断是否需要新建文件
73
+ unless File.exist?(path)
74
+ FileUtils.makedirs(path)
75
+ end
76
+
77
+ if filename
78
+ path = "#{path}/#{filename}"
79
+ else
80
+ path = "#{path}/#{Time.now.to_i.to_s}_#{rand 1000.9999}"
81
+ end
82
+
83
+ File.open(path, 'wb') do |f|
84
+ f.write(HTTParty.get(url).body)
85
+ end
86
+
87
+ path
88
+ end
89
+
90
+ ###
91
+ # @param <string> longurl -- 需要被压缩的url
92
+ #
93
+ # @return <json> shorturl -- 返回短链接 {"errcode":0,"errmsg":"ok","short_url":"http:\/\/w.url.cn\/s\/AvCo6Ih"}
94
+ # if false
95
+ # @return {"errcode":40013,"errmsg":"invalid appid"}
96
+ #
97
+ def get_shorturl(longurl)
98
+ access_token = get_access_token()
99
+ uri = URI.parse("#{prefix}/cgi-bin/shorturl?access_token=#{access_token}")
100
+ post_data = {action: "long2short", long_url: longurl}
101
+ http = Net::HTTP.new(uri.host, uri.port)
102
+ http.use_ssl = true
103
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
104
+ request = Net::HTTP::Post.new("/cgi-bin/shorturl?access_token=#{access_token}")
105
+ request.add_field('Content-Type', 'application/json')
106
+ request.body = post_data.to_json
107
+ response = http.request(request)
108
+
109
+ JSON.parse(response.body)
110
+ end
111
+ end
112
+ end
data/lib/wxapi/aes.rb ADDED
@@ -0,0 +1,41 @@
1
+ #####################################################
2
+ # 消息加密解密
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-18
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module Aes
10
+ # 解密
11
+ def decrypt(key, dicrypted_string)
12
+ cipher = OpenSSL::Cipher::AES.new(256, :CBC)
13
+ cipher.decrypt
14
+ cipher.key = key
15
+ cipher.iv = '0000000000000000'
16
+ cipher.padding = 0
17
+ cipher.update(dicrypted_string) << cipher.final
18
+ end
19
+
20
+ # 加密
21
+ def encrypt(aes_key, text, app_id)
22
+ text = text.force_encoding("ASCII-8BIT")
23
+ random = SecureRandom.hex(8)
24
+ msg_len = [text.length].pack("N")
25
+ text = "#{random}#{msg_len}#{text}#{app_id}"
26
+ text = WxAuth.encode(text)
27
+ text = handle_cipher(:encrypt, aes_key, text)
28
+ Base64.encode64(text)
29
+ end
30
+
31
+ private
32
+ def handle_cipher(action, aes_key, text)
33
+ cipher = OpenSSL::Cipher.new('AES-256-CBC')
34
+ cipher.send(action)
35
+ cipher.padding = 0
36
+ cipher.key = aes_key
37
+ cipher.iv = aes_key[0...16]
38
+ cipher.update(text) + cipher.final
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,143 @@
1
+ #####################################################
2
+ # 客服消息管理 (about kf_account message)
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-17
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module Kf
10
+
11
+ ###
12
+ # execute post
13
+ # => example
14
+ # message = {
15
+ # touser: openid,
16
+ # msgtype: 'text',
17
+ # text: {content: content},
18
+ # customservice:{
19
+ # kf_account: 'xxxxxxxx'
20
+ # }
21
+ # }
22
+ #
23
+ # @param <JSON> message
24
+ #
25
+ def post_customer_message(message)
26
+ # get access_token
27
+ access_token = get_access_token()
28
+
29
+ uri = URI.parse("#{prefix}/cgi-bin/message/custom/send?access_token=#{access_token}")
30
+ http = Net::HTTP.new(uri.host, uri.port)
31
+ http.use_ssl = true
32
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
33
+ request = Net::HTTP::Post.new("/cgi-bin/message/custom/send?access_token=#{access_token}")
34
+ request.add_field('Content-Type', 'application/json')
35
+ request.body = message.to_json.gsub(/\\u([0-9a-z]{4})/) {|s| [$1.to_i(16)].pack("U")}
36
+ response = http.request(request)
37
+ JSON.parse(response.body)
38
+ end
39
+
40
+ ###
41
+ # @param <string> openid -- 用户的openid
42
+ # @param <string> content -- 需要发送的客服消息内容
43
+ #
44
+ # 发送文字消息
45
+ def kf_text_message(openid, content, kf_account=nil)
46
+
47
+ custom_message = {
48
+ touser: openid,
49
+ msgtype: 'text',
50
+ text: {content: content}
51
+ }
52
+
53
+ if kf_account
54
+ custom_message.merge({customservice:{account: kf_account}})
55
+ end
56
+
57
+ post_customer_message custom_message
58
+ end
59
+
60
+ ###
61
+ # @param <string> media_id -- 发送的图片/语音/视频/图文消息(点击跳转到图文消息页)的媒体ID
62
+ #
63
+ # 发送图片消息
64
+ def kf_image_message(openid, media_id, kf_account=nil)
65
+
66
+ custom_message = {
67
+ touser: openid,
68
+ msgtype: 'image',
69
+ image: {media_id: media_id}
70
+ }
71
+
72
+ if kf_account
73
+ custom_message.merge({customservice:{account: kf_account}})
74
+ end
75
+
76
+ post_customer_message custom_message
77
+ end
78
+
79
+ # 发送图文消息(点击跳转到图文消息页面)
80
+ def kf_mpnews_message(openid, media_id, kf_account=nil)
81
+ custom_message = {
82
+ touser: openid,
83
+ msgtype: 'mpnews',
84
+ mpnews: {media_id: media_id}
85
+ }
86
+ if kf_account
87
+ custom_message.merge({customservice:{account: kf_account}})
88
+ end
89
+
90
+ post_customer_message custom_message
91
+ end
92
+
93
+ ###
94
+ # @param <JSON> articles -- 图文消息列表
95
+ # => example
96
+ # articles = [
97
+ # {
98
+ # "title":"Happy Day",
99
+ # "description":"Is Really A Happy Day",
100
+ # "url":"URL",
101
+ # "picurl":"PIC_URL"
102
+ # },
103
+ # {
104
+ # "title":"Happy Day",
105
+ # "description":"Is Really A Happy Day",
106
+ # "url":"URL",
107
+ # "picurl":"PIC_URL"
108
+ # }
109
+ # ]
110
+ #
111
+ # 发送图文消息(点击跳转到外链)
112
+ def kf_news_message(openid, articles, kf_account=nil)
113
+ custom_message = {
114
+ touser: openid,
115
+ msgtype: 'news',
116
+ news: {
117
+ articles: articles
118
+ }
119
+ }
120
+
121
+ if kf_account
122
+ custom_message.merge({customservice:{account: kf_account}})
123
+ end
124
+ post_customer_message custom_message
125
+ end
126
+
127
+ # 发送语音消息
128
+ def kf_voice_message(openid, media_id, kf_account=nil)
129
+
130
+ custom_message = {
131
+ touser: openid,
132
+ msgtype: 'voice',
133
+ voice: {
134
+ media_id: media_id
135
+ }
136
+ }
137
+ if kf_account
138
+ custom_message.merge({customservice:{account: kf_account}})
139
+ end
140
+ post_customer_message custom_message
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,48 @@
1
+ #####################################################
2
+ # 微信公众号素材管理
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-18
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module Material
10
+ ###
11
+ # 获得临时图片素材的 media_id
12
+ # @param <string> file_path -- 图片素材的路径
13
+ #
14
+ # @return <json> {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789}
15
+ # if false
16
+ # @return <json> {"errcode":40004,"errmsg":"invalid media type"}
17
+ #
18
+ def upload_image_media(file_path)
19
+ # request access_token
20
+ access_token = get_access_token()
21
+ response = RestClient.post("#{prefix}/cgi-bin/media/upload",
22
+ {
23
+ access_token: access_token,
24
+ type: 'image',
25
+ media: File.new(file_path, 'rb')})
26
+ JSON.parse(response)
27
+ end
28
+
29
+ ###
30
+ # 新增永久图片素材
31
+ # @param <string> file_path -- 图片素材的路径
32
+ #
33
+ # @return <json> {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789}
34
+ # if false
35
+ # @return <json> {"errcode":40004,"errmsg":"invalid media type"}
36
+ #
37
+ def upload_image_material(file_path)
38
+ # request access_token
39
+ access_token = get_access_token()
40
+ response = RestClient.post('https://api.weixin.qq.com/cgi-bin/material/add_material',
41
+ {
42
+ access_token: access_token,
43
+ type: 'image',
44
+ media: File.new(file_path, 'rb')})
45
+ JSON.parse(response)
46
+ end
47
+ end
48
+ end
data/lib/wxapi/menu.rb ADDED
@@ -0,0 +1,72 @@
1
+ #####################################################
2
+ # 公众号自定义菜单栏模块 (about wechat public menu)
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-17
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module Menu
10
+ ###
11
+ # create wechat public menu
12
+ # @param <json> post_data
13
+ #
14
+ # => post_data example
15
+ # {
16
+ # "button": [
17
+ # {
18
+ # "type": "view",
19
+ # "name": "",
20
+ # "url": "",
21
+ # "sub_button": []
22
+ # },
23
+ # {
24
+ # "type": "click",
25
+ # "name": "",
26
+ # "key": "menu_3",
27
+ # "sub_button": []
28
+ # }
29
+ # ]
30
+ # }
31
+ #
32
+ # if success
33
+ # @return <JSON> {"errcode"=>0, "errmsg"=>"ok"}
34
+ # if failed
35
+ # @return <JSON> {"errcode"=>40166, "errmsg"=>"...."}
36
+ #
37
+ def create_menu(post_data)
38
+ # request access_token
39
+ access_token = get_access_token()
40
+ post_data = post_data.to_json.gsub(/\\u([0-9a-z]{4})/) {|s| [$1.to_i(16)].pack("U")}
41
+
42
+ uri = URI.parse("#{prefix}/cgi-bin/menu/create?access_token=#{access_token}")
43
+ http = Net::HTTP.new(uri.host, uri.port)
44
+ http.use_ssl = true
45
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
46
+ request = Net::HTTP::Post.new("/cgi-bin/menu/create?access_token=#{access_token}")
47
+ request.add_field('Content-Type', 'application/json')
48
+ request.body = post_data
49
+ response = http.request(request)
50
+ (JSON.parse response.body)
51
+ end
52
+
53
+ # get wechat public menu list
54
+ def query_menu()
55
+ # request access_token
56
+ access_token = get_access_token()
57
+ response = HTTParty.get("#{prefix}/cgi-bin/menu/get?access_token=#{access_token}").body
58
+ (JSON.parse response)
59
+ end
60
+
61
+ ###
62
+ # delete wechat query from access_token
63
+ # @return <JSON> {"errcode"=>0, "errmsg"=>"ok"}
64
+ #
65
+ def delete_menu()
66
+ # request access_token
67
+ access_token = get_access_token
68
+ response = HTTParty.get("#{prefix}/cgi-bin/menu/delete?access_token=#{access_token}").body
69
+ (JSON.parse response)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,103 @@
1
+ #####################################################
2
+ # 微信公众号模板消息相关接口 (About templet message of wechat public)
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-17
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module Tp
10
+ ###
11
+ # @param <JSON> message
12
+ # @param <string> url_params -- 消息路径
13
+ # 发送模板消息接口
14
+ def post_template_message(message, url_params)
15
+ # get access_token
16
+ access_token = get_access_token()
17
+
18
+ uri = URI.parse("#{prefix}/cgi-bin/message/template/#{url_params}?access_token=#{access_token}")
19
+ http = Net::HTTP.new(uri.host, uri.port)
20
+ http.use_ssl = true
21
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
22
+ request = Net::HTTP::Post.new("/cgi-bin/message/template/#{url_params}?access_token=#{access_token}")
23
+ request.add_field('Content-Type', 'application/json')
24
+ # 部分字符转换为json后 成为unicode编码
25
+ request.body = message.to_json.gsub(/\\u([0-9a-z]{4})/) {|s| [$1.to_i(16)].pack("U")}
26
+ response = http.request(request)
27
+ JSON.parse(response.body)
28
+ end
29
+
30
+ ###
31
+ # @param <string> openid -- 用户的openid
32
+ # @param <string> templet_id -- 微信公众号后台提供的模板ID
33
+ # @param <string> url -- 模板跳转链接
34
+ # @param <string> appid -- 小程序的appid
35
+ # @param <string> pagepath -- 小程序页面路径 (eq: /index/index)
36
+ # @param <JSON> data -- 模板数据
37
+ # => data example
38
+ # data = {
39
+ # "first": {
40
+ # "value":"恭喜你购买成功!",
41
+ # "color":"#173177"
42
+ # },
43
+ # "keyword1":{
44
+ # "value":"巧克力",
45
+ # "color":"#173177"
46
+ # },
47
+ # "keyword2": {
48
+ # "value":"39.8元",
49
+ # "color":"#173177"
50
+ # },
51
+ # "keyword3": {
52
+ # "value":"2014年9月22日",
53
+ # "color":"#173177"
54
+ # },
55
+ # "remark":{
56
+ # "value":"欢迎再次购买!",
57
+ # "color":"#173177"
58
+ # }
59
+ # }
60
+ # 小程序模板消息
61
+ def tp_miniprogram_message(openid, templet_id, url, appid, pagepath, data)
62
+ message = {
63
+ 'touser': openid,
64
+ 'template_id': templet_id,
65
+ 'url': url,
66
+ "miniprogram":{
67
+ "appid": appid,
68
+ "pagepath": pagepath
69
+ },
70
+ 'data': data
71
+ }
72
+
73
+ post_template_message message, 'send'
74
+ end
75
+
76
+ # 普通的模板消息,不跳转小程序
77
+ def tp_general_message(openid, templet_id, url, data)
78
+ message = {
79
+ 'touser': openid,
80
+ 'template_id': templet_id,
81
+ 'url': url,
82
+ 'data': data
83
+ }
84
+ post_template_message message, 'send'
85
+ end
86
+
87
+ # 删除模板
88
+ def tp_delete_template(templet_id)
89
+ message = {
90
+ 'template_id': template_id
91
+ }
92
+ post_template_message message, 'del_private_template'
93
+ end
94
+
95
+ # 获得模板列表
96
+ def tp_get_all()
97
+ access_token = get_access_token()
98
+ response = RestClient.get("#{prefix}cgi-bin/template/get_all_private_template?access_token=#{access_token}").body
99
+ response_body = (JSON.parse response)
100
+ response_body
101
+ end
102
+ end
103
+ end
data/lib/wxapi/user.rb ADDED
@@ -0,0 +1,41 @@
1
+ #####################################################
2
+ # 公众号用户管理
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-18
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module User
10
+ # 获取用户信息
11
+ # @return <JSON>
12
+ # {
13
+ # "subscribe": 1,
14
+ # "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",
15
+ # "nickname": "Band",
16
+ # "sex": 1,
17
+ # "language": "zh_CN",
18
+ # "city": "广州",
19
+ # "province": "广东",
20
+ # "country": "中国",
21
+ # "headimgurl":"http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
22
+ # "subscribe_time": 1382694957,
23
+ # "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
24
+ # "remark": "",
25
+ # "groupid": 0,
26
+ # "tagid_list":[128,2],
27
+ # "subscribe_scene": "ADD_SCENE_QR_CODE",
28
+ # "qr_scene": 98765,
29
+ # "qr_scene_str": ""
30
+ # }
31
+ #
32
+ # if failed
33
+ # {"errcode":40013,"errmsg":"invalid appid"}
34
+ #
35
+ def get_userinfo(openid)
36
+ # request access_token
37
+ access_token = get_access_token()
38
+ JSON.parse RestClient.get("#{prefix}/cgi-bin/user/info?access_token=#{access_token}&openid=#{openid}&lang=zh_CN").body
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ #####################################################
2
+ # 基于微信公众号的小工具,公用
3
+ # Created by zhangmingxin
4
+ # Date: 2018-05-18
5
+ # Wechat number: zmx119966
6
+ ####################################################
7
+
8
+ class WxApi
9
+ module Utils
10
+ # todo
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ class WxApi
2
+ VERSION = "1.0.0"
3
+ end
data/wxapi.gemspec ADDED
@@ -0,0 +1,39 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "wxapi/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "wxapi"
8
+ spec.version = WxApi::VERSION
9
+ spec.authors = ["黄滚 twitter: @hg_nohair"]
10
+ spec.email = ["xurenlu@gmail.com"]
11
+
12
+ spec.summary = %q{微信公众号开发API,包含对微信公众号菜单栏、客服消息、模板消息、帐号管理等接口的封装,API长期更新维护,建议使用最新版本.}
13
+ spec.description = %q{微信公众号开发API,包含对微信公众号菜单栏、客服消息、模板消息、帐号管理等接口的封装,API长期更新维护,建议使用最新版本,开发者使用文档:https://yuque.com/qianlansedehei/wechat_public_api}
14
+ spec.homepage = "https://github.com/xurenlu/wxapi"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against " \
23
+ # "public gem pushes."
24
+ # end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_development_dependency "bundler"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "redis"
36
+ spec.add_development_dependency "json"
37
+ spec.add_development_dependency "rest-client"
38
+ spec.add_development_dependency 'unf_ext', '~> 0.0.7.5'
39
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wxapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - '黄滚 twitter: @hg_nohair'
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-02-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rest-client
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: unf_ext
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.0.7.5
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.0.7.5
97
+ description: 微信公众号开发API,包含对微信公众号菜单栏、客服消息、模板消息、帐号管理等接口的封装,API长期更新维护,建议使用最新版本,开发者使用文档:https://yuque.com/qianlansedehei/wechat_public_api
98
+ email:
99
+ - xurenlu@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - Gemfile
106
+ - LICENSE.txt
107
+ - README.md
108
+ - Rakefile
109
+ - bin/console
110
+ - bin/setup
111
+ - lib/wx_api.rb
112
+ - lib/wxapi/access_token.rb
113
+ - lib/wxapi/account.rb
114
+ - lib/wxapi/aes.rb
115
+ - lib/wxapi/kf_message.rb
116
+ - lib/wxapi/material.rb
117
+ - lib/wxapi/menu.rb
118
+ - lib/wxapi/templet_message.rb
119
+ - lib/wxapi/user.rb
120
+ - lib/wxapi/utils.rb
121
+ - lib/wxapi/version.rb
122
+ - wxapi.gemspec
123
+ homepage: https://github.com/xurenlu/wxapi
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubygems_version: 3.5.4
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: 微信公众号开发API,包含对微信公众号菜单栏、客服消息、模板消息、帐号管理等接口的封装,API长期更新维护,建议使用最新版本.
146
+ test_files: []