getSpecialsSSG

Create a ShoeBot !specials list

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         getSpecialsSSG
// @namespace    none
// @version      2021.09.26.2340
// @description  Create a ShoeBot !specials list
// @author       MagentaRV
// @match        http://www.ddo.com/en/news/ddo-chronicle-issue-*
// @match        https://www.ddo.com/en/news/ddo-chronicle-issue-*
// @match        http://www.lotro.com/en/game/articles/lotro-beacon-issue-*
// @match        https://www.lotro.com/en/game/articles/lotro-beacon-issue-*
// @grant        none
// ==/UserScript==

( function() {

    // objOptions = {                   trim: 'false/start/end/true',
    //     nbsp: 'false/replacement/true', b: 'false/replacement/true',
    //     i: 'false/replacement/true',    u: 'false/replacement/true',
    //     span: 'false/replacement/true', a: 'false/replacement/true' }
    function parseHTMLtoDiscord( htmlString, objOptions = {} ) {
        var rxpA = new RegExp( '<a href="((?:https?|mailto):(?:\/\/)?(?:[^"]*?))"(?:.*?)>(.*?)<\/a>', 'g' );
        var rxpB = new RegExp( '<\/?(b|strong)(?:.*?)>', 'gi' );
        var rxpI = new RegExp( '<\/?(i|emphasis)(?:.*?)>', 'gi' );
        var rxpSpan = new RegExp( '<\/?span(?:.*?)>', 'gi' );
        var rxpU = new RegExp( '<\/?u(?:.*?)>', 'gi' );
        switch ( objOptions.trim ) {// Options are false to skip, start, end, or -default- both
            case false: break;
            case 'start': htmlString = htmlString.trimStart(); break;
            case 'end': htmlString = htmlString.trimEnd(); break;
            default: htmlString = htmlString.trim();
        }
        switch ( objOptions.nbsp ) {// Options are false to skip, text to replace &nbsp; with, or -default- a plain space
            case false: break;
            default: htmlString = htmlString.replace( /&nbsp;/g, ( typeof objOptions.nbsp === 'string' ? objOptions.nbsp : ' ' ) );
        }
        switch ( objOptions.b ) {// Options are false to skip, text to replace bold tag with, or -default- **
            case false: break;
            default: htmlString = htmlString.replace( rxpB, ( typeof objOptions.b === 'string' ? objOptions.b : '**' ) );
        }
        switch ( objOptions.i ) {// Options are false to skip, text to replace italic tag with, or -default- *
            case false: break;
            default: htmlString = htmlString.replace( rxpI, ( typeof objOptions.i === 'string' ? objOptions.i : '*' ) );
        }
        switch ( objOptions.u ) {// Options are false to skip, text to replace underline tag with, or -default- __
            case false: break;
            default: htmlString = htmlString.replace( rxpU, ( typeof objOptions.u === 'string' ? objOptions.u : '__' ) );
        }
        switch ( objOptions.span ) {// Options are false to skip, text to replace span tag with, or -default- strip the span
            case false: break;
            default: htmlString = htmlString.replace( rxpSpan, ( typeof objOptions.span === 'string' ? objOptions.span : '' ) );
        }
        switch ( objOptions.a ) {// Options are false to skip, text to replace anchor tag with, or -default- [link text](<link href>)
            case false: break;
            default: htmlString = htmlString.replace( rxpA, ( typeof objOptions.a === 'string' ? objOptions.a : '[$2](<$1>)' ) );
        }
        return htmlString;
    }
// Define which game we're parsing data for
    var strGame = document.URL.replace( /https?:\/\/(?:www\.)?/i, '' );
    strGame = strGame.slice( 0, strGame.indexOf( '.' ) ).toLowerCase();

// Initialize variables
    var now = new Date( document.querySelector( 'time.submitted.pretty' ).title ),
        today = now,
        published = now,
        aWeekFromNow = new Date( now.getFullYear(), String( now.getMonth() ).padStart( 2, '0' ), String( now.getDate() + 7 ).padStart( 2, '0' ) ),
        author = '',
        sSotW = document.querySelectorAll( 'p.rtecenter a img' ),
        IMGsS = sSotW[ sSotW.length - 1 ].src,
        SSdesc = parseHTMLtoDiscord( sSotW[ sSotW.length - 1 ].closest( 'p' ).nextElementSibling.childNodes[ 0 ].innerHTML, { trim: false } ),
        InTheNews = [],
        freebieLinks = {},
        rowFreebie = [],
        arrSalesFree = [],
        strSaleName = '',
        strSalePercent = '',
        arrSalesVal = [],
        arrSaleDates = [],
        objSaleDates = {},
        arrBonusDays = [],
        objBonusDays = {},
        arrBonusDaysVal = [],
        strWeeklyCoupon = '',
        arrWeeklyCoupon = [],
        arrFreebieDates = [],
        objFreebieDates = {},
        strFreebieCode = '',
        strFreebieName = '',
        strFreebieLink = '',
        intFreebieQuantity = 0,
        strFreebieWeek = '';
    if ( strGame === 'ddo' ) {
        var chronicleSections = [];
// Create an object of possible freebies links
        freebieLinks = {
            'unknown': 'https://ddowiki.com/index.php?title=Special:Search&go=Go&search=',
        };
    } else {
        var beaconSections = [];
// Create an object of possible freebies links
        freebieLinks = {
            'unknown': 'https://lotro-wiki.com/index.php?title=Special:Search&go=Go&search=',
            '+5% Attack Damage x25 (90 minutes)': 'https://lotro-wiki.com/index.php/LOTRO_Store#Damage',
            '+5 Hope Boost': 'https://lotro-wiki.com/index.php/LOTRO_Store#Hope',
            '+20% Mount Speed (90 minute) boost': 'https://lotro-wiki.com/index.php/Item:%2B20%25_Mount_Speed_%2890_Min%29',
            '100% XP Boost (1 hour)': 'https://lotro-wiki.com/index.php/Item:Tome_of_Extraordinary_Experience_%28100%29',
//            '100% Mark Acquisition (90 minute) boost': '',// NO LINK
            'Dye: Crimson': 'https://lotro-wiki.com/index.php/Item:Crimson_Dye',
            'Dye: Ered Luin Blue': 'https://lotro-wiki.com/index.php/Item:Ered_Luin_Blue_Dye',
            'Landscape Soldier Token': 'https://lotro-wiki.com/index.php/Item:Landscape_Soldier_Token',
            'Morale Bubble Potion': 'https://lotro-wiki.com/index.php/Item:Morale_Bubble_Potion',
            'Rejuvenation Potion x5': 'https://lotro-wiki.com/index.php/Item:Rejuvenation_Potion',
            'Skill and Slayer Deed Boost': 'https://lotro-wiki.com/index.php/Item:Slayer_and_Skill_Deed_Boost_%2890_min%29',
            'Slayer Deed Accelerator (60 minutes)': '',
            'Universal Ingredient Pack': 'https://lotro-wiki.com/index.php/Item:Universal_Ingredient_Pack',
            'Universal Solvent': 'https://lotro-wiki.com/index.php/Item:Universal_Solvent'
        }
    }

    function parseDates( dateString ) {
        let startDate = dateString.slice( ( dateString.indexOf( ', ' ) === -1 ? ( dateString.indexOf( 'Now' ) === -1 ? dateString.indexOf( ' through ' ) : 0 ) : dateString.indexOf( ', ' ) + 2 ), dateString.indexOf( ' through ' ) ).trim();
        if ( startDate.match( /now/i ) ) { startDate = startDate.toLowerCase(); }
        if ( startDate.indexOf( 'starting ' ) !== -1 ) {
            let arrDate = startDate.match( /starting (\d?\d):\d\d ([AP])M Eastern \((-[45]) GMT\) on (January|February|March|April|May|June|July|August|September|October|November|December) (\d?\d)(?:st|nd|rd|th) and running/ );
            startDate = arrDate[ 4 ] + ' ' + arrDate[ 5 ].padStart( 2, '0' ) + ', ' + ( new Date( now ) ).getFullYear() + ' ' + ( ( parseInt( arrDate[ 1 ] ) + ( arrDate[ 2 ] === 'A' ? 0 : 12 ) ) % 24 ) + ':00:00';
            startDate = ( new Date( startDate ) );
        }
        startDate = ( startDate === 'now' || startDate === '' ? now : ( startDate instanceof Date && !isNaN( startDate.valueOf() ) ? startDate.valueOf() : startDate + ', ' + ( new Date() ).getFullYear() ) );
        let endDate = dateString.match( /(January|February|March|April|May|June|July|August|September|October|November|December) (\d?\d)(?:st|nd|rd|th)/ );
        if ( !endDate ) { endDate = aWeekFromNow.valueOf(); }
        else { endDate = endDate[ 1 ] + ' ' + endDate[ 2 ].replace( /(st|nd|rd|th)[\.!:]/, '' ).padStart( 2, '0' ) + ', ' + ( new Date() ).getFullYear(); }
        return [ new Date( startDate ), new Date( endDate ) ];
    }

// Populate variables depending on which game
    if ( strGame === 'ddo' ) {
        chronicleSections = document.getElementsByTagName( 'h1' );
        InTheNews = chronicleSections[ ( chronicleSections.length - 1 ) ].nextElementSibling.nextElementSibling.children;
    } else {
        author = document.querySelector( 'span.news.author' ).innerText.replace( 'By ', '' );
        beaconSections = document.getElementsByTagName( 'h3' );
        InTheNews = beaconSections[ ( beaconSections.length - 1 ) ].nextSibling.nextSibling.children;
    }

        arrBonusDays = Array.from( InTheNews ).splice( 0, ( Array.from( InTheNews ).length - 2 ) );
        arrBonusDays.forEach( ( bonusDay, i ) => {
            bonusDay = parseHTMLtoDiscord( bonusDay.children[ 0 ].innerHTML );
            let strMatchStart = bonusDay.match( new RegExp( '(Bonus Days brings? you )' ) );
            strMatchStart = ( strMatchStart ? strMatchStart[ 0 ] : '' );
            let intBonusDayStart = bonusDay.indexOf( strMatchStart );
            let thisBD = bonusDay.slice( ( intBonusDayStart === -1 ? 0 : intBonusDayStart ), bonusDay.indexOf( ', ' ) ).replace( strMatchStart, '' );
            if ( thisBD.indexOf( ' through ' ) !== -1 ) { thisBD = thisBD.slice( 0, thisBD.indexOf( ' through ' ) ); }
            if ( bonusDay.lastIndexOf( '!' ) === bonusDay.length ) { thisBD += '!'; }
            else { thisBD += bonusDay.slice( bonusDay.lastIndexOf( '!' ) ); }
            let arrBonusDates = parseDates( bonusDay );
            let objBonusDates = { start: arrBonusDates[ 0 ], end: arrBonusDates[ 1 ] };
            if ( intBonusDayStart !== -1 ) { objBonusDays[ i ] = { name: thisBD, dates: objBonusDates }; }
        } );
        arrSalesFree = InTheNews[ ( InTheNews.length - 2 ) ];
        strSaleName = parseHTMLtoDiscord( arrSalesFree.children[ 0 ].children[ 0 ].innerHTML );
        strSalePercent = arrSalesFree.children[ 0 ].innerText.replace( strSaleName, '' ).match( /(\d+%)/ )[ 1 ];
        arrSalesVal = Array.from( arrSalesFree.children[ 1 ].children );
        arrSalesVal.forEach( ( item, i ) => { arrSalesVal[ i ] = item.innerText; } );
        let tmpSale = arrSalesVal.pop();
        arrSaleDates = parseDates( tmpSale );
        objSaleDates = { start: arrSaleDates[ 0 ], end: arrSaleDates[ 1 ] };
        rowFreebie = parseHTMLtoDiscord( InTheNews[ ( InTheNews.length - 1 ) ].children[ 0 ].innerHTML );
        strFreebieName = rowFreebie.replace( /\*\*/g, '' ).trim().slice( 0, strFreebieName.indexOf( ' with the Coupon Code' ) );
        strFreebieName = strFreebieName.slice( strFreebieName.indexOf( ' gets you a free ' ) ).replace( ' gets you a free ', '' );
        intFreebieQuantity = ( strFreebieName.match( / x([\d]+)/ ) ? parseInt( strFreebieName.match( / x([\d]+)/ )[ 1 ] ) : 1 );
        strFreebieLink = ( Object.keys( freebieLinks ).indexOf( strFreebieName.replace( / x([\d]+)/, '' ) ) === -1 ? freebieLinks.unknown + encodeURI( strFreebieName.replace( ' x' + intFreebieQuantity, '' ) ) : freebieLinks[ strFreebieName.replace( / x([\d]+)/, '' ) ] );
        strFreebieCode = '**' + rowFreebie.slice( rowFreebie.indexOf( 'Coupon Code&nbsp;' ), rowFreebie.indexOf( ', now through ' ) ).replace( 'Coupon Code&nbsp;', '' ) + '**';
        if ( strFreebieCode === '****' ) { strFreebieCode = rowFreebie.slice( rowFreebie.indexOf( 'Coupon Code ' ), rowFreebie.indexOf( ', now through ' ) ).replace( 'Coupon Code ', '' ); }
        arrFreebieDates = parseDates( rowFreebie );
        objFreebieDates = { start: arrFreebieDates[ 0 ], end: arrFreebieDates[ 1 ] };
        strFreebieWeek = objFreebieDates.start.toLocaleDateString( 'en-US', { month: 'short', day: '2-digit' } ) + ' - ' + objFreebieDates.end.toLocaleDateString( 'en-US', { month: 'short', day: '2-digit' } );

// Hand validate all components and make any needed adjustments
    /* Do the thing. */

// Create our object
    var objSpecials = {
        URI: document.URL,
        image: document.getElementsByClassName( 'news content' )[ 0 ].querySelector( 'img' ).getAttribute( 'src' ).replace( 'https://', 'http://' ),
        author: ( strGame === 'ddo' ? '*unknown*' : author ),
        pubDate: published,
        sSotW: {
            img: IMGsS,
            desc: SSdesc
        },
        bonusDays: objBonusDays,
        freebie: {
            dates: objFreebieDates,
            code: strFreebieCode,
            item: {
                name: strFreebieName,
                link: strFreebieLink
            },
            quantity: intFreebieQuantity,
            week: strFreebieWeek
        },
        sales: {
            name: strSaleName,
            dates: objSaleDates,
            percent: strSalePercent,
            value: arrSalesVal
        }
    }

    console.log( '%o', objSpecials );
    console.info( '%s', JSON.stringify( objSpecials ) );
} )();