sogou-search-api 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +8 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/account.rb +22 -0
- data/examples/keyword.rb +40 -0
- data/examples/plan.rb +29 -0
- data/examples/promotion_group.rb +43 -0
- data/examples/report.rb +37 -0
- data/lib/sogou/search/api.rb +35 -0
- data/lib/sogou/search/api/auth.rb +54 -0
- data/lib/sogou/search/api/core/api_command.rb +114 -0
- data/lib/sogou/search/api/core/base_service.rb +59 -0
- data/lib/sogou/search/api/core/logging.rb +15 -0
- data/lib/sogou/search/api/core/regions.rb +429 -0
- data/lib/sogou/search/api/errors.rb +50 -0
- data/lib/sogou/search/api/options.rb +17 -0
- data/lib/sogou/search/api/service/account.rb +20 -0
- data/lib/sogou/search/api/service/keyword.rb +45 -0
- data/lib/sogou/search/api/service/plan.rb +25 -0
- data/lib/sogou/search/api/service/promotion_group.rb +33 -0
- data/lib/sogou/search/api/service/report.rb +49 -0
- data/lib/sogou/search/api/version.rb +24 -0
- data/sogou-search-api.gemspec +30 -0
- metadata +142 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e5c716c3f0a2b1c807eb08265f4b9ee700ae33d7
|
4
|
+
data.tar.gz: c66b6cd0d0e800170161a24b8ebde867ce54fce7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 627e5d14ebd0314ecb7330761522375dff5a54fa20152d0177493120c44235ddcea00841f76b5285912a2e960afc5dfcffd49f3c2a112b12081f87fd5b1d6214
|
7
|
+
data.tar.gz: a944be7071287c114fc205fb730d55c8e763fd7ef57e005e94e0c32c279da5a5b75fb6d09bba2620ff1a3fd9434cb6b3c5b1419d448348b97a1fa747df6dfc54
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018-present Forward3D
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Sogou Search API
|
2
|
+
|
3
|
+
A Client GEM for [Sogou Search API](http://apihome.sogou.com/document/ss/doc1-1.jsp)
|
4
|
+
|
5
|
+
## Alpha
|
6
|
+
|
7
|
+
This library is in Alpha. We will make an effort to support the library, but we reserve the right to make incompatible
|
8
|
+
changes when necessary.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "sogou/search/api"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/examples/account.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'sogou/search/api/auth'
|
2
|
+
require 'sogou/search/api/service/account'
|
3
|
+
require 'sogou/search/api/service/plan'
|
4
|
+
|
5
|
+
#
|
6
|
+
# Set your Sogou api credentials to ENVs
|
7
|
+
#
|
8
|
+
# ENV['SOGOU_API_TOKEN'] = 'xxxx'
|
9
|
+
# ENV['SOGOU_USERNAME'] = 'xxxx'
|
10
|
+
# ENV['SOGOU_PASSWORD'] = 'xxxx'
|
11
|
+
|
12
|
+
include Sogou::Search::Api
|
13
|
+
|
14
|
+
account = Service::Account.new
|
15
|
+
account.authorization = Auth.get_application_default
|
16
|
+
account.get_account_info(convert_regions_to_string: true) do |result, err|
|
17
|
+
if err != nil
|
18
|
+
p err
|
19
|
+
else
|
20
|
+
p result
|
21
|
+
end
|
22
|
+
end
|
data/examples/keyword.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'sogou/search/api/auth'
|
2
|
+
require 'sogou/search/api/service/plan'
|
3
|
+
require 'sogou/search/api/service/promotion_group'
|
4
|
+
require 'sogou/search/api/service/keyword'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Set your Sogou api credentials to ENVs
|
8
|
+
#
|
9
|
+
# ENV['SOGOU_API_TOKEN'] = 'xxxx'
|
10
|
+
# ENV['SOGOU_USERNAME'] = 'xxxx'
|
11
|
+
# ENV['SOGOU_PASSWORD'] = 'xxxx'
|
12
|
+
|
13
|
+
include Sogou::Search::Api
|
14
|
+
|
15
|
+
plan = Service::Plan.new
|
16
|
+
plan.authorization = Auth.get_application_default
|
17
|
+
plan_ids = plan.get_all_cpc_plan_id.slice(0, 2)
|
18
|
+
|
19
|
+
group = Service::PromotionGroup.new
|
20
|
+
group.authorization = Auth.get_application_default
|
21
|
+
group_ids = group.get_cpc_grp_id_by_cpc_plan_id(plan_ids).
|
22
|
+
map { |id| id['cpc_grp_ids'] }.flatten.slice(0, 2)
|
23
|
+
|
24
|
+
keyword = Service::Keyword.new
|
25
|
+
keyword.authorization = Auth.get_application_default
|
26
|
+
keyword.get_cpc_by_cpc_grp_id(group_ids) do |result, err|
|
27
|
+
if err != nil
|
28
|
+
p err
|
29
|
+
else
|
30
|
+
p result
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
keyword.get_cpc_id_by_cpc_grp_id(group_ids) do |result, err|
|
35
|
+
if err != nil
|
36
|
+
p err
|
37
|
+
else
|
38
|
+
p result
|
39
|
+
end
|
40
|
+
end
|
data/examples/plan.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'sogou/search/api/auth'
|
2
|
+
require 'sogou/search/api/service/plan'
|
3
|
+
|
4
|
+
#
|
5
|
+
# Set your Sogou api credentials to ENVs
|
6
|
+
#
|
7
|
+
# ENV['SOGOU_API_TOKEN'] = 'xxxx'
|
8
|
+
# ENV['SOGOU_USERNAME'] = 'xxxx'
|
9
|
+
# ENV['SOGOU_PASSWORD'] = 'xxxx'
|
10
|
+
|
11
|
+
include Sogou::Search::Api
|
12
|
+
|
13
|
+
plan = Service::Plan.new
|
14
|
+
plan.authorization = Auth.get_application_default
|
15
|
+
plan.get_all_cpc_plan(convert_regions_to_string: true) do |result, err|
|
16
|
+
if err != nil
|
17
|
+
p err
|
18
|
+
else
|
19
|
+
p result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
plan.get_all_cpc_plan_id do |result, err|
|
24
|
+
if err != nil
|
25
|
+
p err
|
26
|
+
else
|
27
|
+
p result
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'sogou/search/api/auth'
|
2
|
+
require 'sogou/search/api/service/plan'
|
3
|
+
require 'sogou/search/api/service/promotion_group'
|
4
|
+
|
5
|
+
#
|
6
|
+
# Set your Sogou api credentials to ENVs
|
7
|
+
#
|
8
|
+
# ENV['SOGOU_API_TOKEN'] = 'xxxx'
|
9
|
+
# ENV['SOGOU_USERNAME'] = 'xxxx'
|
10
|
+
# ENV['SOGOU_PASSWORD'] = 'xxxx'
|
11
|
+
|
12
|
+
include Sogou::Search::Api
|
13
|
+
|
14
|
+
plan = Service::Plan.new
|
15
|
+
plan.authorization = Auth.get_application_default
|
16
|
+
plan_ids = plan.get_all_cpc_plan_id.slice(0, 2)
|
17
|
+
|
18
|
+
group = Service::PromotionGroup.new
|
19
|
+
group.authorization = Auth.get_application_default
|
20
|
+
group.get_cpc_grp_by_cpc_plan_id(plan_ids) do |result, err|
|
21
|
+
if err != nil
|
22
|
+
p err
|
23
|
+
else
|
24
|
+
p result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
group.get_cpc_grp_id_by_cpc_plan_id(plan_ids) do |result, err|
|
29
|
+
if err != nil
|
30
|
+
p err
|
31
|
+
else
|
32
|
+
p result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Error
|
37
|
+
group.get_cpc_grp_by_cpc_plan_id([1, 2]) do |result, err|
|
38
|
+
if err != nil
|
39
|
+
p err
|
40
|
+
else
|
41
|
+
p result
|
42
|
+
end
|
43
|
+
end
|
data/examples/report.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'sogou/search/api/auth'
|
2
|
+
require 'sogou/search/api/service/report'
|
3
|
+
|
4
|
+
#
|
5
|
+
# Set your Sogou api credentials to ENVs
|
6
|
+
#
|
7
|
+
# ENV['SOGOU_API_TOKEN'] = 'xxxx'
|
8
|
+
# ENV['SOGOU_USERNAME'] = 'xxxx'
|
9
|
+
# ENV['SOGOU_PASSWORD'] = 'xxxx'
|
10
|
+
|
11
|
+
include Sogou::Search::Api
|
12
|
+
|
13
|
+
report = Service::Report.new
|
14
|
+
report.authorization = Auth.get_application_default
|
15
|
+
report_id = report.get_report_id(
|
16
|
+
'2018-02-20T00:00:00',
|
17
|
+
'2018-02-20T23:59:59',
|
18
|
+
1,
|
19
|
+
['cost', 'cpc', 'click', 'ctr', 'position']
|
20
|
+
)
|
21
|
+
|
22
|
+
state = '0'
|
23
|
+
max_try = 10
|
24
|
+
try = 0
|
25
|
+
|
26
|
+
while state != '1' && try < 10
|
27
|
+
sleep(5)
|
28
|
+
state = report.get_report_state(report_id)
|
29
|
+
try += 1
|
30
|
+
end
|
31
|
+
|
32
|
+
if state == '1'
|
33
|
+
path = report.get_report_path(report_id)
|
34
|
+
p path
|
35
|
+
else
|
36
|
+
puts 'Timed out'
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'sogou/search/api/version'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Sogou
|
5
|
+
module Search
|
6
|
+
module Api
|
7
|
+
ROOT = File.expand_path('..', File.dirname(__dir__))
|
8
|
+
|
9
|
+
def self.logger
|
10
|
+
@logger ||= (rails_logger || default_logger)
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_writer :logger
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.default_logger
|
20
|
+
logger = Logger.new(STDOUT)
|
21
|
+
logger.level = Logger::WARN
|
22
|
+
logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.rails_logger
|
26
|
+
if defined?(::Rails) && ::Rails.respond_to?(:logger) &&
|
27
|
+
!::Rails.logger.nil?
|
28
|
+
::Rails.logger
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Sogou
|
2
|
+
module Search
|
3
|
+
module Api
|
4
|
+
module Auth
|
5
|
+
class V1AcccountCredentials
|
6
|
+
USERNAME_HEADER = 'v1:username'.freeze
|
7
|
+
PASSWORD_HEADER = 'v1:password'.freeze
|
8
|
+
API_TOKEN_HEADER = 'v1:token'.freeze
|
9
|
+
AUTH_HEADER = 'v1:AuthHeader'.freeze
|
10
|
+
|
11
|
+
attr_reader :api_token
|
12
|
+
attr_reader :username
|
13
|
+
attr_reader :password
|
14
|
+
|
15
|
+
def initialize(api_token, username, password)
|
16
|
+
@api_token = api_token
|
17
|
+
@username = username
|
18
|
+
@password = password
|
19
|
+
end
|
20
|
+
|
21
|
+
def apply(hash)
|
22
|
+
hash ||= {}
|
23
|
+
hash[AUTH_HEADER] = {
|
24
|
+
USERNAME_HEADER => username,
|
25
|
+
PASSWORD_HEADER => password,
|
26
|
+
API_TOKEN_HEADER => api_token
|
27
|
+
}
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class DefaultCredentials
|
33
|
+
API_TOKEN_ENV_VAR = 'SOGOU_API_TOKEN'.freeze
|
34
|
+
USERNAME_ENV_VAR = 'SOGOU_USERNAME'.freeze
|
35
|
+
PASSWORD_ENV_VAR = 'SOGOU_PASSWORD'.freeze
|
36
|
+
|
37
|
+
def self.from_env
|
38
|
+
V1AcccountCredentials.new(
|
39
|
+
ENV[API_TOKEN_ENV_VAR],
|
40
|
+
ENV[USERNAME_ENV_VAR],
|
41
|
+
ENV[PASSWORD_ENV_VAR]
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_application_default
|
47
|
+
DefaultCredentials.from_env
|
48
|
+
end
|
49
|
+
|
50
|
+
module_function :get_application_default
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require_relative '../errors'
|
2
|
+
require_relative 'logging'
|
3
|
+
require_relative 'regions'
|
4
|
+
|
5
|
+
module Sogou
|
6
|
+
module Search
|
7
|
+
module Api
|
8
|
+
module Core
|
9
|
+
class ApiCommand
|
10
|
+
include Logging
|
11
|
+
include Regions
|
12
|
+
|
13
|
+
attr_accessor :operation
|
14
|
+
attr_accessor :options
|
15
|
+
attr_accessor :params
|
16
|
+
|
17
|
+
def initialize(operation)
|
18
|
+
@operation = operation
|
19
|
+
@params = {}
|
20
|
+
@options = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute(client, &block)
|
24
|
+
logger.debug("Executing SOAP #{operation}")
|
25
|
+
|
26
|
+
resp = client.call(@operation, message: @params)
|
27
|
+
result = process_response(resp)
|
28
|
+
|
29
|
+
logger.debug("Success - #{result}")
|
30
|
+
success(result, &block)
|
31
|
+
rescue => e
|
32
|
+
logger.debug("Error - #{e.inspect}")
|
33
|
+
error(e, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_response(response)
|
37
|
+
check_status(response)
|
38
|
+
decode_response_body(response.body)
|
39
|
+
end
|
40
|
+
|
41
|
+
def check_status(response)
|
42
|
+
logger.debug("Response Header - #{response.header}")
|
43
|
+
res_header = response.header['res_header']
|
44
|
+
check_error_code(res_header['failures']) if res_header['desc'] != 'success'
|
45
|
+
end
|
46
|
+
|
47
|
+
def decode_response_body(body)
|
48
|
+
result = body["#{@operation}_response"]
|
49
|
+
if result
|
50
|
+
result = result.is_a?(Hash) ? result.values[0] : result
|
51
|
+
result = convert_regions_to_string(result) if options[:convert_regions_to_string]
|
52
|
+
result
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# More error codes and descriptions can be found here
|
58
|
+
# http://apihome.sogou.com/document/ss/doc11.jsp
|
59
|
+
#
|
60
|
+
def check_error_code(header, raise_error = true)
|
61
|
+
res_header = header.is_a?(Array) ? header[0] : header
|
62
|
+
error_klass = case res_header['code'].to_i
|
63
|
+
when 6 # Invalid username
|
64
|
+
InvalidUserNameError
|
65
|
+
when 8 # Wrong password
|
66
|
+
WrongPasswordError
|
67
|
+
when 10 # Token is invalid
|
68
|
+
InvalidTokenError
|
69
|
+
when 18 # Insufficient quotas
|
70
|
+
RateLimitError
|
71
|
+
when 1000011 # Plan ID does not exist
|
72
|
+
PlanIDNotExistError
|
73
|
+
when 1000012 # Promotion group ID does not exist
|
74
|
+
PromotionGroupIDNotExistError
|
75
|
+
when 1000013 # Keyword ID does not exist
|
76
|
+
KeywordIDNotExistError
|
77
|
+
else
|
78
|
+
UnknownError
|
79
|
+
end
|
80
|
+
|
81
|
+
exception = error_klass.new(
|
82
|
+
res_header.fetch('message', 'Unknown error'),
|
83
|
+
code: res_header['code'],
|
84
|
+
header: header
|
85
|
+
)
|
86
|
+
raise_error ? (raise exception) : exception
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def success(result)
|
92
|
+
yield(result, nil) if block_given?
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
def error(err)
|
97
|
+
if err.is_a?(Net::ReadTimeout) || err.is_a?(SocketError)
|
98
|
+
err = TransmissionError.new(err)
|
99
|
+
end
|
100
|
+
|
101
|
+
if block_given?
|
102
|
+
if err.respond_to?(:header) && err.header.is_a?(Array)
|
103
|
+
err = err.header.map { |h| check_error_code(h, false) }
|
104
|
+
end
|
105
|
+
yield(nil, err)
|
106
|
+
else
|
107
|
+
raise err
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|