// Interencheres.js 'use strict'; const {Scraper} = require('../Scraper'); const DrouotData = require('./DrouotData'); class Drouot extends Scraper { constructor(Url) { super(Url); this.platformData = new DrouotData(); this.platformData.getUrlInfo(Url).then((data) => { if(data.lotID == 0 && data.saleID == 0){ throw new Error('Invalid URL'); } }); this._Name = 'drouot' this._PAGE_MAIN = "https://drouot.com/fr/" this._PAGE_LOGIN = "https://auth.drouot.com/login" this._USER = "jp.ranu@cogip.de" this._PWD = "LYPYRKDUsSMH5BaWQxvH#" this._PATH_SESSION_FILE = ".session/session_drouot.json" this._PATH_PROTOBUF_FILE = "./AuctionServices/Scraper/Drouot/drouot.proto" this._BROWSER_TOOL = "playwrightBrowser" } getPictures = async ({ page, data}) => { const PictList = await this.platformData.getPictures(page, this.Url); console.log('PictList : '+PictList) return PictList } getLotInfos = async ({ page, data}) => { console.log("getLotInfos "+this._Name+": "+this.Url) // Navigate the page to a URL await page.goto(this.Url); let idLot = await this.platformData.getLotID(this.Url); console.log('idLot : '+idLot) // ## LotNumber let lotNumber = await this.platformData.getLotNumber(page); console.log('lotNumber : '+lotNumber) // ## Title let title = await this.platformData.getLotTitle(page); console.log('title : '+title) // ## Estimate let {EstimateLow, EstimateHigh} = await this.platformData.getEstimate(page); console.log('EstimateLow : '+EstimateLow) // ## Description let Description = await this.platformData.getDescription(page); //console.log('Description : '+Description) // ## Fees let {feesText, fees} = await this.platformData.getFees(page); console.log('feesText : '+feesText) console.log('fees : '+fees) // ################ // ### SALE let {id_sale, urlSale} = await this.platformData.getSaleID(page); console.log('SellNumber : '+id_sale) console.log('url : '+urlSale) let LotInfos = { idPlatform: idLot, platform : this._Name, url: this.Url, title: title, lotNumber: lotNumber, EstimateLow: EstimateLow, EstimateHigh: EstimateHigh, Description: Description, feesText: feesText, fees: fees, saleInfo: { idSale: id_sale, url: urlSale } } console.log('LotInfos : '+LotInfos) return LotInfos } getSaleInfos = async ({ page, data}) => { console.log("getSaleInfos "+this._Name+": "+this.Url) // Navigate the page to a URL await page.goto(this.Url); let {saleID, urlSale} = await this.platformData.getUrlInfo(this.Url); console.log('saleID : '+saleID) console.log('urlSale : '+urlSale) // ## Title let title = await this.platformData.getSaleTitle(page); console.log('title : '+title) // ## Date let date = await this.platformData.getSaleDate(page); console.log('date : '+date) // ## Location let location = await this.platformData.getSaleLocation(page); console.log('location : '+location) // ## SaleHouseName let saleHouseName = await this.platformData.getSaleHouseName(page); console.log('saleHouseName : '+saleHouseName) // ## Status // ready : ready to be followed // following : sale is followed by the AuctionAgent // askStop : sale is followed by the AuctionAgent and the user ask to stop following // pause : the Sale is stopped by the Auction House and ready to restart // end : the Sale is ended let status = 'ready' let SaleInfo = { idPlatform: saleID, platform : this._Name, url: urlSale, title: title, date: date, location: location, saleHouseName: saleHouseName, status: status } console.log('SaleInfo : ', JSON.stringify(SaleInfo, null, 2)); return SaleInfo } getLotList = async ({ page, data}) => { console.log("getLotList "+this._Name+": "+this.Url) // Navigate the page to a URL await page.goto(this.Url); const LotList = await this.platformData.getLotList(page); console.log('LotList : '+LotList) return LotList } // ### Playwright // ########################################################################### async CheckCookieDialog(page) { console.log("-- CheckCookieDialog --") return new Promise(async (resolve, reject) => { //let XpathCookieButton = "xpath=//button[contains(@class, 'rgpd') and contains(text(), 'Tout refuser')" // const CookieButton = await page.$$(XpathCookieButton); const CookieButton = await page.$('//button[contains(@class, "rgpd") and contains(text(), "Tout refuser")]'); //console.log(CookieButton) if (CookieButton && await CookieButton.isVisible()) { console.log("-- Click on CookieButton --") await CookieButton.click(); } resolve(true) }) } async CheckAndConnect(page) { return new Promise(async (resolve, reject) => { await page.goto(this._PAGE_MAIN); await this.CheckCookieDialog(page); console.log("-- CheckConnexion --") //get the Connexion button let XpathConnexion = "//div[contains(@class, 'btn') and contains(@class, 'ghost') and contains(text(), 'Connexion')]" const Connexion = await page.$$(XpathConnexion); // if Connection button found => Login if (Connexion.length > 0) { console.log("-- Login --") await page.goto(this._PAGE_LOGIN); //get the Email field //console.log("-- get Email Input --") //await page.type('#email', this._USER); await page.fill('#email', this._USER); //console.log("-- get password Input --") //wait page.type("#password", this._PWD); await page.fill('#password', this._PWD); //await page.screenshot({ path: 'screenshot.png', fullPage: true }); //console.log("-- get ConnexionButton --") //const [ConnexionButton] = await page.$x("//button[contains(text(), 'Connexion')]"); //await ConnexionButton.evaluate(b => b.click()); await page.click("//button[contains(text(), 'Connexion')]"); //console.log("-- Login wait --") await page.waitForTimeout(1000); //resolve(page) const ConnexionOK = await page.$$("//button[contains(text(), 'Continuer en tant que')]"); //console.log(ConnexionOK) if (ConnexionOK.length > 0) { //await ConnexionOK.screenshot({ path: 'screenshot.png', fullPage: true }); //await ConnexionOK.evaluate(b => b.click()); await ConnexionOK[0].click(); await page.waitForTimeout(1000); await this._saveSession(page) console.log("-- Connection OK --") resolve(page) } else { console.error("-- !!!! Connection ERROR !!!! --"); reject() } // Allready connected } else { console.log("-- Allready connected --") resolve(page) } }) } Live = async (browser) => { const context = await this._getContext(browser); console.log("Live "+this._Name+": "+this.Url) let page = await context.newPage(); page = await this.CheckAndConnect(page); //init Protobuf Decoder const protobuf = require("protobufjs"); const root = await protobuf.load(this._PATH_PROTOBUF_FILE); const LiveBiddingMessage = root.lookupType("drouot.LiveBiddingMessage"); const listener = async (params) => { let buffer = params.payload; const err = LiveBiddingMessage.verify(buffer); if (err) { throw err; } const message = LiveBiddingMessage.decode(buffer); const BideMessage = LiveBiddingMessage.toObject(message, {enums: String}); let DataLot = null if(BideMessage.vente && BideMessage.vente.lot && BideMessage.vente.lot.lotId != undefined){ DataLot = await this.platformData.getLiveDataLot(BideMessage.vente.lot.lotId); } //console.log('BideMessage Type: '+BideMessage.type+' Lot: '+BideMessage.vente.lot.lotId); switch (BideMessage.type) { case 'PING': break; case 'PONG': break; case 'INIT': console.log('INIT'); break; case 'CLOSE': console.log('CLOSE'); break; case 'BID': console.log('BID'); console.log('Lot: '+BideMessage.vente.lot.lotId+' Amount: '+BideMessage.vente.bid.amount); await this.JucundusBid( BideMessage.vente.lot.lotId, Date.now(), BideMessage.vente.bid.amount, BideMessage.vente.bid.type ); break; case 'SELECT_LOT': console.log('SELECT_LOT'); console.log('Lot: '+BideMessage.vente.lot.lotId); //console.log('DataLot : '+DataLot) await this.JucunduNextItem( BideMessage.vente.venteId, Date.now(), BideMessage.vente.lot.lotId, DataLot.num, DataLot.titre, DataLot.description, DataLot.estimationBasse, DataLot.estimationHaute, DataLot ); break; case 'STARTING_PRICE': console.log('STARTING_PRICE'); await this.JucunduNextItem( BideMessage.vente.venteId, Date.now(), BideMessage.vente.lot.lotId, DataLot.num, DataLot.titre, DataLot.description, DataLot.estimationBasse, DataLot.estimationHaute, DataLot ); break; case 'INCREMENT': console.log('INCREMENT'); break; case 'TENDERING': console.log('TENDERING'); console.log('Lot: '+BideMessage.vente.lot.lotId+' Amount: '+BideMessage.vente.bid.amount); await this.JucunduAuctionedItem( BideMessage.vente.lot.lotId, Date.now(), BideMessage.vente.bid.amount, BideMessage.vente.lot.state == 'ADJ' ? true : false, BideMessage.vente.bid.type ); break; case 'GENERIC_MESSAGE': console.log('GENERIC_MESSAGE'); break; case 'CREATE_LOT': console.log('CREATE_LOT'); break; case 'NOT_INTERESTED': break; case 'INTERESTED': break; case 'ACTIVITY': break; case 'NEW_CLIENT': console.log('NEW_CLIENT'); break; case 'CLOSED_CLIENT': break; case 'KILL': break; case 'MULTILOT_REQUEST': break; case 'MULTILOT_RESPONSE': break; case 'SYNC_PAYMENT_LIMIT': break; case 'SWITCH_FREE_BID_AUTHORIZE': break; case 'REFRESH_CLIENTS_COUNT': console.log('REFRESH_CLIENTS_COUNT'); break; default: //console.log('Unknown type:', BideMessage); } }; // create the live URL let {saleID} = await this.platformData.getUrlInfo(this.Url); let UrlLive = this._PAGE_MAIN + "live/bidlive/" + saleID; let CheckAskStop = null; let Socket = null; // Stop the Live const StopLive = async (params) => { clearInterval(CheckAskStop); page.close() browser.close() } try{ console.log('UrlLive : '+UrlLive) await page.goto(UrlLive, { waitUntil: 'domcontentloaded' }); // intercept Lots Data await page.route('https://api.drouot.com/drouot/gingolem/api/live/venteInfos/'+saleID+'?lang=fr', async route => { console.log('GetLiveData') const response = await route.fetch(); const LotData = await response.json(); this.platformData.setLiveData(LotData.data); route.continue(); }); page.on('websocket', ws => { Socket = ws; Socket.on('framereceived', listener); console.log('Websocket connected'); }); console.log('UrlLive : reload') await page.reload(); // check if stop was asked CheckAskStop = setInterval(async () => { this.JucundusCheckStop() .then(AskStop => { if(AskStop){ StopLive() } }) }, 10000); // 10000 milliseconds = 10 seconds }catch(e){ console.log('Error : '+e) throw new Error('Error: '+e) } } }; module.exports = Drouot