ppc 1.3.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -339
  3. data/README.md +46 -28
  4. data/Rakefile +1 -0
  5. data/lib/ppc.rb +1 -1
  6. data/lib/ppc/api.rb +18 -14
  7. data/lib/ppc/api/baidu.rb +5 -14
  8. data/lib/ppc/api/baidu/account.rb +28 -23
  9. data/lib/ppc/api/baidu/bulk.rb +1 -1
  10. data/lib/ppc/api/baidu/creative.rb +47 -86
  11. data/lib/ppc/api/baidu/group.rb +43 -74
  12. data/lib/ppc/api/baidu/keyword.rb +73 -166
  13. data/lib/ppc/api/baidu/phone_new_creative.rb +8 -7
  14. data/lib/ppc/api/baidu/plan.rb +51 -34
  15. data/lib/ppc/api/baidu/report.rb +9 -29
  16. data/lib/ppc/api/qihu.rb +7 -111
  17. data/lib/ppc/api/qihu/account.rb +31 -25
  18. data/lib/ppc/api/qihu/bulk.rb +1 -2
  19. data/lib/ppc/api/qihu/creative.rb +57 -74
  20. data/lib/ppc/api/qihu/group.rb +40 -63
  21. data/lib/ppc/api/qihu/keyword.rb +58 -74
  22. data/lib/ppc/api/qihu/plan.rb +52 -32
  23. data/lib/ppc/api/qihu/rank.rb +11 -10
  24. data/lib/ppc/api/qihu/report.rb +41 -94
  25. data/lib/ppc/api/qihu/sublink.rb +44 -41
  26. data/lib/ppc/api/sm.rb +3 -12
  27. data/lib/ppc/api/sm/account.rb +20 -19
  28. data/lib/ppc/api/sm/creative.rb +36 -50
  29. data/lib/ppc/api/sm/group.rb +40 -61
  30. data/lib/ppc/api/sm/keyword.rb +44 -102
  31. data/lib/ppc/api/sm/phone_new_creative.rb +9 -8
  32. data/lib/ppc/api/sm/plan.rb +36 -26
  33. data/lib/ppc/api/sm/report.rb +5 -24
  34. data/lib/ppc/api/sogou.rb +5 -55
  35. data/lib/ppc/api/sogou/account.rb +17 -17
  36. data/lib/ppc/api/sogou/creative.rb +52 -75
  37. data/lib/ppc/api/sogou/group.rb +44 -91
  38. data/lib/ppc/api/sogou/keyword.rb +60 -143
  39. data/lib/ppc/api/sogou/plan.rb +37 -23
  40. data/lib/ppc/api/sogou/report.rb +2 -19
  41. data/lib/ppc/ext.rb +0 -8
  42. data/lib/ppc/operation.rb +60 -83
  43. data/lib/ppc/operation/account.rb +13 -19
  44. data/lib/ppc/operation/creative.rb +0 -20
  45. data/lib/ppc/operation/group.rb +18 -27
  46. data/lib/ppc/operation/keyword.rb +0 -27
  47. data/lib/ppc/operation/plan.rb +34 -22
  48. data/lib/ppc/operation/report.rb +4 -4
  49. data/lib/ppc/operation/sublink.rb +8 -0
  50. data/ppc.gemspec +5 -5
  51. metadata +16 -10
  52. data/lib/ppc/api/shenma.rb +0 -64
  53. data/lib/ppc/api/shenma/report.rb +0 -135
@@ -29,120 +29,16 @@ module PPC
29
29
  end
30
30
 
31
31
  def self.request_http_body(auth, params = {})
32
- params["format"] = 'json'
33
- params.map{|k,v| "#{k.to_s}=#{v.to_s}"}.join('&')
32
+ "format=json&" + params.map{|k,v| "#{k.to_s}=#{v.is_a?(Array) ? v.to_json : v}"}.join('&')
34
33
  end
35
34
 
36
- # def self.request( auth, service, method, params = {} )
37
- # p 123
38
- # url = "https://api.e.360.cn/2.0/#{service}/#{method}"
39
- # # 日后考虑将httpparty用Net/http代替
40
- # response = HTTParty.post(url,
41
- # body: params,
42
- # headers: {'apiKey' => auth[:api_key],
43
- # 'accessToken' => auth[:token],
44
- # 'serveToken' => Time.now.to_i.to_s }
45
- # )
46
- # p response
47
- # response.parsed_response
48
- # end
49
-
50
35
  def self.process( response, key, failure_key = '', &func )
51
- # special case solution
52
- # 只有account getInfo 的key 为 '',因为其特殊逻辑,单独处理
53
- result = { }
54
- if response == []
55
- result[:succ] = true
56
- result[:failure] = nil
57
- result[:result] = nil
58
- result[:no_quota] = false
59
- return result
60
- end
61
- if key == ''
62
- if !response['failures'].nil?
63
- result[:succ] = false
64
- result[:result] = nil
65
- result[:failure] = response['failures']
66
- result[:no_quota] = is_no_quota(response['failures'], '90401')
67
- return result
68
- end
69
- result[:succ] = true
70
- result[:failure] = nil
71
- result[:no_quota] = false
72
- result[:result] = func[response]
73
- return result
74
- end
75
- result[:result] = func[response[key]]
76
- result[:failure] = response['failures']
77
- result[:no_quota] = is_no_quota(response['failures'], '90401')
78
- result[:succ] = response['failures'].nil? || response['failures'].size.zero?
79
- #if response['failures'] != nil
80
- # result[:succ] = false
81
- # result[:failure] = response['failures']['item']
82
- # result[:result] = nil
83
- #else
84
- # result[:succ] = true
85
- # result[:result] = func[ key==''? response : response[ key ] ]
86
- # result[:failure] = failure_key == ''? nil : response[ failure_key ]
87
- #end
88
- return result
89
- end
90
-
91
- def self.to_json_string( items )
92
- '''
93
- convert list of string/int to list of json string
94
- '''
95
- return '' if items == nil
96
- items = [items] unless items.is_a? Array
97
- items_str = []
98
- items.each{ |x| items_str << x.to_s }
99
- items_str.to_json
100
- end
101
-
102
- def self.to_id_list( ids_str )
103
- return [] if ids_str == nil
104
- ids_str = [ids_str] unless ids_str.is_a? Array
105
- ids_i = []
106
- ids_str.each{ |id| ids_i<<id.to_i }
107
- ids_i
108
- end
109
-
110
- def self.make_type( params, map = @map)
111
- '''
112
- '''
113
- params = [ params ] unless params.is_a? Array
114
-
115
- types = []
116
- params.each do |param|
117
- type = {}
118
-
119
- map.each do |key|
120
- value = param[ key[0] ]
121
- type[ key[1] ] = value if value != nil
122
- end
123
-
124
- types << type
125
- end
126
- return types
127
- end
128
-
129
- def self.reverse_type( types, map = @map )
130
- '''
131
- '''
132
- types = [ types ] unless types.is_a? Array
133
-
134
- params = []
135
- types.each do |type|
136
- param = {}
137
-
138
- map.each do |key|
139
- value = type[ key[1].to_s ]
140
- param[ key[0] ] = value if value != nil
141
- end
142
-
143
- params << param
144
- end
145
- return params
36
+ result = {}
37
+ result[:succ] = response['failures'].nil? || response['failures'].size.zero?
38
+ result[:failure] = response['failures'] || response["failure_key"]
39
+ result[:result] = (key.empty? ? func[response] : func[response[key]]) rescue nil
40
+ result[:no_quota] = (response['failures']['code'] == '90401') rescue false
41
+ result
146
42
  end
147
43
 
148
44
  end
@@ -6,18 +6,26 @@ module PPC
6
6
  class Account < Qihu
7
7
  Service = 'account'
8
8
 
9
- @map = [
10
- [ :id, :uid ],
11
- [ :name, :userName ],
12
- [ :email, :email],
13
- [ :company, :companyName],
14
- [ :industry1, :industry1],
15
- [ :industry2, :industry2],
16
- [ :balance, :balance],
17
- [ :budget, :budget],
18
- [ :resources, :resources],
19
- [ :open_domains, :allowDomain]
20
- ]
9
+ AccountType = {
10
+ id: :uid,
11
+ name: :userName,
12
+ email: :email,
13
+ category: :category,
14
+ company: :companyName,
15
+ industry1: :industry1,
16
+ industry2: :industry2,
17
+ balance: :balance,
18
+ budget: :budget,
19
+ mv_budget: :mvBudget,
20
+ resources: :Resources,
21
+ "resources" => :resources,
22
+ status: :status,
23
+ open_mobile_domain: :allowMobileDomain,
24
+ open_domains: :allowDomain,
25
+ agency: :agency,
26
+ }
27
+ @map = AccountType
28
+
21
29
 
22
30
  def self.info( auth )
23
31
  response = request( auth, Service, 'getInfo' )
@@ -29,36 +37,35 @@ module PPC
29
37
  对奇虎两个update的在封装。如果所有操作成功,succ为true,否则为false
30
38
  failure中以字符串方式返回失败的操作
31
39
  '''
32
- result = {}
33
- result[:succ] = true
34
- result[:failure] = []
35
- result[:result] = []
40
+ result = {
41
+ succ: true,
42
+ failure: [],
43
+ result: [],
44
+ }
36
45
 
37
- if params[:budget] != nil
46
+ if params[:budget]
38
47
  budget_result = update_budget( auth, params[:budget] )
39
48
  result[:succ] = result[:succ] && budget_result[:succ]
40
49
  result[:failure] << 'budget' unless budget_result[:succ]
41
50
  end
42
51
 
43
- if params[:exclude_ip] != nil
52
+ if params[:exclude_ip]
44
53
  ip_result = update_exclude_ip( auth, params[:exclude_ip] )
45
54
  result[:succ] = result[:succ] && ip_result[:succ]
46
55
  result[:failure] << 'exclude_ip' unless budget_result[:succ]
47
56
  end
48
57
 
49
- return result
58
+ result
50
59
  end
51
60
 
52
61
  def self.get_all_object( auth, ids )
53
62
  #文档上面写的输入类型是String?
54
- body = { 'idList' => ids }
55
- response = request( auth, Service, 'getAllObjects' )
63
+ response = request( auth, Service, 'getAllObjects', { idList: ids } )
56
64
  process( response, 'account_getAllObjects_response' ){ |x| x }
57
65
  end
58
66
 
59
67
  def self.get_file_state( auth, id )
60
- body = { 'fileId' => id }
61
- response = request( auth, Service, 'getAllObjects' , body )
68
+ response = request( auth, Service, 'getAllObjects' , { fileId: id } )
62
69
  process( response, 'account_getFileState_response' ){ |x| x }
63
70
  end
64
71
 
@@ -69,13 +76,12 @@ module PPC
69
76
 
70
77
  private
71
78
  def self.update_budget( auth, budget )
72
- response = request( auth, Service, 'updateBudget', { budget:budget })
79
+ response = request( auth, Service, 'updateBudget', { budget: budget })
73
80
  process( response, 'affectedRecords' ){ | x | x.to_i==1 ? 'success' : 'failure' }
74
81
  end
75
82
 
76
83
  private
77
84
  def self.update_exclude_ip( auth, ips )
78
- ips = to_json_string( ips )
79
85
  response = request( auth, Service, 'updateExcludeIp', { excludeIpList: ips } )
80
86
  process( response, '' ){|x| x}
81
87
  end
@@ -8,8 +8,7 @@ module PPC
8
8
 
9
9
  def self.get_all_object( auth, ids )
10
10
  #文档上面写的输入类型是String?
11
- body = nil
12
- body = { 'idList' => to_json_string(ids) } if ids
11
+ body = { 'idList' => ids.map(&:to_s) }
13
12
  response = request( auth, Service, 'getAllObjects', body )
14
13
  process( response, 'fileId' ){ |x| x }
15
14
  end
@@ -5,101 +5,84 @@ module PPC
5
5
  class Creative< Qihu
6
6
  Service = 'creative'
7
7
 
8
- @map = [
9
- [:id,:id] ,
10
- [:group_id, :groupId],
11
- [:title,:title],
12
- [:description1, :description1],
13
- [:description2, :description2],
14
- [:pc_destination, :destinationUrl],
15
- [:pc_display, :displayUrl],
16
- [:mobile_destination, :mobileDestinationUrl],
17
- [:mobile_display, :mobileDisplayUrl]
18
- ]
8
+ CreativeType = {
9
+ id: :id,
10
+ plan_id: :campaignId,
11
+ group_id: :groupId,
12
+ title: :title,
13
+ description1: :description1,
14
+ description2: :description2,
15
+ description3: :descSecondLine,
16
+ pc_destination: :destinationUrl,
17
+ pc_display: :displayUrl,
18
+ mobile_destination: :mobileDestinationUrl,
19
+ mobile_display: :mobileDisplayUrl,
20
+ pause: :status,
21
+ mobile_pause: :mobileStatus,
22
+ add_time: :addTime,
23
+ updateTime: :updateTime,
24
+ }
25
+ @map = CreativeType
19
26
 
20
- @status_map = [
21
- [:id,:id],
22
- [:quality,:qualityScore],
23
- [:status,:status]
24
- ]
27
+ @status_map = {
28
+ id: :id,
29
+ quality: :qualityScore,
30
+ pause: :status,
31
+ }
32
+
33
+ def self.info( auth, ids )
34
+ response = request( auth, Service, 'getInfoByIdList', { idList: ids } )
35
+ process( response, 'creativeList'){ |x| reverse_type(x)[0] }
36
+ end
37
+
38
+ # combine two methods to provide another mether
39
+ def self.all( auth, group_id )
40
+ results = self.ids( auth, group_id )
41
+ return results unless results[:succ]
42
+ self.get( auth , results[:result] )
43
+ end
44
+
45
+ def self.ids( auth, group_id )
46
+ response = request( auth, Service, 'getIdListByGroupId', {"groupId" => group_id[0]} )
47
+ process( response, 'creativeIdList' ){ |x| x.map(&:to_i) }
48
+ end
25
49
 
26
50
  def self.get( auth, ids )
27
- body = { 'idList' => to_json_string( ids ) }
28
- response = request( auth, Service, 'getInfoByIdList', body )
51
+ response = request( auth, Service, 'getInfoByIdList', { idList: ids } )
29
52
  process( response, 'creativeList'){ |x| reverse_type(x) }
30
53
  end
31
54
 
32
55
  def self.add( auth, creatives )
33
- creative_types = make_type( creatives ).to_json
34
- body = { 'creatives' => creative_types}
35
- response = request( auth, Service, 'add', body )
36
- process( response, 'creativeIdList'){ |x| to_id_hash_list(x) }
37
- end
38
-
39
- # helper function for self.add() method
40
- private
41
- def self.to_id_hash_list( str )
42
- reuturn [] if str == nil
43
- str = [str] unless str.is_a?Array
44
- x= []
45
- str.each{ |i| x << { id: i.to_i } }
46
- return x
56
+ response = request( auth, Service, 'add', { creatives: make_type( creatives ) } )
57
+ process( response, 'creativeIdList'){ |x| x.map{|tmp| { id: i.to_i } } }
47
58
  end
48
59
 
49
60
  def self.update( auth, creatives )
50
- creative_types = make_type( creatives ).to_json
51
- body = { 'creatives' => creative_types}
52
- response = request( auth, Service, 'update', body )
53
- process( response, 'affectedRecords', 'failCreativeIds' ){ |x| x }
61
+ response = request( auth, Service, 'update', { creatives: make_type( creatives ) } )
62
+ process( response, 'affectedRecords', 'failCreativeIds' ){ |x| x }
54
63
  end
55
64
 
56
- # 对update的再封装实现activate方法,未测试
57
- def self.activate( auth, ids )
58
- creatives = []
59
- ids.each{ |id| creatives << { id: id, status:'enable'} }
60
- update( auth, creatives )
61
- end
62
-
63
65
  def self.delete( auth, ids )
64
- ids = to_json_string( ids )
65
- body = { 'idList' => ids }
66
- response = request( auth, Service, 'deleteByIdList', body )
67
- process( response, 'affectedRecords' ){ |x|x }
66
+ response = request( auth, Service, 'deleteByIdList', { idList: ids } )
67
+ process( response, 'affectedRecords' ){ |x|x }
68
68
  end
69
69
 
70
- def self.status( auth, ids )
71
- body = { idList: to_json_string( ids ) }
72
- response = request( auth, Service, 'getStatusByIdList', body )
73
- process( response, 'creativeList' ){ |x| reverse_type( x, @status_map ) }
70
+ def self.enable( auth, ids )
71
+ self.update( auth, ids.map{ |id| { id: id, pause: "enable"} } )
74
72
  end
75
73
 
76
- # quality 本质上和 status 在一个方法里面
77
- def self.quality( auth, ids )
78
- status( auth, ids)
74
+ def self.pause( auth, ids )
75
+ self.update( auth, ids.map{ |id| { id: id, pause: "pause"} } )
79
76
  end
80
77
 
81
- def self.search_id_by_group_id( auth, id, status = nil)
82
- # 处理条件
83
- body = {}
84
- body['status'] = status if status
85
- body['groupId'] = id
86
- response = request( auth, Service, 'getIdListByGroupId', body )
87
- # 伪装成百度接口
88
- process( response, 'creativeIdList' ){
89
- |x|
90
- [ { group_id:id, creative_ids:to_id_list(x) } ]
91
- }
78
+ def self.status( auth, ids )
79
+ response = request( auth, Service, 'getStatusByIdList', { idList: ids } )
80
+ process( response, 'creativeList' ){ |x| reverse_type( x, @status_map ) }
92
81
  end
93
82
 
94
- # combine two methods to provide another mether
95
- def self.search_by_group_id( auth, id )
96
- creative_ids = search_id_by_group_id( auth, id )
97
- response = get( auth , creative_ids )
98
- # 伪装成百度接口
99
- if response[:succ]
100
- response[:result] = [ { group_id:id, creatives:response[:result ] } ]
101
- end
102
- return response
83
+ # quality 本质上和 status 在一个方法里面
84
+ def self.quality( auth, ids )
85
+ self.status( auth, ids)
103
86
  end
104
87
 
105
88
  end
@@ -5,89 +5,66 @@ module PPC
5
5
  class Group < Qihu
6
6
  Service = 'group'
7
7
 
8
- @map = [
9
- [:id, :id ],
10
- [:plan_id, :campaignId],
11
- [:status, :status ],
12
- [:name, :name ],
13
- [:price, :price ],
8
+ GroupType = {
9
+ id: :id,
10
+ plan_id: :campaignId,
11
+ pause: :status,
12
+ name: :name,
13
+ price: :price,
14
14
  # negateive为json格式,make_type要定制
15
- [:add_time, :addTime],
16
- [:update_time, :updateTime]
17
- ]
15
+ negative: :negativeWords,
16
+ exact_negative: :exactNegativeWords,
17
+ add_time: :addTime,
18
+ update_time: :updateTime,
19
+ from_time: :fromTime,
20
+ }
21
+ @map = GroupType
18
22
 
19
- # 再次封装提供all和ids
20
- def self.ids( auth )
21
- '''
22
- 接口与其他的相同,但是无论如何:succ均为true,不返回任何错误信息
23
- 因为是重复调用了search的接口,速度非常的慢,* 慎用 *
24
- '''
25
- plan_ids = ::PPC::API::Qihu::Plan::ids( auth )[:result]
26
- plan_group_ids = []
23
+ def self.info( auth, ids )
24
+ response = request( auth, Service, 'getInfoByIdList', { idList: ids } )
25
+ process( response, 'groupList' ){ |x| reverse_type(x)[0] }
26
+ end
27
+
28
+ def self.all( auth, plan_id )
29
+ group_ids = self.ids( auth, plan_id )[:result][:group_ids]
30
+ self.get( auth, group_ids )
31
+ end
27
32
 
28
- plan_ids.each do |plan_id|
29
- plan_group_id = {}
30
- plan_group_id[:plan_id] = plan_id
31
- plan_group_id[:group_ids] = search_id_by_plan_id( auth, plan_id )[:result]
32
- plan_group_ids << plan_group_id
33
- end
34
- return { succ: true, failure:nil, result: plan_group_ids }
33
+ def self.ids( auth, plan_id )
34
+ response = request( auth, Service, 'getIdListByCampaignId', { campaignId: plan_id[0] } )
35
+ process( response, 'groupIdList' ){ |x| { plan_id: plan_id[0], group_ids: x.map(&:to_i) } }
35
36
  end
36
37
 
37
- def self.all( auth )
38
- '''
39
- unimplemented due to ineffciency
40
- '''
41
- # unimplement
38
+ def self.get( auth, ids )
39
+ response = request( auth, Service, 'getInfoByIdList', { idList: ids } )
40
+ process( response, 'groupList' ){ |x| reverse_type(x) }
42
41
  end
43
42
 
44
- # 奇虎组服务不提供批量add,delete和update方法
45
- def self.add( auth, group )
46
- response = request( auth, Service, 'add', make_type( group )[0] )
47
- # 保证接口一致性
48
- process( response, 'id' ){ |x| [ { id:x.to_i } ] }
43
+ def self.add( auth, groups )
44
+ groups.each{ |group| group[:negative] = {exact: group.delete(:exact_negative), phrase: group.delete(:negative)} if group[:exact_negative] || group[:negative] }
45
+ response = request( auth, Service, 'batchAdd', {groups: make_type( groups )} )
46
+ process( response, 'groupIdList' ){ |x| x.map.with_index{|id, index| {id: id, name: groups[index][:name]}} }
49
47
  end
50
48
 
49
+ # 奇虎组服务不提供批量delete和update方法
51
50
  def self.update( auth, group )
52
- if group[:negative] || group[:exact_negative]
53
- ng = {"exact" => group[:exact_negative], "phrase" => group[:negative]}.to_json
54
- end
51
+ group[0][:negative] = {exact: group[0].delete(:exact_negative), phrase: group[0].delete(:negative)}.to_json if group[0][:exact_negative] || group[0][:negative]
55
52
  params = make_type(group)[0]
56
- params.merge!({:negativeWords => ng}) if ng
57
53
  response = request( auth, Service, 'update', params )
58
- # 保证接口一致性
59
- process( response, 'id' ){ |x|[ { id:x.to_i } ] }
60
- end
61
-
62
- def self.get( auth, ids )
63
- ids = to_json_string( ids )
64
- body = { 'idList' => ids }
65
- response = request( auth, Service, 'getInfoByIdList', body )
66
- process( response, 'groupList' ){ |x| reverse_type(x) }
54
+ process( response, 'id' ){ |x| [{id: x, name: group[0][:name]}] }
67
55
  end
68
56
 
69
57
  def self.delete( auth, id )
70
- response = request( auth, Service, 'deleteById', { id: id} )
58
+ response = request( auth, Service, 'deleteById', {id: id[0]} )
71
59
  process( response, 'affectedRecords' ){ |x| x == '1' }
72
60
  end
73
61
 
74
- def self.search_id_by_plan_id( auth, id )
75
- response = request( auth, Service, 'getIdListByCampaignId', { 'campaignId' => id.to_s })
76
- #为了保持接口一致性,这里也是伪装成了百度的接口
77
- process( response, 'groupIdList' ){
78
- |x|
79
- [ { plan_id:id, group_ids: to_id_list(x) } ]
80
- }
62
+ def self.enable( auth, id )
63
+ self.update(auth, [{id: id[0], pause: "enable"}])
81
64
  end
82
65
 
83
- # combine searchIdbyId and get to provide another method
84
- def self.search_by_plan_id( auth, id )
85
- group_ids = search_id_by_plan_id( auth, id )[:result][0][:group_ids]
86
- response = get( auth, group_ids )
87
- if response[:succ]
88
- response[:result] = [ { plan_id:id, groups:response[:result]}]
89
- end
90
- return response
66
+ def self.pause( auth, id )
67
+ self.update(auth, [{id: id[0], pause: "pause"}])
91
68
  end
92
69
 
93
70
  end