ifree-sms 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.
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
+