smart_sms 0.0.3 → 0.1.0
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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +97 -24
- data/README.md +245 -3
- data/Rakefile +1 -1
- data/lib/generators/smart_sms/install_generator.rb +7 -2
- data/lib/generators/smart_sms/templates/smart_sms_config.rb +1 -1
- data/lib/smart_sms.rb +2 -2
- data/lib/smart_sms/account.rb +6 -4
- data/lib/smart_sms/config.rb +4 -3
- data/lib/smart_sms/has_sms_verification.rb +39 -34
- data/lib/smart_sms/helpers/fake_sms.rb +14 -13
- data/lib/smart_sms/helpers/verification_code.rb +15 -7
- data/lib/smart_sms/message_service.rb +15 -13
- data/lib/smart_sms/model/message.rb +2 -0
- data/lib/smart_sms/request.rb +9 -8
- data/lib/smart_sms/template.rb +8 -7
- data/lib/smart_sms/version.rb +2 -2
- data/smart_sms.gemspec +11 -8
- data/spec/account_spec.rb +80 -0
- data/spec/config/config_spec.rb +172 -0
- data/spec/fake_app/active_record/config.rb +9 -0
- data/spec/fake_app/active_record/models.rb +42 -0
- data/spec/fake_app/initializers/smart_sms.rb +15 -0
- data/spec/fake_app/rails_app.rb +23 -0
- data/spec/has_sms_verificaton_spec.rb +275 -0
- data/spec/helpers/fake_sms_spec.rb +15 -0
- data/spec/helpers/verification_code_spec.rb +62 -0
- data/spec/smart_sms_spec.rb +260 -0
- data/spec/spec_helper.rb +15 -2
- data/spec/support/database_cleaner.rb +13 -0
- data/spec/template_spec.rb +168 -0
- metadata +69 -3
@@ -7,7 +7,11 @@ module SmartSms
|
|
7
7
|
|
8
8
|
source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
9
9
|
|
10
|
-
desc
|
10
|
+
desc <<-EOF
|
11
|
+
Generates (but does not run) a migration to add a messages table.
|
12
|
+
You need to set `store_sms_in_local` to `true` in your config file
|
13
|
+
before running this command
|
14
|
+
EOF
|
11
15
|
|
12
16
|
def create_migration_file
|
13
17
|
add_smart_sms_migration('create_smart_sms_messages') if SmartSMS.config.store_sms_in_local
|
@@ -18,10 +22,11 @@ module SmartSms
|
|
18
22
|
end
|
19
23
|
|
20
24
|
protected
|
25
|
+
|
21
26
|
def add_smart_sms_migration(template)
|
22
27
|
migration_dir = File.expand_path('db/migrate')
|
23
28
|
|
24
|
-
|
29
|
+
unless self.class.migration_exists?(migration_dir, template)
|
25
30
|
migration_template "#{template}.rb", "db/migrate/#{template}.rb"
|
26
31
|
end
|
27
32
|
end
|
data/lib/smart_sms.rb
CHANGED
@@ -6,7 +6,7 @@ require 'smart_sms/helpers/verification_code'
|
|
6
6
|
require 'smart_sms/message_service'
|
7
7
|
require 'smart_sms/account'
|
8
8
|
|
9
|
-
|
9
|
+
unless defined? ActiveRecord
|
10
10
|
begin
|
11
11
|
require 'active_record'
|
12
12
|
rescue LoadError; end
|
@@ -32,4 +32,4 @@ require 'smart_sms/has_sms_verification'
|
|
32
32
|
|
33
33
|
ActiveSupport.on_load(:active_record) do
|
34
34
|
include SmartSMS::HasSmsVerification
|
35
|
-
end
|
35
|
+
end
|
data/lib/smart_sms/account.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
#encoding: utf-8
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
3
|
module SmartSMS
|
4
|
+
# Module that handle user information
|
5
|
+
#
|
4
6
|
module Account
|
5
|
-
|
7
|
+
module_function
|
6
8
|
|
7
9
|
# 获取用户信息
|
8
10
|
def info
|
@@ -13,8 +15,8 @@ module SmartSMS
|
|
13
15
|
# emergency_contact: 紧急联系人
|
14
16
|
# emergency_mobile: 紧急联系人手机号
|
15
17
|
# alarm_balance: 短信余额提醒阈值。一天只提示一次
|
16
|
-
def set
|
18
|
+
def set(options = {})
|
17
19
|
Request.post 'user/set.json', options
|
18
20
|
end
|
19
21
|
end
|
20
|
-
end
|
22
|
+
end
|
data/lib/smart_sms/config.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
#encoding: utf-8
|
1
|
+
# encoding: utf-8
|
2
2
|
require 'active_support/configurable'
|
3
3
|
require 'active_support/core_ext'
|
4
4
|
|
5
5
|
module SmartSMS
|
6
|
-
|
7
6
|
# Configures global settings for SmartSMS
|
8
7
|
# SmartSMS.configure do |config|
|
9
8
|
# config.api_key = 'd63124354422b046081a44466'
|
@@ -17,6 +16,8 @@ module SmartSMS
|
|
17
16
|
@config
|
18
17
|
end
|
19
18
|
|
19
|
+
# Configuration class
|
20
|
+
#
|
20
21
|
class Configuration #:nodoc:
|
21
22
|
include ActiveSupport::Configurable
|
22
23
|
config_accessor :api_key # 授权 API KEY
|
@@ -45,4 +46,4 @@ module SmartSMS
|
|
45
46
|
config.store_sms_in_local = false
|
46
47
|
config.verification_code_algorithm = :simple
|
47
48
|
end
|
48
|
-
end
|
49
|
+
end
|
@@ -2,23 +2,25 @@
|
|
2
2
|
require File.expand_path(File.join(File.dirname(__FILE__), 'model/message'))
|
3
3
|
|
4
4
|
module SmartSMS
|
5
|
+
# Module that will be hooked into ActiveRecord to provide magic methods
|
6
|
+
#
|
5
7
|
module HasSmsVerification
|
6
|
-
|
7
8
|
def self.included(base)
|
8
9
|
base.send :extend, ClassMethods
|
9
10
|
end
|
10
11
|
|
12
|
+
# Class methods that will be extended
|
11
13
|
module ClassMethods
|
12
14
|
|
13
15
|
# 在您的Model里面声明这个方法, 以添加SMS短信验证功能
|
14
16
|
# moible_column: mobile 绑定的字段, 用于发送短信
|
15
|
-
# verification_column: 验证绑定的字段,
|
17
|
+
# verification_column: 验证绑定的字段, 用于判断是否已验证
|
16
18
|
#
|
17
19
|
# Options:
|
18
20
|
# :class_name 自定义的Message类名称. 默认是 `::SmartSMS::Message`
|
19
21
|
# :messages 自定义的Message关联名称. 默认是 `:versions`.
|
20
22
|
#
|
21
|
-
def has_sms_verification
|
23
|
+
def has_sms_verification(moible_column = :phone, verification_column = :verified_at, options = {})
|
22
24
|
send :include, InstanceMethods
|
23
25
|
|
24
26
|
# 用于判断是否已经验证的字段, Datetime 类型, 例如 :verified_at
|
@@ -40,33 +42,34 @@ module SmartSMS
|
|
40
42
|
self.message_class_name = options[:class_name] || '::SmartSMS::Message'
|
41
43
|
|
42
44
|
if ::ActiveRecord::VERSION::MAJOR >= 4 # Rails 4 里面, 在 `has_many` 声明中定义order lambda的语法
|
43
|
-
has_many
|
44
|
-
|
45
|
-
:
|
45
|
+
has_many messages_association_name,
|
46
|
+
-> { order('send_time ASC') },
|
47
|
+
class_name: message_class_name,
|
48
|
+
as: :smsable
|
46
49
|
else
|
47
|
-
has_many
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
50
|
+
has_many messages_association_name,
|
51
|
+
class_name: message_class_name,
|
52
|
+
as: :smsable,
|
53
|
+
order: 'send_time ASC'
|
51
54
|
end
|
52
55
|
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
59
|
+
# Instance methods
|
56
60
|
module InstanceMethods
|
57
|
-
|
58
61
|
# 非安全verify!方法, 验证成功后会存储成功的结果到数据表中
|
59
|
-
def verify!
|
62
|
+
def verify!(code)
|
60
63
|
result = verify code
|
61
64
|
if result
|
62
|
-
|
63
|
-
|
65
|
+
send("#{self.class.sms_verification_column}=", Time.now)
|
66
|
+
save(validate: false)
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
67
70
|
# 安全verify方法, 用于校验短信验证码是否正确, 返回: true 或 false
|
68
71
|
#
|
69
|
-
def verify
|
72
|
+
def verify(code)
|
70
73
|
sms = latest_message
|
71
74
|
return false if sms.blank?
|
72
75
|
if SmartSMS.config.store_sms_in_local
|
@@ -90,16 +93,16 @@ module SmartSMS
|
|
90
93
|
#
|
91
94
|
def latest_message
|
92
95
|
end_time = Time.now
|
93
|
-
start_time = end_time - SmartSMS.config.expires_in
|
96
|
+
start_time = end_time - SmartSMS.config.expires_in
|
94
97
|
if SmartSMS.config.store_sms_in_local
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
+
send(self.class.messages_association_name)
|
99
|
+
.where('send_time >= ? and send_time <= ?', start_time, end_time)
|
100
|
+
.last
|
98
101
|
else
|
99
102
|
result = SmartSMS.find(
|
100
103
|
start_time: start_time,
|
101
104
|
end_time: end_time,
|
102
|
-
mobile:
|
105
|
+
mobile: send(self.class.sms_mobile_column),
|
103
106
|
page_size: 1
|
104
107
|
)
|
105
108
|
result['sms'].first
|
@@ -108,27 +111,29 @@ module SmartSMS
|
|
108
111
|
|
109
112
|
# 发送短信至手机
|
110
113
|
#
|
111
|
-
def deliver
|
112
|
-
result = SmartSMS.deliver
|
114
|
+
def deliver(text = SmartSMS::VerificationCode.random)
|
115
|
+
result = SmartSMS.deliver send(self.class.sms_mobile_column), text
|
113
116
|
if result['code'] == 0
|
114
117
|
sms = SmartSMS.find_by_sid(result['result']['sid'])['sms']
|
115
|
-
|
116
|
-
message = self.send(self.messages_association_name).build sms
|
117
|
-
message.code = text
|
118
|
-
message.save
|
119
|
-
else
|
120
|
-
sms
|
121
|
-
end
|
118
|
+
save_or_return_message sms, text
|
122
119
|
else
|
123
|
-
|
120
|
+
errors.add :deliver, result
|
124
121
|
false
|
125
122
|
end
|
126
123
|
end
|
127
124
|
|
128
|
-
def deliver_fake_sms
|
129
|
-
|
125
|
+
def deliver_fake_sms(text = SmartSMS::VerificationCode.random)
|
126
|
+
mobile = send(self.class.sms_mobile_column)
|
127
|
+
company = SmartSMS.config.company
|
128
|
+
sms = SmartSMS::FakeSMS.build_fake_sms mobile, text, company
|
129
|
+
save_or_return_message sms, text
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def save_or_return_message(sms, text)
|
130
135
|
if SmartSMS.config.store_sms_in_local
|
131
|
-
message =
|
136
|
+
message = send(self.class.messages_association_name).build sms
|
132
137
|
message.code = text
|
133
138
|
message.save
|
134
139
|
else
|
@@ -138,4 +143,4 @@ module SmartSMS
|
|
138
143
|
end
|
139
144
|
end
|
140
145
|
end
|
141
|
-
end
|
146
|
+
end
|
@@ -1,22 +1,23 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module SmartSMS
|
4
|
+
# This will generate fake sms
|
5
|
+
#
|
4
6
|
module FakeSMS
|
5
|
-
|
7
|
+
module_function
|
6
8
|
|
7
|
-
def build_fake_sms
|
9
|
+
def build_fake_sms(mobile, code, company)
|
8
10
|
{
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
'sid' => SecureRandom.uuid,
|
12
|
+
'mobile' => mobile,
|
13
|
+
'send_time' => Time.zone.now,
|
14
|
+
'text' => "您的验证码是#{code}。如非本人操作,请忽略本短信【#{company}】",
|
15
|
+
'send_status' => 'SUCCESS',
|
16
|
+
'report_status' => 'UNKNOWN',
|
17
|
+
'fee' => 1,
|
18
|
+
'user_receive_time' => nil,
|
19
|
+
'error_msg' => nil
|
18
20
|
}
|
19
21
|
end
|
20
|
-
|
21
22
|
end
|
22
|
-
end
|
23
|
+
end
|
@@ -1,32 +1,40 @@
|
|
1
1
|
require 'securerandom'
|
2
2
|
|
3
3
|
module SmartSMS
|
4
|
+
# This module provides some methods to generate random verification code
|
5
|
+
# Algorithm:
|
6
|
+
# short: Generate short code with 4 numbers
|
7
|
+
# simple: Generate simple code with 6 numbers
|
8
|
+
# middle: Generate middle complex code of 6 charactors with mixed numbers and letters
|
9
|
+
# complex: Generate complex code of 8 charactors with mixed numbers, letters or special charactors
|
4
10
|
module VerificationCode
|
5
|
-
|
11
|
+
module_function
|
6
12
|
|
7
|
-
REGISTERED_ALGORITHMS = [:simple, :middle, :complex]
|
13
|
+
REGISTERED_ALGORITHMS = [:short, :simple, :middle, :complex]
|
8
14
|
|
9
|
-
def random
|
15
|
+
def random(algorithm = '')
|
10
16
|
algorithm = SmartSMS.config.verification_code_algorithm if algorithm.blank?
|
11
17
|
if REGISTERED_ALGORITHMS.include? algorithm
|
12
18
|
SmartSMS::VerificationCode.send algorithm
|
13
19
|
else
|
14
|
-
|
20
|
+
fail NoMethodError
|
15
21
|
end
|
16
22
|
end
|
17
23
|
|
18
|
-
|
24
|
+
def short
|
25
|
+
SecureRandom.random_number.to_s.slice(-4..-1)
|
26
|
+
end
|
19
27
|
|
20
28
|
def simple
|
21
29
|
SecureRandom.random_number.to_s.slice(-6..-1)
|
22
30
|
end
|
23
31
|
|
24
32
|
def middle
|
25
|
-
SecureRandom.base64.gsub!(/[^0-9a-zA-Z]/,'').slice(1..6).downcase
|
33
|
+
SecureRandom.base64.gsub!(/[^0-9a-zA-Z]/, '').slice(1..6).downcase
|
26
34
|
end
|
27
35
|
|
28
36
|
def complex
|
29
37
|
SecureRandom.base64.slice(1..8).downcase
|
30
38
|
end
|
31
39
|
end
|
32
|
-
end
|
40
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module SmartSMS
|
2
|
+
# Message service: methods that are used to manage messages
|
2
3
|
module MessageService
|
3
|
-
|
4
4
|
def self.included(base)
|
5
5
|
base.send :extend, ClassMethods
|
6
6
|
end
|
7
7
|
|
8
|
+
# Class methods
|
8
9
|
module ClassMethods
|
9
|
-
|
10
|
-
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
|
10
|
+
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
|
11
11
|
|
12
12
|
# 发送短信到手机, 默认使用模板发送, 提供通用接口支持
|
13
13
|
# phone: 需要接受短信的手机号码
|
@@ -16,7 +16,7 @@ module SmartSMS
|
|
16
16
|
# Options:
|
17
17
|
# :method 如若要使用通用短信接口, 需要 :method => :general
|
18
18
|
# :tpl_id 选择发送短信的模板, 默认是2
|
19
|
-
def deliver
|
19
|
+
def deliver(phone, content, options = {})
|
20
20
|
if options[:method] == :general
|
21
21
|
Request.post 'sms/send.json', mobile: phone, text: content, extend: options[:extend]
|
22
22
|
else
|
@@ -29,20 +29,20 @@ module SmartSMS
|
|
29
29
|
|
30
30
|
# 根据sid来查询短信记录
|
31
31
|
#
|
32
|
-
def find_by_sid
|
32
|
+
def find_by_sid(sid)
|
33
33
|
Request.post 'sms/get.json', sid: sid
|
34
34
|
end
|
35
35
|
|
36
36
|
# 参见 `find_messages` 方法
|
37
|
-
def find
|
37
|
+
def find(options = {})
|
38
38
|
find_messages 'sms/get.json', options
|
39
39
|
end
|
40
40
|
|
41
|
-
def get_black_word
|
41
|
+
def get_black_word(text = '')
|
42
42
|
Request.post 'sms/get_black_word.json', text: text
|
43
43
|
end
|
44
44
|
|
45
|
-
def get_reply
|
45
|
+
def get_reply(options = {})
|
46
46
|
find_messages 'sms/get_reply.json', options
|
47
47
|
end
|
48
48
|
|
@@ -55,7 +55,7 @@ module SmartSMS
|
|
55
55
|
# page_size: 每页个数,最大100个
|
56
56
|
# mobile: 接收短信的手机号
|
57
57
|
#
|
58
|
-
def find_messages
|
58
|
+
def find_messages(api, options = {})
|
59
59
|
options[:end_time] = Time.now if options[:end_time].blank?
|
60
60
|
options[:start_time] = options[:end_time] - SmartSMS.config.default_interval if options[:start_time].blank?
|
61
61
|
options[:end_time] = parse_time(options[:end_time])
|
@@ -65,21 +65,23 @@ module SmartSMS
|
|
65
65
|
Request.post api, options
|
66
66
|
end
|
67
67
|
|
68
|
-
def parse_time
|
68
|
+
def parse_time(time = '')
|
69
69
|
if time.present? && time.is_a?(Time)
|
70
70
|
time.strftime DATETIME_FORMAT
|
71
|
+
elsif time.is_a? String
|
72
|
+
time
|
71
73
|
else
|
72
74
|
''
|
73
75
|
end
|
74
76
|
end
|
75
77
|
|
76
|
-
def parse_content
|
78
|
+
def parse_content(options = {})
|
77
79
|
options[:code] ||= ''
|
78
80
|
options[:company] ||= SmartSMS.config.company
|
79
81
|
SmartSMS.config.template_value.map do |key|
|
80
|
-
"##{key
|
82
|
+
"##{key}#=#{options[key]}"
|
81
83
|
end.join('&')
|
82
84
|
end
|
83
85
|
end
|
84
86
|
end
|
85
|
-
end
|
87
|
+
end
|
data/lib/smart_sms/request.rb
CHANGED
@@ -3,17 +3,16 @@ require 'net/http'
|
|
3
3
|
require 'active_support/json'
|
4
4
|
|
5
5
|
module SmartSMS
|
6
|
+
# Module that manage requests
|
6
7
|
module Request
|
7
|
-
|
8
|
-
|
9
|
-
def post api, options = {}
|
8
|
+
def post(api, options = {})
|
10
9
|
options[:apikey] = SmartSMS.config.api_key
|
11
10
|
uri = URI.join(base_url, api)
|
12
11
|
res = Net::HTTP.post_form(uri, options)
|
13
12
|
result res.body
|
14
13
|
end
|
15
14
|
|
16
|
-
def get
|
15
|
+
def get(api, options = {})
|
17
16
|
options[:apikey] = SmartSMS.config.api_key
|
18
17
|
uri = URI.join(base_url, api)
|
19
18
|
result Net::HTTP.get(uri, options)
|
@@ -21,20 +20,22 @@ module SmartSMS
|
|
21
20
|
|
22
21
|
private
|
23
22
|
|
24
|
-
def result
|
23
|
+
def result(body)
|
25
24
|
begin
|
26
25
|
ActiveSupport::JSON.decode body
|
27
26
|
rescue => e
|
28
27
|
{
|
29
28
|
code: 502,
|
30
|
-
msg:
|
29
|
+
msg: '内容解析错误',
|
31
30
|
detail: e.to_s
|
32
31
|
}
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
36
35
|
def base_url
|
37
|
-
"http://yunpian.com/#{SmartSMS.config.api_version
|
36
|
+
"http://yunpian.com/#{SmartSMS.config.api_version}/"
|
38
37
|
end
|
38
|
+
|
39
|
+
module_function :post, :get, :result, :base_url
|
39
40
|
end
|
40
|
-
end
|
41
|
+
end
|