2024-02-24 20:30:01 +08:00
// ==UserScript==
// @name AliExpress Invoice Downloader
// @namespace https://www.aliexpress.com
2024-06-27 02:29:02 +08:00
// @version 1.1
2024-02-24 20:30:01 +08:00
// @description Adds download buttons to the Aliexpress order page (https://www.aliexpress.com/p/order/index.html) and a bulk download button to download all invoices on the order page to save time.
// @match https://www.aliexpress.com/p/order/index.html*
// @grant GM_download
// @author Peter Tanner
// @namespace https://github.com/peter-tanner/AliExpress-Invoice-Downloader/
// @supportURL https://github.com/peter-tanner/AliExpress-Invoice-Downloader/issues
// @updateURL https://github.com/peter-tanner/AliExpress-Invoice-Downloader/AliExpress-Invoice-Downloader.user.js
// @license GPL-3.0
// @website https://www.petertanner.dev/
// ==/UserScript==
( function ( ) {
"use strict" ;
let orderIds = [ ] ;
function handleDownloadOrNavigate ( link , orderId , name ) {
const downloadButton = document . createElement ( "button" ) ;
downloadButton . textContent = "Download Invoice" ;
downloadButton . className = "aliexpress-invoice-download-button" ;
downloadButton . style . marginLeft = "10px" ;
downloadButton . addEventListener ( "click" , ( ) => {
downloadInvoice ( orderId , name ) ;
} ) ;
link . parentNode . appendChild ( downloadButton ) ;
}
function downloadInvoice ( orderId , name ) {
const sanitizedFileName = sanitizeFileName ( name ) ;
const url = ` https://trade.aliexpress.com/ajax/invoice/invoiceExportAjax.htm?orderId= ${ orderId } &name= ${ sanitizedFileName } ` ;
GM _download ( {
url : url ,
name : ` invoice_ ${ orderId } _ ${ sanitizedFileName } .pdf ` ,
onerror : function ( error ) {
console . error ( "Error downloading:" , error ) ;
} ,
} ) ;
}
function sanitizeFileName ( name ) {
2024-06-27 02:22:35 +08:00
const sanitized = name
. replace ( /[^\x00-\x7F]/g , "" )
. replace ( /\s+/g , "_" )
. replace ( /[/\\?%*:|"<>]/g , "_" ) ; // Remove illegal filename characters
2024-02-24 20:30:01 +08:00
return sanitized . substring ( 0 , 16 ) ;
}
function addDownloadAllButton ( ) {
const downloadAllButton = document . createElement ( "button" ) ;
downloadAllButton . textContent = "Download All Invoices" ;
downloadAllButton . className = "aliexpress-download-all-button" ;
downloadAllButton . style . position = "fixed" ;
downloadAllButton . style . bottom = "10px" ;
downloadAllButton . style . left = "10px" ;
downloadAllButton . style . zIndex = "1000" ;
downloadAllButton . style . backgroundColor = "red" ;
downloadAllButton . style . color = "white" ;
downloadAllButton . style . padding = "10px" ;
downloadAllButton . style . border = "none" ;
downloadAllButton . style . cursor = "pointer" ;
downloadAllButton . addEventListener ( "click" , ( ) => {
orderIds . forEach ( ( order ) => {
downloadInvoice ( order . orderId , order . name ) ;
} ) ;
} ) ;
document . body . appendChild ( downloadAllButton ) ;
}
function createDownloadButtons ( ) {
const invoiceItems = document . querySelectorAll ( "div.order-item" ) ;
invoiceItems . forEach ( ( item ) => {
const nameElement = item . querySelector (
"div.order-item-content-info-name"
) ;
const name = nameElement ? nameElement . textContent . trim ( ) : "" ;
const link = item . querySelector (
"div.order-item-header > div.order-item-header-right > a"
) ;
if ( link ) {
const href = link . getAttribute ( "href" ) ;
if ( href ) {
const url = new URL ( href , window . location . href ) ;
const orderId = url . searchParams . get ( "orderId" ) ;
if ( orderId ) {
orderIds . push ( { orderId : orderId , name : name } ) ;
handleDownloadOrNavigate ( link , orderId , name ) ;
}
}
}
} ) ;
}
const observerCallback = ( mutationsList ) => {
for ( let mutation of mutationsList ) {
if ( mutation . type === "childList" ) {
const addedNodes = mutation . addedNodes ;
addedNodes . forEach ( ( node ) => {
if ( node . matches && node . matches ( "div.order-item" ) ) {
const nameElement = node . querySelector (
"div.order-item-content-info-name"
) ;
const name = nameElement ? nameElement . textContent . trim ( ) : "" ;
const link = node . querySelector (
"div.order-item-header > div.order-item-header-right > a"
) ;
if ( link ) {
const href = link . getAttribute ( "href" ) ;
if ( href ) {
const url = new URL ( href , window . location . href ) ;
const orderId = url . searchParams . get ( "orderId" ) ;
if ( orderId ) {
orderIds . push ( { orderId : orderId , name : name } ) ;
handleDownloadOrNavigate ( link , orderId , name ) ;
}
}
}
}
} ) ;
}
}
} ;
const startObserverWhenReady = ( ) => {
const targetNode = document . querySelector ( "div.comet-checkbox-group" ) ;
if ( targetNode ) {
const observer = new MutationObserver ( observerCallback ) ;
const config = { childList : true , subtree : true } ;
observer . observe ( targetNode , config ) ;
} else {
setTimeout ( startObserverWhenReady , 100 ) ;
}
} ;
startObserverWhenReady ( ) ;
addDownloadAllButton ( ) ;
createDownloadButtons ( ) ;
} ) ( ) ;