ppc 0.3.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.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/.rspec +2 -0
- data/LICENSE +339 -0
- data/README.md +78 -0
- data/lib/ppc.rb +17 -0
- data/lib/ppc/api.rb +10 -0
- data/lib/ppc/api/baidu.rb +138 -0
- data/lib/ppc/api/baidu/account.rb +47 -0
- data/lib/ppc/api/baidu/bulk.rb +41 -0
- data/lib/ppc/api/baidu/creative.rb +125 -0
- data/lib/ppc/api/baidu/group.rb +111 -0
- data/lib/ppc/api/baidu/keyword.rb +204 -0
- data/lib/ppc/api/baidu/plan.rb +68 -0
- data/lib/ppc/api/baidu/report.rb +143 -0
- data/lib/ppc/api/qihu.rb +107 -0
- data/lib/ppc/api/qihu/account.rb +86 -0
- data/lib/ppc/api/qihu/creative.rb +106 -0
- data/lib/ppc/api/qihu/group.rb +113 -0
- data/lib/ppc/api/qihu/keyword.rb +111 -0
- data/lib/ppc/api/qihu/plan.rb +64 -0
- data/lib/ppc/api/qihu/report.rb +159 -0
- data/lib/ppc/api/shenma.rb +64 -0
- data/lib/ppc/api/shenma/report.rb +135 -0
- data/lib/ppc/api/sogou.rb +122 -0
- data/lib/ppc/api/sogou/account.rb +42 -0
- data/lib/ppc/api/sogou/creative.rb +117 -0
- data/lib/ppc/api/sogou/group.rb +116 -0
- data/lib/ppc/api/sogou/keyword.rb +182 -0
- data/lib/ppc/api/sogou/plan.rb +66 -0
- data/lib/ppc/api/sogou/report.rb +129 -0
- data/lib/ppc/ext.rb +9 -0
- data/lib/ppc/operation.rb +196 -0
- data/lib/ppc/operation/account.rb +53 -0
- data/lib/ppc/operation/creative.rb +28 -0
- data/lib/ppc/operation/group.rb +59 -0
- data/lib/ppc/operation/keyword.rb +32 -0
- data/lib/ppc/operation/plan.rb +47 -0
- data/lib/ppc/operation/report.rb +19 -0
- data/ppc.gemspec +26 -0
- data/spec/baidu/api_baidu_account_spec.rb +15 -0
- data/spec/baidu/api_baidu_creative_spec.rb +67 -0
- data/spec/baidu/api_baidu_group_spec.rb +45 -0
- data/spec/baidu/api_baidu_keyword_spec.rb +61 -0
- data/spec/baidu/api_baidu_plan_spec.rb +43 -0
- data/spec/baidu/api_baidu_report_spec.rb +44 -0
- data/spec/baidu/api_baidu_spec.rb +55 -0
- data/spec/operation/operation_baidu_report_spec.rb +17 -0
- data/spec/operation/operation_baidu_spec.rb +78 -0
- data/spec/operation/operation_qihu_report_spec.rb +18 -0
- data/spec/operation/operation_qihu_spec.rb +51 -0
- data/spec/operation/operation_sogou_report_spec.rb +17 -0
- data/spec/operation/operation_sogou_spec.rb +51 -0
- data/spec/operation/operation_spec_helper.rb +51 -0
- data/spec/qihu/api_qihu_account_spec.rb +25 -0
- data/spec/qihu/api_qihu_creative_spec.rb +48 -0
- data/spec/qihu/api_qihu_group_spec.rb +40 -0
- data/spec/qihu/api_qihu_keyword_spec.rb +50 -0
- data/spec/qihu/api_qihu_plan_spec.rb +39 -0
- data/spec/qihu/api_qihu_report_spec.rb +54 -0
- data/spec/sogou/api_sogou_account_spec.rb +15 -0
- data/spec/sogou/api_sogou_creative_spec.rb +48 -0
- data/spec/sogou/api_sogou_group_spec.rb +45 -0
- data/spec/sogou/api_sogou_keyword_spec.rb +50 -0
- data/spec/sogou/api_sogou_plan_spec.rb +39 -0
- data/spec/sogou/api_sogou_report_spec.rb +51 -0
- data/spec/spec_helper.rb +134 -0
- metadata +177 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
module PPC
|
2
|
+
module API
|
3
|
+
class Sogou
|
4
|
+
class Plan< Sogou
|
5
|
+
Service = 'CpcPlan'
|
6
|
+
|
7
|
+
@map = [
|
8
|
+
[:id,:cpcPlanId],
|
9
|
+
[:name,:cpcPlanName],
|
10
|
+
[:budget,:budget],
|
11
|
+
[:region,:regions],
|
12
|
+
[:ip,:excludeIps] ,
|
13
|
+
[:negative,:negativeWords],
|
14
|
+
[:exact_negative,:exactNegativeWords],
|
15
|
+
[:schedule,:schedule],
|
16
|
+
[:budget_offline_time,:budgetOfflineTime],
|
17
|
+
[:show_prob,:showProb],
|
18
|
+
[:join_union ,:joinUnion],
|
19
|
+
[:device,:device],
|
20
|
+
[:price_ratio,:mobilePriceRate ],
|
21
|
+
[:pause,:pause],
|
22
|
+
[:status,:status],
|
23
|
+
]
|
24
|
+
|
25
|
+
def self.all( auth, debug = false )
|
26
|
+
response = request( auth, Service, 'getAllCpcPlan' )
|
27
|
+
process( response, 'cpcPlanTypes' , debug ){ |x| reverse_type(x) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.ids( auth, debug = false )
|
31
|
+
response = request( auth, Service, 'getAllCpcPlanId' )
|
32
|
+
process( response, 'cpcPlanIds' , debug ){ |x| x }
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.get( auth, ids, debug = false )
|
36
|
+
ids = [ ids ] unless ids.is_a? Array
|
37
|
+
body = { cpcPlanIds: ids }
|
38
|
+
response = request( auth, Service, 'getCpcPlanByCpcPlanId', body)
|
39
|
+
process( response, 'cpcPlanTypes' , debug ){ |x| reverse_type(x) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.add( auth, plans, debug = false )
|
43
|
+
cpcPlanTypes = make_type( plans )
|
44
|
+
body = { cpcPlanTypes: cpcPlanTypes }
|
45
|
+
response = request( auth, Service, 'addCpcPlan', body)
|
46
|
+
process( response, 'cpcPlanTypes' , debug ){ |x| reverse_type(x) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.update(auth,plans, debug = false )
|
50
|
+
cpcPlanTypes = make_type( plans )
|
51
|
+
body = { cpcPlanTypes: cpcPlanTypes }
|
52
|
+
response = request( auth, Service, 'updateCpcPlan', body)
|
53
|
+
process( response, 'cpcPlanTypes' , debug ){ |x| reverse_type(x) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.delete(auth, ids, debug = false )
|
57
|
+
ids = [ ids ] unless ids.class == Array
|
58
|
+
body = { cpcPlanIds: ids }
|
59
|
+
response = request( auth, Service, 'deleteCpcPlan', body)
|
60
|
+
process( response, '' , debug ){ |x| x }
|
61
|
+
end
|
62
|
+
|
63
|
+
end # Service
|
64
|
+
end # baidu
|
65
|
+
end # API
|
66
|
+
end # PPC
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# -*- coding:utf-8 -*-
|
2
|
+
module PPC
|
3
|
+
module API
|
4
|
+
class Sogou
|
5
|
+
class Report< Sogou
|
6
|
+
Service = 'Report'
|
7
|
+
|
8
|
+
# 需要用到的映射集合
|
9
|
+
Type_map = { 'account' => 1, 'plan'=> 2, 'group'=> 3,
|
10
|
+
'keyword'=> 5, 'creative'=> 4, 'pair'=> 15,
|
11
|
+
'query'=> 3 }
|
12
|
+
|
13
|
+
Range_map = { 'account' => 1, 'plan' => 2, 'group' => 3,
|
14
|
+
'creative' => 4, 'keyword' => 5 }
|
15
|
+
|
16
|
+
Device_map = { 'all' => 0, 'pc' => 1, 'mobile' => 2 }
|
17
|
+
|
18
|
+
Unit_map = { 'day' => 1, 'week' => 2, 'month' => 3 }
|
19
|
+
|
20
|
+
def self.get_id( auth, params, debug = false )
|
21
|
+
request = make_reportrequest( params )
|
22
|
+
body = { reportRequestType: request }
|
23
|
+
response = request( auth, Service, 'getReportId' ,body)
|
24
|
+
process( response, 'reportId', debug ){ |x| x }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.get_state( auth, id, debug = false)
|
28
|
+
'''
|
29
|
+
input id should be string
|
30
|
+
'''
|
31
|
+
status = {'-1'=>'Waiting' ,'0'=>'Opearting' ,'1'=>'Finished'}
|
32
|
+
body = { reportId: id }
|
33
|
+
response = request( auth, Service, 'getReportState' ,body)
|
34
|
+
process( response, 'isGenerated', debug ){ |x| status[x] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.get_url( auth, id, debug = false )
|
38
|
+
body = { reportId: id }
|
39
|
+
response = request( auth, Service, 'getReportPath' ,body)
|
40
|
+
process( response, 'reportFilePath', debug ){ |x| x }
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def self.make_reportrequest( param )
|
45
|
+
'''
|
46
|
+
make RepoerRequestType
|
47
|
+
======================
|
48
|
+
For more docs please have a look at
|
49
|
+
::PPC::API::Baidu::Report:make_reportrequest()
|
50
|
+
'''
|
51
|
+
requesttype = {}
|
52
|
+
requesttype[:performanceData] = param[:fields] || %w(click)
|
53
|
+
requesttype[:reportType] = Type_map[ param[:type] ] if param[:type]
|
54
|
+
requesttype[:statRange] = Range_map[ param[:range] ] if param[:range]
|
55
|
+
requesttype[:unitOfTime] = Unit_map[ param[:unit] ] if param[:unit]
|
56
|
+
requesttype[:platform] = Device_map[ param[:device] ] if param[:device]
|
57
|
+
requesttype[:idOnly] = param[:id_only] if param[:id_only]!=nil
|
58
|
+
requesttype[:startDate] = parse_date( param[:startDate] )
|
59
|
+
requesttype[:endDate] = parse_date( param[:endDate] )
|
60
|
+
return requesttype
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def self.parse_date( date )
|
65
|
+
"""
|
66
|
+
Cast string to time:
|
67
|
+
'YYYYMMDD' => Time
|
68
|
+
"""
|
69
|
+
if date
|
70
|
+
y = date[0..3]
|
71
|
+
m = date[4..5]
|
72
|
+
d = date[6..7]
|
73
|
+
date = Time.new( y, m, d )
|
74
|
+
else
|
75
|
+
date = (Time.now - 24*3600)
|
76
|
+
end
|
77
|
+
date.utc.iso8601
|
78
|
+
end
|
79
|
+
|
80
|
+
###########################
|
81
|
+
# intreface for Operation #
|
82
|
+
###########################
|
83
|
+
def self.download_report( auth, param, debug = false )
|
84
|
+
response = get_id( auth, param )
|
85
|
+
if response[:succ]
|
86
|
+
id = response[:result]
|
87
|
+
p "Got report id:" + id.to_s if debug
|
88
|
+
loop do
|
89
|
+
sleep 2
|
90
|
+
break if get_state( auth, id )[:result] == 'Finished'
|
91
|
+
p "Report is not generated, waiting..." if debug
|
92
|
+
end
|
93
|
+
|
94
|
+
url = get_url( auth, id )[:result]
|
95
|
+
ActiveSupport::Gzip.decompress( open(url).read )
|
96
|
+
else
|
97
|
+
raise response[:failure][:message]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.query_report( auth, param = nil, debug = false )
|
102
|
+
param = {} if not param
|
103
|
+
param[:type] ||= 'query'
|
104
|
+
param[:fields] ||= %w(click)
|
105
|
+
param[:range] ||= 'account'
|
106
|
+
param[:unit] ||= 'day'
|
107
|
+
download_report( auth, param, debug )
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.creative_report( auth, param = nil, debug = false )
|
111
|
+
param = {} if not param
|
112
|
+
param[:type] ||= 'creative'
|
113
|
+
param[:fields] ||= %w( cost cpc click impression ctr )
|
114
|
+
param[:range] ||= 'account'
|
115
|
+
download_report( auth, param, debug )
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.keyword_report( auth, param = nil, debug = false )
|
119
|
+
param = {} if not param
|
120
|
+
param[:type] ||= 'keyword'
|
121
|
+
param[:fields] ||= %w( cost cpc click impression ctr )
|
122
|
+
param[:range] ||= 'account'
|
123
|
+
download_report( auth, param, debug )
|
124
|
+
end
|
125
|
+
|
126
|
+
end # Repost
|
127
|
+
end # Baidu
|
128
|
+
end # API
|
129
|
+
end # PPC
|
data/lib/ppc/ext.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
module PPC
|
2
|
+
module Operation
|
3
|
+
|
4
|
+
attr_accessor :id
|
5
|
+
|
6
|
+
def initialize( params )
|
7
|
+
|
8
|
+
if params[:se] == nil
|
9
|
+
raise 'please specific a search engine'
|
10
|
+
else
|
11
|
+
@se = params[:se]
|
12
|
+
end
|
13
|
+
@id = params[:id]
|
14
|
+
@auth = {
|
15
|
+
username: params[:username],
|
16
|
+
password: params[:password],
|
17
|
+
# 在qihu360的api中,apikey就是auth[:token]
|
18
|
+
token: params[:token]
|
19
|
+
}
|
20
|
+
# add support for qihu360
|
21
|
+
if @se == 'qihu' && params[:accessToken] == nil
|
22
|
+
raise "you are using qihu service, please enter cipherkey" if params[:cipherkey] == nil
|
23
|
+
raise "you are using qihu service, please enter cipheriv" if params[:cipheriv] == nil
|
24
|
+
cipher = { key: params[:cipherkey], iv: params[:cipheriv] }
|
25
|
+
@auth[:accessToken] = qihu_refresh_token( @auth, cipher )
|
26
|
+
else
|
27
|
+
@auth[:accessToken] = params[:accessToken]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def qihu_refresh_token( auth, cipher )
|
32
|
+
cipher_aes = OpenSSL::Cipher::AES.new(128, :CBC)
|
33
|
+
cipher_aes.encrypt
|
34
|
+
cipher_aes.key = cipher[:key]
|
35
|
+
cipher_aes.iv = cipher[:iv]
|
36
|
+
encrypted = (cipher_aes.update(Digest::MD5.hexdigest( auth[:password] )) + cipher_aes.final).unpack('H*').join
|
37
|
+
url = "https://api.e.360.cn/account/clientLogin"
|
38
|
+
response = HTTParty.post(url,
|
39
|
+
:body => {
|
40
|
+
:username => auth[:username],
|
41
|
+
:passwd => encrypted[0,64]
|
42
|
+
},
|
43
|
+
:headers => {'apiKey' => auth[:token] }
|
44
|
+
)
|
45
|
+
data = response.parsed_response
|
46
|
+
data["account_clientLogin_response"]["accessToken"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def call(service)
|
50
|
+
eval "::PPC::API::#{@se.capitalize}::#{service.capitalize}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def download(params = {})
|
54
|
+
bulk = ::PPC::Baidu::Bulk.new({
|
55
|
+
username: @username,
|
56
|
+
password: @password,
|
57
|
+
token: @token,
|
58
|
+
debug: @debug
|
59
|
+
})
|
60
|
+
|
61
|
+
params[:extended] = params[:extended] || 2
|
62
|
+
|
63
|
+
begin
|
64
|
+
file_id = bulk.file_id_of_all(params)
|
65
|
+
puts "file_id: #{file_id}" if @debug
|
66
|
+
|
67
|
+
loop do
|
68
|
+
state = bulk.state(file_id)
|
69
|
+
raise "invalid file state: #{state}" unless %w(1 2 3 null).include? state
|
70
|
+
break if state == '3'
|
71
|
+
puts "waiting for #{file_id} to be ready. current state:#{state}" if @debug
|
72
|
+
sleep 3
|
73
|
+
end
|
74
|
+
puts "#{file_id} is ready" if @debug
|
75
|
+
return bulk.path(file_id)
|
76
|
+
rescue
|
77
|
+
raise BulkException.new(file_id,bulk)
|
78
|
+
end
|
79
|
+
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
|
83
|
+
# ++++++++ #
|
84
|
+
# Lazy Code #
|
85
|
+
# ++++++++ #
|
86
|
+
|
87
|
+
# helper fucntion
|
88
|
+
def get_obj( ids, service )
|
89
|
+
'''
|
90
|
+
Return service object.
|
91
|
+
Providing single id, return single object.
|
92
|
+
Providing multiple ids, return Array of objects
|
93
|
+
'''
|
94
|
+
class_obj = eval "::PPC::Opeartion::#{service.capitalize}"
|
95
|
+
param = @auth
|
96
|
+
param[:se] = @se
|
97
|
+
objs = []
|
98
|
+
|
99
|
+
ids.each do |id|
|
100
|
+
param[:id] = id
|
101
|
+
objs << class_obj.new( param )
|
102
|
+
end
|
103
|
+
|
104
|
+
return objs.length == 1 ? objs[0] : objs
|
105
|
+
end
|
106
|
+
|
107
|
+
# +++++ Plan opeartion funcitons +++++ #
|
108
|
+
module Plan_operation
|
109
|
+
def get_plan( ids )
|
110
|
+
::PPC::Opeartion::get_obj( ids, 'plan')
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_plan( plans )
|
114
|
+
call('plan').add(@auth,plans)
|
115
|
+
end
|
116
|
+
|
117
|
+
def update_plan( plans )
|
118
|
+
call('plan').update(@auth,plans)
|
119
|
+
end
|
120
|
+
|
121
|
+
def delete_plan( ids )
|
122
|
+
call('plan').delete(@auth,ids)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# +++++ Group opeartion funcitons +++++ #
|
127
|
+
module Group_operation
|
128
|
+
def get_group( ids )
|
129
|
+
::PPC::Opeartion::get_obj( ids, 'group')
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_group( groups )
|
133
|
+
call('group').add(@auth,groups )
|
134
|
+
end
|
135
|
+
|
136
|
+
def update_group( groups )
|
137
|
+
call('group').update( @auth, groups )
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete_group( ids )
|
141
|
+
call('group').delete( @auth, ids )
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# +++++ Keyword opeartion funcitons +++++ #
|
146
|
+
module Keyword_operation
|
147
|
+
def get_keyword( ids )
|
148
|
+
::PPC::Opeartion::get_obj( ids, 'keyword')
|
149
|
+
end
|
150
|
+
|
151
|
+
def add_keyword( keywords )
|
152
|
+
call('keyword').add( @auth, keywords )
|
153
|
+
end
|
154
|
+
|
155
|
+
def update_keyword( keywords)
|
156
|
+
call('keyword').update( @auth, keywords )
|
157
|
+
end
|
158
|
+
|
159
|
+
def delete_keyword( ids )
|
160
|
+
call('keyword').delete( @auth, ids )
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# +++++ Creative opeartion funcitons +++++ #
|
165
|
+
module Creative_operation
|
166
|
+
def get_creative( ids )
|
167
|
+
::PPC::Opeartion::get_obj( ids, 'creative')
|
168
|
+
end
|
169
|
+
|
170
|
+
def add_creative( creatives )
|
171
|
+
call('creative').add( @auth, creatives )
|
172
|
+
end
|
173
|
+
|
174
|
+
def update_creative( creatives )
|
175
|
+
call('creative').update( @auth, creatives )
|
176
|
+
end
|
177
|
+
|
178
|
+
def delete_creative( ids )
|
179
|
+
call('creative').delete( @auth, ids )
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end # Opeartion
|
184
|
+
end # PPC
|
185
|
+
|
186
|
+
# requires are moved to the end of the file
|
187
|
+
# because if we load files before defining the
|
188
|
+
# module ::PPC::Operation. Errors of 'uninitialized constance'
|
189
|
+
# will occur
|
190
|
+
require 'ppc/operation/report'
|
191
|
+
require 'ppc/operation/account'
|
192
|
+
require 'ppc/operation/group'
|
193
|
+
require 'ppc/operation/plan'
|
194
|
+
require 'ppc/operation/keyword'
|
195
|
+
require 'ppc/operation/creative'
|
196
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module PPC
|
2
|
+
module Operation
|
3
|
+
class Account
|
4
|
+
include ::PPC::Operation
|
5
|
+
|
6
|
+
# self operations
|
7
|
+
def info
|
8
|
+
info = call('account').info(@auth)
|
9
|
+
p info
|
10
|
+
@id = info[:result][:id] if @id == nil
|
11
|
+
return info
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(account)
|
15
|
+
call('account').update(@auth,account)
|
16
|
+
end
|
17
|
+
|
18
|
+
# subobject(plan) operations
|
19
|
+
def plans
|
20
|
+
call('plan').all(@auth)
|
21
|
+
end
|
22
|
+
|
23
|
+
def plan_ids
|
24
|
+
call('plan').ids(@auth)
|
25
|
+
end
|
26
|
+
|
27
|
+
# some useful keyword operations
|
28
|
+
def keywords(group_id)
|
29
|
+
call( 'keyword' ).search_by_group_id( @auth, group_id )
|
30
|
+
end
|
31
|
+
|
32
|
+
def keyword_ids(group_id)
|
33
|
+
call( 'keyword' ).search_id_by_group_id( @auth, group_id )
|
34
|
+
end
|
35
|
+
|
36
|
+
# plan operations
|
37
|
+
include ::PPC::Operation::Plan_operation
|
38
|
+
|
39
|
+
# group opeartions
|
40
|
+
include ::PPC::Operation::Group_operation
|
41
|
+
|
42
|
+
# keyword opeartions
|
43
|
+
include ::PPC::Operation::Keyword_operation
|
44
|
+
|
45
|
+
# creative opeartions
|
46
|
+
include ::PPC::Operation::Creative_operation
|
47
|
+
|
48
|
+
# report operations
|
49
|
+
include ::PPC::Operation::Report
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|