// ==UserScript==
// @name 亚马逊批量下载业务报告
// @namespace https://github.com/MaiXiaoMeng
// @version 0.0.8
// @description 垃圾亚马逊,连批量下载业务报告的方法都没有,只好自己写一个了
// @author XiaoMeng Mai
// @license GPLv3
// @match https://*.amazon.com/*
// @match https://*.amazon.co.uk/*
// @match https://*.amazon.fr/*
// @match https://*.amazon.it/*
// @match https://*.amazon.es/*
// @match https://*.amazon.co.jp/*
// @match https://*.amazon.com.au/*
// @match https://*.amazon.sg/*
// @grant GM_info
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_download
// @grant GM_addStyle
// @grant GM_openInTab
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant unsafeWindow
// @compatible firefox Tampermonkey
// @compatible chrome Tampermonkey
// @compatible edge Tampermonkey
// @require https://unpkg.com/jquery
// @require https://unpkg.com/moment
// @require https://unpkg.com/sweetalert2
// @resource element-plus https://unpkg.com/element-plus/dist/index.css
// @note 0.0.8 [Amazon] 修复 业务报告 修复个别 日本站 不显示下载按钮的问题
// @note 0.0.7 [Amazon] 新增 业务报告 支持按每天或者每月下载
// @note 0.0.6 [Amazon] 新增 业务报告 自动获取业务报告的全部字段
// @note 0.0.5 [Amazon] 新增 库存管理 显示库存管理产品的分类
// @note 0.0.4 [Amazon] 修复 业务报告 父商品详情页面销售和流量 和 详情页面销售和流量 错误显示批量下载按钮
// @note 0.0.3 [Amazon] 新增 业务报告 亚马逊企业购(B2B)相关数据列
// @note 0.0.2 [Amazon] 修复 业务报告 下载报告没有子ASIN列的问题
// @note 0.0.1 [Amazon] 新增 业务报告 详情页面销售和流量(按子商品)批量下载
// https://sweetalert2.github.io/
// https://element-plus.gitee.io/
// append() - 在被选元素的结尾插入内容(内容的结尾,比如说有个a标签,则是在</a>这个标签之前添加东西) <a> [append]</a>
// prepend() - 在被选元素的开头插入内容(内容的开始,比如说有个a标签,则是在<a>这个标签之后添加东西) <a>[prepend] </a>
// after() - 在被选元素之后插入内容(元素的结尾,比如说有个a标签,则是在</a>这个标签之后添加东西) <a> </a>[after]
// before() - 在被选元素之前插入内容(内容的开始,比如说有个a标签,则是在<a>这个标签之前添加东西) [before]<a> </a>
// waitForKeyElements('.entry-wrapper', 'xxxxx520.com', ['/'], false, false, (node, selector_txt, active_host, active_url) => {
// if (GM_getValue('menu_amazon')) {
// (async () => {
// var response = await makeGetRequest(offcie365_url)
// node.find('.xxx').append($(`<li><span><a target='_blank' href='${response.url}'">response</a></span></li>`))
// })()
// }
// })
// ==/UserScript==
var version_url = "https://greasyfork.org/zh-CN/scripts/449460"
// 初始化脚本
initializationScript()
// [Amazon] 业务报告 | 详情页面销售和流量
waitForKeyElements('.css-1lafdix', '*', ['business-reports'], false, false, (node, selector_txt, active_host, active_url) => {
if (GM_getValue('menu_amazon')) {
if (node.attr('class').indexOf('business_report') == -1) {
node.after($(`<div class="css-1lafdix business_report"><kat-button id='business_month' label="批量下载[月](.csv)" variant="primary" size="base" type="button"></kat-button></div> <div class="css-ix5zus"><kat-link label="" class="css-4g6ai3"></kat-link></div>`))
node.after($(`<div class="css-1lafdix business_report"><kat-button id='business_day' label="批量下载[天](.csv)" variant="primary" size="base" type="button"></kat-button></div> <div class="css-ix5zus"><kat-link label="" class="css-4g6ai3"></kat-link></div>`))
$(".business_report").click(function (event) {
var legacy_report_id = $('.css-1qgr8dx').parent().attr('href').split('=')[1]
var start_date = new Date($(".css-jfggi0")[0].value)
var end_date = new Date($(".css-jfggi0")[1].value)
var site_brand = $(".partner-dropdown-button > span").text().split(" | ");
(async () => {
var api_url = `https://${document.domain}/business-reports/api`
var data = JSON.stringify({
operationName: "reportDataQuery",
variables: {
input: {
legacyReportId: legacy_report_id,
},
},
query: "query reportDataQuery($input: GetReportDataInput) {\n getReportData(input: $input) {\n columns {\n label\n translationKey\n isDefaultSortAscending\n isDefaultGraphed\n isDefaultSelected\n isDefaultSortColumn\n __typename\n }\n rows\n __typename\n }\n}\n",
})
var response = await makeGetRequest(url = api_url, method = 'POST', data = data)
var columns = JSON.parse(response.responseText)['data']['getReportData']['columns']
var selected_columns = []
columns.forEach(element => { selected_columns.push(element['translationKey']) })
var download_date_list = []
if (event['target']['id'] == 'business_day') {
while (start_date <= end_date) {
var _start_date = moment(start_date).format('YYYY-MM-DD')
var _end_date = moment(start_date).format('YYYY-MM-DD')
download_date_list.push({ 'start_date': _start_date, 'end_date': _end_date })
start_date = new Date((start_date / 1000 + 86400 * 1) * 1000)
}
} else if (event['target']['id'] == 'business_month') {
while (start_date <= end_date) {
var _date = new Date(start_date)
var _date_label = true
var _start_date = moment(_date.setDate(1)).format('YYYY-MM-DD')
_date.setMonth(_date.getMonth() + 1)
var _end_date = moment(_date.setDate(0)).format('YYYY-MM-DD')
for (let index = 0; index < download_date_list.length; index++) {
const element = download_date_list[index]
if (_start_date == element['start_date']) {
_date_label = false
break
}
}
if (_date_label) download_date_list.push({ 'start_date': _start_date, 'end_date': _end_date })
start_date = new Date((start_date / 1000 + 86400 * 1) * 1000)
}
}
for (let index = 0; index < download_date_list.length; index++) {
const element = download_date_list[index]
var data = JSON.stringify({
operationName: "reportDataDownloadQuery",
variables: {
input: {
legacyReportId: legacy_report_id,
startDate: element['start_date'],
endDate: element['end_date'],
userSelectedRows: [],
selectedColumns: selected_columns,
},
},
query: "query reportDataDownloadQuery($input: GetReportDataInput) {\n getReportDataDownload(input: $input) {\n url\n __typename\n }\n}\n",
})
var response = await makeGetRequest(url = api_url, method = 'POST', data = data)
var download_url = JSON.parse(response.responseText)["data"]["getReportDataDownload"]["url"]
var download_file_name = `${site_brand[1]}_${site_brand[0]}_${element['start_date']}.csv`
ElementPlus.ElMessage(`正在下载: ${download_file_name}`)
console.log(`正在下载: ${download_file_name} URL: ${download_url}`)
GM_download(download_url, download_file_name)
}
})()
})
}
}
})
// [Amazon] 管理库存 | 显示管理库存产品的分类
waitForKeyElements('.myi-sprite-container.myi-image > a', '*', ['inventory'], false, false, (node, selector_txt, active_host, active_url) => {
var grab_node = node.attr('href').split('&')[2].replace('productType=', '');
node.parent().after($(`<div><div><span style="color:#00F;font-size: initial;">${grab_node}</span></div>`))
})
// // Listing页面
// waitForKeyElements("#productTitle", getGigab2bProductInfo, ['/dp/', '/gp/'])
// waitForKeyElements("#ASIN", actionFunction, ['/dp/', '/gp/'])
// // 订单管理页面
// waitForKeyElements(".cell-body > .cell-body-title", getGigab2bTrackingNumber, ['orders-v3/']);
// waitForKeyElements(".a-spacing-mini > div > span.a-text-bold", getGigab2bTrackingNumber, ['orders-v3/'])
// waitForKeyElements(".myo-list-orders-product-name-cell> div:nth-child(3) > div", getGigab2bProductInfo, ['orders-v3/'])
// var flow_score_url = 'https://api.xiyouzhaoci.com/v1/flowScore/country/US/asin/'
// var word_counts_url = 'https://api.xiyouzhaoci.com/v1/wordCounts'
// var search_by_asin = 'https://api.xiyouzhaoci.com/v1/searchByAsin'
// GM_xmlhttpRequest({
// url: flow_score_url + asin,
// method: 'GET',
// onload: function (response) {
// console.log(response.responseText);
// }
// })
// GM_xmlhttpRequest({
// url: word_counts_url,
// method: 'POST',
// data: JSON.stringify({
// 'asin': `${asin}`,
// 'country': 'US',
// 'filters': [],
// 'query': '',
// 'rangeFilters': []
// }),
// onload: function (response) {
// console.log(response.responseText);
// }
// })
// GM_xmlhttpRequest({
// url: search_by_asin,
// method: 'POST',
// data: JSON.stringify({
// 'asin': `${asin}`, 'country': 'US',
// 'page': 1,
// 'pageSize': 100,
// 'orders': [
// {
// 'field': 'follow',
// 'order': 'desc'
// }
// ],
// 'filters': [],
// 'query': '',
// 'rangeFilters': []
// }),
// onload: function (response) {
// console.log(response.responseText);
// }
// })
/**
* @description: 一个实用程序函数,用于 Greasemonkey 脚本, 检测和处理 AJAXed 内容
* @param {*} selector_txt 元素选择器
* @param {*} active_host 激活的域名
* @param {*} active_url 激活的页面URL
* @param {*} b_wait_once 是否只扫描一次
* @param {*} iframe_selector 是否扫描Frame框架
* @param {*} action_function 找到元素时运行的方法,传送 node, selector_txt, active_host, active_url 四个变量
*/
function waitForKeyElements(selector_txt, active_host, active_url, b_wait_once, iframe_selector, action_function) {
if (active_host != '*' && document.domain.split('.').slice(-2).join('.') != active_host) return
var active_url_type = false
if (typeof active_url == "object") {
for (let index = 0; index < active_url.length; index++) {
if (window.location.href.indexOf(active_url[index]) > -1) {
active_url_type = true
break
}
}
} else if (typeof active_url == "string") {
if (window.location.href.indexOf(active_url) > -1) active_url_type = true
}
if (active_url_type) {
var target_nodes, b_targets_found
if (iframe_selector) {
target_nodes = $(iframe_selector).contents().find(selector_txt)
} else {
target_nodes = $(selector_txt)
}
if (target_nodes && target_nodes.length > 0) {
b_targets_found = true
target_nodes.each(function () {
var j_this = $(this)
var already_found = j_this.data("alreadyFound") || false
if (!already_found) {
logPrint(`selector_txt > ${selector_txt} active_host > ${active_host} active_url > ${active_url} b_wait_once > ${b_wait_once} iframe_selector > ${iframe_selector}`)
console.log(j_this);
var cancel_found = false
if (typeof action_function == "object") {
action_function.forEach(element => {
cancel_found = element(j_this, selector_txt, active_host, active_url);
})
} else if (typeof action_function == "function") {
cancel_found = action_function(j_this, selector_txt, active_url);
}
if (cancel_found) {
b_targets_found = false
} else {
j_this.data("alreadyFound", true);
}
}
})
} else {
b_targets_found = false;
}
var control_obj = waitForKeyElements.control_obj || {};
var control_key = selector_txt.replace(/[^\w]/g, "_");
var time_control = control_obj[control_key];
if (b_targets_found && b_wait_once && time_control) {
clearInterval(time_control);
delete control_obj[control_key];
} else {
if (!time_control) {
time_control = setInterval(function () {
waitForKeyElements(selector_txt, active_host, active_url, b_wait_once, iframe_selector, action_function);
}, 300);
control_obj[control_key] = time_control;
}
}
waitForKeyElements.control_obj = control_obj;
}
}
function makeGetRequest(url, method = 'GET', data = null) {
logPrint(`${method} -> ${url}`)
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
url: url,
method: method,
data: data,
onload: function (response) {
resolve(response);
},
onerror: function (error) {
reject(error);
}
});
});
}
function logPrint(params) {
var date_time = $.trim(new Date(new Date().setHours(new Date().getHours() + 8)).toISOString().replace("Z", " ").replace("T", " "))
var function_name = (new Error()).stack.split("\n")[2].trim().split(" ")[1]
console.log(`[${date_time}][DEBUG] ${function_name} - ${params}`)
}
function sleep(interval) {
return new Promise(resolve => { setTimeout(resolve, interval) })
}
function getGigab2bTrackingNumber(jNode, selectorTxt, activeURL) {
var ERROR_ON_ORDER_NUMBER = "Upload Time refers to the time when the sales order is uploaded to the Marketplace.";
var order_numbers = jNode.text();
var query_order_url = "https://www.gigab2b.com/index.php?route=account/sales_order/sales_order_management/salesOrderList&filter_orderId=";
GM_xmlhttpRequest({
method: "GET",
url: query_order_url + order_numbers,
onload: function (response) {
if (response.finalUrl.indexOf("account/login") > 0) {
jNode.append($(`<a target='_blank' href='${query_order_url + order_numbers}'> 检测到未登录 </a>`))
} else if (response.responseText.indexOf(ERROR_ON_ORDER_NUMBER) == -1) {
jNode.append($(`<a target='_blank' href='${query_order_url + order_numbers}'> 订单号不存在 </a>`))
} else {
var response_text = $(response.responseText);
if (window.location.href.indexOf("sellercentral.amazon.com") > 0) {
var tracking_numbers = response_text.find(".tracking-number > a");
var tracking_href = $.trim(
response_text.find(".tracking-number > a").attr("href")
);
} else if (window.location.href.indexOf("sellercentral-japan.amazon.com") > 0) {
var tracking_numbers = response_text.find(".tracking-number > span");
}
if (tracking_numbers.length > 0) {
var tracking = $.trim(response_text.find(".tracking-number").prop("firstChild").nodeValue)
jNode.append($(`<a target='_blank' href='${tracking_href}'> ${tracking} </a>`))
for (let index = 0; index < tracking_numbers.length; index++) {
jNode.append($(`<span> ${tracking_numbers[index].textContent} </span>`))
}
} else {
jNode.append($(`<a target='_blank' href='${query_order_url + order_numbers}'> 没有查询到物流追踪编号 </a>`))
}
}
},
});
}
function getGigab2bProductInfo(jNode, selectorTxt, activeURL) {
var query_product_info_url = 'https://www.gigab2b.com/index.php?route=product/search&search='
if (selectorTxt == '#productTitle') {
(async () => {
var sku = $.trim(jNode.text().match(/\(.*?, (.*?)\)/)[1])
var response = await makeGetRequest(query_product_info_url + sku)
var response_text = $(response.responseText);
var product_url = response_text.find(".product-image > a").attr("href")
if (typeof product_url == "undefined") product_url = query_product_info_url + sku
logPrint(`product_url -> ${product_url}`)
let result = $.trim(jNode.text()).replace(/(\(.*, .*\))/, `<a target='_blank' href='${product_url}'>$1</a>`)
logPrint(`result -> ${result}`)
jNode.empty()
jNode.append(result)
})()
} else if (selectorTxt == '.myo-list-orders-product-name-cell> div:nth-child(3) > div') {
(async () => {
var sku = $.trim(jNode.text().replace('SKU: ', ''))
var response = await makeGetRequest(query_product_info_url + sku)
var response_text = $(response.responseText);
var product_url = response_text.find(".product-image > a").attr("href")
if (typeof product_url == "undefined") product_url = query_product_info_url + sku
logPrint(`product_url -> ${product_url}`)
jNode.empty()
jNode.append($(`<div><span>SKU</span>: <a target='_blank' href='${product_url}'>${sku}</a></div>`))
})()
}
}
function initializationScript() {
loadMenu()
var $ = window.$
var VueCDN = "https://lib.baomitu.com/vue/3.2.36/vue.global.prod.min.js"
var ElementPlusCDN = "https://lib.baomitu.com/element-plus/2.2.2/index.full.min.js"
GM_addStyle(GM_getResourceText("element-plus"))
$.getScript(VueCDN, function () {
console.log("[" + VueCDN + "] Vue 加载成功");
$.getScript(ElementPlusCDN, function () {
console.log("[" + ElementPlusCDN + "] ElementPlus 加载成功")
var ElementPlus = unsafeWindow.ElementPlus;
var Vue = unsafeWindow.Vue;
})
})
}
function loadMenu() {
var menu_ALL = [
['menu_amazon', 'Amazon', 'Amazon', true],
], menu_ID = []
// 如果读取到的值为 null 就写入默认值
for (let i = 0; i < menu_ALL.length; i++) {
if (GM_getValue(menu_ALL[i][0]) == null) { GM_setValue(menu_ALL[i][0], menu_ALL[i][3]) }
}
registerMenuCommand()
// 注册脚本菜单
function registerMenuCommand() {
// 如果菜单ID数组多于菜单数组,说明不是首次添加菜单,需要卸载所有脚本菜单
if (menu_ID.length > menu_ALL.length) {
for (let i = 0; i < menu_ID.length; i++) {
GM_unregisterMenuCommand(menu_ID[i])
}
}
// 循环注册脚本菜单
for (let i = 0; i < menu_ALL.length; i++) {
menu_ALL[i][3] = GM_getValue(menu_ALL[i][0])
menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][3] ? '✅' : '❌'} ${menu_ALL[i][2]}`, function () {
menu_switch(`${menu_ALL[i][0]}`, `${menu_ALL[i][1]}`, `${menu_ALL[i][2]}`, `${menu_ALL[i][3]}`)
})
}
// 加入版本信息
menu_ID[menu_ID.length] = GM_registerMenuCommand(`🏁 当前版本 ${GM_info['script']['version']}`, function () {
window.GM_openInTab(version_url, { active: true, insert: true, setParent: true })
})
}
//切换选项
function menu_switch(name, ename, cname, value) {
if (value == 'false') {
console.log(name)
// 重新注册脚本菜单,刷新网页
GM_setValue(`${name}`, true)
registerMenuCommand()
location.reload()
GM_notification({ text: `「${cname}」已开启\n`, timeout: 3500 })
} else {
console.log(name)
// 重新注册脚本菜单,刷新网页
GM_setValue(`${name}`, false)
registerMenuCommand()
location.reload()
GM_notification({ text: `「${cname}」已关闭\n`, timeout: 3500 })
}
// 重新注册脚本菜单
registerMenuCommand()
}
}