iphone-xudale 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. package/.eslintrc.json +3 -0
  2. package/app/(pages)/options/page.tsx +720 -0
  3. package/app/(pages)/popup/page.tsx +166 -0
  4. package/app/(pages)/tips/page.tsx +40 -0
  5. package/app/components/DropListBox.tsx +157 -0
  6. package/app/components/SVGPlay.tsx +14 -0
  7. package/app/favicon.ico +0 -0
  8. package/app/globals.css +27 -0
  9. package/app/layout.tsx +15 -0
  10. package/app/page.tsx +3 -0
  11. package/app/scripts/content/checkoutSteps.ts +341 -0
  12. package/app/scripts/content/doFroApplePages.ts +206 -0
  13. package/app/scripts/content/getPageInitInfo.ts +49 -0
  14. package/app/scripts/content/getStoreCanPickInfo.ts +251 -0
  15. package/app/scripts/content/goOrderSteps.ts +179 -0
  16. package/app/scripts/content/index.ts +56 -0
  17. package/app/scripts/content/playSystemNotifacation.ts +39 -0
  18. package/app/scripts/content/sendSelfNotificatioin.ts +27 -0
  19. package/app/scripts/inject/index.ts +18 -0
  20. package/app/shared/constants.ts +236 -0
  21. package/app/shared/interface.ts +25 -0
  22. package/app/shared/location/city.json +1774 -0
  23. package/app/shared/location/county.json +17115 -0
  24. package/app/shared/location/province.json +94 -0
  25. package/app/shared/util.ts +93 -0
  26. package/buildAfter.js +86 -0
  27. package/bunBuild.ts +54 -0
  28. package/extension/content-script.js +7 -0
  29. package/extension/favicon.ico +0 -0
  30. package/extension/icons/icon128.png +0 -0
  31. package/extension/icons/icon16.png +0 -0
  32. package/extension/icons/icon19-disable.png +0 -0
  33. package/extension/icons/icon19.png +0 -0
  34. package/extension/icons/icon32.png +0 -0
  35. package/extension/icons/icon38.png +0 -0
  36. package/extension/icons/icon48.png +0 -0
  37. package/extension/inject-script.js +1 -0
  38. package/extension/manifest.json +38 -0
  39. package/extension/service-worker.js +62 -0
  40. package/extension.next.config.js +25 -0
  41. package/icon_generator.py +30 -0
  42. package/middleware.ts +21 -0
  43. package/next.config.js +15 -0
  44. package/package.json +67 -0
  45. package/postcss.config.js +6 -0
  46. package/public/assets/images/SCR-20230916-nbkz.png +0 -0
  47. package/public/assets/images/SCR-20230916-nbyv.png +0 -0
  48. package/public/assets/images/SCR-20230916-ncte.png +0 -0
  49. package/public/assets/images/SCR-20230916-ndgw.png +0 -0
  50. package/public/assets/images/SCR-20230916-ndks.png +0 -0
  51. package/public/assets/images/SCR-20230916-neaa.png +0 -0
  52. package/public/assets/images/SCR-20230916-neeq.jpeg +0 -0
  53. package/public/assets/images/SCR-20230916-nfkt.png +0 -0
  54. package/public/assets/images/SCR-20230919-ulfn.png +0 -0
  55. package/public/assets/images/SCR-20230919-ulzd.png +0 -0
  56. package/public/assets/images/SCR-20230919-uocr.png +0 -0
  57. package/public/icon_original.png +0 -0
  58. package/public/next.svg +1 -0
  59. package/public/vercel.svg +1 -0
  60. package/tailwind.config.ts +20 -0
  61. package/tsconfig.json +27 -0
  62. package/types/global.d.ts +19 -0
@@ -0,0 +1,251 @@
1
+ import { IPHONEORDER_CONFIG } from '@/app/shared/interface'
2
+ import { applePageUrl, iPhoneModels, fetchHeaders, defaultAres } from '@/app/shared/constants'
3
+ import { sleep, randomSleep } from '@/app/shared/util'
4
+ import crossfetch from 'cross-fetch'
5
+ import { each as _each, map as _map } from 'lodash'
6
+
7
+ const fetch = crossfetch.bind(this)
8
+
9
+ /*
10
+ * @partNumber iPhone 型号
11
+ * @isNoWait 是否等待,不等待表示纯粹调用接口
12
+ */
13
+ interface IGetStoreCanPickInfoProps {
14
+ x_aos_stk: string
15
+ partNumber: string
16
+ isNoWait?: boolean
17
+ iPhoneOrderConfig: IPHONEORDER_CONFIG
18
+ }
19
+ const getStoreCanPickInfo = async ({
20
+ x_aos_stk,
21
+ partNumber,
22
+ isNoWait,
23
+ iPhoneOrderConfig,
24
+ }: IGetStoreCanPickInfoProps) => {
25
+ storeSearchInPage({ iPhoneOrderConfig })
26
+ let pickupStoreInfo: Record<string, any> = {}
27
+ const { host, protocol } = window.location || {}
28
+ // let url = `${protocol}//www.apple.com.cn/shop/fulfillment-messages`
29
+ let url = `/shop/checkoutx?_a=search&_m=checkout.fulfillment.pickupTab.pickup.storeLocator`
30
+
31
+ const districtName = iPhoneOrderConfig.districtName || defaultAres.districtName
32
+ const provinceName = iPhoneOrderConfig.provinceName || defaultAres.provinceName
33
+ const cityName = iPhoneOrderConfig.cityName || defaultAres.cityName
34
+ let reqQuery = {
35
+ 'parts.0': partNumber, // 型号 `MQ8G3CH/A`,
36
+ 'mts.0': `regular`,
37
+ pl: true,
38
+ location: `${provinceName} ${cityName} ${districtName}`,
39
+ geoLocated: false,
40
+ state: provinceName,
41
+ city: cityName,
42
+ district: districtName,
43
+ }
44
+
45
+ const querystring = _map(reqQuery, (value, key) => {
46
+ return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
47
+ }).join(`&`)
48
+
49
+ let dataString = '',
50
+ data = []
51
+ const provinceCityDistrict =
52
+ provinceName == cityName ? cityName + ' ' + districtName : provinceName + ' ' + cityName + ' ' + districtName
53
+ data = [
54
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.showAllStores=false`,
55
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.selectStore=`,
56
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.searchInput=${encodeURIComponent(
57
+ provinceName + ' ' + cityName + ' ' + districtName
58
+ )}`,
59
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.address.stateCitySelectorForCheckout.city=${encodeURIComponent(
60
+ cityName
61
+ )}`,
62
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.address.stateCitySelectorForCheckout.state=${encodeURIComponent(
63
+ provinceName
64
+ )}`,
65
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.address.stateCitySelectorForCheckout.provinceCityDistrict=${encodeURIComponent(
66
+ provinceCityDistrict
67
+ )}`,
68
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.address.stateCitySelectorForCheckout.countryCode=CN`,
69
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.address.stateCitySelectorForCheckout.district=${encodeURIComponent(
70
+ districtName
71
+ )}`,
72
+ ]
73
+ dataString = data.join(`&`)
74
+
75
+ let options = {
76
+ method: 'POST',
77
+ headers: {
78
+ ...fetchHeaders,
79
+ referer: applePageUrl.buyiPhone,
80
+ 'X-Aos-Model-Page': 'checkoutPage',
81
+ 'X-Aos-Stk': x_aos_stk,
82
+ },
83
+ credentials: 'include' as RequestCredentials,
84
+ body: dataString,
85
+ }
86
+
87
+ console.log(`getStoreCanPickInfo options`, options)
88
+ try {
89
+ let resResult = (await fetch(url, options)) as Record<string, any>
90
+
91
+ let pickupResults: any = {}
92
+
93
+ // 如果请求失败, 表示被封禁
94
+ if (![200, 301, 302].includes(Number(resResult?.status))) {
95
+ if (!isNoWait) {
96
+ console.log(`********** GMfetch failed, stepWait add 1 sec **********`)
97
+ const resText = await resResult.text()
98
+ // console.log(`resText`, resText)
99
+ iPhoneOrderConfig.stepWait = iPhoneOrderConfig.stepWait + 1
100
+ if (resText?.indexOf(`503 Service Temporarily Unavailable`) > -1) {
101
+ console.log(`********** and wait 1 min **********`)
102
+ // 换一个型号调用,让apple认为是正常请求
103
+ const iPhoneProAll = iPhoneModels.iPhone15Pro
104
+ const randomPartNumberiPhonePro =
105
+ iPhoneProAll[Math.floor(Math.random() * iPhoneProAll.length)]?.model
106
+ await getStoreCanPickInfo({
107
+ x_aos_stk,
108
+ partNumber: randomPartNumberiPhonePro,
109
+ isNoWait: true,
110
+ iPhoneOrderConfig,
111
+ })
112
+ await sleep(60)
113
+ }
114
+ } else {
115
+ console.log(`********** GMfetch failed, NoWait failed **********`)
116
+ }
117
+ } else {
118
+ const resJson = await resResult.json()
119
+ console.log(`resJson`, resJson)
120
+ pickupResults =
121
+ resJson?.body?.checkout?.fulfillment?.pickupTab?.pickup?.storeLocator?.searchResults?.d || {}
122
+ }
123
+
124
+ let partPickupStores = pickupResults?.retailStores || [],
125
+ pickupNumbers = ''
126
+ _each(partPickupStores, store => {
127
+ const {
128
+ retailAddress,
129
+ storeDisabled,
130
+ pickupMessages,
131
+ availability,
132
+ storeId: storeNumber,
133
+ storeName,
134
+ } = store || {}
135
+ const { availableNowForAllLines } = availability || {}
136
+ const { city } = retailAddress || {}
137
+ // 有时候会搜出周边城市,这里用于排除周边城市
138
+ const isInCity = city == cityName
139
+ if (isInCity && pickupMessages?.length && (!storeDisabled || availableNowForAllLines)) {
140
+ pickupStoreInfo = {
141
+ ...pickupStoreInfo,
142
+ storeNumber,
143
+ storeName,
144
+ availableNowForAllLines,
145
+ }
146
+ return false
147
+ }
148
+ })
149
+ } catch (e) {
150
+ console.log(e)
151
+ if (!isNoWait) {
152
+ console.log(`********** GMfetch failed, stepWait add 1 sec, and wait 1 min **********`)
153
+ iPhoneOrderConfig.stepWait = iPhoneOrderConfig.stepWait + 1
154
+ // 换一个型号调用,让apple认为是正常请求
155
+ const iPhoneProAll = iPhoneModels.iPhone15Pro
156
+ const randomPartNumberiPhonePro = iPhoneProAll[Math.floor(Math.random() * iPhoneProAll.length)]?.model
157
+ await getStoreCanPickInfo({
158
+ x_aos_stk,
159
+ partNumber: randomPartNumberiPhonePro,
160
+ isNoWait: true,
161
+ iPhoneOrderConfig,
162
+ })
163
+ await sleep(10)
164
+ } else {
165
+ console.log(`********** GMfetch failed, NoWait failed **********`)
166
+ }
167
+ }
168
+
169
+ console.log(`pickupStoreInfo`, pickupStoreInfo)
170
+ return pickupStoreInfo
171
+ }
172
+
173
+ export default getStoreCanPickInfo
174
+
175
+ interface IStoreSearchInPageProps {
176
+ iPhoneOrderConfig: IPHONEORDER_CONFIG
177
+ }
178
+
179
+ const randomRange = 3
180
+ const storeSearchInPage = async ({ iPhoneOrderConfig }: IStoreSearchInPageProps) => {
181
+ const storeSearchDataAutom = `fulfillment-pickup-store-search-button`
182
+ const storeSearchBtn = document.querySelector(`button[data-autom="${storeSearchDataAutom}"]`)
183
+ if (!storeSearchBtn) return
184
+ const { cityName, districtName, provinceName } = iPhoneOrderConfig
185
+
186
+ if (!cityName || !districtName || !provinceName) return
187
+
188
+ // 已纠正的情况下,不需要重复点击了
189
+ if (storeSearchBtn.textContent) {
190
+ if (storeSearchBtn.textContent.includes(districtName) && storeSearchBtn.textContent.includes(provinceName)) {
191
+ return
192
+ }
193
+ }
194
+
195
+ await randomSleep({ min: 0, max: randomRange })
196
+ const isSelectionOpen = document.querySelectorAll(`li[role="listitem"]>button`)?.length > 0
197
+
198
+ if (!isSelectionOpen) {
199
+ ;(storeSearchBtn as HTMLButtonElement).click()
200
+ }
201
+
202
+ await randomSleep({ min: 0, max: randomRange })
203
+
204
+ const provinceTabBtn = document.getElementById(
205
+ 'checkout.fulfillment.pickupTab.pickup.storeLocator.address.stateCitySelectorForCheckout.state'
206
+ )
207
+ provinceTabBtn?.click()
208
+
209
+ let hasTheProvince = false
210
+ if (provinceName) {
211
+ const provinceItems = document.querySelectorAll(`li[role="listitem"]>button`)
212
+ _each(provinceItems, p_item => {
213
+ if (p_item?.textContent?.includes(provinceName)) {
214
+ ;(p_item as HTMLButtonElement)?.click()
215
+ hasTheProvince = true
216
+ return false
217
+ }
218
+ })
219
+ await randomSleep({ min: 0, max: randomRange })
220
+ }
221
+
222
+ const cityTabBtn = document.getElementById(
223
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.address.stateCitySelectorForCheckout.city`
224
+ )
225
+ cityTabBtn?.click()
226
+ if (hasTheProvince && cityName && cityName != provinceName && cityTabBtn) {
227
+ const cityItems = document.querySelectorAll(`li[role="listitem"]>button`)
228
+ _each(cityItems, p_item => {
229
+ if (p_item?.textContent?.includes(cityName)) {
230
+ ;(p_item as HTMLButtonElement)?.click()
231
+ return false
232
+ }
233
+ })
234
+ await randomSleep({ min: 0, max: randomRange })
235
+ }
236
+
237
+ const districtTabBtn = document.getElementById(
238
+ `checkout.fulfillment.pickupTab.pickup.storeLocator.address.stateCitySelectorForCheckout.district`
239
+ )
240
+ districtTabBtn?.click()
241
+ if (districtName && districtTabBtn) {
242
+ const districtItems = document.querySelectorAll(`li[role="listitem"]>button`)
243
+ _each(districtItems, p_item => {
244
+ if (p_item?.textContent?.includes(districtName)) {
245
+ ;(p_item as HTMLButtonElement)?.click()
246
+ return false
247
+ }
248
+ })
249
+ await randomSleep({ min: 0, max: randomRange })
250
+ }
251
+ }
@@ -0,0 +1,179 @@
1
+ import getStoreCanPickInfo from './getStoreCanPickInfo'
2
+ import checkoutSteps from './checkoutSteps'
3
+ import type { IPHONEORDER_CONFIG } from '@/app/shared/interface'
4
+ import { CHECKOUT_STEPS, BILL_OPTIONS_TYPE } from '@/app/shared/constants'
5
+ import { sleep } from '@/app/shared/util'
6
+ import { isEmpty as _isEmpty, each as _each, some as _some } from 'lodash'
7
+ import { iframeMessagePass } from '@/app/shared/constants'
8
+
9
+ interface IGoOrderSteps {
10
+ partNumber: string
11
+ x_aos_stk: string
12
+ count?: number
13
+ iPhoneOrderConfig: IPHONEORDER_CONFIG
14
+ }
15
+
16
+ const goOrderSteps = async ({ partNumber, x_aos_stk, count, iPhoneOrderConfig }: IGoOrderSteps) => {
17
+ count = count || 1 // 重试次数
18
+ const { afterCountThenReload = 50 } = iPhoneOrderConfig || {}
19
+ // ********** 发送消息给 tips page **********
20
+ const iframeWindow = (document?.getElementById(iframeMessagePass.iframeID) as HTMLIFrameElement)?.contentWindow
21
+ const message = { action: iframeMessagePass.messageAction, count: count, beforeReload: afterCountThenReload }
22
+ iframeWindow?.postMessage(message, '*')
23
+
24
+ // 获取可以提货的商店信息
25
+ let pickupStoreInfo = await getStoreCanPickInfo({ x_aos_stk, partNumber, iPhoneOrderConfig })
26
+
27
+ const { storeNumber, availableNowForAllLines } = pickupStoreInfo || {}
28
+ const { host, protocol } = location || {}
29
+
30
+ if (storeNumber) {
31
+ // 走第一步, 选店,获取提货时间
32
+ let canPickupTime = await checkoutSteps({
33
+ step: CHECKOUT_STEPS.selectStore,
34
+ x_aos_stk,
35
+ stepInfo: {
36
+ storeNumber,
37
+ },
38
+ iPhoneOrderConfig,
39
+ })
40
+ console.log(`firstStep:`, canPickupTime)
41
+
42
+ const timeSlot = canPickupTime?.timeSlot as Record<string, any>
43
+ const noNeedTimeSlot = canPickupTime?.noNeedTimeSlot
44
+ const canPickup = canPickupTime?.isSuccess
45
+ if (!canPickup) return
46
+
47
+ if (noNeedTimeSlot) {
48
+ // 所有时间可取货,直接走 checkoutFulfillment
49
+ let canCheckout = await checkoutSteps({
50
+ noNeedTimeSlot,
51
+ step: CHECKOUT_STEPS.checkoutFulfillment,
52
+ x_aos_stk,
53
+ stepInfo: {
54
+ storeNumber,
55
+ },
56
+ iPhoneOrderConfig,
57
+ })
58
+
59
+ if (!canCheckout?.isSuccess) return
60
+ } else {
61
+ // 第二步, 选日期时间
62
+ let pickupTimeInStore = await checkoutSteps({
63
+ step: CHECKOUT_STEPS.selectPickupTime,
64
+ noNeedTimeSlot,
65
+ x_aos_stk,
66
+ stepInfo: {
67
+ storeNumber,
68
+ ...timeSlot,
69
+ },
70
+ iPhoneOrderConfig,
71
+ })
72
+
73
+ if (!pickupTimeInStore?.isSuccess) return
74
+ }
75
+ // 第三步 填写证件信息
76
+ let addContactResult = await checkoutSteps({
77
+ step: CHECKOUT_STEPS.pickupContact,
78
+ x_aos_stk,
79
+ stepInfo: {
80
+ lastName: iPhoneOrderConfig.lastName,
81
+ firstName: iPhoneOrderConfig.firstName,
82
+ emailAddress: iPhoneOrderConfig.appleId,
83
+ fullDaytimePhone: iPhoneOrderConfig.mobile,
84
+ nationalIdSelf: iPhoneOrderConfig.last4code,
85
+ },
86
+ iPhoneOrderConfig,
87
+ })
88
+ const { allBillingOptions } = addContactResult || {}
89
+
90
+ if (!addContactResult?.isSuccess) return
91
+
92
+ let selectBillType = BILL_OPTIONS_TYPE[iPhoneOrderConfig.payBill] || BILL_OPTIONS_TYPE.alipay
93
+ let selectBillValue = 'ALIPAY'
94
+
95
+ if (!_isEmpty(allBillingOptions)) {
96
+ _each(allBillingOptions, bill_option => {
97
+ const { label, labelImageAlt, a11yLabel, value } = bill_option || {}
98
+ const labelText = label || labelImageAlt || a11yLabel || ''
99
+ if (labelText.indexOf(selectBillType) > -1) {
100
+ selectBillValue = value || 'ALIPAY'
101
+ return false
102
+ }
103
+ })
104
+ }
105
+ // 第四步一, 选择付款方式
106
+ let addBill = await checkoutSteps({
107
+ step: CHECKOUT_STEPS.selectBill,
108
+ x_aos_stk,
109
+ stepInfo: {
110
+ billOption: selectBillValue,
111
+ billInstallment: iPhoneOrderConfig.payInstallment, // 分期
112
+ },
113
+ iPhoneOrderConfig,
114
+ })
115
+
116
+ if (!addBill?.isSuccess) return
117
+
118
+ if (iPhoneOrderConfig.payInstallment && iPhoneOrderConfig.payInstallment > 0) {
119
+ const { installmentOptions = [] } = addBill || {}
120
+ // 判断传入的分期是否合法
121
+ let isPayInstallmentValidated = _some(installmentOptions, item => {
122
+ return item?.value == iPhoneOrderConfig.payInstallment
123
+ })
124
+ if (!isPayInstallmentValidated) {
125
+ iPhoneOrderConfig.payInstallment = installmentOptions[installmentOptions?.length - 1]?.value || 0
126
+ }
127
+ }
128
+
129
+ let confirmBillStepsInfo: Record<string, any> = {
130
+ billOption: selectBillValue,
131
+ }
132
+ if (iPhoneOrderConfig.payInstallment) {
133
+ confirmBillStepsInfo = {
134
+ ...confirmBillStepsInfo,
135
+ billInstallment: iPhoneOrderConfig.payInstallment, // 分期
136
+ }
137
+ }
138
+ // 第四步二,确认付款方式
139
+ let confirmBill = await checkoutSteps({
140
+ step: CHECKOUT_STEPS.checkoutBill,
141
+ x_aos_stk,
142
+ stepInfo: {
143
+ ...confirmBillStepsInfo,
144
+ },
145
+ iPhoneOrderConfig,
146
+ })
147
+
148
+ if (!confirmBill?.isSuccess) return
149
+
150
+ // 最后下单
151
+ let placeOrderRes = await checkoutSteps({
152
+ step: CHECKOUT_STEPS.placeOrder,
153
+ x_aos_stk,
154
+ stepInfo: {},
155
+ iPhoneOrderConfig,
156
+ })
157
+
158
+ if (placeOrderRes?.isSuccess) {
159
+ let jumpUrl = `${protocol}//${host}/shop/checkout/interstitial`
160
+ if (placeOrderRes?.url) {
161
+ // `${protocol}//${host}/shop/checkout/status`
162
+ jumpUrl = `${protocol}//${host}${placeOrderRes.url || ''}`
163
+ }
164
+
165
+ location.href = jumpUrl
166
+ }
167
+ } else if (count >= afterCountThenReload) {
168
+ await sleep(1, `goOrderSteps failed over ${afterCountThenReload} times, then reload page`)
169
+ location.reload()
170
+ } else {
171
+ await sleep(
172
+ iPhoneOrderConfig.stepWait,
173
+ `goOrderSteps again, count: ${count}, limit times: ${afterCountThenReload}`
174
+ )
175
+ await goOrderSteps({ partNumber, x_aos_stk, count: count + 1, iPhoneOrderConfig })
176
+ }
177
+ }
178
+
179
+ export default goOrderSteps
@@ -0,0 +1,56 @@
1
+ import doFroApplePages from './doFroApplePages'
2
+ import { iframeMessagePass } from '@/app/shared/constants'
3
+ import playSystemNotification from './playSystemNotifacation'
4
+
5
+ // @ts-ignore
6
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
7
+ const { data, voiceInfo } = message || {}
8
+ const { text } = voiceInfo || {}
9
+ if (data == 'playSystemNotification' && text) {
10
+ playSystemNotification({ voiceInfo: voiceInfo })
11
+ }
12
+ })
13
+
14
+ const addTipsToPage = () => {
15
+ var extensionId = chrome.runtime.id
16
+ const iframe = document.createElement('iframe')
17
+ iframe.id = iframeMessagePass.iframeID
18
+ iframe.style.cssText = `
19
+ right: 0;
20
+ top: 50%;
21
+ width: 250px;
22
+ height: 160px;
23
+ `
24
+ iframe.src = `chrome-extension://${extensionId}/dist/tips.html`
25
+ document.documentElement.appendChild(iframe)
26
+ iframe.style.position = 'fixed'
27
+ console.log(`addTipsToPage`)
28
+ }
29
+
30
+ console.log(`this is content`)
31
+ const contentRun = async () => {
32
+ addTipsToPage()
33
+
34
+ // const orderEnabled = !!(await restoreFromStorage(storeKeys.orderEnabled))
35
+ // console.log(`orderEnabled`, orderEnabled)
36
+ // if (!orderEnabled) return
37
+ await doFroApplePages()
38
+ }
39
+
40
+ contentRun()
41
+
42
+ window.addEventListener('message', function (event) {
43
+ if (event.source === window && event.data.action === 'doFroApplePages') {
44
+ // 在这里执行你的方法逻辑
45
+ doFroApplePages(event.data.url)
46
+ }
47
+ })
48
+
49
+ // 监听页面的加载完成事件, 注入自定义脚本到页面中
50
+ window.addEventListener('load', function injectCustomScript() {
51
+ var script = document.createElement('script')
52
+ var extensionId = chrome.runtime.id
53
+ console.log(extensionId)
54
+ script.src = `chrome-extension://${extensionId}/inject-script.js`
55
+ document.documentElement.appendChild(script)
56
+ })
@@ -0,0 +1,39 @@
1
+ import { VOICE_OBJ } from '@/app/shared/interface'
2
+
3
+ const defaultVoiceInfo = {
4
+ text: `抢到了`,
5
+ times: 1,
6
+ }
7
+
8
+ const playSystemNotification = async ({ voiceInfo }: { voiceInfo: VOICE_OBJ }) => {
9
+ let extensionId
10
+ if (typeof chrome !== 'undefined' && chrome?.runtime) {
11
+ extensionId = chrome.runtime.id
12
+ chrome.runtime.sendMessage({ data: 'bellring', extensionId, voiceInfo })
13
+ }
14
+
15
+ // 检查浏览器是否支持 Web Notifications API
16
+ if ('Notification' in window) {
17
+ const icon = extensionId ? `chrome-extension://${extensionId}/icons/icon38.png` : undefined
18
+ // 请求权限显示通知
19
+ Notification.requestPermission().then(permission => {
20
+ if (permission === 'granted') {
21
+ // 创建通知对象
22
+ const notification = new Notification('提醒', {
23
+ body: voiceInfo?.text || defaultVoiceInfo.text,
24
+ icon,
25
+ })
26
+
27
+ // 将通知点击时的行为处理逻辑添加在这里
28
+ notification.onclick = function () {
29
+ // 处理通知点击事件
30
+ console.log('通知被点击了')
31
+ }
32
+ }
33
+ })
34
+ } else {
35
+ console.log(`notification fail`)
36
+ }
37
+ }
38
+
39
+ export default playSystemNotification
@@ -0,0 +1,27 @@
1
+ import { sleep } from '@/app/shared/util'
2
+
3
+ const sendSelfNotificatioin = async ({ url }: { url?: string }) => {
4
+ if (!url) return
5
+
6
+ return Promise.race([
7
+ sleep(5),
8
+ new Promise((resolve, reject) => {
9
+ const img = new Image()
10
+ img.src = url
11
+ img.style.display = 'none'
12
+ img.style.width = '1px'
13
+ img.style.height = '1px'
14
+ document.body.appendChild(img)
15
+ img.addEventListener('load', function () {
16
+ console.log('请求成功')
17
+ resolve(true)
18
+ })
19
+ img.addEventListener('error', function () {
20
+ console.log('请求失败')
21
+ resolve(false)
22
+ })
23
+ }),
24
+ ])
25
+ }
26
+
27
+ export default sendSelfNotificatioin
@@ -0,0 +1,18 @@
1
+ ;(function (history) {
2
+ var originalPushState = history.pushState
3
+
4
+ history.pushState = function (state, title, url) {
5
+ if (typeof history.onpushstate == 'function') {
6
+ history.onpushstate({ state: state })
7
+ }
8
+
9
+ // @ts-ignore
10
+ url = url && url.search(/^http/) > -1 ? url : ''
11
+
12
+ window.postMessage({ action: 'doFroApplePages', url: url }, '*')
13
+ // 调用原生的 history.pushState 方法
14
+ // @ts-ignore
15
+ return originalPushState.apply(history, arguments)
16
+ }
17
+ console.log(history.pushState)
18
+ })(history)