node - How to deploy script to Google Cloud functions - node.js

I need to deploy a node script to cloud functions. I've created a new function and pasted the code inside the editor, all the necessary dependencies are listed into the package.json and I've also created an .env file that will hold some informations.
The script initially was made to be executed from cli but now that I need to deploy it I'm not sure if it will work, it's a telegram bot script and I don't know if I need to create any action to be sure that it will be executed when the bot receive a commend from the users.
Anyone with more experience on gcloud can explain to me if my deployment is made correctly?Do I need to use cloud shell or I can just copy/paste my code into a new function and it will run?
This is the code I have at the moment, but it will be not executed in cloud functions
const process = require('process');
const puppeteer = require('puppeteer');
const TelegramBot = require('node-telegram-bot-api');
const bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { polling: true });
const webHookURL = 'https://example.com/'
let baseURL = 'https://example.com/events.php';
let chatID;
let username;
let eventsList = [];
puppeteer.launch({
headless: true
}).then( async (browser) => {
const page = await browser.newPage()
const navigationPromise = page.waitForNavigation()
await page.goto(baseURL, {
waitUntil: 'networkidle0'
})
await page.setViewport({ width: 1280, height: 607 })
await page.waitForSelector('.sc-VigVT > #qc-cmp2-ui > .qc-cmp2-footer > .qc-cmp2-summary-buttons > .ljEJIv')
await page.click('.sc-VigVT > #qc-cmp2-ui > .qc-cmp2-footer > .qc-cmp2-summary-buttons > .ljEJIv')
await navigationPromise
await page.waitForSelector('.container > .row > .results-item > .kode_ticket_wraper > .container')
await navigationPromise
await page.waitForSelector('.container > .kode_ticket_list > li')
eventsList = await page.$$eval('ul.kode_ticket_list > li', el => {
return el.map( e => {
let url = e.querySelector('.ticket_btn > a').getAttribute('href')
return {
type: e.querySelector('.kode_ticket_text > h6').innerHTML,
time: e.querySelector('.kode_ticket_text > p').innerHTML,
url: url,
name: new URL(url).pathname.split('-').join(' ').slice(6)
}
})
})
await browser.close()
})
// I need to set the webhhok, I've seen the documentations of the library I'm using but seems not working
bot.setWebHook(`${webHookURL}/bot${process.env.TELEGRAM_BOT_TOKEN}`)
bot.onText(/\/dirette/, (msg, match) => {
username = msg.chat.first_name
chatID = msg.chat.id
let response = `Hi ${username}! Here is a list of today live events:\n\n`
eventsList.map( item => {
response += `<b>${item.type}</b>\n`
response += `<b>${item.time}</b>\n`
response += `<a href='${item.url}'>${item.name.toUpperCase()}</a>\n\n`
})
bot.sendMessage(chatID, response, { parse_mode: 'HTML', disable_web_page_preview: true })
})

Related

Can't click link using puppeteer - Thingiverse

I'm trying to automate away downloading multiple files on thingiverse. I choose an object at random. But I'm having a hard time locating the link I need, clicking and then downloading. Has someone run into this before can I get some help?
I've tried several other variations.
import puppeteer from 'puppeteer';
async function main() {
const browser = await puppeteer.launch({
headless: true,
});
const page = await browser.newPage();
const response = await page.goto('https://www.thingiverse.com/thing:2033856/files');
const buttons = await page.$x(`//a[contains(text(), 'Download')]`);
if(buttons.length > 0){
console.log(buttons.length);
} else {
console.log('no buttons');
}
await wait(5000);
await browser.close();
return 'Finish';
}
async function wait(time: number) {
return new Promise(function (resolve) {
setTimeout(resolve, time);
});
}
function start() {
main()
.then((test) => console.log('DONE'))
.catch((reason) => console.log('Error: ', reason));
}
start();
Download Page
Code
I was able to get it to work.
The selector is: a[class^="ThingFile__download"]
Puppeteer is: const puppeteer = require('puppeteer-extra');
Before the await page.goto() I always recommend setting the viewport:
await page.setViewport({width: 1920, height: 720});
After that is set, change the await page.goto() to have a waitUntil option:
const response = await page.goto('https://www.thingiverse.com/thing:2033856/files', { waitUntil: 'networkidle0' }); // wait until page load
Next, this is a very important part. You have to do what is called waitForSelector() or waitForFunction().
I added both of these lines of code after the const response:
await page.waitForSelector('a[class^="ThingFile__download"]', {visible: true})
await page.waitForFunction("document.querySelector('a[class^=\"ThingFile__download\"]') && document.querySelector('a[class^=\"ThingFile__download\"]').clientHeight != 0");
Next, get the buttons. For my testing I just grabbed the button href.
const buttons = await page.$eval('a[class^="ThingFile__download"]', anchor => anchor.getAttribute('href'));
Lastly, do not check the .length of this variable. In this case we are just returning the href value which is a string. You will get a Promise of an ElementHandle when you try getting just the button:
const button = await page.$('a[class^="ThingFile__download"]');
console.log(button)
if (button) { ... }
Now if you change that page.$ to be page.$$, you will be getting a Promise of an Array<ElementHandle>, and will be able to use .length there.
const buttonsAll = await page.$$('a[class^="ThingFile__download"]');
console.log(buttonsAll)
if (buttons.length > 0) { ... }
Hopefully this helps, and if you can't figure it out I can post my full source later if I have time to make it look better.

How to download pdf file that opens in new tab with puppeteer?

I am trying to download invoice from website using puppeteer, I just started to learn puppeteer. I am using node to create and execute the code. I have managed to login and navigate to the invoice page, but it opens in new tab, so, code is not detecting it since its not the active tab. This is the code I used:
const puppeteer = require('puppeteer')
const SECRET_EMAIL = 'emailid'
const SECRET_PASSWORD = 'password'
const main = async () => {
const browser = await puppeteer.launch({
headless: false,
})
const page = await browser.newPage()
await page.goto('https://my.apify.com/sign-in', { waitUntil: 'networkidle2' })
await page.waitForSelector('div.sign_shared__SignForm-sc-1jf30gt-2.kFKpB')
await page.type('input#email', SECRET_EMAIL)
await page.type('input#password', SECRET_PASSWORD)
await page.click('input[type="submit"]')
await page.waitForSelector('#logged-user')
await page.goto('https://my.apify.com/billing#/invoices', { waitUntil: 'networkidle2' })
await page.waitForSelector('#reactive-table-1')
await page.click('#reactive-table-1 > tbody > tr:nth-child(1) > td.number > a')
const newPagePromise = new Promise(x => browser.once('targetcreated', target => x(target.page())))
const page2 = await newPagePromise
await page2.bringToFront()
await page2.screenshot({ path: 'apify1.png' })
//await browser.close()
}
main()
In the above code I am just trying to take screenshot. Can anyone help me?
Here is an example of a work-around for the chromium issue mentioned in the comments above. Adapt to fit your specific needs and use-case. Basically, you need to capture the new page (target) and then do whatever you need to do to download the file, possibly pass it as a buffer to Node as per the example below if no other means work for you (including a direct request to the download location via fetch or ideally some request library on the back-end)
const [PDF_page] = await Promise.all([
browser
.waitForTarget(target => target.url().includes('my.apify.com/account/invoices/' && target).then(target => target.page()),
ATT_page.click('#reactive-table-1 > tbody > tr:nth-child(1) > td.number > a'),
]);
const asyncRes = PDF_page.waitForResponse(response =>
response
.request()
.url()
.includes('my.apify.com/account/invoices'));
await PDF_page.reload();
const res = await asyncRes;
const url = res.url();
const headers = res.headers();
if (!headers['content-type'].includes('application/pdf')) {
await PDF_page.close();
return null;
}
const options = {
// target request options
};
const pdfAb = await PDF_page.evaluate(
async (url, options) => {
function bufferToBase64(buffer) {
return btoa(
new Uint8Array(buffer).reduce((data, byte) => {
return data + String.fromCharCode(byte);
}, ''),
);
}
return await fetch(url, options)
.then(response => response.arrayBuffer())
.then(arrayBuffer => bufferToBase64(arrayBuffer));
},
url,
options,
);
const pdf = Buffer.from(pdfAb, 'base64');
await PDF_page.close();

Why am I not able to navigate through iFrames using Apify/Puppeteer?

I'm trying to manipulate forms of sites w/ iFrames in it using Puppeteer. I tried different ways to reach a specific iFrame, or even to count iFrames in a website, with no success.
Why isn't Puppeteer's object recognizing the iFrames / child frames of the page I'm trying to navigate through?
It's happening with other pages as well, such as https://www.veiculos.itau.com.br/simulacao
const Apify = require('apify');
const sleep = require('sleep-promise');
Apify.main(async () => {
// Launch the web browser.
const browser = await Apify.launchPuppeteer();
// Create and navigate new page
console.log('Open target page');
const page = await browser.newPage();
await page.goto('https://www.credlineitau.com.br/');
await sleep(15 * 1000);
for (const frame in page.mainFrame().childFrames()) {
console.log('test');
}
await browser.close();
});
Perhaps you'll find some helpful inspiration below.
const waitForIframeContent = async (page, frameSelector, contentSelector) => {
await page.waitForFunction((frameSelector, contentSelector) => {
const frame = document.querySelector(frameSelector);
const node = frame.contentDocument.querySelector(contentSelector);
return node && node.innerText;
}, {
timeout: TIMEOUTS.ten,
}, frameSelector, contentSelector);
};
const $frame = await waitForSelector(page, SELECTORS.frame.iframeNode).catch(() => null);
if ($frame) {
const frame = page.frames().find(frame => frame.name() === 'content-iframe');
const $cancelStatus = await waitForSelector(frame, SELECTORS.frame.membership.cancelStatus).catch(() => null);
await waitForIframeContent(page, SELECTORS.frame.iframeNode, SELECTORS.frame.membership.cancelStatus);
}
Give it a shot.

Linkedin Like bot

I have a bot that logs in as different users and likes the latest post by a company. 3 weeks ago it stopped working. I didn't notice because on my end when I ran it, everything seemed fine. But if you go to the Linkedin pages the articles have not been liked. It was working perfectly up until that point.
I'm convinced it has something to do with Linkedin changing something up.
Here is the main code :
const config = require('./config')
const logger = require('./logger')
const { all } = require('./options')
const { sanitize } = require('./util')
module.exports = async (page, company) => {
logger.info(`Go to ${company} page...`)
await page.goto(`${config.get('url')}/company/${company}/`)
logger.info('Waiting for new articles...')
const feed = await page.waitForSelector('#organization-feed')
await feed.hover()
let article
while (
article = await page.waitForSelector('#organization-feed .feed-shared-update-v2').catch(() => null)
) {
await article.hover()
const button = await article.$('.feed-shared-social-action-bar [aria-label="Like"]')
if(button === null)
{
await page.evaluate(node => node.remove(), article);
await page.waitFor(config.get('sleep'))
await page.evaluate(() => window.scrollBy({ top: -100 }))
await page.waitFor(100)
await page.evaluate(() => window.scrollBy({ top: 1000 }))
continue;
}
await button.hover()
const liked = await page.evaluate(node => node.getAttribute('aria-pressed') === 'true', button)
const text = await page.evaluate(node => node.querySelector('.feed-shared-text').innerText, article)
if (!liked) {
logger.info(`Like → ${sanitize(text)}...`)
await button.click({ delay: 20 })
} else if (!all) {
break
}
await page.evaluate(node => node.remove(), article)
await page.waitFor(config.get('sleep'))
await page.evaluate(() => window.scrollBy({ top: -115 }))
await page.waitFor(100)
await page.evaluate(() => window.scrollBy({ top: 1000 }))
}
}
When I view what's happening, the bot opens the browser, logs in as the user, goes to the company page and starts scrolling the articles. It used to click the like button at this point, but it seems like it's missing the like button now.
Thanks in advance!
Linkedin changed their aria labels from [aria-label="Like"] to [aria-label="Like company-names' post"] Just had to update my code to that

Running Puppeteer from GitLab

I am trying to execute the below script from GitLab CI/CD.
This puppeteer script is in a .js which is getting called from the GitLab repositories .gitlab-ci.yml file.
The purpose of the script is to navigate to INITIAL_PAGE_URL, login and navigate to the HOME_PAGE. The sign-in-button has a click method which on successful login navigates to the HOME_PAGE.
The script runs perfectly when running from the local system but when running from GitLab:
no error is shown
console.log("logged in") is executed and prints the message.
However, it does not navigate to the next page and page.url() still shows the INITIAL_PAGE_URL.
Any suggestions?
const HOME_PAGE = "https://www.abcd.com/home"
const SIGN_IN_FORM = "#frmSignIn";
const USERNAME_SELECTOR = 'input#EmailAddress';
const PASSWORD_SELECTOR = 'input#Password';
const LOGIN_BUTTON_SELECTOR = '#sign-in-button';
const SECRET_EMAIL = 'username';
const SECRET_PASSWORD = 'password';
const CHROME_EXE_PATH =
process.env.CHROME_EXE_PATH === "" ? "" : process.env.CHROME_EXE_PATH || "/usr/bin/chromium-browser";
const puppeteer = require('puppeteer')
const main = async () => {
const browser = await puppeteer.launch({
headless: true,
executablePath: CHROME_EXE_PATH,
args: ['--no-sandbox', '--disable-setuid-sandbox']
})
console.log("browser loaded")
const page = await browser.newPage()
await page.setViewport({width: 1366, height: 768})
//Script for login page - start
console.log("Navigating to initial page")
await page.goto(INITIAL_PAGE_URL, { waitUntil: 'networkidle2' })
await page.waitForSelector('#frmSignIn')
await page.type('input#EmailAddress', SECRET_EMAIL)
await page.type('input#Password', SECRET_PASSWORD)
await page.click(LOGIN_BUTTON_SELECTOR)
console.log("logged in")
console.log(page.url());
await browser.close();
}
main()

Resources