yx-smart_sms 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/.rdoc_options +21 -0
- data/.travis.yml +9 -0
- data/CONTRIBUTING.md +32 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +145 -0
- data/LICENSE +21 -0
- data/README.md +283 -0
- data/Rakefile +11 -0
- data/lib/generators/smart_sms/config_generator.rb +16 -0
- data/lib/generators/smart_sms/install_generator.rb +36 -0
- data/lib/generators/smart_sms/templates/add_uid_to_smart_sms_messages.rb +6 -0
- data/lib/generators/smart_sms/templates/create_smart_sms_messages.rb +19 -0
- data/lib/generators/smart_sms/templates/smart_sms_config.rb +15 -0
- data/lib/smart_sms.rb +35 -0
- data/lib/smart_sms/account.rb +25 -0
- data/lib/smart_sms/config.rb +49 -0
- data/lib/smart_sms/has_sms_verification.rb +149 -0
- data/lib/smart_sms/helpers/fake_sms.rb +31 -0
- data/lib/smart_sms/helpers/verification_code.rb +42 -0
- data/lib/smart_sms/message_service.rb +98 -0
- data/lib/smart_sms/model/message.rb +9 -0
- data/lib/smart_sms/request.rb +48 -0
- data/lib/smart_sms/template.rb +44 -0
- data/lib/smart_sms/version.rb +3 -0
- data/smart_sms.gemspec +35 -0
- 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 +44 -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 +261 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/database_cleaner.rb +13 -0
- data/spec/template_spec.rb +168 -0
- metadata +264 -0
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module SmartSms
|
2
|
+
module Generators
|
3
|
+
class ConfigGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
5
|
+
|
6
|
+
desc <<-DESC
|
7
|
+
Description:
|
8
|
+
Copies SmartSMS configuration file to your application's initializer directory.
|
9
|
+
DESC
|
10
|
+
|
11
|
+
def copy_config_file
|
12
|
+
template 'smart_sms_config.rb', 'config/initializers/smart_sms_config.rb'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/active_record'
|
3
|
+
|
4
|
+
module SmartSms
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
6
|
+
include ::Rails::Generators::Migration
|
7
|
+
|
8
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
9
|
+
|
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
|
15
|
+
|
16
|
+
def create_migration_file
|
17
|
+
return unless SmartSMS.config.store_sms_in_local
|
18
|
+
add_smart_sms_migration('create_smart_sms_messages')
|
19
|
+
add_smart_sms_migration('add_uid_to_smart_sms_messages')
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.next_migration_number(dirname)
|
23
|
+
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def add_smart_sms_migration(template)
|
29
|
+
migration_dir = File.expand_path('db/migrate')
|
30
|
+
|
31
|
+
unless self.class.migration_exists?(migration_dir, template)
|
32
|
+
migration_template "#{template}.rb", "db/migrate/#{template}.rb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateSmartSmsMessages < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :smart_sms_messages do |t|
|
4
|
+
t.string :sid
|
5
|
+
t.string :mobile
|
6
|
+
t.datetime :send_time
|
7
|
+
t.text :text
|
8
|
+
t.string :code
|
9
|
+
t.string :send_status
|
10
|
+
t.string :report_status
|
11
|
+
t.string :fee
|
12
|
+
t.datetime :user_receive_time
|
13
|
+
t.text :error_msg
|
14
|
+
t.belongs_to :smsable, polymorphic: true
|
15
|
+
end
|
16
|
+
add_index :smart_sms_messages, :sid
|
17
|
+
add_index :smart_sms_messages, [:smsable_id, :smsable_type]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
SmartSMS.configure do |config|
|
4
|
+
# config.api_key = nil
|
5
|
+
# config.api_version = :v1
|
6
|
+
# config.template_id = '2'
|
7
|
+
# config.template_value = [:code, :company]
|
8
|
+
# config.page_num = 1
|
9
|
+
# config.page_size = 20
|
10
|
+
# config.company = '云片网'
|
11
|
+
# config.expires_in = 1.hour
|
12
|
+
# config.default_interval = 1.day
|
13
|
+
# config.store_sms_in_local = false
|
14
|
+
# config.verification_code_algorithm = :simple
|
15
|
+
end
|
data/lib/smart_sms.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'smart_sms/config'
|
2
|
+
require 'smart_sms/request'
|
3
|
+
require 'smart_sms/template'
|
4
|
+
require 'smart_sms/helpers/fake_sms'
|
5
|
+
require 'smart_sms/helpers/verification_code'
|
6
|
+
require 'smart_sms/message_service'
|
7
|
+
require 'smart_sms/account'
|
8
|
+
|
9
|
+
unless defined? ActiveRecord
|
10
|
+
begin
|
11
|
+
require 'active_record'
|
12
|
+
rescue LoadError; end
|
13
|
+
end
|
14
|
+
|
15
|
+
module SmartSMS
|
16
|
+
include SmartSMS::MessageService
|
17
|
+
|
18
|
+
def self.active_record_protected_attributes?
|
19
|
+
@active_record_protected_attributes ||= ::ActiveRecord::VERSION::MAJOR < 4 || !!defined?(ProtectedAttributes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Ensure `ProtectedAttributes` gem gets required if it is available before the `Message` class gets loaded in
|
24
|
+
unless SmartSMS.active_record_protected_attributes?
|
25
|
+
SmartSMS.send(:remove_instance_variable, :@active_record_protected_attributes)
|
26
|
+
begin
|
27
|
+
require 'protected_attributes'
|
28
|
+
rescue LoadError; end # will rescue if `ProtectedAttributes` gem is not available
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'smart_sms/has_sms_verification'
|
32
|
+
|
33
|
+
ActiveSupport.on_load(:active_record) do
|
34
|
+
include SmartSMS::HasSmsVerification
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SmartSMS
|
4
|
+
# Module that handle user information
|
5
|
+
#
|
6
|
+
module Account
|
7
|
+
module_function
|
8
|
+
|
9
|
+
# 获取用户信息
|
10
|
+
#
|
11
|
+
def info
|
12
|
+
Request.post 'user/get.json'
|
13
|
+
end
|
14
|
+
|
15
|
+
# 设置用户信息
|
16
|
+
#
|
17
|
+
# * emergency_contact: 紧急联系人
|
18
|
+
# * emergency_mobile: 紧急联系人手机号
|
19
|
+
# * alarm_balance: 短信余额提醒阈值。一天只提示一次
|
20
|
+
#
|
21
|
+
def set(options = {})
|
22
|
+
Request.post 'user/set.json', options
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'active_support/configurable'
|
3
|
+
require 'active_support/core_ext'
|
4
|
+
|
5
|
+
module SmartSMS
|
6
|
+
# Configures global settings for SmartSMS
|
7
|
+
# SmartSMS.configure do |config|
|
8
|
+
# config.api_key = 'd63124354422b046081a44466'
|
9
|
+
# end
|
10
|
+
def self.configure(&block)
|
11
|
+
yield @config ||= SmartSMS::Configuration.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Global settings for SmartSMS
|
15
|
+
def self.config
|
16
|
+
@config
|
17
|
+
end
|
18
|
+
|
19
|
+
# Configuration class
|
20
|
+
#
|
21
|
+
class Configuration #:nodoc:
|
22
|
+
include ActiveSupport::Configurable
|
23
|
+
config_accessor :api_key # 授权 API KEY
|
24
|
+
config_accessor :api_version # API 的版本, 当前仅有v1
|
25
|
+
config_accessor :template_id # 指定发送信息时使用的模板
|
26
|
+
config_accessor :template_value # 用于指定信息文本中的可替换内容, 数组形势: [:code, :company]
|
27
|
+
config_accessor :page_num # 获取信息时, 指定默认的页数
|
28
|
+
config_accessor :page_size # 获取信息时, 一页包含信息数量
|
29
|
+
config_accessor :company # 默认公司名称
|
30
|
+
config_accessor :expires_in # 短信验证过期时间
|
31
|
+
config_accessor :default_interval # 查询短信时的默认时间段: end_time - start_time
|
32
|
+
config_accessor :store_sms_in_local # 是否存储SMS信息在本地: true or false
|
33
|
+
config_accessor :verification_code_algorithm # :simple, :middle, :complex
|
34
|
+
end
|
35
|
+
|
36
|
+
configure do |config|
|
37
|
+
config.api_key = nil
|
38
|
+
config.api_version = :v1
|
39
|
+
config.template_id = '2'
|
40
|
+
config.template_value = [:code, :company]
|
41
|
+
config.page_num = 1
|
42
|
+
config.page_size = 20
|
43
|
+
config.company = '云片网'
|
44
|
+
config.expires_in = 1.hour
|
45
|
+
config.default_interval = 1.day
|
46
|
+
config.store_sms_in_local = false
|
47
|
+
config.verification_code_algorithm = :simple
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'model/message'))
|
3
|
+
|
4
|
+
module SmartSMS
|
5
|
+
# Module that will be hooked into ActiveRecord to provide magic methods
|
6
|
+
#
|
7
|
+
module HasSmsVerification
|
8
|
+
def self.included(base)
|
9
|
+
base.send :extend, ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# Class methods that will be extended
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
# 在您的Model里面声明这个方法, 以添加SMS短信验证功能
|
16
|
+
#
|
17
|
+
# * moible_column: mobile 绑定的字段, 用于发送短信, 默认 :phone
|
18
|
+
# * verification_column: 验证绑定的字段, 用于判断是否已验证, 默认 :verified_at
|
19
|
+
#
|
20
|
+
# Options:
|
21
|
+
# * :class_name 自定义的Message类名称. 默认是 `::SmartSMS::Message`
|
22
|
+
# * :messages 自定义的Message关联名称. 默认是 `:messages`.
|
23
|
+
#
|
24
|
+
def has_sms_verification(moible_column = :phone, verification_column = :verified_at, options = {})
|
25
|
+
send :include, InstanceMethods
|
26
|
+
|
27
|
+
# 用于判断是否已经验证的字段, Datetime 类型, 例如 :verified_at
|
28
|
+
class_attribute :sms_verification_column
|
29
|
+
self.sms_verification_column = verification_column
|
30
|
+
|
31
|
+
class_attribute :sms_mobile_column
|
32
|
+
self.sms_mobile_column = moible_column
|
33
|
+
|
34
|
+
class_attribute :verify_regexp
|
35
|
+
self.verify_regexp = /(【.+】|[^a-zA-Z0-9\.\-\+_])/ # 用于抽取校验码, 如若修改过模板, 可能需要修改这个这正则
|
36
|
+
|
37
|
+
if SmartSMS.config.store_sms_in_local
|
38
|
+
|
39
|
+
class_attribute :messages_association_name
|
40
|
+
self.messages_association_name = options[:messages] || :messages
|
41
|
+
|
42
|
+
class_attribute :message_class_name
|
43
|
+
self.message_class_name = options[:class_name] || '::SmartSMS::Message'
|
44
|
+
|
45
|
+
if ::ActiveRecord::VERSION::MAJOR >= 4 # Rails 4 的 `has_many` 中定义order lambda的新语法
|
46
|
+
has_many messages_association_name,
|
47
|
+
-> { order('send_time ASC') },
|
48
|
+
class_name: message_class_name,
|
49
|
+
as: :smsable
|
50
|
+
else
|
51
|
+
has_many messages_association_name,
|
52
|
+
class_name: message_class_name,
|
53
|
+
as: :smsable,
|
54
|
+
order: 'send_time ASC'
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Instance methods
|
61
|
+
#
|
62
|
+
module InstanceMethods
|
63
|
+
# 非安全verify!方法, 验证成功后会存储成功的结果到数据表中
|
64
|
+
#
|
65
|
+
def verify!(code)
|
66
|
+
result = verify code
|
67
|
+
if result
|
68
|
+
send("#{self.class.sms_verification_column}=", Time.now)
|
69
|
+
save(validate: false)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# 安全verify方法, 用于校验短信验证码是否正确, 返回: true 或 false
|
74
|
+
#
|
75
|
+
def verify(code)
|
76
|
+
sms = latest_message
|
77
|
+
return false if sms.blank?
|
78
|
+
if SmartSMS.config.store_sms_in_local
|
79
|
+
sms.code == code.to_s
|
80
|
+
else
|
81
|
+
sms['text'].gsub(self.class.verify_regexp, '') == code.to_s
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# 判断是否已经验证成功
|
86
|
+
#
|
87
|
+
def verified?
|
88
|
+
verified_at.present?
|
89
|
+
end
|
90
|
+
|
91
|
+
def verified_at
|
92
|
+
self[self.class.sms_verification_column]
|
93
|
+
end
|
94
|
+
|
95
|
+
# 获取最新的一条有效短信记录
|
96
|
+
#
|
97
|
+
def latest_message
|
98
|
+
end_time = Time.now
|
99
|
+
start_time = end_time - SmartSMS.config.expires_in
|
100
|
+
if SmartSMS.config.store_sms_in_local
|
101
|
+
send(self.class.messages_association_name)
|
102
|
+
.where('send_time >= ? and send_time <= ?', start_time, end_time)
|
103
|
+
.last
|
104
|
+
else
|
105
|
+
result = SmartSMS.find(
|
106
|
+
start_time: start_time,
|
107
|
+
end_time: end_time,
|
108
|
+
mobile: send(self.class.sms_mobile_column),
|
109
|
+
page_size: 1
|
110
|
+
)
|
111
|
+
result['sms'].first
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# 发送短信至手机
|
116
|
+
#
|
117
|
+
def deliver(text = SmartSMS::VerificationCode.random)
|
118
|
+
result = SmartSMS.deliver send(self.class.sms_mobile_column), text
|
119
|
+
if result['code'] == 0
|
120
|
+
sms = SmartSMS.find_by_sid(result['result']['sid'])['sms']
|
121
|
+
save_or_return_message sms, text
|
122
|
+
else
|
123
|
+
errors.add :deliver, result
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def deliver_fake_sms(text = SmartSMS::VerificationCode.random)
|
129
|
+
mobile = send(self.class.sms_mobile_column)
|
130
|
+
company = SmartSMS.config.company
|
131
|
+
sms = SmartSMS::FakeSMS.build_fake_sms mobile, text, company
|
132
|
+
save_or_return_message sms, text
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def save_or_return_message(sms, text)
|
138
|
+
if SmartSMS.config.store_sms_in_local
|
139
|
+
message = send(self.class.messages_association_name).build sms
|
140
|
+
message.code = text
|
141
|
+
message.save
|
142
|
+
else
|
143
|
+
sms
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SmartSMS
|
4
|
+
# Module that contains methods to generate fake message
|
5
|
+
#
|
6
|
+
module FakeSMS
|
7
|
+
module_function
|
8
|
+
|
9
|
+
# This will generate fake sms with all necessary attributes
|
10
|
+
#
|
11
|
+
# Options:
|
12
|
+
#
|
13
|
+
# * mobile: mobile number
|
14
|
+
# * code: verification code
|
15
|
+
# * company: assigned company, format is【company】
|
16
|
+
#
|
17
|
+
def build_fake_sms(mobile, code, company)
|
18
|
+
{
|
19
|
+
'sid' => SecureRandom.uuid,
|
20
|
+
'mobile' => mobile,
|
21
|
+
'send_time' => Time.zone.now,
|
22
|
+
'text' => "您的验证码是#{code}。如非本人操作,请忽略本短信【#{company}】",
|
23
|
+
'send_status' => 'SUCCESS',
|
24
|
+
'report_status' => 'UNKNOWN',
|
25
|
+
'fee' => 1,
|
26
|
+
'user_receive_time' => nil,
|
27
|
+
'error_msg' => nil
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module SmartSMS
|
4
|
+
# This module provides some methods to generate random verification code
|
5
|
+
#
|
6
|
+
# Algorithm:
|
7
|
+
#
|
8
|
+
# * short: Generate short code with 4 numbers
|
9
|
+
# * simple: Generate simple code with 6 numbers
|
10
|
+
# * middle: Generate middle complex code of 6 charactors with mixed numbers and letters
|
11
|
+
# * complex: Generate complex code of 8 charactors with mixed numbers, letters or special charactors
|
12
|
+
module VerificationCode
|
13
|
+
module_function
|
14
|
+
|
15
|
+
REGISTERED_ALGORITHMS = [:short, :simple, :middle, :complex]
|
16
|
+
|
17
|
+
def random(algorithm = '')
|
18
|
+
algorithm = SmartSMS.config.verification_code_algorithm if algorithm.blank?
|
19
|
+
if REGISTERED_ALGORITHMS.include? algorithm
|
20
|
+
SmartSMS::VerificationCode.send algorithm
|
21
|
+
else
|
22
|
+
fail NoMethodError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def short
|
27
|
+
SecureRandom.random_number.to_s.slice(-4..-1)
|
28
|
+
end
|
29
|
+
|
30
|
+
def simple
|
31
|
+
SecureRandom.random_number.to_s.slice(-6..-1)
|
32
|
+
end
|
33
|
+
|
34
|
+
def middle
|
35
|
+
SecureRandom.base64.gsub!(/[^0-9a-zA-Z]/, '').slice(1..6).downcase
|
36
|
+
end
|
37
|
+
|
38
|
+
def complex
|
39
|
+
SecureRandom.base64.slice(1..8).downcase
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|