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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +37 -0
  3. data/.rspec +2 -0
  4. data/LICENSE +339 -0
  5. data/README.md +78 -0
  6. data/lib/ppc.rb +17 -0
  7. data/lib/ppc/api.rb +10 -0
  8. data/lib/ppc/api/baidu.rb +138 -0
  9. data/lib/ppc/api/baidu/account.rb +47 -0
  10. data/lib/ppc/api/baidu/bulk.rb +41 -0
  11. data/lib/ppc/api/baidu/creative.rb +125 -0
  12. data/lib/ppc/api/baidu/group.rb +111 -0
  13. data/lib/ppc/api/baidu/keyword.rb +204 -0
  14. data/lib/ppc/api/baidu/plan.rb +68 -0
  15. data/lib/ppc/api/baidu/report.rb +143 -0
  16. data/lib/ppc/api/qihu.rb +107 -0
  17. data/lib/ppc/api/qihu/account.rb +86 -0
  18. data/lib/ppc/api/qihu/creative.rb +106 -0
  19. data/lib/ppc/api/qihu/group.rb +113 -0
  20. data/lib/ppc/api/qihu/keyword.rb +111 -0
  21. data/lib/ppc/api/qihu/plan.rb +64 -0
  22. data/lib/ppc/api/qihu/report.rb +159 -0
  23. data/lib/ppc/api/shenma.rb +64 -0
  24. data/lib/ppc/api/shenma/report.rb +135 -0
  25. data/lib/ppc/api/sogou.rb +122 -0
  26. data/lib/ppc/api/sogou/account.rb +42 -0
  27. data/lib/ppc/api/sogou/creative.rb +117 -0
  28. data/lib/ppc/api/sogou/group.rb +116 -0
  29. data/lib/ppc/api/sogou/keyword.rb +182 -0
  30. data/lib/ppc/api/sogou/plan.rb +66 -0
  31. data/lib/ppc/api/sogou/report.rb +129 -0
  32. data/lib/ppc/ext.rb +9 -0
  33. data/lib/ppc/operation.rb +196 -0
  34. data/lib/ppc/operation/account.rb +53 -0
  35. data/lib/ppc/operation/creative.rb +28 -0
  36. data/lib/ppc/operation/group.rb +59 -0
  37. data/lib/ppc/operation/keyword.rb +32 -0
  38. data/lib/ppc/operation/plan.rb +47 -0
  39. data/lib/ppc/operation/report.rb +19 -0
  40. data/ppc.gemspec +26 -0
  41. data/spec/baidu/api_baidu_account_spec.rb +15 -0
  42. data/spec/baidu/api_baidu_creative_spec.rb +67 -0
  43. data/spec/baidu/api_baidu_group_spec.rb +45 -0
  44. data/spec/baidu/api_baidu_keyword_spec.rb +61 -0
  45. data/spec/baidu/api_baidu_plan_spec.rb +43 -0
  46. data/spec/baidu/api_baidu_report_spec.rb +44 -0
  47. data/spec/baidu/api_baidu_spec.rb +55 -0
  48. data/spec/operation/operation_baidu_report_spec.rb +17 -0
  49. data/spec/operation/operation_baidu_spec.rb +78 -0
  50. data/spec/operation/operation_qihu_report_spec.rb +18 -0
  51. data/spec/operation/operation_qihu_spec.rb +51 -0
  52. data/spec/operation/operation_sogou_report_spec.rb +17 -0
  53. data/spec/operation/operation_sogou_spec.rb +51 -0
  54. data/spec/operation/operation_spec_helper.rb +51 -0
  55. data/spec/qihu/api_qihu_account_spec.rb +25 -0
  56. data/spec/qihu/api_qihu_creative_spec.rb +48 -0
  57. data/spec/qihu/api_qihu_group_spec.rb +40 -0
  58. data/spec/qihu/api_qihu_keyword_spec.rb +50 -0
  59. data/spec/qihu/api_qihu_plan_spec.rb +39 -0
  60. data/spec/qihu/api_qihu_report_spec.rb +54 -0
  61. data/spec/sogou/api_sogou_account_spec.rb +15 -0
  62. data/spec/sogou/api_sogou_creative_spec.rb +48 -0
  63. data/spec/sogou/api_sogou_group_spec.rb +45 -0
  64. data/spec/sogou/api_sogou_keyword_spec.rb +50 -0
  65. data/spec/sogou/api_sogou_plan_spec.rb +39 -0
  66. data/spec/sogou/api_sogou_report_spec.rb +51 -0
  67. data/spec/spec_helper.rb +134 -0
  68. 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
@@ -0,0 +1,9 @@
1
+ class String
2
+ def snake_case
3
+ self.gsub(/::/, '/').
4
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
5
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
6
+ tr("-", "_").
7
+ downcase
8
+ end
9
+ end
@@ -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