ppc 1.3.0 → 1.3.2

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/LICENSE +334 -17
  4. data/README.md +75 -3
  5. data/lib/ppc.rb +3 -1
  6. data/lib/ppc/api.rb +137 -2
  7. data/lib/ppc/api/baidu.rb +20 -99
  8. data/lib/ppc/api/baidu/account.rb +21 -21
  9. data/lib/ppc/api/baidu/bulk.rb +39 -3
  10. data/lib/ppc/api/baidu/creative.rb +33 -33
  11. data/lib/ppc/api/baidu/group.rb +14 -14
  12. data/lib/ppc/api/baidu/keyword.rb +57 -46
  13. data/lib/ppc/api/baidu/phone_new_creative.rb +51 -0
  14. data/lib/ppc/api/baidu/plan.rb +34 -31
  15. data/lib/ppc/api/baidu/rank.rb +23 -0
  16. data/lib/ppc/api/baidu/report.rb +100 -81
  17. data/lib/ppc/api/qihu.rb +150 -0
  18. data/lib/ppc/api/qihu/account.rb +86 -0
  19. data/lib/ppc/api/qihu/bulk.rb +35 -0
  20. data/lib/ppc/api/qihu/creative.rb +108 -0
  21. data/lib/ppc/api/qihu/group.rb +96 -0
  22. data/lib/ppc/api/qihu/keyword.rb +113 -0
  23. data/lib/ppc/api/qihu/plan.rb +64 -0
  24. data/lib/ppc/api/qihu/rank.rb +25 -0
  25. data/lib/ppc/api/qihu/report.rb +159 -0
  26. data/lib/ppc/api/qihu/sublink.rb +71 -0
  27. data/lib/ppc/api/shenma.rb +64 -0
  28. data/lib/ppc/api/shenma/report.rb +135 -0
  29. data/lib/ppc/api/sm.rb +50 -0
  30. data/lib/ppc/api/sm/account.rb +44 -0
  31. data/lib/ppc/api/sm/bulk.rb +69 -0
  32. data/lib/ppc/api/sm/creative.rb +86 -0
  33. data/lib/ppc/api/sm/group.rb +94 -0
  34. data/lib/ppc/api/sm/keyword.rb +132 -0
  35. data/lib/ppc/api/sm/phone_new_creative.rb +51 -0
  36. data/lib/ppc/api/sm/plan.rb +63 -0
  37. data/lib/ppc/api/sm/report.rb +136 -0
  38. data/lib/ppc/api/sogou.rb +118 -0
  39. data/lib/ppc/api/sogou/account.rb +42 -0
  40. data/lib/ppc/api/sogou/bulk.rb +69 -0
  41. data/lib/ppc/api/sogou/creative.rb +117 -0
  42. data/lib/ppc/api/sogou/group.rb +123 -0
  43. data/lib/ppc/api/sogou/keyword.rb +188 -0
  44. data/lib/ppc/api/sogou/plan.rb +66 -0
  45. data/lib/ppc/api/sogou/report.rb +129 -0
  46. data/lib/ppc/ext.rb +17 -0
  47. data/lib/ppc/operation.rb +50 -36
  48. data/lib/ppc/operation/account.rb +19 -7
  49. data/lib/ppc/operation/creative.rb +7 -7
  50. data/lib/ppc/operation/group.rb +2 -2
  51. data/lib/ppc/operation/keyword.rb +11 -8
  52. data/lib/ppc/operation/report.rb +23 -0
  53. data/ppc.gemspec +14 -4
  54. data/spec/baidu/api_baidu_account_spec.rb +16 -0
  55. data/spec/baidu/api_baidu_creative_spec.rb +73 -0
  56. data/spec/{api_baidu_group_spec.rb → baidu/api_baidu_group_spec.rb} +12 -17
  57. data/spec/baidu/api_baidu_keyword_spec.rb +61 -0
  58. data/spec/baidu/api_baidu_phone_spec.rb +22 -0
  59. data/spec/{api_baidu_plan_spec.rb → baidu/api_baidu_plan_spec.rb} +11 -10
  60. data/spec/baidu/api_baidu_report_spec.rb +44 -0
  61. data/spec/{api_baidu_spec.rb → baidu/api_baidu_spec.rb} +10 -15
  62. data/spec/operation/operation_baidu_report_spec.rb +17 -0
  63. data/spec/operation/operation_baidu_spec.rb +126 -0
  64. data/spec/operation/operation_qihu_report_spec.rb +18 -0
  65. data/spec/operation/operation_qihu_spec.rb +94 -0
  66. data/spec/operation/operation_sm_report_spec.rb +21 -0
  67. data/spec/operation/operation_sogou_report_spec.rb +17 -0
  68. data/spec/operation/operation_sogou_spec.rb +88 -0
  69. data/spec/{operation_spec_helper.rb → operation/operation_spec_helper.rb} +3 -5
  70. data/spec/qihu/api_qihu_account_spec.rb +29 -0
  71. data/spec/qihu/api_qihu_creative_spec.rb +48 -0
  72. data/spec/qihu/api_qihu_group_spec.rb +40 -0
  73. data/spec/qihu/api_qihu_keyword_spec.rb +50 -0
  74. data/spec/qihu/api_qihu_plan_spec.rb +39 -0
  75. data/spec/qihu/api_qihu_report_spec.rb +54 -0
  76. data/spec/qihu/api_qihu_sublink_spec.rb +36 -0
  77. data/spec/sm/api_sm_account_spec.rb +8 -0
  78. data/spec/sm/api_sm_creative_spec.rb +52 -0
  79. data/spec/sm/api_sm_group_spec.rb +75 -0
  80. data/spec/sm/api_sm_keyword_spec.rb +59 -0
  81. data/spec/sm/api_sm_plan_spec.rb +39 -0
  82. data/spec/sm/api_sm_report_spec.rb +30 -0
  83. data/spec/sogou/api_sogou_account_spec.rb +17 -0
  84. data/spec/sogou/api_sogou_creative_spec.rb +51 -0
  85. data/spec/sogou/api_sogou_group_spec.rb +50 -0
  86. data/spec/sogou/api_sogou_keyword_spec.rb +54 -0
  87. data/spec/sogou/api_sogou_plan_spec.rb +43 -0
  88. data/spec/sogou/api_sogou_report_spec.rb +51 -0
  89. data/spec/spec_helper.rb +49 -10
  90. metadata +140 -46
  91. data/lib/ppc/baidu.rb +0 -152
  92. data/lib/ppc/baidu/account.rb +0 -88
  93. data/lib/ppc/baidu/bulk.rb +0 -52
  94. data/lib/ppc/baidu/group.rb +0 -99
  95. data/lib/ppc/baidu/key.rb +0 -38
  96. data/lib/ppc/baidu/plan.rb +0 -85
  97. data/lib/ppc/baidu/report.rb +0 -82
  98. data/spec/api_baidu_account_spec.rb +0 -18
  99. data/spec/api_baidu_creative_spec.rb +0 -71
  100. data/spec/api_baidu_keyword_spec.rb +0 -64
  101. data/spec/api_baidu_report_spec.rb +0 -32
  102. data/spec/baidu_account_spec.rb +0 -32
  103. data/spec/baidu_bulk_spec.rb +0 -21
  104. data/spec/baidu_group_spec.rb +0 -56
  105. data/spec/baidu_plan_spec.rb +0 -129
  106. data/spec/baidu_report_spec.rb +0 -24
  107. data/spec/operation_spec.rb +0 -87
@@ -0,0 +1,150 @@
1
+ # -*- coding:utf-8 -*-
2
+ require 'ppc/api/qihu/account'
3
+ require 'ppc/api/qihu/plan'
4
+ require 'ppc/api/qihu/group'
5
+ require 'ppc/api/qihu/keyword'
6
+ require 'ppc/api/qihu/creative'
7
+ require 'ppc/api/qihu/report'
8
+ require 'ppc/api/qihu/sublink'
9
+ require 'ppc/api/qihu/bulk'
10
+ require 'ppc/api/qihu/rank'
11
+
12
+ module PPC
13
+ module API
14
+ class Qihu
15
+
16
+ extend ::PPC::API
17
+
18
+ def self.request_uri(param = {})
19
+ URI("https://api.e.360.cn/2.0/#{param[:service]}/#{param[:method]}")
20
+ end
21
+
22
+ def self.request_http_header(auth)
23
+ {
24
+ 'Content-Type' => 'application/x-www-form-urlencoded',
25
+ 'apiKey' => auth[:api_key],
26
+ 'accessToken' => auth[:token],
27
+ 'serveToken' => Time.now.to_i.to_s
28
+ }
29
+ end
30
+
31
+ def self.request_http_body(auth, params = {})
32
+ params["format"] = 'json'
33
+ params.map{|k,v| "#{k.to_s}=#{v.to_s}"}.join('&')
34
+ end
35
+
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
+ 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
146
+ end
147
+
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,86 @@
1
+ # -*- coding:utf-8 -*-
2
+ require 'json'
3
+ module PPC
4
+ module API
5
+ class Qihu
6
+ class Account < Qihu
7
+ Service = 'account'
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
+ ]
21
+
22
+ def self.info( auth )
23
+ response = request( auth, Service, 'getInfo' )
24
+ process( response, '' ){ |x| reverse_type( x )[0]}
25
+ end
26
+
27
+ def self.update( auth, params )
28
+ '''
29
+ 对奇虎两个update的在封装。如果所有操作成功,succ为true,否则为false
30
+ failure中以字符串方式返回失败的操作
31
+ '''
32
+ result = {}
33
+ result[:succ] = true
34
+ result[:failure] = []
35
+ result[:result] = []
36
+
37
+ if params[:budget] != nil
38
+ budget_result = update_budget( auth, params[:budget] )
39
+ result[:succ] = result[:succ] && budget_result[:succ]
40
+ result[:failure] << 'budget' unless budget_result[:succ]
41
+ end
42
+
43
+ if params[:exclude_ip] != nil
44
+ ip_result = update_exclude_ip( auth, params[:exclude_ip] )
45
+ result[:succ] = result[:succ] && ip_result[:succ]
46
+ result[:failure] << 'exclude_ip' unless budget_result[:succ]
47
+ end
48
+
49
+ return result
50
+ end
51
+
52
+ def self.get_all_object( auth, ids )
53
+ #文档上面写的输入类型是String?
54
+ body = { 'idList' => ids }
55
+ response = request( auth, Service, 'getAllObjects' )
56
+ process( response, 'account_getAllObjects_response' ){ |x| x }
57
+ end
58
+
59
+ def self.get_file_state( auth, id )
60
+ body = { 'fileId' => id }
61
+ response = request( auth, Service, 'getAllObjects' , body )
62
+ process( response, 'account_getFileState_response' ){ |x| x }
63
+ end
64
+
65
+ def self.get_exclude_ip( auth )
66
+ response = request( auth, Service, 'getExcludeIp' )
67
+ process( response, 'excludeIpList' ){ |x| x}
68
+ end
69
+
70
+ private
71
+ def self.update_budget( auth, budget )
72
+ response = request( auth, Service, 'updateBudget', { budget:budget })
73
+ process( response, 'affectedRecords' ){ | x | x.to_i==1 ? 'success' : 'failure' }
74
+ end
75
+
76
+ private
77
+ def self.update_exclude_ip( auth, ips )
78
+ ips = to_json_string( ips )
79
+ response = request( auth, Service, 'updateExcludeIp', { excludeIpList: ips } )
80
+ process( response, '' ){|x| x}
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,35 @@
1
+ # -*- coding:utf-8 -*-
2
+ require 'json'
3
+ module PPC
4
+ module API
5
+ class Qihu
6
+ class Bulk < Qihu
7
+ Service = 'account'
8
+
9
+ def self.get_all_object( auth, ids )
10
+ #文档上面写的输入类型是String?
11
+ body = nil
12
+ body = { 'idList' => to_json_string(ids) } if ids
13
+ response = request( auth, Service, 'getAllObjects', body )
14
+ process( response, 'fileId' ){ |x| x }
15
+ end
16
+
17
+ def self.get_file_state( auth, id )
18
+ body = { 'fileId' => id }
19
+ response = request( auth, Service, 'getFileState' , body )
20
+ process( response, '' ){ |x| x }
21
+ end
22
+
23
+ def self.download( auth, ids = nil)
24
+ result = get_all_object(auth, ids)
25
+ field_id = result[:result]
26
+ loop do
27
+ status = get_file_state(auth, field_id)
28
+ return status if status[:result]['isGenerated'] == 'success'
29
+ sleep 15
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,108 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Qihu
5
+ class Creative< Qihu
6
+ Service = 'creative'
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
+ ]
19
+
20
+ @status_map = [
21
+ [:id,:id],
22
+ [:quality,:qualityScore],
23
+ [:status,:status]
24
+ ]
25
+
26
+ def self.get( auth, ids )
27
+ body = { 'idList' => to_json_string( ids ) }
28
+ response = request( auth, Service, 'getInfoByIdList', body )
29
+ process( response, 'creativeList'){ |x| reverse_type(x) }
30
+ end
31
+
32
+ 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
47
+ end
48
+
49
+ 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 }
54
+ end
55
+
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
+ 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 }
68
+ end
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 ) }
74
+ end
75
+
76
+ # quality 本质上和 status 在一个方法里面
77
+ def self.quality( auth, ids )
78
+ status( auth, ids)
79
+ end
80
+
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
+ }
92
+ end
93
+
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
103
+ end
104
+
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,96 @@
1
+ # -*- coding:utf-8 -*-
2
+ module PPC
3
+ module API
4
+ class Qihu
5
+ class Group < Qihu
6
+ Service = 'group'
7
+
8
+ @map = [
9
+ [:id, :id ],
10
+ [:plan_id, :campaignId],
11
+ [:status, :status ],
12
+ [:name, :name ],
13
+ [:price, :price ],
14
+ # negateive为json格式,make_type要定制
15
+ [:add_time, :addTime],
16
+ [:update_time, :updateTime]
17
+ ]
18
+
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 = []
27
+
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 }
35
+ end
36
+
37
+ def self.all( auth )
38
+ '''
39
+ unimplemented due to ineffciency
40
+ '''
41
+ # unimplement
42
+ end
43
+
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 } ] }
49
+ end
50
+
51
+ 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
55
+ params = make_type(group)[0]
56
+ params.merge!({:negativeWords => ng}) if ng
57
+ 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) }
67
+ end
68
+
69
+ def self.delete( auth, id )
70
+ response = request( auth, Service, 'deleteById', { id: id} )
71
+ process( response, 'affectedRecords' ){ |x| x == '1' }
72
+ end
73
+
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
+ }
81
+ end
82
+
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
91
+ end
92
+
93
+ end
94
+ end
95
+ end
96
+ end