您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Adds an order directly from Weidian to your agent
当前为
// ==UserScript== // @name Weidian to Agent // @namespace https://www.reddit.com/user/RobotOilInc // @version 1.2.5 // @description Adds an order directly from Weidian to your agent // @author RobotOilInc // @match https://weidian.com/item.html* // @match https://*.weidian.com/item.html* // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @license MIT // @homepageURL http://greasyfork.icu/en/scripts/427774-weidian-to-agent // @supportURL http://greasyfork.icu/en/scripts/427774-weidian-to-agent // @require https://unpkg.com/[email protected]/src/logger.min.js // @require https://unpkg.com/[email protected]/dist/jquery.min.js // @require http://greasyfork.icu/scripts/401399-gm-xhr/code/GM%20XHR.js?version=938754 // @require http://greasyfork.icu/scripts/11562-gm-config-8/code/GM_config%208+.js?version=66657 // @connect basetao.com // @connect superbuy.com // @connect wegobuy.com // @run-at document-end // @icon https://assets.geilicdn.com/fxxxx/favicon.ico // ==/UserScript== // Chinese SKU names for colors const chineseForColors = ['颜色', '彩色', '色', '色彩', '配色', '配色方案']; // Chinese SKU names for sizing const chineseForSizing = ['尺寸', '尺码', '型号尺寸', '大小', '浆液', '码数', '码']; // Chinese SKU names for model const chineseForModel = ['型号', '模型', '模型']; /** * Trims the input text and removes all inbetween spaces as well. * * @param string {string} */ const removeWhitespaces = (string) => string.trim().replace(/\s(?=\s)/g, ''); class Item { constructor() { // Build item information this._itemName = removeWhitespaces($('.item-title').text()); this._itemId = window.location.href.match(/[?&]itemId=(\d+)/i)[1]; this._itemImageUrl = $('img#skuPic').attr('src'); // Create dynamic items this._model = null; this._color = null; this._size = null; // Load dynamic items $('.sku-content .sku-row').each((key, value) => { const rowTitle = $(value).find('.row-title').text(); const selectedItem = $(value).find('.sku-item.selected'); // Check if this is model if (chineseForModel.includes(rowTitle)) { if (selectedItem.length === 0) { throw new Error('Model is missing'); } this._model = removeWhitespaces(selectedItem.text()); } // Check if this is color if (chineseForColors.includes(rowTitle)) { if (selectedItem.length === 0) { throw new Error('Color is missing'); } this._color = removeWhitespaces(selectedItem.text()); } // Check if this is size if (chineseForSizing.includes(rowTitle)) { if (selectedItem.length === 0) { throw new Error('Sizing is missing'); } this._size = removeWhitespaces(selectedItem.text()); } }); } get id() { return this._itemId; } get name() { return this._itemName; } get imageUrl() { return this._itemImageUrl; } get model() { return this._model; } get color() { return this._color; } get size() { return this._size; } } class Shop { constructor() { let $shop = $('.shop-toggle-header-name'); if ($shop.length !== 0) { this.shopName = removeWhitespaces($shop.text()); } $shop = $('.item-header-logo'); if ($shop.length !== 0) { this._shopUrl = $shop.attr('href').replace('//weidian.com', 'https://weidian.com'); this._shopId = this._shopUrl.replace(/^\D+/g, ''); this._shopName = removeWhitespaces($shop.text()); } $shop = $('.shop-name-str'); if ($shop.length !== 0) { this._shopUrl = $shop.parents('a').first().attr('href').replace('//weidian.com', 'https://weidian.com'); this._shopId = this._shopUrl.replace(/^\D+/g, ''); this._shopName = removeWhitespaces($shop.text()); } // If no shop name is defined, just set shop ID if ((this._shopName === null || this._shopName.length === 0) && this._shopId !== null) { this._shopName = this._shopId; } } get id() { return this._shopId; } get name() { return this._shopName; } get url() { return this._shopUrl; } } class Order { constructor() { // Build order price this._price = Number(removeWhitespaces($('.sku-cur-price').text()).replace(/(\D+)/, '')); // Decide on shipping (if we can't find any numbers, assume free) const postageMatches = removeWhitespaces($('.postage-block').text()).match(/([\d.]+)/); this._shipping = postageMatches !== null ? Number(postageMatches[0]) : 0; // Build shop information this.shop = new Shop(); // Build item information this.item = new Item(); } get price() { return this._price; } get shipping() { return this._shipping; } } /** * Creates a SKU toast, which is shown because of Weidians CSS * * @param toast {string} */ const Snackbar = function (toast) { const $toast = $(`<div class="sku-toast">${toast}</div>`).css('font-size', '20px'); // Append the toast to the body $('.sku-body').append($toast); // Set a timeout to remove it setTimeout(() => $toast.fadeOut('slow', () => { $toast.remove(); }), 2000); }; /** * Waits for an element satisfying selector to exist, then resolves promise with the element. * Useful for resolving race conditions. * * @param selector * @returns {Promise} */ const elementReady = function (selector) { return new Promise((resolve) => { const el = document.querySelector(selector); if (el) { resolve(el); } new MutationObserver((mutationRecords, observer) => { // Query for elements matching the specified selector Array.from(document.querySelectorAll(selector)).forEach((element) => { resolve(element); // Once we have resolved we don't need the observer anymore. observer.disconnect(); }); }).observe(document.documentElement, { childList: true, subtree: true, }); }); }; class BaseTao { /** * @returns {string} */ async getCsrf() { // Grab data required to add the order const data = await $.get('https://www.basetao.com/index/selfhelporder.html'); // Check if user is actually logged in if (data.indexOf('long time no operation ,please sign in again') !== -1) { throw new Error('You need to be logged in on BaseTao to use this extension (CSRF).'); } // Convert into jQuery object const $data = $(data); // Get the username const username = $data.find('#dropdownMenu1').text(); if (typeof username === 'undefined' || username == null || username === '') { throw new Error('You need to be logged in on BaseTao to use this extension (CSRF).'); } // Return CSRF return $data.find('input[name=csrf_test_name]').first().val(); } /** * @param order {Order} */ async send(order) { // Build some extra stuff we'll need const csrf = await this.getCsrf(); const modelNote = order.item.model !== null ? `Model: ${order.item.model}` : null; // Build the data we will send const purchaseData = { csrf_test_name: csrf, color: order.item.color, size: order.item.size, number: 1, pric: order.price, shipping: order.shipping, totalpric: order.price + 10, t_title: order.item.name, t_seller: order.shop.name, t_img: order.item.imageUrl, t_href: window.location.href, s_url: window.location.href, buyyourself: 1, note: modelNote, site: null, }; Logger.info('Sending order to BaseTao...', purchaseData); // Do the actual call await $.ajax({ url: 'https://www.basetao.com/index/Ajax_data/buyonecart', data: purchaseData, type: 'POST', headers: { origin: 'https://www.basetao.com', referer: 'https://www.basetao.com/index/selfhelporder.html', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36', 'x-requested-with': 'XMLHttpRequest', }, }).then((response) => { if (removeWhitespaces(response) !== '1') { Logger.error('Item could not be added', response); throw new Error('Item could not be added, make sure you are logged in'); } }).catch((err) => { Logger.error('An error happened when uploading the order', err); throw new Error('An error happened when adding the order'); }); } } class WeGoBuy { /** * @param host {string} */ constructor(host) { this.host = host; } /** * @param order {Order} * @returns {string|null} */ _buildDescription(order) { const descriptionParts = []; if (order.item.color !== null) descriptionParts.push(`Color: ${order.item.color}`); if (order.item.size !== null) descriptionParts.push(`Size: ${order.item.size}`); if (order.item.model !== null) descriptionParts.push(`Model: ${order.item.model}`); let description = null; if (descriptionParts.length !== 0) { description = descriptionParts.join(' / '); } return description; } /** * @param order {Order} */ _buildPurchaseData(order) { // Build the description const description = this._buildDescription(order); // Create the purchasing data return { type: 1, shopItems: [{ shopSource: 'NOCRAWLER', goodsItems: [{ count: 1, beginCount: 0, freight: order.shipping, freightServiceCharge: 0, goodsAddTime: Math.floor(Date.now() / 1000), goodsId: order.item.id, goodsPrifex: 'NOCRAWLER', goodsCode: `NOCRAWLER-${order.item.id}-${order.item.model}-${order.item.color}-${order.item.size}`, goodsLink: window.location.href, goodsName: order.item.name, goodsRemark: description, desc: description, picture: order.item.imageUrl, sku: order.item.imageUrl, platForm: 'pc', price: order.price, warehouseId: '1', }], }], }; } /** * @param order {Order} */ async send(order) { // Build the purchase data const purchaseData = this._buildPurchaseData(order); Logger.info('Sending order to WeGoBuy...', purchaseData); // Do the actual call await $.ajax({ url: `https://front.${this.host}/cart/add-cart`, data: JSON.stringify(purchaseData), type: 'POST', headers: { origin: `https://www.${this.host}`, referer: `https://www.${this.host}/`, 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36', }, }).then((response) => { if (response.state !== 0 || response.msg !== 'Success') { Logger.error('Item could not be added', response.msg); throw new Error('Item could not be added'); } }).catch((err) => { Logger.error('An error happened when uploading the order', err); throw new Error('An error happened when adding the order'); }); } } /** * @param agentSelection * @returns {*} */ const getAgent = (agentSelection) => { switch (agentSelection) { case 'basetao': return new BaseTao(); case 'wegobuy': return new WeGoBuy('wegobuy.com'); case 'superbuy': return new WeGoBuy('superbuy.com'); default: throw new Error(`Agent '${agentSelection}' is not implemented`); } }; // Inject config styling GM_addStyle('div.config-dialog.config-dialog-ani { z-index: 2147483647; }'); // Setup proper settings menu GM_config.init('Settings', { serverSection: { label: 'Select your agent', type: 'section', }, agentSelection: { label: 'Your agent', type: 'select', default: 'empty', options: { empty: 'Select your agent...', basetao: 'BaseTao', superbuy: 'SuperBuy', wegobuy: 'WeGoBuy', }, }, }); // Reload page if config changed GM_config.onclose = (saveFlag) => { if (saveFlag) { window.location.reload(); } }; // Register menu within GM GM_registerMenuCommand('Settings', GM_config.open); // eslint-disable-next-line func-names (async function () { // Setup the logger. Logger.useDefaults(); // Log the start of the script. Logger.info(`Starting extension '${GM_info.script.name}', version ${GM_info.script.version}`); // Setup GM_XHR $.ajaxSetup({ xhr() { return new GM_XHR(); } }); // Setup for when someone presses the buy button $('.footer-btn-container > span').add('.item-container > .sku-button').on('click', () => { // Force someone to select an agent if (GM_config.get('agentSelection') === 'empty') { alert('Please select what agent you use'); GM_config.open(); return; } // Attach button the the footer elementReady('.sku-footer').then((element) => { const $button = $(`<button>Add to ${GM_config.get('agentSelection')}</button>`) .css('background', '#f29800') .css('color', '#FFFFFF') .css('font-size', '15px') .css('text-align', 'center') .css('padding', '15px 0') .css('width', '100%') .css('height', '100%') .css('cursor', 'pointer'); $button.on('click', async () => { // Disable button to prevent double clicks and show clear message $button.attr('disabled', true).text('Processing...'); // Get the agent related to our config const agent = getAgent(GM_config.get('agentSelection')); // Try to build and send the order try { await agent.send(new Order()); } catch (err) { Logger.error(err); $button.attr('disabled', false).text(`Add to ${GM_config.get('agentSelection')}`); return Snackbar(err); } $button.attr('disabled', false).text(`Add to ${GM_config.get('agentSelection')}`); // Success, tell the user return Snackbar('Item has been added, be sure to double check it'); }); $(element).before($button); }); }); }());