qy_wechat_api 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ module QyWechatApi
3
+
4
+ class ResultHandler
5
+
6
+ attr_accessor :code, :cn_msg, :en_msg, :result
7
+
8
+ def initialize(code, en_msg, result={})
9
+ @code = code || OK_CODE
10
+ @en_msg = en_msg || OK_MSG
11
+ @cn_msg = GLOBAL_CODES[@code.to_i]
12
+ @result = package_result(result)
13
+ end
14
+
15
+ # This method is to valid the current request if is true or is false
16
+ def is_ok?
17
+ code == OK_CODE
18
+ end
19
+ alias_method :ok?, :is_ok?
20
+
21
+ # e.g.:
22
+ # 45009: api freq out of limit(接口调用超过限制)
23
+ def full_message
24
+ "#{code}: #{en_msg}(#{cn_msg})."
25
+ end
26
+ alias_method :full_messages, :full_message
27
+
28
+ def full_error_message
29
+ full_message if !is_ok?
30
+ end
31
+ alias_method :full_error_messages, :full_error_message
32
+ alias_method :errors, :full_error_message
33
+
34
+ private
35
+
36
+ # if define Rails constant
37
+ # result = QyWechatApi::ResultHandler.new("0", "success", {:ok => "true"})
38
+ # result.result["ok"] #=> true
39
+ # result.result[:ok] #=> true
40
+ # result.result['ok'] #=> true
41
+ def package_result(result)
42
+ if defined?(Rails)
43
+ ActiveSupport::HashWithIndifferentAccess.new(result)
44
+ else
45
+ result
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ module QyWechatApi
3
+ class ObjectStorage < Storage
4
+ def valid?
5
+ super
6
+ end
7
+
8
+ def token_expired?
9
+ # 如果当前token过期时间小于现在的时间,则重新获取一次
10
+ client.expired_at <= Time.now.to_i
11
+ end
12
+
13
+ def refresh_token
14
+ super
15
+ end
16
+
17
+ def access_token
18
+ super
19
+ client.access_token
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module QyWechatApi
3
+ class RedisStorage < Storage
4
+ def valid?
5
+ weixin_redis.del(client.redis_key)
6
+ super
7
+ end
8
+
9
+ def token_expired?
10
+ weixin_redis.hvals(client.redis_key).empty?
11
+ end
12
+
13
+ def refresh_token
14
+ super
15
+ weixin_redis.hmset(client.redis_key, "access_token", client.access_token,
16
+ "expired_at", client.expired_at)
17
+ weixin_redis.expireat(client.redis_key, client.expired_at.to_i-10) # 提前10秒超时
18
+ end
19
+
20
+ def access_token
21
+ super
22
+ client.access_token = weixin_redis.hget(client.redis_key, "access_token")
23
+ client.expired_at = weixin_redis.hget(client.redis_key, "expired_at")
24
+ client.access_token
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ module QyWechatApi
4
+ class Storage
5
+
6
+ attr_accessor :client
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ def self.init_with(client)
13
+ if QyWechatApi.weixin_redis.nil?
14
+ ObjectStorage.new(client)
15
+ else
16
+ RedisStorage.new(client)
17
+ end
18
+ end
19
+
20
+ def valid?
21
+ authenticate["valid"]
22
+ end
23
+
24
+ def authenticate
25
+ auth_result = http_get_access_token
26
+ auth = false
27
+ if auth_result.is_ok?
28
+ set_access_token_for_client(auth_result.result)
29
+ auth = true
30
+ end
31
+ {"valid" => auth, "handler" => auth_result}
32
+ end
33
+
34
+ def refresh_token
35
+ handle_valid_exception
36
+ set_access_token_for_client
37
+ end
38
+
39
+ def access_token
40
+ refresh_token if token_expired?
41
+ end
42
+
43
+ def token_expired?
44
+ raise NotImplementedError, "Subclasses must implement a token_expired? method"
45
+ end
46
+
47
+ def set_access_token_for_client(access_token_infos=nil)
48
+ token_infos = access_token_infos || http_get_access_token.result
49
+ client.access_token = token_infos["access_token"]
50
+ client.expired_at = Time.now.to_i + token_infos["expires_in"].to_i
51
+ end
52
+
53
+ def http_get_access_token
54
+ QyWechatApi.http_get_without_token("/gettoken", authenticate_headers)
55
+ end
56
+
57
+ def authenticate_headers
58
+ {corpid: client.corp_id, corpsecret: client.group_secret}
59
+ end
60
+
61
+ private
62
+
63
+ def handle_valid_exception
64
+ auth_result = authenticate
65
+ if !auth_result["valid"]
66
+ result_handler = auth_result["handler"]
67
+ raise Errors::ValidAccessTokenException, result_handler.full_error_message
68
+ end
69
+ end
70
+
71
+ def weixin_redis
72
+ QyWechatApi.weixin_redis
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ module QyWechatApi
2
+ VERSION = "1.0.0.beta1"
3
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'qy_wechat_api/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "qy_wechat_api"
9
+ spec.version = QyWechatApi::VERSION
10
+ spec.authors = ["lanrion"]
11
+ spec.email = ["huaitao-deng@foxmail.com"]
12
+ spec.summary = %q{企业微信高级API Ruby版本}
13
+ spec.description = %q{企业微信高级API Ruby版本}
14
+ spec.homepage = "https://github.com/lanrion/qy_wechat_api"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "rest-client", ">= 1.6.7"
23
+ # A streaming JSON parsing and encoding library for Ruby (C bindings to yajl)
24
+ # https://github.com/brianmario/yajl-ruby
25
+ spec.add_dependency "yajl-ruby", "~> 1.2.0"
26
+
27
+ spec.add_dependency "carrierwave", "~> 0.10.0"
28
+ spec.add_dependency 'mini_magick', '~> 3.7.0'
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.6"
31
+ spec.add_development_dependency "rake"
32
+ end
@@ -0,0 +1,72 @@
1
+ require "spec_helper"
2
+
3
+ describe QyWechatApi::Api::Media do
4
+
5
+ let(:image_jpg_path) do
6
+ "#{File.dirname(__FILE__)}/medias/ruby-logo.jpg"
7
+ end
8
+
9
+ let(:image_ico_path) do
10
+ "#{File.dirname(__FILE__)}/medias/favicon.ico"
11
+ end
12
+
13
+ let(:image_jpg_file) do
14
+ File.new(image_jpg_path)
15
+ end
16
+
17
+ let(:image_ico_file) do
18
+ File.new(image_ico_path)
19
+ end
20
+
21
+ let(:remote_png_path) do
22
+ "https://ruby-china-files.b0.upaiyun.com/user/big_avatar/273.jpg"
23
+ end
24
+
25
+ let(:remote_jpg_path) do
26
+ "http://g.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=ce55457e4334970a537e187df4a3baad/03087bf40ad162d99455ef4d13dfa9ec8b13632762d0ed14.jpg"
27
+ end
28
+
29
+ it "can upload a jpg File image" do
30
+ response = $client.media.upload(image_jpg_file, "image")
31
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
32
+ expect(response.result.keys).to eq(["type", "media_id", "created_at"])
33
+ end
34
+
35
+ it "can upload a ico File image" do
36
+ response = $client.media.upload(image_ico_file, "image")
37
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
38
+ expect(response.result.keys).to eq(["type", "media_id", "created_at"])
39
+ end
40
+
41
+ it "can upload a local image" do
42
+ response = $client.media.upload(image_jpg_path, "image")
43
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
44
+ expect(response.result.keys).to eq(["type", "media_id", "created_at"])
45
+ end
46
+
47
+ it "can upload a local ico image" do
48
+ response = $client.media.upload(image_ico_path, "image")
49
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
50
+ expect(response.result.keys).to eq(["type", "media_id", "created_at"])
51
+ end
52
+
53
+ it "can upload a remote png image" do
54
+ response = $client.media.upload(remote_png_path, "image")
55
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
56
+ expect(response.result.keys).to eq(["type", "media_id", "created_at"])
57
+ end
58
+
59
+ it "can upload a remote jpg image" do
60
+ response = $client.media.upload(remote_jpg_path, "image")
61
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
62
+ expect(response.result.keys).to eq(["type", "media_id", "created_at"])
63
+ end
64
+
65
+ # it "#download_media_url return a String url" do
66
+ # image = $client.media.upload(image_ico_path, "image")
67
+ # media_id = image.result["media_id"]
68
+ # image_url = $client.download_media_url(media_id)
69
+ # expect(image_url.class).to eq(String)
70
+ # end
71
+
72
+ end
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+
3
+ describe QyWechatApi::Api::Menu do
4
+ let(:menu_string) do
5
+ '{
6
+ "button": [
7
+ {
8
+ "name": "扫码",
9
+ "sub_button": [
10
+ {
11
+ "type": "scancode_waitmsg",
12
+ "name": "扫码带提示",
13
+ "key": "rselfmenu_0_0",
14
+ "sub_button": [ ]
15
+ },
16
+ {
17
+ "type": "scancode_push",
18
+ "name": "扫码推事件",
19
+ "key": "rselfmenu_0_1",
20
+ "sub_button": [ ]
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "name": "发图",
26
+ "sub_button": [
27
+ {
28
+ "type": "pic_sysphoto",
29
+ "name": "系统拍照发图",
30
+ "key": "rselfmenu_1_0",
31
+ "sub_button": [ ]
32
+ },
33
+ {
34
+ "type": "pic_photo_or_album",
35
+ "name": "拍照或者相册发图",
36
+ "key": "rselfmenu_1_1",
37
+ "sub_button": [ ]
38
+ },
39
+ {
40
+ "type": "pic_weixin",
41
+ "name": "微信相册发图",
42
+ "key": "rselfmenu_1_2",
43
+ "sub_button": [ ]
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "name": "发送位置",
49
+ "type": "location_select",
50
+ "key": "rselfmenu_2_0"
51
+ }
52
+ ]
53
+ }'
54
+ end
55
+
56
+ it "#create" do
57
+ response = $client.menu.create(menu_string, 1)
58
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
59
+ end
60
+ end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+ describe QyWechatApi::Api::Message do
3
+ let(:text_message) do
4
+ "text message"
5
+ end
6
+
7
+ let(:image_path) do
8
+ "#{File.dirname(__FILE__)}/medias/ruby-logo.jpg"
9
+ end
10
+
11
+ let(:image_file) do
12
+ File.new(image_path)
13
+ end
14
+
15
+ it "#send_text_message" do
16
+ response = $client.message.send_text("@all", "@all", "@all", 1, text_message)
17
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
18
+ end
19
+
20
+ it "#send_image_message" do
21
+ response = $client.media.upload(image_path, "image").result
22
+ response = $client.message.send_image("@all", "@all", "@all", 1, response["media_id"])
23
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
24
+ end
25
+
26
+
27
+ it "#send_news" do
28
+ articles = [{
29
+ "title" => "Happy Day",
30
+ "description" => "Is Really A Happy Day",
31
+ "url" => "http://www.baidu.com",
32
+ "picurl" => "http://www.baidu.com/img/bdlogo.gif"
33
+ },
34
+ {
35
+ "title" => "Happy Day",
36
+ "description" => "Is Really A Happy Day",
37
+ "url" => "http://www.baidu.com",
38
+ "picurl"=> "http://www.baidu.com/img/bdlogo.gif"
39
+ }]
40
+ response = $client.message.send_news("@all", "@all", "@all", 1, articles)
41
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
42
+ end
43
+
44
+ it "#send_file" do
45
+ response = $client.media.upload(image_path, "image").result
46
+ response = $client.message.send_file("@all", "@all", "@all", 1, response["media_id"])
47
+ expect(response.code).to eq(QyWechatApi::OK_CODE)
48
+ end
49
+
50
+ end
@@ -0,0 +1,115 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ require "redis-namespace"
18
+ require "redis"
19
+
20
+ require "qy_wechat_api"
21
+ require "pry-rails"
22
+ corpid = "wxb9ce1d023fe6eb69"
23
+ corpsecret = "UOofFIah4PVLmkG8xMH3lpDxj6NTnQSKMrFt-HubiPB4kjB09EmTVcUjgNeermps"
24
+
25
+
26
+ # Comment to test for ClientStorage
27
+ redis = Redis.new(:host => "127.0.0.1",:port => "6379")
28
+
29
+ namespace = "qy_wechat_api:redis_storage"
30
+
31
+ # cleanup keys in the current namespace when restart server everytime.
32
+ exist_keys = redis.keys("#{namespace}:*")
33
+ exist_keys.each{|key|redis.del(key)}
34
+
35
+ redis_with_ns = Redis::Namespace.new("#{namespace}", :redis => redis)
36
+
37
+ QyWechatApi.configure do |config|
38
+ config.redis = redis_with_ns
39
+ end
40
+
41
+ $client = QyWechatApi::Client.new(corpid, corpsecret)
42
+
43
+ RSpec.configure do |config|
44
+ # rspec-expectations config goes here. You can use an alternate
45
+ # assertion/expectation library such as wrong or the stdlib/minitest
46
+ # assertions if you prefer.
47
+ config.expect_with :rspec do |expectations|
48
+ # This option will default to `true` in RSpec 4. It makes the `description`
49
+ # and `failure_message` of custom matchers include text for helper methods
50
+ # defined using `chain`, e.g.:
51
+ # be_bigger_than(2).and_smaller_than(4).description
52
+ # # => "be bigger than 2 and smaller than 4"
53
+ # ...rather than:
54
+ # # => "be bigger than 2"
55
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
56
+ end
57
+
58
+ # rspec-mocks config goes here. You can use an alternate test double
59
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
60
+ config.mock_with :rspec do |mocks|
61
+ # Prevents you from mocking or stubbing a method that does not exist on
62
+ # a real object. This is generally recommended, and will default to
63
+ # `true` in RSpec 4.
64
+ mocks.verify_partial_doubles = true
65
+ end
66
+
67
+ # The settings below are suggested to provide a good initial experience
68
+ # with RSpec, but feel free to customize to your heart's content.
69
+ =begin
70
+ # These two settings work together to allow you to limit a spec run
71
+ # to individual examples or groups you care about by tagging them with
72
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
73
+ # get run.
74
+ config.filter_run :focus
75
+ config.run_all_when_everything_filtered = true
76
+
77
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
78
+ # For more details, see:
79
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
80
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
81
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
82
+ config.disable_monkey_patching!
83
+
84
+ # This setting enables warnings. It's recommended, but in some cases may
85
+ # be too noisy due to issues in dependencies.
86
+ config.warnings = true
87
+
88
+ # Many RSpec users commonly either run the entire suite or an individual
89
+ # file, and it's useful to allow more verbose output when running an
90
+ # individual spec file.
91
+ if config.files_to_run.one?
92
+ # Use the documentation formatter for detailed output,
93
+ # unless a formatter has already been configured
94
+ # (e.g. via a command-line flag).
95
+ config.default_formatter = 'doc'
96
+ end
97
+
98
+ # Print the 10 slowest examples and example groups at the
99
+ # end of the spec run, to help surface which specs are running
100
+ # particularly slow.
101
+ config.profile_examples = 10
102
+
103
+ # Run specs in random order to surface order dependencies. If you find an
104
+ # order dependency and want to debug it, you can fix the order by providing
105
+ # the seed, which is printed after each run.
106
+ # --seed 1234
107
+ config.order = :random
108
+
109
+ # Seed global randomization in this process using the `--seed` CLI option.
110
+ # Setting this allows you to use `--seed` to deterministically reproduce
111
+ # test failures related to randomization by passing the same `--seed` value
112
+ # as the one that triggered the failure.
113
+ Kernel.srand config.seed
114
+ =end
115
+ end