Code: Select all
/*
* UtahCertify.js by Dave Bates
*
* For running by stake presidency. This script certifies everyone in your stake with the certification. After it
* is done go through the sex offender registries in your area and remove their certifications. If you are like our
* Stake there will be under 10.
*
* You must put your church username and password in the constants below. This allows it to retry when the church's
* site has a failure. If the script is interrupted in any way you will hopefully know about how far it got and you
* can resume by changing the START index below to that index in the member list.
*
* To use this script you need to:
* install node
* npm -i puppeteer puppeteer-core
* put this script on your computer somewhere with filename UtahCertify.js
* change the username and password to your church username and password
* then node ./ UtahCertify.js
*
* Enjoy the slowness of the church website. I tried to make this use multiple pages at once, but the website kept
* having trouble.
*/
const username = 'add your username';
const password = 'add your password';
const CERTIFICATION_NAME = 'Utah 2024 Youth Service Organizations';
const START = 0;
const MAX_RETRIES = 10;
const puppeteer = require('puppeteer');
(async () => {
var browser;
var page;
var profilePage;
var retries = 0;
var index = START;
async function openAndLogin() {
if (browser) {
try {
await browser.close();
} catch {
}
}
browser = await puppeteer.launch({
headless:false,
defaultViewport:null
});
page = (await browser.pages())[0];
profilePage = null;
await page.goto('https://lcr.churchofjesuschrist.org/mlt/records/member-list?lang=eng');
console.log("Logging in...");
await page.waitForSelector('input[data-form-type="username"]');
await page.click('input[data-form-type="username"]', {
clickCount:3
});
await page.type(
'input[data-form-type="username"]',
username
);
await page.click('#button-primary');
await page.waitForSelector('input[data-form-type="password"]');
await page.click('input[data-form-type="password"]', {
clickCount:3
});
await page.type(
'input[data-form-type="password"]',
password
);
await page.click('#button-primary');
await page.waitForFunction(() => {
const sel = document.querySelector('select.eden-form-part-input__control');
return sel && sel.options.length > 1;
}, {timeout:0});
console.log("Login complete.");
retries++;
}
await openAndLogin();
await page.select(
'select.eden-form-part-input__control:has(option[value="ALL"])',
'ALL'
);
await page.waitForFunction(() => {
return document.querySelectorAll(
'button[data-member-card-person-uuid]'
).length > 0;
}, {timeout:0});
// Collect UUIDs
const uuids = await page.$$eval(
'button[data-member-card-person-uuid]',
buttons => buttons.map(b => b.dataset.memberCardPersonUuid)
);
console.log(`Found ${uuids.length} members.`);
for (; index < uuids.length; index++) {
const uuid = uuids[index];
do {
if (retries > MAX_RETRIES)
{
console.log("Giving up after " + MAX_RETRIES + " retries for " + index);
retries = 0;
break;
}
console.log(`(${index + 1}/${uuids.length}) ${uuid}`);
try {
if (profilePage === null)
{
profilePage = await browser.newPage();
}
await profilePage.goto(
`https://lcr.churchofjesuschrist.org/mlt/records/member-profile/${uuid}?lang=eng`,
{
waitUntil:'networkidle2'
}
);
// Open Certifications tab
const which = await Promise.race([
profilePage.waitForSelector('#tab-certification').then(() => 'profile'),
profilePage.waitForSelector('input[data-form-type="password"]').then(() => 'login'),
new Promise(resolve =>
setTimeout(() => resolve('timeout'), 20000)
)
]);
if (which === 'login') {
console.log("Need to log in again.");
await openAndLogin();
continue;
}
await profilePage.evaluate(() => {
document.querySelector('#tab-certification').click();
});
// Wait for certification section
await profilePage.waitForSelector('#certification');
await profilePage.waitForNetworkIdle();
// Does this certification already exist?
const hasCertification = await profilePage.evaluate((certName) => {
return [...document.querySelectorAll('#certification td.eden-table-td')]
.some(td => td.textContent.includes(certName));
}, CERTIFICATION_NAME);
if (hasCertification) {
console.log(' Already has certification.');
retries = 0;
break;
}
// Click Add Certification
const addButton = await profilePage.waitForSelector(
'.add-certification',
{timeout:15000}
).catch(() => null);
if (!addButton) {
console.log(" Member cannot receive certifications. Skipping.");
retries = 0;
break;
}
console.log(' Adding certification...');
await addButton.evaluate(button => button.click());
// Wait for first text input
const input = await profilePage.waitForSelector('input[type="text"]');
await input.click({clickCount:3});
await input.type(CERTIFICATION_NAME);
// Click add button
await profilePage.evaluate(() => {
const addButton = [...document.querySelectorAll('button')]
.find(button => button.textContent.trim() === 'Add');
if (!addButton) {
throw new Error('Add button not found.');
}
addButton.click();
});
// Wait until the certification appears in the table
await profilePage.waitForFunction(
(certName) => {
return [...document.querySelectorAll('#certification td.eden-table-td')]
.some(td => td.textContent.includes(certName));
},
{
timeout:15000
},
CERTIFICATION_NAME
);
console.log(' Saved.');
retries = 0;
break;
} catch (err) {
console.error(` FAILED for ${uuid}`);
console.error(err);
// retry with new browser
await openAndLogin();
}
} while (true);
}
await profilePage.close();
console.log('Finished processing all members.');
})();