ileti_merkezi 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ # Authentication
5
+ module Authentication
6
+ module_function
7
+
8
+ def content
9
+ send(IletiMerkezi.configuration.auth_method)
10
+ end
11
+
12
+ def auth_basic
13
+ config = IletiMerkezi.configuration
14
+ <<-XML.gsub(/^[ ]+/, '').strip
15
+ <authentication>
16
+ <username>#{config.username}</username>
17
+ <password>#{config.password}</password>
18
+ </authentication>
19
+ XML
20
+ end
21
+
22
+ def auth_token
23
+ config = IletiMerkezi.configuration
24
+ <<-XML.gsub(/^[ ]+/, '').strip
25
+ <authentication>
26
+ <key>#{config.public_key}</key>
27
+ <hash>#{config.hmac}</hash>
28
+ </authentication>
29
+ XML
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ # Balance
5
+ module Balance
6
+ PATH = '/get-balance'.freeze
7
+
8
+ module_function
9
+
10
+ def query
11
+ request = Request.new(path: PATH)
12
+ request.call
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ # Cancel
5
+ class Cancel
6
+ PATH = '/cancel-order'.freeze
7
+
8
+ attr_reader :order_id
9
+
10
+ def initialize(order_id)
11
+ @order_id = order_id
12
+ end
13
+
14
+ def confirm
15
+ request = Request.new(content: content, path: PATH)
16
+ request.call
17
+ end
18
+
19
+ private
20
+
21
+ def content
22
+ "<id>#{order_id}</id>"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module IletiMerkezi
6
+ # Configuration
7
+ # :reek:TooManyInstanceVariables { max_instance_variables: 8 }
8
+ class Configuration < Module
9
+ # :reek:Attribute
10
+ attr_accessor :endpoint,
11
+ :username,
12
+ :password,
13
+ :sender,
14
+ :public_key,
15
+ :secret_key,
16
+ :request_overrides
17
+
18
+ ENDPOINT = 'http://api.iletimerkezi.com/v1'.freeze
19
+ SENDER = 'ILETI MRKZI'.freeze
20
+
21
+ def initialize
22
+ @endpoint = ENV['IM_ENDPOINT'] || ENDPOINT
23
+ @username = ENV['IM_USERNAME']
24
+ @password = ENV['IM_PASSWORD']
25
+ @public_key = ENV['IM_PUBLIC_KEY']
26
+ @secret_key = ENV['IM_SECRET_KEY']
27
+ @sender = ENV['IM_SENDER'] || SENDER
28
+ @request_overrides = {}
29
+ end
30
+
31
+ def auth_method
32
+ if public_key && secret_key
33
+ :auth_token
34
+ elsif username && password
35
+ :auth_basic
36
+ else
37
+ raise CredentialMissingError, 'Credentials missing'
38
+ end
39
+ end
40
+
41
+ def hmac
42
+ OpenSSL::HMAC.hexdigest(
43
+ OpenSSL::Digest.new('sha256'), secret_key.to_s, public_key.to_s
44
+ )
45
+ end
46
+
47
+ # set default config
48
+ def default_config
49
+ self.username = ENV['IM_USERNAME']
50
+ self.password = ENV['IM_PASSWORD']
51
+ self.public_key = ENV['IM_PUBLIC_KEY']
52
+ self.secret_key = ENV['IM_SECRET_KEY']
53
+ true
54
+ end
55
+
56
+ # config resets
57
+ def reset
58
+ self.username = nil
59
+ self.password = nil
60
+ self.public_key = nil
61
+ self.secret_key = nil
62
+ true
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ class CredentialMissingError < StandardError; end
5
+ class InvalidMessageError < StandardError; end
6
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ # Message
5
+ class Message
6
+ attr_reader :phones, :text
7
+
8
+ def initialize(phones, text)
9
+ @phones = phones.is_a?(Array) ? phones : [phones]
10
+ @text = text.to_s
11
+ end
12
+
13
+ def content
14
+ raise InvalidMessageError, to_s unless valid?
15
+ <<-XML.gsub(/^[ ]+/, '').strip
16
+ <message>
17
+ <text>#{text}</text>
18
+ <receipents>
19
+ #{phones.compact.map { |phone| phone_xmlify(phone) }.join("\n")}
20
+ </receipents>
21
+ </message>
22
+ XML
23
+ end
24
+
25
+ def summary
26
+ {
27
+ text: text,
28
+ phones: phones,
29
+ receipents_count: phones.count,
30
+ valid: valid?
31
+ }
32
+ end
33
+
34
+ def to_s
35
+ "Text: #{text}, Phones: #{phones}"
36
+ end
37
+
38
+ def valid?
39
+ phones.any? && !text.empty?
40
+ end
41
+
42
+ private
43
+
44
+ def phone_xmlify(phone)
45
+ "<number>#{phone.to_s.tr(' ', '')}</number>" unless phone.to_s.empty?
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ # Report
5
+ class Report
6
+ PATH = '/get-report'.freeze
7
+
8
+ attr_reader :order_id, :page, :row_count
9
+
10
+ def initialize(order_id, page: 1, row_count: 1000)
11
+ @order_id = order_id
12
+ @page = page
13
+ @row_count = row_count
14
+ end
15
+
16
+ def query
17
+ request = Request.new(content: content, path: PATH)
18
+ request.call
19
+ end
20
+
21
+ private
22
+
23
+ def content
24
+ <<-XML.gsub(/^[ ]+/, '').strip
25
+ <id>#{order_id}</id>
26
+ <page>#{page}</page>
27
+ <rowCount>#{row_count}</rowCount>
28
+ XML
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ # Request
5
+ # :reek:TooManyInstanceVariables { max_instance_variables: 6 }
6
+ class Request
7
+ DEFAULT_OPTIONS = {
8
+ use_ssl: false,
9
+ verify_mode: OpenSSL::SSL::VERIFY_PEER,
10
+ read_timeout: 30,
11
+ open_timeout: 30
12
+ }.freeze
13
+
14
+ DEFAULT_HEADERS = {
15
+ 'accept' => 'xml',
16
+ 'content-type' => 'text/xml;charset=utf-8'
17
+ }.freeze
18
+
19
+ private_constant :DEFAULT_OPTIONS
20
+ private_constant :DEFAULT_HEADERS
21
+
22
+ # :reek:Attribute
23
+ attr_writer :uri
24
+
25
+ def initialize(body: nil, content: '', path: '')
26
+ @config = IletiMerkezi.configuration
27
+ @content = content
28
+ @path = path
29
+ @body = body
30
+ @uri = URI.parse(@config.endpoint + @path)
31
+ end
32
+
33
+ def call
34
+ req = Net::HTTP::Post.new(@uri.request_uri, DEFAULT_HEADERS)
35
+ req.body = @body || body
36
+ Response.new http.request(req)
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :config, :uri, :content
42
+
43
+ def create_order
44
+ <<-XML.gsub(/^[ ]+/, '').strip
45
+ <order>
46
+ #{content}
47
+ </order>
48
+ XML
49
+ end
50
+
51
+ def body
52
+ <<-XML.gsub(/^[ ]+/, '').strip
53
+ <?xml version="1.0" encoding="UTF-8" ?>
54
+ <request>
55
+ #{Authentication.content}
56
+ #{create_order}
57
+ </request>
58
+ XML
59
+ end
60
+
61
+ def http
62
+ options = DEFAULT_OPTIONS.merge(config.request_overrides)
63
+ http = Net::HTTP.new(
64
+ uri.hostname, (options[:use_ssl] ? 443 : @uri.port)
65
+ )
66
+ http_configure(http, options)
67
+ end
68
+
69
+ def http_configure(http, options)
70
+ options.each do |option, value|
71
+ setter = "#{option.to_sym}="
72
+ http.send(setter, value) if http.respond_to?(setter)
73
+ end
74
+ http
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module IletiMerkezi
6
+ # Response
7
+ class Response
8
+ extend Forwardable
9
+
10
+ def_delegators :status, :message, :error?, :info?, :success?
11
+
12
+ attr_reader :response, :code, :body
13
+
14
+ def initialize(response)
15
+ @response = response
16
+ @code = response.code.to_i
17
+ @body = response.body
18
+ end
19
+
20
+ def status
21
+ Status.find(code)
22
+ end
23
+
24
+ def to_h
25
+ hash = Ox.load(
26
+ body.force_encoding('utf-8'),
27
+ mode: :hash
28
+ )
29
+ hash.fetch(:response, hash)
30
+ rescue Ox::ParseError
31
+ {}
32
+ end
33
+
34
+ alias to_hash to_h
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ # Sms
5
+ class Sms
6
+ attr_reader :args, :sender
7
+
8
+ PATH = '/send-sms'.freeze
9
+
10
+ def initialize(args)
11
+ @args = args
12
+ @sender = args[:sender] || IletiMerkezi.configuration.sender
13
+ end
14
+
15
+ def messages
16
+ messages = args.fetch(:messages, [args])
17
+ messages.map { |msg| Message.new(msg[:phones], msg[:text]) }
18
+ end
19
+
20
+ def send_datetime
21
+ args.fetch(:send_datetime, Time.now.strftime('%d/%m/%Y %H:%M'))
22
+ end
23
+
24
+ def send
25
+ request = Request.new(content: content, path: PATH)
26
+ request.call
27
+ end
28
+
29
+ private
30
+
31
+ def content
32
+ <<-XML.gsub(/^[ ]+/, '').strip
33
+ <sender>#{sender}</sender>
34
+ <sendDateTime>#{send_datetime}</sendDateTime>
35
+ #{messages.map(&:content).join}
36
+ XML
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ # Status
5
+ class Status
6
+ CODES = {
7
+ 110 => { type: :info, message: 'Mesaj gönderiliyor' },
8
+ 111 => { type: :info, message: 'Mesaj gönderildi' },
9
+ 112 => { type: :error, message: 'Mesaj gönderilemedi' },
10
+ 113 => { type: :info, message: 'Siparişin gönderimi devam ediyor' },
11
+ 114 => { type: :info, message: 'Siparişin gönderimi tamamlandı' },
12
+ 115 => { type: :info, message: 'Sipariş gönderilemedi' },
13
+ 200 => { type: :info, message: 'İşlem başarılı' },
14
+ 400 => { type: :error, message: 'İstek çözümlenemedi' },
15
+ 401 => { type: :error, message: 'Üyelik bilgileri hatalı' },
16
+ 402 => { type: :error, message: 'Bakiye yetersiz' },
17
+ 404 => { type: :error, message: 'API istek yapılan yönteme sahip değil' },
18
+ 450 => { type: :error, message: 'Gönderilen başlık kullanıma uygun değil' },
19
+ 451 => { type: :error, message: 'Tekrar eden sipariş' },
20
+ 452 => { type: :error, message: 'Mesaj alıcıları hatalı' },
21
+ 453 => { type: :error, message: 'Sipariş boyutu aşıldı' },
22
+ 454 => { type: :error, message: 'Mesaj metni boş' },
23
+ 455 => { type: :error, message: 'Sipariş bulunamadı' },
24
+ 456 => { type: :info, message: 'Sipariş gönderim tarihi henüz gelmedi' },
25
+ 457 => { type: :error, message: 'Mesaj gönderim tarihinin formatı hatalı' },
26
+ 503 => { type: :error, message: 'Sunucu geçici olarak servis dışı' }
27
+ }.freeze
28
+
29
+ attr_reader :code, :type, :message
30
+
31
+ def initialize(options = {})
32
+ @code = options[:code].to_i
33
+ @type = options[:type]
34
+ @message = options[:message]
35
+ end
36
+
37
+ def self.find(code)
38
+ value = CODES[code.to_i] || {}
39
+ new(value.merge(code: code))
40
+ end
41
+
42
+ def error?
43
+ type == :error
44
+ end
45
+
46
+ def info?
47
+ type == :info
48
+ end
49
+
50
+ def success?
51
+ info?
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IletiMerkezi
4
+ VERSION = '0.1.0'.freeze
5
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'ox'
5
+
6
+ files = %w[
7
+ version
8
+ error
9
+ configuration
10
+ account
11
+ authentication
12
+ balance
13
+ cancel
14
+ message
15
+ request
16
+ report
17
+ response
18
+ sms
19
+ status
20
+ ]
21
+
22
+ files.each { |path| require_relative "./ileti_merkezi/#{path}" }
23
+
24
+ # IletiMerkezi
25
+ module IletiMerkezi
26
+ class << self
27
+ # :reek:Attribute
28
+ attr_writer :configuration
29
+ end
30
+
31
+ module_function
32
+
33
+ def configuration
34
+ @configuration ||= Configuration.new
35
+ end
36
+
37
+ def configure
38
+ yield(configuration)
39
+ end
40
+
41
+ # Helper Methods
42
+
43
+ # Get balance information
44
+ #
45
+ # @return [IletiMerkezi::Response]
46
+ #
47
+ # @api public
48
+ def balance
49
+ Balance.query
50
+ end
51
+
52
+ # Sms order cancellation
53
+ #
54
+ # @param order_id [Integer] Sms order id
55
+ #
56
+ # @return [IletiMerkezi::Response]
57
+ #
58
+ # @api public
59
+ def cancel(order_id)
60
+ Cancel.new(order_id).confirm
61
+ end
62
+
63
+ # Find status with code id
64
+ #
65
+ # @param code_id [Integer] Http status code
66
+ #
67
+ # @return [IletiMerkezi::Status]
68
+ #
69
+ # @api public
70
+ def code(code_id)
71
+ Status.find(code_id)
72
+ end
73
+
74
+ # Get account information
75
+ #
76
+ # @return [Hash]
77
+ # {
78
+ # name_surname: 'name-surname',
79
+ # tc_number: nil,
80
+ # title: nil,
81
+ # email: 'email',
82
+ # gsm: 'phone',
83
+ # password: 'password',
84
+ # commercial_title: nil,
85
+ # gsm2: nil,
86
+ # phone_number: nil,
87
+ # fax_number: nil,
88
+ # bill_address: nil,
89
+ # zone: nil,
90
+ # city: nil,
91
+ # zip: nil,
92
+ # charge_home: nil,
93
+ # charge_number: nil
94
+ # }
95
+ #
96
+ # @api public
97
+ def info
98
+ response = Account.info
99
+ response.to_h.fetch(:userInfo, {})
100
+ end
101
+
102
+ # Get Sms order report
103
+ #
104
+ # @param order_id [Integer], page [Integer], row_count [Integer]
105
+ #
106
+ # @return [IletiMerkezi::Response]
107
+ #
108
+ # @api public
109
+ def report(order_id, page: 1, row_count: 1000)
110
+ Report.new(order_id,
111
+ page: page,
112
+ row_count: row_count).query
113
+ end
114
+
115
+ # Sms send method
116
+ #
117
+ # @param args [Hash]
118
+ #
119
+ # @return [IletiMerkezi::Response]
120
+ #
121
+ # Example:
122
+ #
123
+ # args1 = {
124
+ # send_datetime: '15/01/2017 12:00' # Opsiyonel
125
+ # sender: 'TEST' # Opsiyonel
126
+ # phones: ['0555 555 00 01', '0555 555 00 02']
127
+ # text: 'Test Message'
128
+ # }
129
+ #
130
+ # args2 = {
131
+ # send_datetime: '15/01/2017 12:00' # Opsiyonel
132
+ # sender: 'TEST' # Opsiyonel
133
+ # messages: [
134
+ # {
135
+ # text: 'First Test Message',
136
+ # phones: ['0555 555 00 01', '0555 555 00 02'],
137
+ # },
138
+ # {
139
+ # text: 'Second Test Message',
140
+ # phones: ['0555 555 00 03', '0555 555 00 04'],
141
+ # }
142
+ # ]
143
+ # }
144
+ #
145
+ # response = IletiMerkezi.send(args1)
146
+ # or
147
+ # response = IletiMerkezi.send(args2)
148
+ #
149
+ # @api public
150
+ def send(args)
151
+ Sms.new(args).send
152
+ end
153
+
154
+ # Get senders
155
+ #
156
+ # @return [Hash]
157
+ #
158
+ # @api public
159
+ def senders
160
+ response = Account.senders
161
+ response.to_h.fetch(:smsHeaderInfo, {})
162
+ end
163
+
164
+ # Get Service Status
165
+ #
166
+ # @return [Boolean]
167
+ #
168
+ # @api public
169
+ def status
170
+ balance.code == 200
171
+ end
172
+ end