ifree-sms 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2011 Aimbulance.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,57 @@
1
+ = IfreeSms
2
+
3
+ The IfreeSms gem for i-free sms provider
4
+
5
+ == Install
6
+
7
+ gem 'ifree-sms'
8
+
9
+ rails generate ifree_sms:install
10
+
11
+ == Usage
12
+
13
+ Use middleware with options:
14
+
15
+ config.app_middleware.use IfreeSms::Manager do |c|
16
+ c.routes = "/ifree/sms"
17
+ end
18
+
19
+ Initialize IfreeSms and set its configurations.
20
+
21
+ # config/initializers/ifree_sms.rb
22
+ if Object.const_defined?("IfreeSms")
23
+
24
+ IfreeSms.setup do |config|
25
+ config.secret_key = ""
26
+ config.project_name = ""
27
+ config.service_number = ""
28
+ config.debug = true
29
+ end
30
+
31
+ IfreeSms::Manager.incoming_message do |env, message|
32
+ # set it if you want to send answer for user
33
+ message.answer_text = "put here sms answer for user"
34
+ end
35
+ end
36
+
37
+ Send sms answer to user (asynchronous)
38
+
39
+ * phone format => 380971606179
40
+ * text length => 160 Latin or 70 Unicode
41
+ * sms_id - unique sms id
42
+
43
+ IfreeSms.send_sms(phone, text, sms_id)
44
+
45
+ or
46
+
47
+ IfreeSms::Message.first.send_answer("some text")
48
+
49
+ If you want to send sms to custom user, don't put sms_id (Paid action)
50
+
51
+ IfreeSms.send_sms(phone, text)
52
+
53
+ == Dependences
54
+
55
+ * {curb}[https://github.com/taf2/curb]
56
+
57
+ sudo apt-get install libcurl3 libcurl3-gnutls libcurl4-openssl-dev
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require File.join(File.dirname(__FILE__), 'lib', 'ifree_sms', 'version')
6
+
7
+ desc 'Default: run unit tests.'
8
+ task :default => :test
9
+
10
+ desc 'Test the ifree_sms plugin.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = true
16
+ end
17
+
18
+ desc 'Generate documentation for the ifree_sms plugin.'
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'IfreeSms'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+ begin
28
+ require 'jeweler'
29
+ Jeweler::Tasks.new do |s|
30
+ s.name = "ifree-sms"
31
+ s.version = IfreeSms::VERSION.dup
32
+ s.summary = "The IfreeSms gem for i-free sms provider"
33
+ s.description = "The IfreeSms gem for i-free sms provider"
34
+ s.email = "superp1987@gmail.com"
35
+ s.homepage = "https://github.com/superp/ifree-sms"
36
+ s.authors = ["Igor Galeta", "Pavlo Galeta"]
37
+ s.files = FileList["[A-Z]*", "{app,lib}/**/*"] - ["Gemfile"]
38
+ #s.extra_rdoc_files = FileList["[A-Z]*"]
39
+ end
40
+
41
+ Jeweler::GemcutterTasks.new
42
+ rescue LoadError
43
+ puts "Jeweler not available. Install it with: gem install jeweler"
44
+ end
@@ -0,0 +1,5 @@
1
+ module IfreeSms
2
+ class Message < ::ActiveRecord::Base
3
+ include IfreeSms::Smsing
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ Usage:
2
+ rails generate ifree_sms:install
3
+
4
+ Options:
5
+
6
+ Example:
7
+ rails g ifree_sms:install
@@ -0,0 +1,36 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module IfreeSms
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ desc "Create ifree_sms migration"
10
+ source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
11
+
12
+ # copy migration files
13
+ def create_migrations
14
+ migration_template "migrate/create_messages.rb", File.join('db/migrate', "ifree_sms_create_messages.rb")
15
+ end
16
+
17
+ # copy configurations
18
+ def copy_configurations
19
+ copy_file('config/ifree_sms.rb', 'config/initializers/ifree_sms.rb')
20
+ end
21
+
22
+ def self.next_migration_number(dirname)
23
+ if ActiveRecord::Base.timestamped_migrations
24
+ current_time.utc.strftime("%Y%m%d%H%M%S")
25
+ else
26
+ "%.3d" % (current_migration_number(dirname) + 1)
27
+ end
28
+ end
29
+
30
+ def self.current_time
31
+ @current_time ||= Time.now
32
+ @current_time += 1.minute
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ # config/initializers/ifree_sms.rb
2
+ if Object.const_defined?("IfreeSms")
3
+
4
+ IfreeSms.setup do |config|
5
+ config.secret_key = ""
6
+ config.project_name = ""
7
+ config.service_number = ""
8
+ config.debug = true
9
+ end
10
+
11
+ IfreeSms::Manager.incoming_message do |env, message|
12
+ # set it if you want to send answer for user
13
+ message.answer_text = "put here sms answer for user"
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ class IfreeSmsCreateMessages < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :ifree_sms_messages do |t|
4
+ # Messageable
5
+ t.string :messageable_type, :limit => 40
6
+ t.integer :messageable_id
7
+
8
+ # Sms info
9
+ t.integer :sms_id
10
+ t.integer :phone, :limit => 8
11
+ t.integer :service_number
12
+ t.string :sms_text
13
+ t.datetime :now
14
+
15
+ t.integer :status_id, :limit => 1, :default => 1
16
+
17
+ t.timestamps
18
+ end
19
+
20
+ add_index :ifree_sms_messages, [:messageable_type, :messageable_id]
21
+ add_index :ifree_sms_messages, :sms_id
22
+ add_index :ifree_sms_messages, :phone
23
+ add_index :ifree_sms_messages, :service_number
24
+ add_index :ifree_sms_messages, :status_id
25
+ end
26
+
27
+ def self.down
28
+ drop_table :ifree_sms_messages
29
+ end
30
+ end
data/lib/ifree-sms.rb ADDED
@@ -0,0 +1 @@
1
+ require 'ifree_sms'
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ module IfreeSms
3
+ module Callbacks
4
+ # Hook to _run_callbacks asserting for conditions.
5
+ def _run_callbacks(kind, *args) #:nodoc:
6
+ options = args.last # Last callback arg MUST be a Hash
7
+
8
+ send("_#{kind}").each do |callback, conditions|
9
+ invalid = conditions.find do |key, value|
10
+ value.is_a?(Array) ? !value.include?(options[key]) : (value != options[key])
11
+ end
12
+
13
+ callback.call(*args) unless invalid
14
+ end
15
+ end
16
+
17
+ # A callback that runs after message created
18
+ # Example:
19
+ # IfreeSms::Manager.incoming_message do |env, opts|
20
+ # end
21
+ #
22
+ def incoming_message(options = {}, method = :push, &block)
23
+ raise BlockNotGiven unless block_given?
24
+ _incoming_message.send(method, [block, options])
25
+ end
26
+
27
+ # Provides access to the callback array for incoming_message
28
+ # :api: private
29
+ def _incoming_message
30
+ @_incoming_message ||= []
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ module IfreeSms
3
+ class Config < Hash
4
+ # Creates an accessor that simply sets and reads a key in the hash:
5
+ #
6
+ # class Config < Hash
7
+ # hash_accessor :routes, :secret_key, :service_number, :project_name
8
+ # end
9
+ #
10
+ # config = Config.new
11
+ # config.routes = '/posts/message'
12
+ # config[:routes] #=> '/posts/message'
13
+ #
14
+ def self.hash_accessor(*names) #:nodoc:
15
+ names.each do |name|
16
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
17
+ def #{name}
18
+ self[:#{name}]
19
+ end
20
+
21
+ def #{name}=(value)
22
+ self[:#{name}] = value
23
+ end
24
+ METHOD
25
+ end
26
+ end
27
+
28
+ hash_accessor :routes, :secret_key, :service_number, :project_name, :debug
29
+
30
+ def initialize(other={})
31
+ merge!(other)
32
+ self[:routes] ||= "/ifree/sms"
33
+ self[:secret_key] ||= "some_very_secret_key_given_ifree"
34
+ self[:service_number] ||= "some_service_number"
35
+ self[:project_name] ||= "project_name"
36
+ self[:debug] ||= false
37
+ end
38
+
39
+ def url
40
+ "http://srv1.com.ua/#{self[:project_name]}/second.php"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+ require 'rails'
3
+ require 'ifree_sms'
4
+
5
+ module IfreeSms
6
+ class Engine < ::Rails::Engine
7
+
8
+ end
9
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ module IfreeSms
3
+ class Manager
4
+ extend IfreeSms::Callbacks
5
+
6
+ attr_accessor :config
7
+
8
+ # Initialize the middleware. If a block is given, a IfreeSms::Config is yielded so you can properly
9
+ # configure the IfreeSms::Manager.
10
+ def initialize(app, options={})
11
+ options.symbolize_keys!
12
+
13
+ @app, @config = app, IfreeSms::Config.new(options)
14
+ yield @config if block_given?
15
+ self
16
+ end
17
+
18
+ def call(env) # :nodoc:
19
+ if smsing_path?(env['PATH_INFO'])
20
+ create(env)
21
+ else
22
+ @app.call(env)
23
+ end
24
+ end
25
+
26
+ # :api: private
27
+ def _run_callbacks(*args) #:nodoc:
28
+ self.class._run_callbacks(*args)
29
+ end
30
+
31
+ protected
32
+
33
+ def create(env, body = '', status = 500)
34
+ request = Rack::Request.new(env)
35
+ message = IfreeSms::Message.new(:request => request)
36
+
37
+ unless message.test?
38
+ _run_callbacks(:incoming_message, env, message) if message.save
39
+
40
+ body, status = message.response_to_ifree
41
+ else
42
+ body, status = [message.test_to_ifree, 200]
43
+ end
44
+
45
+ [status, {'Content-Type' => 'application/xml', 'Content-Length' => body.size.to_s}, body]
46
+ end
47
+
48
+ def smsing_path?(request_path)
49
+ return false if @config.routes.nil?
50
+
51
+ @config.routes == request_path
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,109 @@
1
+ # encoding: utf-8
2
+ require "base64"
3
+
4
+ module IfreeSms
5
+ module Smsing
6
+ def self.included(base)
7
+ base.send :include, InstanceMethods
8
+ base.send :extend, ClassMethods
9
+ end
10
+
11
+ module ClassMethods
12
+ def self.extended(base)
13
+ base.class_eval do
14
+ # Associations
15
+ belongs_to :messageable, :polymorphic => true
16
+
17
+ # Validations
18
+ validates :sms_id, :phone, :service_number, :sms_text, :now, :presence => true
19
+ validate :check_secret_key
20
+
21
+ attr_accessible :request
22
+ attr_accessor :md5key, :test, :answer_text
23
+
24
+ scope :with_messageable, lambda { |record| where(["messageable_id = ? AND messageable_type = ?", record.id, record.class.name]) }
25
+ end
26
+ end
27
+ end
28
+
29
+ module InstanceMethods
30
+
31
+ def request
32
+ @request
33
+ end
34
+
35
+ def request=(req)
36
+ self.sms_id = req.params["smsId"].to_i
37
+ self.phone = req.params["phone"].to_i
38
+ self.service_number = req.params["serviceNumber"].to_i
39
+ self.encoded_sms_text = req.params["smsText"]
40
+ self.now = parse_date(req.params["now"])
41
+ self.md5key = req.params["md5key"]
42
+ self.test = req.params["test"]
43
+
44
+ @request = req
45
+ end
46
+
47
+ def encoded_sms_text
48
+ @encoded_sms_text
49
+ end
50
+
51
+ def encoded_sms_text=(value)
52
+ self.sms_text = Base64.decode64(value) unless value.blank?
53
+ @encoded_sms_text = value
54
+ end
55
+
56
+ def to_ifree
57
+ if self.answer_text.blank?
58
+ "<Response noresponse='true'/>"
59
+ else
60
+ "<Response><SmsText>#{self.answer_text}</SmsText></Response>"
61
+ end
62
+ end
63
+
64
+ def test_to_ifree
65
+ "<Response><SmsText>#{self.test}</SmsText></Response>"
66
+ end
67
+
68
+ def response_to_ifree
69
+ unless self.new_record?
70
+ [self.to_ifree, 200]
71
+ else
72
+ [self.errors.to_xml, 422]
73
+ end
74
+ end
75
+
76
+ def test?
77
+ !self.test.blank?
78
+ end
79
+
80
+ def send_answer(text)
81
+ IfreeSms.send_sms(self.phone, text, self.sms_id)
82
+ end
83
+
84
+ protected
85
+
86
+ def check_secret_key
87
+ errors.add(:md5key, :invalid) unless valid_secret?
88
+ end
89
+
90
+ def valid_secret?
91
+ return false if now.nil?
92
+
93
+ digest = IfreeSms.calc_digest(service_number, encoded_sms_text, now.utc.strftime("%Y%m%d%H%M%S"))
94
+
95
+ IfreeSms.log("md5key: #{md5key}, calc_digest: #{digest}")
96
+
97
+ self.md5key == digest
98
+ end
99
+
100
+ def parse_date(value)
101
+ begin
102
+ DateTime.strptime(value, "%Y%m%d%H%M%S")
103
+ rescue Exception => e
104
+ nil
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+ module IfreeSms
3
+ VERSION = "0.1.0".freeze
4
+ end
data/lib/ifree_sms.rb ADDED
@@ -0,0 +1,68 @@
1
+ # encoding: utf-8
2
+ require "curb"
3
+ require "base64"
4
+ require 'digest/md5'
5
+
6
+ module IfreeSms
7
+ autoload :Smsing, 'ifree_sms/smsing'
8
+ autoload :Manager, 'ifree_sms/manager'
9
+ autoload :Config, 'ifree_sms/config'
10
+ autoload :Callbacks, 'ifree_sms/callbacks'
11
+
12
+ mattr_accessor :config
13
+ @@config = Config.new
14
+
15
+ def self.setup(&block)
16
+ yield config
17
+ end
18
+
19
+ def self.log(message)
20
+ if IfreeSms.config.debug
21
+ Rails.logger.info("[IfreeSms] #{message}")
22
+ end
23
+ end
24
+
25
+ def self.table_name_prefix
26
+ 'ifree_sms_'
27
+ end
28
+
29
+ # Send sms message
30
+ # Sample request:
31
+ # http://srv1.com.ua/mcdonalds/second.php?smsId=noID&phone=380971606179&serviceNumber=3533&smsText=test-message&md5key=f920c72547012ece62861938b7731415&now=20110527160613
32
+ #
33
+ def self.send_sms(phone, text, sms_id='noID')
34
+ now = Time.now.utc.strftime("%Y%m%d%H%M%S")
35
+
36
+ params = {}
37
+ params[:smsId] = sms_id
38
+ params[:phone] = phone
39
+ params[:serviceNumber] = config.service_number
40
+ params[:smsText] = Base64.encode64(text)
41
+ params[:now] = now
42
+ params[:md5key] = calc_digest(config.service_number, params[:smsText], now)
43
+
44
+ get(params)
45
+ end
46
+
47
+ def self.get(params)
48
+ query = Rack::Utils.build_query(params)
49
+ url = [config.url, query].join('?')
50
+
51
+ log("request: #{url}")
52
+
53
+ http = Curl::Easy.new(url)
54
+ http.perform
55
+
56
+ log("response: #{http.body_str}")
57
+
58
+ http.body_str
59
+ end
60
+
61
+ def self.calc_digest(number, text, now)
62
+ log("service_number: #{number}, sms_text: #{text}, now: #{now}, secret: #{config.secret_key}")
63
+
64
+ Digest::MD5.hexdigest([number, text, config.secret_key, now].map(&:to_s).join)
65
+ end
66
+ end
67
+
68
+ require 'ifree_sms/engine' if defined?(Rails)
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ifree-sms
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Igor Galeta
14
+ - Pavlo Galeta
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-06-08 00:00:00 +03:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ type: :runtime
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 29
30
+ segments:
31
+ - 0
32
+ - 7
33
+ - 15
34
+ version: 0.7.15
35
+ name: curb
36
+ version_requirements: *id001
37
+ prerelease: false
38
+ - !ruby/object:Gem::Dependency
39
+ type: :runtime
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 15
46
+ segments:
47
+ - 1
48
+ - 4
49
+ - 4
50
+ version: 1.4.4
51
+ name: nokogiri
52
+ version_requirements: *id002
53
+ prerelease: false
54
+ - !ruby/object:Gem::Dependency
55
+ type: :runtime
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ name: activemodel
66
+ version_requirements: *id003
67
+ prerelease: false
68
+ description: The IfreeSms gem for i-free sms provider
69
+ email: superp1987@gmail.com
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ extra_rdoc_files:
75
+ - LICENSE
76
+ - README.rdoc
77
+ files:
78
+ - LICENSE
79
+ - README.rdoc
80
+ - Rakefile
81
+ - app/models/ifree_sms/message.rb
82
+ - lib/generators/ifree_sms/USAGE
83
+ - lib/generators/ifree_sms/install_generator.rb
84
+ - lib/generators/ifree_sms/templates/config/ifree_sms.rb
85
+ - lib/generators/ifree_sms/templates/migrate/create_messages.rb
86
+ - lib/ifree-sms.rb
87
+ - lib/ifree_sms.rb
88
+ - lib/ifree_sms/callbacks.rb
89
+ - lib/ifree_sms/config.rb
90
+ - lib/ifree_sms/engine.rb
91
+ - lib/ifree_sms/manager.rb
92
+ - lib/ifree_sms/smsing.rb
93
+ - lib/ifree_sms/version.rb
94
+ has_rdoc: true
95
+ homepage: https://github.com/superp/ifree-sms
96
+ licenses: []
97
+
98
+ post_install_message:
99
+ rdoc_options: []
100
+
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ hash: 3
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project:
124
+ rubygems_version: 1.6.2
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: The IfreeSms gem for i-free sms provider
128
+ test_files: []
129
+