yx-smart_sms 0.1.2

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.rdoc_options +21 -0
  4. data/.travis.yml +9 -0
  5. data/CONTRIBUTING.md +32 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +145 -0
  8. data/LICENSE +21 -0
  9. data/README.md +283 -0
  10. data/Rakefile +11 -0
  11. data/lib/generators/smart_sms/config_generator.rb +16 -0
  12. data/lib/generators/smart_sms/install_generator.rb +36 -0
  13. data/lib/generators/smart_sms/templates/add_uid_to_smart_sms_messages.rb +6 -0
  14. data/lib/generators/smart_sms/templates/create_smart_sms_messages.rb +19 -0
  15. data/lib/generators/smart_sms/templates/smart_sms_config.rb +15 -0
  16. data/lib/smart_sms.rb +35 -0
  17. data/lib/smart_sms/account.rb +25 -0
  18. data/lib/smart_sms/config.rb +49 -0
  19. data/lib/smart_sms/has_sms_verification.rb +149 -0
  20. data/lib/smart_sms/helpers/fake_sms.rb +31 -0
  21. data/lib/smart_sms/helpers/verification_code.rb +42 -0
  22. data/lib/smart_sms/message_service.rb +98 -0
  23. data/lib/smart_sms/model/message.rb +9 -0
  24. data/lib/smart_sms/request.rb +48 -0
  25. data/lib/smart_sms/template.rb +44 -0
  26. data/lib/smart_sms/version.rb +3 -0
  27. data/smart_sms.gemspec +35 -0
  28. data/spec/account_spec.rb +80 -0
  29. data/spec/config/config_spec.rb +172 -0
  30. data/spec/fake_app/active_record/config.rb +9 -0
  31. data/spec/fake_app/active_record/models.rb +44 -0
  32. data/spec/fake_app/initializers/smart_sms.rb +15 -0
  33. data/spec/fake_app/rails_app.rb +23 -0
  34. data/spec/has_sms_verificaton_spec.rb +275 -0
  35. data/spec/helpers/fake_sms_spec.rb +15 -0
  36. data/spec/helpers/verification_code_spec.rb +62 -0
  37. data/spec/smart_sms_spec.rb +261 -0
  38. data/spec/spec_helper.rb +26 -0
  39. data/spec/support/database_cleaner.rb +13 -0
  40. data/spec/template_spec.rb +168 -0
  41. metadata +264 -0
@@ -0,0 +1,98 @@
1
+ module SmartSMS
2
+ # Message service: methods that are used to manage messages
3
+ module MessageService
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ # Class methods
9
+ module ClassMethods
10
+ DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
11
+
12
+ # 发送短信到手机, 默认使用模板发送, 提供通用接口支持
13
+ #
14
+ # * phone: 需要接受短信的手机号码
15
+ # * content: 短信验证内容
16
+ #
17
+ # Options:
18
+ #
19
+ # * method 如若要使用通用短信接口, 需要 :method => :general
20
+ # * tpl_id 选择发送短信的模板, 默认是2
21
+ def deliver(phone, content, options = {})
22
+ if options[:method] == :general
23
+ Request.post 'sms/send.json', mobile: phone, text: content, extend: options[:extend]
24
+ else
25
+ options[:code] = content
26
+ message = parse_content options
27
+ tpl_id = options[:tpl_id] || SmartSMS.config.template_id
28
+ Request.post 'sms/tpl_send.json', tpl_id: tpl_id, mobile: phone, tpl_value: message
29
+ end
30
+ end
31
+
32
+ # 根据sid来查询短信记录
33
+ #
34
+ def find_by_sid(sid)
35
+ Request.post 'sms/get.json', sid: sid
36
+ end
37
+
38
+ # 参见 `find_messages` 方法
39
+ def find(options = {})
40
+ find_messages 'sms/get.json', options
41
+ end
42
+
43
+ # 查询黑名单词语, 用于预先测试可能无法通过审核的模板
44
+ #
45
+ def get_black_word(text = '')
46
+ Request.post 'sms/get_black_word.json', text: text
47
+ end
48
+
49
+ # 查询用户回复的短信, 参见 `find_messages` 方法
50
+ def get_reply(options = {})
51
+ find_messages 'sms/get_reply.json', options
52
+ end
53
+
54
+ private
55
+
56
+ # 批量查短信, 参数:
57
+ #
58
+ # * start_time: 短信提交开始时间
59
+ # * end_time: 短信提交结束时间
60
+ # * page_num: 页码,从1开始
61
+ # * page_size: 每页个数,最大100个
62
+ # * mobile: 接收短信的手机号
63
+ #
64
+ def find_messages(api, options = {})
65
+ options[:end_time] = Time.now if options[:end_time].blank?
66
+ options[:start_time] = options[:end_time] - SmartSMS.config.default_interval if options[:start_time].blank?
67
+ options[:end_time] = parse_time(options[:end_time])
68
+ options[:start_time] = parse_time(options[:start_time])
69
+ options[:page_num] ||= SmartSMS.config.page_num
70
+ options[:page_size] ||= SmartSMS.config.page_size
71
+ Request.post api, options
72
+ end
73
+
74
+ # 解析日期时间
75
+ # 格式可以是 `2014-05-01 08:40:20`, 也可以是Time类型
76
+ #
77
+ def parse_time(time = '')
78
+ if time.present? && time.is_a?(Time)
79
+ time.strftime DATETIME_FORMAT
80
+ elsif time.is_a? String
81
+ time
82
+ else
83
+ ''
84
+ end
85
+ end
86
+
87
+ # 生成信息发送内容
88
+ #
89
+ def parse_content(options = {})
90
+ options[:code] ||= ''
91
+ options[:company] ||= SmartSMS.config.company
92
+ SmartSMS.config.template_value.map do |key|
93
+ "##{key}#=#{options[key]}"
94
+ end.join('&')
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ module SmartSMS
2
+ # Message model to store sms messages
3
+ #
4
+ class Message < ::ActiveRecord::Base
5
+ self.table_name = 'smart_sms_messages'
6
+ belongs_to :smsable, polymorphic: true
7
+ attr_accessible :sid, :uid, :mobile, :send_time, :text, :code, :send_status, :report_status, :fee, :user_receive_time, :error_msg if SmartSMS.active_record_protected_attributes?
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+ require 'net/http'
3
+ require 'active_support/json'
4
+
5
+ module SmartSMS
6
+ # Module that manage requests
7
+ module Request
8
+ # Method that use `Net::HTTP.post_form` to perform `POST` action
9
+ #
10
+ def post(api, options = {})
11
+ options[:apikey] = SmartSMS.config.api_key
12
+ uri = URI.join(base_url, api)
13
+ res = Net::HTTP.post_form(uri, options)
14
+ result res.body
15
+ end
16
+
17
+ # Method that use `Net::HTTP.get` to perform `GET` action
18
+ #
19
+ def get(api, options = {})
20
+ options[:apikey] = SmartSMS.config.api_key
21
+ uri = URI.join(base_url, api)
22
+ result Net::HTTP.get(uri, options)
23
+ end
24
+
25
+ private
26
+
27
+ # Method that parse JSON to Hash
28
+ #
29
+ def result(body)
30
+ begin
31
+ ActiveSupport::JSON.decode body
32
+ rescue => e
33
+ {
34
+ code: 502,
35
+ msg: '内容解析错误',
36
+ detail: e.to_s
37
+ }
38
+ end
39
+ end
40
+
41
+ # Base uri for yunpian API
42
+ def base_url
43
+ "http://yunpian.com/#{SmartSMS.config.api_version}/"
44
+ end
45
+
46
+ module_function :post, :get, :result, :base_url
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ module SmartSMS
4
+ # module that handle `Template`
5
+ module Template
6
+ module_function
7
+
8
+ # 取默认模板
9
+ # Options:
10
+ #
11
+ # * tpl_id: 指定tpl_id时返回tpl_id对应的默认模板. 未指定时返回所有默认模板
12
+ #
13
+ def find_default(tpl_id = '')
14
+ Request.post 'tpl/get_default.json', tpl_id: tpl_id
15
+ end
16
+
17
+ # 取自定义模板
18
+ # Options:
19
+ #
20
+ # * tpl_id: 指定tpl_id时返回tpl_id对应的自定义模板. 未指定时返回所有自定义模板
21
+ #
22
+ def find(tpl_id = '')
23
+ Request.post 'tpl/get.json', tpl_id: tpl_id
24
+ end
25
+
26
+ # 创建新模板
27
+ # 规则请参见: <http://www.yunpian.com/api/tpl.html>
28
+ #
29
+ def create(tpl_content = '')
30
+ Request.post 'tpl/add.json', tpl_content: tpl_content
31
+ end
32
+
33
+ # 更新模板, 需指定id和content
34
+ #
35
+ def update(tpl_id = '', tpl_content = '')
36
+ Request.post 'tpl/update.json', tpl_id: tpl_id, tpl_content: tpl_content
37
+ end
38
+
39
+ # 删除模板, 需指定id
40
+ def destroy(tpl_id = '')
41
+ Request.post 'tpl/del.json', tpl_id: tpl_id
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module SmartSMS
2
+ VERSION = '0.1.2'
3
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'smart_sms/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'yx-smart_sms'
9
+ s.version = SmartSMS::VERSION
10
+ s.authors = ['lyfeyaj']
11
+ s.email = ['lyfeyaj@gmail.com']
12
+ s.description = %q{A smart sms verification tool}
13
+ s.summary = %q{A smart sms verification tool used in China and integrate with yunpian.com}
14
+ s.homepage = 'https://github.com/lyfeyaj/smart_sms'
15
+ s.license = 'MIT'
16
+
17
+ s.files = `git ls-files`.split($/)
18
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
20
+ s.require_paths = ['lib']
21
+
22
+ s.add_dependency 'activerecord', ['>= 3.0']
23
+ s.add_dependency 'activesupport', ['>= 3.0']
24
+
25
+ s.add_development_dependency 'bundler', ['>= 1.0.0']
26
+ s.add_development_dependency 'rake', ['>= 0']
27
+ s.add_development_dependency 'rspec-rails', ['>= 3.0.0']
28
+ s.add_development_dependency 'rspec-its', ['>= 1.0.0']
29
+ s.add_development_dependency 'database_cleaner', ['~> 1.2.0']
30
+ s.add_development_dependency 'webmock', ['~> 1.17.0']
31
+ s.add_development_dependency 'rails', ['>= 3.1.0']
32
+ s.add_development_dependency 'sqlite3', ['>= 0']
33
+ s.add_development_dependency 'pry', ['>= 0']
34
+ s.add_development_dependency 'sdoc', ['>= 0']
35
+ end
@@ -0,0 +1,80 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe SmartSMS::Template do
5
+
6
+ describe '#info' do
7
+ let(:url) { 'http://yunpian.com/v1/user/get.json' }
8
+ subject { SmartSMS::Account.info }
9
+
10
+ before do
11
+ stub_request(:post, url).with(
12
+ body: {
13
+ 'apikey' => SmartSMS.config.api_key
14
+ },
15
+ headers: {
16
+ 'Content-Type' => 'application/x-www-form-urlencoded'
17
+ }
18
+ ).to_return(
19
+ body: {
20
+ 'code' => 0,
21
+ 'msg' => 'OK',
22
+ 'user' => {
23
+ 'nick' => 'LYFEYAJ',
24
+ 'gmt_created' => '2014-04-01 14:00:52',
25
+ 'mobile' => '13096953122',
26
+ 'email' => 'lyfeyaj@gmail.com',
27
+ 'ip_whitelist' => nil,
28
+ 'api_version' => 'v1',
29
+ 'alarm_balance' => 150,
30
+ 'emergency_contact' => '',
31
+ 'emergency_mobile' => '',
32
+ 'balance' => 676
33
+ }
34
+ }.to_json
35
+ )
36
+ end
37
+
38
+ its(['code']) { should eq 0 }
39
+ its(['msg']) { should eq 'OK' }
40
+ its(:keys) { should include 'user' }
41
+ end
42
+
43
+ describe '#set' do
44
+ let(:url) { 'http://yunpian.com/v1/user/set.json' }
45
+ let(:emergency_contact) { 'Felix Liu' }
46
+ let(:emergency_mobile) { '13394738283' }
47
+ let(:alarm_balance) { '100' }
48
+ subject do
49
+ SmartSMS::Account.set(
50
+ emergency_contact: emergency_contact,
51
+ emergency_mobile: emergency_mobile,
52
+ alarm_balance: alarm_balance
53
+ )
54
+ end
55
+
56
+ before do
57
+ stub_request(:post, url).with(
58
+ body: {
59
+ 'apikey' => SmartSMS.config.api_key,
60
+ 'emergency_contact' => emergency_contact,
61
+ 'emergency_mobile' => emergency_mobile,
62
+ 'alarm_balance' => alarm_balance
63
+ },
64
+ headers: {
65
+ 'Content-Type' => 'application/x-www-form-urlencoded'
66
+ }
67
+ ).to_return(
68
+ body: {
69
+ 'code' => 0,
70
+ 'msg' => 'OK',
71
+ 'detail' => nil
72
+ }.to_json
73
+ )
74
+ end
75
+
76
+ its(['code']) { should eq 0 }
77
+ its(['msg']) { should eq 'OK' }
78
+ its(:keys) { should include 'detail' }
79
+ end
80
+ end
@@ -0,0 +1,172 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe SmartSMS::Configuration do
6
+ subject { SmartSMS.config }
7
+
8
+ describe 'api_key' do
9
+ context 'by_default' do
10
+ its(:api_key) { should == 'fake_api_key' }
11
+ end
12
+ context 'configured via config block' do
13
+ before do
14
+ SmartSMS.configure { |c| c.api_key = 'fdswerfsffsdfdvdsrr23432' }
15
+ end
16
+ its(:api_key) { should == 'fdswerfsffsdfdvdsrr23432' }
17
+ after do
18
+ SmartSMS.configure { |c| c.api_key = 'fake_api_key' }
19
+ end
20
+ end
21
+ end
22
+
23
+ describe 'api_version' do
24
+ context 'by_default' do
25
+ its(:api_version) { should == :v1 }
26
+ end
27
+ context 'configured via config block' do
28
+ before do
29
+ SmartSMS.configure { |c| c.api_version = :v2 }
30
+ end
31
+ its(:api_version) { should == :v2 }
32
+ after do
33
+ SmartSMS.configure { |c| c.api_version = :v1 }
34
+ end
35
+ end
36
+ end
37
+
38
+ describe 'template_id' do
39
+ context 'by_default' do
40
+ its(:template_id) { should == '2' }
41
+ end
42
+ context 'configured via config block' do
43
+ before do
44
+ SmartSMS.configure { |c| c.template_id = '1' }
45
+ end
46
+ its(:template_id) { should == '1' }
47
+ after do
48
+ SmartSMS.configure { |c| c.template_id = '2' }
49
+ end
50
+ end
51
+ end
52
+
53
+ describe 'template_value' do
54
+ context 'by_default' do
55
+ its(:template_value) { should == [:code, :company] }
56
+ end
57
+ context 'configured via config block' do
58
+ before do
59
+ SmartSMS.configure { |c| c.template_value = [:code] }
60
+ end
61
+ its(:template_value) { should == [:code] }
62
+ after do
63
+ SmartSMS.configure { |c| c.template_value = [:code, :company] }
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'page_num' do
69
+ context 'by_default' do
70
+ its(:page_num) { should == 1 }
71
+ end
72
+ context 'configured via config block' do
73
+ before do
74
+ SmartSMS.configure { |c| c.page_num = 2 }
75
+ end
76
+ its(:page_num) { should == 2 }
77
+ after do
78
+ SmartSMS.configure { |c| c.page_num = 1 }
79
+ end
80
+ end
81
+ end
82
+
83
+ describe 'page_size' do
84
+ context 'by_default' do
85
+ its(:page_size) { should == 20 }
86
+ end
87
+ context 'configured via config block' do
88
+ before do
89
+ SmartSMS.configure { |c| c.page_size = 50 }
90
+ end
91
+ its(:page_size) { should == 50 }
92
+ after do
93
+ SmartSMS.configure { |c| c.page_size = 20 }
94
+ end
95
+ end
96
+ end
97
+
98
+ describe 'company' do
99
+ context 'by_default' do
100
+ its(:company) { should == 'Smart SMS' }
101
+ end
102
+ context 'configured via config block' do
103
+ before do
104
+ SmartSMS.configure { |c| c.company = 'Edgepeek' }
105
+ end
106
+ its(:company) { should == 'Edgepeek' }
107
+ after do
108
+ SmartSMS.configure { |c| c.company = 'Smart SMS' }
109
+ end
110
+ end
111
+ end
112
+
113
+ describe 'expires_in' do
114
+ context 'by_default' do
115
+ its(:expires_in) { should == 1.hour }
116
+ end
117
+ context 'configured via config block' do
118
+ before do
119
+ SmartSMS.configure { |c| c.expires_in = 45.minutes }
120
+ end
121
+ its(:expires_in) { should == 45.minutes }
122
+ after do
123
+ SmartSMS.configure { |c| c.expires_in = 1.hour }
124
+ end
125
+ end
126
+ end
127
+
128
+ describe 'default_interval' do
129
+ context 'by_default' do
130
+ its(:default_interval) { should == 1.day }
131
+ end
132
+ context 'configured via config block' do
133
+ before do
134
+ SmartSMS.configure { |c| c.default_interval = 2.day }
135
+ end
136
+ its(:default_interval) { should == 2.day }
137
+ after do
138
+ SmartSMS.configure { |c| c.default_interval = 1.day }
139
+ end
140
+ end
141
+ end
142
+
143
+ describe 'store_sms_in_local' do
144
+ context 'by_default' do
145
+ its(:store_sms_in_local) { should == true }
146
+ end
147
+ context 'configured via config block' do
148
+ before do
149
+ SmartSMS.configure { |c| c.store_sms_in_local = false }
150
+ end
151
+ its(:store_sms_in_local) { should == false }
152
+ after do
153
+ SmartSMS.configure { |c| c.store_sms_in_local = true }
154
+ end
155
+ end
156
+ end
157
+
158
+ describe 'verification_code_algorithm' do
159
+ context 'by_default' do
160
+ its(:verification_code_algorithm) { should == :simple }
161
+ end
162
+ context 'configured via config block' do
163
+ before do
164
+ SmartSMS.configure { |c| c.verification_code_algorithm = :middle }
165
+ end
166
+ its(:verification_code_algorithm) { should == :middle }
167
+ after do
168
+ SmartSMS.configure { |c| c.verification_code_algorithm = :simple }
169
+ end
170
+ end
171
+ end
172
+ end