utils.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. ~(function (exports) {
  2. // 消息策略
  3. const tactics = {
  4. // 请求价格限制配置
  5. async request(requestOptions, sender, sendResponse, callback) {
  6. const res = await exports.req(requestOptions).catch(err => console.log(err))
  7. Object.assign(state.wx, res['weixin.qq.com'])
  8. Object.assign(state.tencent, res['e.qq.com'])
  9. sendResponse(state)
  10. callback && callback(state)
  11. }
  12. }
  13. const simpleRequest = new Set(['get', 'head'])
  14. // xhr请求
  15. exports.req = function (options = {}) {
  16. const { url = "", method = "get", data = {}, headers = {}, responseType = '', withCredentials = false, timeout = 18000 } = options
  17. const xhr = new XMLHttpRequest()
  18. const defaultHeaders = {
  19. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
  20. }
  21. const finalHeaders = { ...defaultHeaders, ...headers }
  22. const finalData = (Object.prototype.toString.call(data).slice(8, -1) === 'Object' && String(finalHeaders['Content-Type']).includes('x-www-form-urlencoded')) ?
  23. Object.keys(data).map(key => `${key}=${encodeURIComponent(data[key])}`).join("&") :
  24. data
  25. const finalUrl = simpleRequest.has(method.toLowerCase()) ? `${url}?${finalData}` : url
  26. xhr.responseType = responseType
  27. xhr.withCredentials = withCredentials
  28. xhr.timeout = timeout
  29. return new Promise((resolve, reject) => {
  30. xhr.addEventListener("readystatechange", function () {
  31. if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
  32. try {
  33. resolve(JSON.parse(xhr.responseText))
  34. } catch {
  35. resolve(xhr.responseText)
  36. } finally {
  37. resolve(xhr.response)
  38. }
  39. }
  40. })
  41. xhr.addEventListener("error", function () {
  42. reject(xhr.response)
  43. })
  44. xhr.addEventListener('timeout', function () {
  45. reject('timeout')
  46. })
  47. xhr.open(method, finalUrl)
  48. Object.keys(finalHeaders).forEach(key => {
  49. xhr.setRequestHeader(key, headers[key])
  50. })
  51. xhr.send(simpleRequest.has(method.toLowerCase()) ? null : finalData)
  52. })
  53. }
  54. // 状态
  55. exports.state = {
  56. // 微信公众平台价格限制
  57. wx: {
  58. ocpa: 0,
  59. ocpx: 0
  60. },
  61. // 腾讯广告价格限制
  62. tencent: {
  63. ocpa: 0,
  64. ocpx: 0
  65. },
  66. // 广告id -> 出价类型 映射
  67. adMap: new Map()
  68. }
  69. // 监听content-scripts 消息
  70. exports.listenContentMessage = function () {
  71. return new Promise((resolve, reject) => {
  72. chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
  73. const handler = tactics[message.type]
  74. handler && handler(message.payload, sender, sendResponse, function (res) {
  75. resolve(res)
  76. })
  77. return true
  78. })
  79. })
  80. }
  81. // 拦截出价
  82. exports.beforeWxPriceRequest = function () {
  83. chrome.webRequest.onBeforeRequest.addListener(
  84. details => {
  85. const { args } = details.requestBody.formData || {}
  86. if (!args) return
  87. const jsonArgs = JSON.parse(args)
  88. const priceType = exports.state.adMap.get((jsonArgs.oper_aids[0] || {}).aid)
  89. const max = +state.wx[priceType]
  90. let invalid = jsonArgs.oper_aids.some(item => item.bid / 100 > max)
  91. if (invalid) {
  92. return {
  93. cancel: true
  94. }
  95. }
  96. },
  97. { urls: ["https://mp.weixin.qq.com/promotion/v3/batch_update_adgroup"] },
  98. ["requestBody", "blocking"]
  99. )
  100. }
  101. // 拦截计划
  102. exports.beforeWxPlansRequest = function (priceType = 'ocpa') {
  103. chrome.webRequest.onBeforeRequest.addListener(
  104. details => {
  105. const { formData } = details.requestBody
  106. const { args } = formData || {}
  107. const max = +state.wx[priceType]
  108. let invalid = !args || JSON.parse(args).target_groups.some(target => target.ad_groups.some(ad => ad.ad_group.bid / 100 > max))
  109. if (invalid) {
  110. return {
  111. cancel: true
  112. }
  113. }
  114. },
  115. { urls: [
  116. "https://mp.weixin.qq.com/promotion/v3/update_campaign_info",
  117. 'https://mp.weixin.qq.com/promotion/v3/create_campaign_info'
  118. ]
  119. },
  120. ["requestBody", "blocking"]
  121. )
  122. }
  123. // 获取cookie
  124. function getCookie(callback) {
  125. chrome.tabs.query({
  126. active: true
  127. }, (tabs) => {
  128. const tabId = tabs[0] && tabs[0].id
  129. chrome.tabs.sendMessage(tabId, { type: 'getCookie' }, res => {
  130. callback(res)
  131. })
  132. })
  133. }
  134. // 拦截出价列表
  135. exports.beforeWxRequestPriceList = function () {
  136. const filters = {
  137. urls: ["https://mp.weixin.qq.com/promotion/as_rock*"]
  138. }
  139. const typeMap = {
  140. '按下单出价': 'ocpx',
  141. '按关注出价': 'ocpa',
  142. '按曝光出价': 'ocpa',
  143. '按点击出价': 'ocpa'
  144. }
  145. const callback = function (details) {
  146. const query = helper.queryToJson(details.url)
  147. if (query.action === 'get_adgroup_data' && query.custom !== '1') {
  148. let headers = details.requestHeaders.reduce((pre, cur) => {
  149. pre[cur.name] = cur.value
  150. return pre
  151. }, {})
  152. getCookie(async (Cookie) => {
  153. const res = await exports.req({
  154. method: 'get',
  155. url: `${details.url}&custom=1`,
  156. headers: {
  157. ...headers,
  158. Cookie,
  159. 'Accept-Encoding': 'gzip, deflate, br',
  160. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
  161. Connection: 'keep-alive',
  162. Host: 'mp.weixin.qq.com',
  163. Referer: 'https://mp.weixin.qq.com/promotion/readtemplate?t=campaign/manage&lang=zh_CN&token=1483508456&type=1'
  164. }
  165. })
  166. if (res.msg === 'ok') {
  167. let map = new Map()
  168. res.list && res.list.forEach(item => {
  169. map.set(item.adgroup_info.aid, typeMap[item.adgroup_info.bid_action_type] || 'ocpa')
  170. })
  171. exports.state.adMap = map
  172. }
  173. })
  174. }
  175. }
  176. chrome.webRequest.onBeforeSendHeaders.addListener(callback, filters, ['requestHeaders'])
  177. }
  178. })(this || window)