wechate 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a110108b510cc216f3545f8560781460ffec9688
4
- data.tar.gz: dcf2e4b471bbe472e0a381fa55bd87f0563f28ff
3
+ metadata.gz: 23a0e41d3c5fcefc8dbe4a86ed23adeff8e775eb
4
+ data.tar.gz: f60d74769afdffbaf129826cbe43e580012667e5
5
5
  SHA512:
6
- metadata.gz: c974f16723bd4d79560fca352c100542abe79dc918917612e4c3de80cc97c150280c6ae0b8211dd18e7bcab06d08f319efa9c7758dac4a95d1ef9e74b1b680d7
7
- data.tar.gz: c6b87ebee98420a598b2b19119acc4de8de4287869476460112ae368369dcb0a7b6d6c0ed6065a3f6d5add3f7700fd1373d672b03a45af50f5a53797386d96ca
6
+ metadata.gz: 9251e27fa2454efde85ff544a5505adad10c78193ba0c42d441d49a3fcec05d8c08aa11fbdb3692d855ffa9be8e62ef9dd09e6ae49f7f701a8f217d41c518637
7
+ data.tar.gz: 80b69aa06f2186acf2087de14d6c7a549afef46ace84be9ebac8e9a54c61ae5fea082e361bcd877e3b2bcd6a84c6777065c0ca4db17f07ed71fb278b8926f6da
@@ -0,0 +1 @@
1
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,16 @@
1
+ require "base64"
2
+ require "openssl"
3
+ require 'digest/sha1'
4
+ require 'faraday'
5
+ require 'nokogiri'
6
+
7
+
8
+ require "wechate/client"
9
+ require "wechate/aes"
10
+ require "wechate/http"
11
+ require "wechate/factory"
12
+ require "wechate/worker"
13
+
14
+ module Wechate
15
+
16
+ end
@@ -0,0 +1,51 @@
1
+ require "base64"
2
+ require "openssl"
3
+ require 'digest/sha1'
4
+ module Wechate
5
+ module Aes
6
+ def pad text
7
+ text_length = text.length
8
+ amount_to_pad = 32 - (text_length % 32)
9
+ amount_to_pad = 32 if amount_to_pad == 0
10
+ pad = amount_to_pad
11
+ text + amount_to_pad.chr * amount_to_pad
12
+ end
13
+
14
+ def encode message,timestamp,nonce,from=nil,prefix='2aab967d2fff18c6'
15
+ msg = (prefix.bytes + [message.bytes.length].pack('N').bytes + "#{message}#{from}".bytes).pack('c*')
16
+ msg = pad(msg)
17
+ key = @aeskey
18
+ key = Base64.decode64(key)
19
+ cipher = OpenSSL::Cipher::Cipher
20
+ aes = cipher.new('aes-256-cbc').encrypt
21
+ aes.padding = 0
22
+ aes.key = key
23
+ aes.iv = key[0..15]
24
+ ret = aes.update(msg) + aes.final
25
+ msg = Base64.encode64(ret)
26
+ msg.gsub!("\n","")
27
+ [msg,sign(@token,timestamp,nonce,msg)]
28
+ end
29
+
30
+ def decode echostr,timestamp,nonce,msg_signature=nil
31
+ key = @aeskey
32
+ sign(@token,timestamp,nonce,echostr)
33
+ cipher = OpenSSL::Cipher::Cipher
34
+ aes = cipher.new('aes-256-cbc').decrypt
35
+ aes.padding = 0
36
+ key = Base64.decode64 key
37
+ msg = Base64.decode64 echostr
38
+ aes.key = key
39
+ aes.iv = key[0..15]
40
+ result = aes.update msg + aes.final
41
+ length = result[16,4].unpack('N').first
42
+ result[20,length].force_encoding('utf-8')
43
+ end
44
+
45
+ def sign(token,timestamp,nonce,msg_encrypt)
46
+ Digest::SHA1.hexdigest([token,timestamp,nonce,msg_encrypt].sort.join)
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,157 @@
1
+ require 'nokogiri'
2
+ require_relative 'http'
3
+ require_relative 'aes'
4
+
5
+ module Wechate
6
+ OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
7
+ class Client
8
+ include Wechate::Http
9
+ include Wechate::Aes
10
+
11
+ def initialize aeskey,token,appid=nil,secret=nil
12
+ @host_api = 'https://qyapi.weixin.qq.com'
13
+ @aeskey = aeskey
14
+ @token = token
15
+ @appid = appid
16
+ @secret = secret
17
+ @access_token = nil
18
+ @worker = nil
19
+
20
+ @urls = {
21
+ :user_info => "/cgi-bin/user/info",
22
+ :menu => "/cgi-bin/menu/create",
23
+ :menu_get => "/cgi-bin/menu/get",
24
+ :user_list => "/cgi-bin/user/get",
25
+ :group_list => "/cgi-bin/groups/get",
26
+ :user_change_group => "/cgi-bin/groups/members/update",
27
+ :send => "/cgi-bin/message/custom/send",
28
+ :media => "/cgi-bin/media/get",
29
+ :message_send => "/cgi-bin/message/send",
30
+ :token => "/cgi-bin/gettoken",
31
+ :department_list => "/cgi-bin/department/list",
32
+ }
33
+
34
+ end
35
+
36
+ def worker=(worker)
37
+ @worker = worker
38
+ end
39
+
40
+ def department_list
41
+ data = api_get(:department_list)
42
+ data["department"]
43
+ end
44
+
45
+ def api_get_user_list(next_openid=nil)
46
+ args = {}
47
+ args = {next_openid:next_openid} if next_openid
48
+ data = api_get(:user_list,args)
49
+ return nil unless data
50
+ data["data"]["openid"]
51
+ end
52
+
53
+ def ping message,users=nil,group=nil
54
+ params = {"touser"=> users ? users.join('|') : "@all","agentid"=>"1","safe"=>"0"}
55
+ case message
56
+ when String
57
+ params["msgtype"] = "text"
58
+ params["text"] = {
59
+ "content"=> message
60
+ }
61
+ when File
62
+ params["msgtype"] = "text"
63
+ params["file"] = {
64
+ "media_id"=> "MEDIA_ID"
65
+ }
66
+ when Array
67
+ params["msgtype"] = "news"
68
+ params["news"] = {
69
+ "articles"=>message
70
+ }
71
+ end
72
+
73
+ api_post(:message_send,params.to_json.gsub(/\\u([0-9a-z]{4})/) {|s| [$1.to_i(16)].pack("U")})
74
+ end
75
+
76
+ def pong message,timestamp,nonce
77
+ xml = Nokogiri::XML(message)
78
+ fromUserName = xml.xpath("//FromUserName").text
79
+ toUserName = xml.xpath("//ToUserName").text
80
+ timestamp = Time.now.to_i.to_s
81
+ response = get_response(xml)
82
+ msg_encrypt = case response
83
+ when String
84
+ build_text(fromUserName,toUserName,timestamp,response)
85
+ when Array
86
+ build_news(fromUserName,toUserName,timestamp,response)
87
+ when URI
88
+ build_image(fromUserName,toUserName,timestamp,response)
89
+ end
90
+ encode(msg_encrypt,timestamp,nonce,toUserName)
91
+ end
92
+
93
+ def get_response xml
94
+ raise "Invalid worker" unless @worker
95
+ create_time = xml.xpath("//CreateTime").text
96
+ msg_type = xml.xpath("//MsgType").text
97
+ fromUserName = xml.xpath("//FromUserName").text
98
+ agent_id = xml.xpath("//AgentID").text
99
+
100
+ ret = case msg_type
101
+ when "event"
102
+ event = xml.xpath("//Event").text
103
+ event_key = xml.xpath("//EventKey").text
104
+ ret = case event
105
+ when "click"
106
+ @worker.click_callback(fromUserName,event_key,create_time)
107
+ when "scancode_push"
108
+ when "scancode_waitmsg"
109
+ @worker.scancode_callback(fromUserName,event_key,xml.xpath("//ScanResult").text)
110
+ when "location_select"
111
+ when "pic_photo_or_album"
112
+ @worker.unknown_event(fromUserName,event_key)
113
+ else
114
+ @worker.unknown_event(fromUserName,event_key)
115
+ end
116
+ else
117
+ msg_id = xml.xpath("//MsgId").text
118
+ case msg_type
119
+ when "text"
120
+ @worker.text_callback(fromUserName,msg_id,xml.xpath("//PicUrl").text)
121
+ when "image"
122
+ args = {:pic=>xml.xpath("//PicUrl").text,
123
+ :media_id=>xml.xpath("//MediaId").text}
124
+ @worker.image_callback(fromUserName,msg_id,args)
125
+ when "location"
126
+ args = {:location=>{:lat=>xml.xpath("//Location_X").text,
127
+ :lng=>xml.xpath("//Location_Y").text},
128
+ :scale=>xml.xpath("//Scale").text,
129
+ :lable=>xml.xpath("//Label").text}
130
+ @worker.location_callback(fromUserName,msg_id,args)
131
+ when "voice"
132
+ args = {:format=>xml.xpath("//Format").text,
133
+ :media_id=>xml.xpath("//MediaId").text}
134
+ @worker.voice_callback(fromUserName,msg_id,args)
135
+ end
136
+ end
137
+ end
138
+
139
+ def build_text fromUserName,toUserName,timestamp,response
140
+ "<xml><ToUserName><![CDATA[#{fromUserName}]]></ToUserName><FromUserName><![CDATA[#{toUserName}]]></FromUserName><CreateTime>#{timestamp}</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[#{response}]]></Content></xml>"
141
+ end
142
+
143
+ def build_image fromUserName,toUserName,timestamp,response
144
+ "<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[news]]></MsgType><ArticleCount>2</ArticleCount><Articles><item><Title><![CDATA[title1]]></Title> <Description><![CDATA[description1]]></Description><PicUrl><![CDATA[picurl]]></PicUrl><Url><![CDATA[url]]></Url></item></Articles></xml>"
145
+ end
146
+
147
+ def build_news fromUserName,toUserName,timestamp,response
148
+ items = response.map do |item|
149
+ "<item><Title><![CDATA[#{item.title}]]></Title><Description><![CDATA[#{item.description}]]></Description><PicUrl><![CDATA[#{item.picurl}]]></PicUrl><Url><![CDATA[#{item.url}]]></Url></item>"
150
+ end.join
151
+
152
+ "<xml><ToUserName><![CDATA[#{fromUserName}]]></ToUserName><FromUserName><![CDATA[#{toUserName}]]></FromUserName><CreateTime>#{timestamp}</CreateTime><MsgType><![CDATA[news]]></MsgType><ArticleCount>#{response.length}</ArticleCount><Articles>#{items}</Articles></xml>"
153
+ end
154
+
155
+ end
156
+
157
+ end
@@ -0,0 +1,7 @@
1
+ module Wechate
2
+ class Factory
3
+ def self.create_worker(worker)
4
+ worker.new
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,50 @@
1
+ require 'faraday'
2
+ require "openssl"
3
+
4
+ module Wechate
5
+ module Http
6
+ def api_get(url,args={})
7
+ get_api_token unless @access_token
8
+ url = "#{@urls[url]}?"
9
+ args.each{|k,v| url = url + "&#{k.to_s}=#{v.to_s}"}
10
+
11
+ if !@access_token then
12
+ get_api_token
13
+ return nil unless @access_token
14
+ end
15
+
16
+ conn = Faraday.new(:url =>@host_api)
17
+ req = url + "&access_token=#{@access_token}"
18
+ response = conn.get req
19
+ ret = JSON.parse(response.body)
20
+ if ret["errcode"]!=0 then #expired
21
+ if "42001"==ret["errcode"] then
22
+ return nil unless get_api_token
23
+ response = conn.get url + "&access_token=#{@access_token}"
24
+ else
25
+ return nil
26
+ end
27
+ end
28
+ return ret
29
+ end
30
+
31
+ def api_post(url,body)
32
+ get_api_token unless @access_token
33
+ conn = Faraday.new(:url => @host_api)
34
+ response = conn.post "#{@urls[url]}?access_token=#{@access_token}" do |req|
35
+ req.body = body
36
+ end
37
+ return response
38
+ end
39
+
40
+ def get_api_token
41
+ conn = Faraday.new(:url =>@host_api)
42
+ response = conn.get "/cgi-bin/gettoken" do |req|
43
+ req.params['corpid'] = @appid
44
+ req.params['corpsecret'] = @secret
45
+ end
46
+ @access_token = JSON.parse(response.body)["access_token"]
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,3 @@
1
+ module Wechate
2
+ VERSION = "0.1.4"
3
+ end
@@ -0,0 +1,32 @@
1
+ module Wechate
2
+ class Worker
3
+
4
+ def text_callback(user,msg_id,text)
5
+ "hi"
6
+ end
7
+
8
+ def image_callback(user,msg_id,args)
9
+ "hi"
10
+ end
11
+
12
+ def location_callback(user,msg_id,args)
13
+ "hi"
14
+ end
15
+
16
+ def voice_callback(user,msg_id,args)
17
+ "hi"
18
+ end
19
+
20
+ def unknown_event(user,event,event_key)
21
+ "hi"
22
+ end
23
+
24
+ def click_callback(user,event_key,create_time)
25
+ "hi"
26
+ end
27
+
28
+ def scancode_callback(user,event_key,args)
29
+ "hi"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "wechate/version"
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'wechate'
6
+ spec.version = Wechate::VERSION
7
+ spec.date = '2010-04-28'
8
+ spec.summary = ""
9
+ spec.description = ""
10
+ spec.authors = ["goxplanet"]
11
+ spec.email = 'otec.teng@gmail.com'
12
+ spec.license = 'MIT'
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "nokogiri"
20
+ spec.add_dependency "faraday"
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.2"
23
+ spec.add_development_dependency "rake"
24
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wechate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - goxplanet
@@ -38,12 +38,50 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
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'
41
69
  description: ''
42
70
  email: otec.teng@gmail.com
43
71
  executables: []
44
72
  extensions: []
45
73
  extra_rdoc_files: []
46
- files: []
74
+ files:
75
+ - .gitignore
76
+ - Gemfile
77
+ - lib/wechate.rb
78
+ - lib/wechate/aes.rb
79
+ - lib/wechate/client.rb
80
+ - lib/wechate/factory.rb
81
+ - lib/wechate/http.rb
82
+ - lib/wechate/version.rb
83
+ - lib/wechate/worker.rb
84
+ - wechate.gemspec
47
85
  homepage:
48
86
  licenses:
49
87
  - MIT