random-stuff/auto-wayfarer/autoWayfarerGemini.user.js

1290 lines
61 KiB
JavaScript

// ==UserScript==
// @name Auto Wayfarer
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Auto Wayfarer with Google Gemini Pro
// @author cyi1341
// @match https://wayfarer.nianticlabs.com/new/review
// @grant none
// ==/UserScript==
(async function() {
'use strict';
let isSubmitted = true;
// Reload the page every 20 minutes (1200000 milliseconds)
setInterval(() => window.location.reload(), 1200000);
while (true) {
if (isSubmitted) {
console.log("%cNew Wayspot", "font-weight: bold;");
isSubmitted = false;
await new Promise(resolve => setTimeout(resolve, 13000));
let bottomText = '\nOnly return the requested JSON based on the schema. Warp the JSON result in markdown codeblock with ```json. Returning the schema is PROHIBITED.'
async function geminiCallAPI(text, base64data) {
const MAX_RETRIES = 3;
for (let retries = 0; retries < MAX_RETRIES; retries++) {
try {
// Your existing code starts here
let url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=MY_GEMINI_KEY'
var inlineData = [];
for (let i = 0; i < base64data.length; i++) {
inlineData.push({
inline_data: {
mime_type: "image/jpeg",
data: base64data[i],
},
});
}
var jsonRequest = {
contents: [{
parts: [{
text: text
},
...inlineData,
],
}, ],
safetySettings: [{
category: "HARM_CATEGORY_HARASSMENT",
threshold: "BLOCK_NONE"
},
{
category: "HARM_CATEGORY_HATE_SPEECH",
threshold: "BLOCK_NONE"
},
{
category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold: "BLOCK_NONE"
},
{
category: "HARM_CATEGORY_DANGEROUS_CONTENT",
threshold: "BLOCK_NONE"
},
],
generationConfig: {
temperature: 0.0
},
};
var jsonString = JSON.stringify(jsonRequest)
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: jsonString,
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
try {
// Extract the text from the response
const parsedData = JSON.parse(data.candidates[0].content.parts[0].text.slice(8, -3)); // remove the backticks and parse the JSON
console.log(parsedData);
return parsedData;
}
catch (error) {
return {}
}
} catch (error) {
console.log(`Error in request. Retrying in 5 seconds. Error: ${error.message}`);
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait for 5 seconds before retrying
}
}
// If all retries fail, throw the last error
throw new Error(`Failed after ${MAX_RETRIES} retries`);
}
function checkNGStar() {
const element = document.getElementsByClassName("ng-star-inserted")[0];
const stringToFind = "Please retry your request.";
if (element.innerText.includes(stringToFind)) {
return true;
} else {
return false;
}
}
// Call the function to check
if (checkNGStar()) {
// Select the button using its class attribute
var button = document.querySelector('.wf-button--primary');
// Check if the button exists
if (button) {
// Click the button
button.click();
// Wait for 13 seconds
await new Promise(resolve => setTimeout(resolve, 13000));
} else {
console.log('Button not found');
}
}
function clickSubmitButton() {
// Select the button with the text "Submit" and the class "wf-button--primary"
const submitButton = document.querySelector('button.wf-button--primary');
if (submitButton && submitButton.textContent.trim() === 'Submit') {
submitButton.click();
return submitButton;
}
// If no match is found, return null
return null;
}
function clickSubmitRejectButton() {
// Find the div element containing the buttons
const div = document.querySelector('div[mat-dialog-actions]');
// Check if the div element exists
if (!div) {
console.log('Div element not found');
return;
}
// Find the Close and Submit buttons
const buttons = div.getElementsByTagName('button');
let closeButton, submitButton;
// Iterate through the buttons and find the Close and Submit buttons
for (let i = 0; i < buttons.length; i++) {
const button = buttons[i];
if (button.textContent.trim() === 'Close') {
closeButton = button;
} else if (button.textContent.trim() === 'Submit') {
submitButton = button;
}
// Break the loop if both buttons are found
if (closeButton && submitButton) {
break;
}
}
// Check if both buttons exist
if (!closeButton || !submitButton) {
console.log('Close and/or Submit button not found');
return;
}
// Click the Submit button
submitButton.click();
}
function clickLastSubmitButton() {
// Select all buttons with the class "wf-button--primary" and the text "Submit"
const submitButtons = Array.from(document.querySelectorAll("button.wf-button--primary")).filter(button => {
return button.textContent.trim() === "Submit";
});
// If there are any submit buttons, click the last one
if (submitButtons.length > 0) {
submitButtons[submitButtons.length - 1].click();
}
// Return the last submit button
return submitButtons[submitButtons.length - 1];
}
function hasCriteriaHeader() {
var header = document.querySelector('h2.wf-page-header__title.ng-star-inserted');
if (!header) {
return false;
}
var text = header.textContent;
return /\s*Select the photos that don't meet the criteria\s*/.test(text);
}
function hasEditDiv() {
var elements = document.querySelectorAll('.ng-star-inserted');
for (let i = 0; i < elements.length; i++) {
var elementText = elements[i].innerText || elements[i].textContent;
if (elementText && elementText.includes('Edit')) {
return true;
}
}
return false;
}
function checkForDivWithEditString() {
var elements = document.querySelectorAll('.ng-star-inserted');
for (let i = 0; i < elements.length; i++) {
var elementText = elements[i].innerText || elements[i].textContent;
if (elementText && elementText.includes(' Edit ')) {
return true;
}
}
return false;
}
function getCriteriaTitle() {
// Get the div with the class "text-lg"
var textLgDiv = document.querySelector('.text-lg');
// Check if the div exists
if (textLgDiv) {
// Get the text inside the div
var text = textLgDiv.textContent;
// Return the text to the console
return text;
} else {
// Log an error message if the div does not exist
console.error('Error: Div with class "text-lg" not found');
}
}
function getCriteriaDescription() {
// Get the div with the class "mt-2"
var textMt2Div = document.querySelector('.mt-2');
// Check if the div exists
if (textMt2Div) {
// Get the text inside the div
var text = textMt2Div.textContent;
// Return the text to the console
return text;
} else {
// Log an error message if the div does not exist
console.error('Error: Div with class "mt-2" not found');
}
}
function getTitleTextByClass() {
var className = 'review-edit-info__info';
var elements = document.getElementsByClassName(className);
if (elements.length > 0) {
return elements[0].textContent.trim();
} else {
throw new Error(`No element found with class '${className}'`);
}
}
function getSecondReviewEditInfoText() {
var elements = document.getElementsByClassName('review-edit-info__info');
if (elements.length >= 4) {
return elements[3].innerText;
} else {
return null;
}
}
function getFirstImageLink() {
var images = document.getElementsByClassName('wf-image-modal');
for (let i = 0; i < images.length; i++) {
if (images[i].tagName === 'IMG') {
return images[i].src;
}
}
return null;
}
function getCriteriaImages() {
var images = document.querySelectorAll('.photo-card__photo img');
var urlsArray = Array.from(images).map(img => {
return img.getAttribute('src');
});
return urlsArray;
}
function extractJsonObjects(limit) {
// Set the default value of the limit parameter to Infinity
limit = limit || Infinity;
// Select all <div> elements with the class "font-medium"
const elements = document.querySelectorAll('div.font-medium');
// Initialize an empty array to store the JSON objects
const jsonObjects = [];
// Loop through the elements
for (const element of elements) {
// Extract the text inside the element
const text = element.textContent;
// Try to parse the text as JSON
try {
// Check if the text is a valid JSON object
if (text.startsWith('{') && text.endsWith('}')) {
// Parse the text as JSON
const jsonObject = JSON.parse(text);
// If the parsing is successful, add the JSON object to the array
jsonObjects.push(jsonObject);
} else {
// If the text is not a valid JSON object, try to parse it as a single property object
const jsonObject = JSON.parse(`{"data": "${text}"}`);
// If the parsing is successful, add the JSON object to the array
jsonObjects.push(jsonObject.data);
}
} catch (error) {
// If the parsing fails, log the text and the error message
console.error(`Failed to parse JSON from element: ${text}`);
console.error(error);
}
// If the limit has been reached, break the loop
if (jsonObjects.length >= limit) {
break;
}
}
// Return the array of JSON objects
return jsonObjects;
}
async function urlsToBlobs(imageUrls) {
const blobPromises = imageUrls.map(imageUrl => {
return fetch(imageUrl)
.then(response => response.blob())
.catch(error => {
console.error('Error converting image URL to Blob:', error);
throw error;
});
});
// Convert each Blob to a base64 string
const base64Promises = await Promise.all(blobPromises).then(blobs => {
return blobs.map(blob => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result.split(',')[1]);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
});
});
// Wait for all base64 strings to be generated
return await Promise.all(base64Promises);
}
function convertBlobToBase64(blob) {
return new Promise((resolve, reject) => {
var reader = new FileReader();
reader.onloadend = () => {
var base64String = reader.result.split(',')[1];
resolve(base64String);
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
function getReviewQuestionTitles() {
var titles = [];
var allElements = document.getElementsByTagName('*');
for (let i = 0; i < allElements.length; i++) {
if (allElements[i].classList.contains('question-title')) {
titles.push(allElements[i].innerText);
}
}
return titles;
}
function clickCheckboxWithLocationMessage() {
// Find the label containing the desired checkbox and its associated text
const label = Array.from(document.getElementsByTagName('label'))
.find(label => label.textContent.trim().includes('Unable to find an appropriate location.'));
// If we found the label, get the corresponding checkbox container span element
if (label) {
const checkboxContainer = label.querySelector('.mat-checkbox-inner-container');
// And if we found the container, simulate a click event on it
if (checkboxContainer) {
checkboxContainer.click();
} else {
console.log("Unable to find the checkbox container.");
}
} else {
console.log("Unable to find the label with the specified message.");
}
}
function getTexts() {
var tooltips = document.querySelectorAll('.question-subtitle-tooltip');
var texts = [];
for (var tooltip of tooltips) {
let text = tooltip.textContent.trim();
var previousSibling = tooltip.previousSibling;
if (previousSibling && previousSibling.nodeType === Node.TEXT_NODE) {
text = previousSibling.textContent.trim();
}
texts.push(text);
}
return texts;
}
function clickOnPhotoCard(i) {
const overlayElements = document.querySelectorAll('.photo-card');
const overlayElementsArray = Array.from(overlayElements);
if (i < overlayElementsArray.length) {
overlayElementsArray[i].click();
} else {
console.warn(`No element with class "photo-card" at index ${i} was found.`);
}
}
function clickOnPhotoCardOverlay() {
const overlayElement = document.querySelector('.photo-card__overlay');
if (overlayElement) {
overlayElement.click();
} else {
console.warn('No element with class "photo-card__overlay" was found.');
}
}
if (hasCriteriaHeader()) {
console.log('Is Criteria')
console.log('Title (Criteria): ', getCriteriaTitle());
console.log('Description (Criteria):', getCriteriaDescription());
console.log('Image links (Criteria): ', getCriteriaImages());
var criteriaBaseText = `You are a Niantic Wayfarer wayspot reviewer. Evaluate and decide on wayspot image criteria, return a JSON based on the schema\nWayspot Information:\ntitle:\n${getCriteriaTitle()}\ndescription:${getCriteriaDescription()}\nDetermine which photo meets the criteria of the title+description. Warp the result in markdown codeblock.\n`
var criteriaSchemeText = `{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"photo_valid":{"type":"boolean"}},"required":["photo_valid"] \nPhoto Guidelines: \nA high-quality photo of a Wayspot is:\n\n Of the actual permanent physical, tangible, identifiable object or placemarker for an area\n Well composed - the Wayspot or placemarker is centered without too much foreground or background, or objects passing by in front\n Clear/sharp with good exposure (show off your photography skills!)\n\n\nThe following should not be submitted and, when reviewing, should be considered as rejection criteria:\n\n Copyrighted photos (e.g. watermarked stock photos)\n Photos that are blurry or under/over exposed\n Includes people, body parts, or live animals\n Pitch black photos where the Wayspot isn't clearly visible\n Photos taken from a car where the car dashboard is visible\n Sideways or upside down photos\n Faction/Player tagging (anything with game specific symbols)\n Obviously edited or doctored photos\n Exact duplicates of existing photos\n Vandalized photos\n Photos with recognizable faces or license plates\n Photos of another location\n\nDetermine if the image shown fits the title and or description.`
var criteriaFinalText = criteriaBaseText + criteriaSchemeText + bottomText
var criteriaArray = []
for (let i = 0; i < getCriteriaImages().length; i++) {
var image = await urlsToBlobs([getCriteriaImages()[i]])
criteriaArray[i] = await geminiCallAPI(criteriaFinalText, image)
}
await new Promise(resolve => setTimeout(resolve, 30000));
console.log(criteriaArray)
var allCriteriaValid = criteriaArray.every(object => object.photo_valid === true);
if (allCriteriaValid) {
clickOnPhotoCardOverlay();
await new Promise(resolve => setTimeout(resolve, 2500));
clickSubmitButton();
isSubmitted = true;
} else {
for (let i = 0; i < criteriaArray.length; i++) {
if (criteriaArray[i].photo_valid == false) {
clickOnPhotoCard(i);
await new Promise(resolve => setTimeout(resolve, 2500));
}
}
await new Promise(resolve => setTimeout(resolve, 10000));
clickSubmitButton();
isSubmitted = true;
}
}
function checkForBestLocation() {
// Get all h4 elements on the page
var h4Elements = document.getElementsByTagName("h4");
// Loop through each h4 element and see if it contains the text "Best Location"
for (var i = 0; i < h4Elements.length; i++) {
if (h4Elements[i].innerText === "Best Location") {
// If we find an h4 with the text "Best Location", return true
return true;
}
}
// If we make it through the entire loop without finding an h4 with the text "Best Location", return false
return false;
}
function getTextNodes(searchText = '') {
const nodes = [];
document.querySelectorAll('.mat-radio-label-content').forEach((el) => {
// Check if the text node contains the search text (case-insensitive)
if (el.textContent.trim().toLowerCase().includes(searchText.toLowerCase())) {
nodes.push(el.textContent.trim());
}
});
return nodes;
}
function isNthSlideToggleOn(n) {
// Check if n is a number and is greater than 0
if (typeof n !== 'number' || n <= 0) {
console.error('Argument must be a positive number.');
return;
}
// Get all slide toggle elements
const slideToggleElements = document.querySelectorAll('.mat-slide-toggle-label');
// Check if any slide toggle elements were found
if (slideToggleElements.length === 0) {
console.error('No slide toggle elements found.');
return;
}
// Check if n is within the bounds of the slide toggle elements
if (n > slideToggleElements.length) {
return;
}
// Get the nth slide toggle button
const nthSlideToggle = slideToggleElements[n - 1];
// Get the associated input element of the nth slide toggle button
const slideToggleInput = nthSlideToggle.querySelector('input');
// Check if the nth slide toggle button is turned on
const ariaCheckedValue = slideToggleInput.getAttribute('aria-checked');
if (ariaCheckedValue === 'true') {
return true;
} else {
return false;
}
}
function clickNthSlideToggle(n) {
// Check if n is a number and is greater than 0
if (typeof n !== 'number' || n <= 0) {
console.error('Argument must be a positive number.');
return;
}
// Get all slide toggle elements
const slideToggleElements = document.querySelectorAll('.mat-slide-toggle-label');
// Check if any slide toggle elements were found
if (slideToggleElements.length === 0) {
console.error('No slide toggle elements found.');
return;
}
// Check if n is within the bounds of the slide toggle elements
if (n > slideToggleElements.length) {
console.error(`There are only ${slideToggleElements.length} slide toggle elements, cannot click on the ${n}th element.`);
return;
}
// Click on the nth slide toggle button
slideToggleElements[n - 1].click();
}
function getAllH4Texts() {
const h4s = document.getElementsByTagName('h4');
const texts = [];
for (let i = 0; i < h4s.length; i++) {
texts.push(h4s[i].innerText.trim());
}
if (texts.length > 0) {
return texts;
} else {
return 'No <h4> elements found.';
}
}
function getTitleFromElement() {
// Select all div elements with class 'text-4xl'
var potentialTitleElements = document.querySelectorAll('div.text-4xl');
// Loop through each element
for (var potentialTitleElement of potentialTitleElements) {
// Check if the element has the required classes
if (potentialTitleElement.classList.contains('text-gray-600') &&
potentialTitleElement.classList.contains('relative') &&
potentialTitleElement.classList.contains('hover:text-blue-700')) {
// Get the text content of the element
var title = potentialTitleElement.textContent;
// Remove the "open_in_new" string from the end of the title
var trimmedTitle = title.replace(' open_in_new', '');
// Return the trimmed title
return trimmedTitle;
}
}
// Return null if no matching element is found
return null;
}
function getDescriptionFromElement() {
// Select all div elements with class 'mt-4' and 'text-lg'
var potentialDescriptionElements = document.querySelectorAll('div.mt-4.text-lg');
// Loop through each element
for (var potentialDescriptionElement of potentialDescriptionElements) {
// Check if the element has the 'ng-star-inserted' class
if (potentialDescriptionElement.classList.contains('ng-star-inserted')) {
// Get the text content of the element
var description = potentialDescriptionElement.textContent;
// Return the description
return description;
}
}
// Return null if no matching element is found
return null;
}
function getMainPhoto() {
// Select the img element inside the div
var img = document.querySelector('.wf-image-modal.flex-grow.bg-contain.bg-center.bg-no-repeat img');
if (img === null) {
isSubmitted = true;
new Promise(resolve => setTimeout(resolve, 30000));
window.location.reload();
}
// Get the src attribute of the img element
var src = img.getAttribute('src');
return src;
}
function getSupportPhoto() {
// Select the img element inside the div
var img = document.querySelector('.wf-image-modal.flex-grow.bg-contain.bg-center.bg-no-repeat.supporting-info-img-container.ng-star-inserted img');
// Get the src attribute of the img element
var src = img.getAttribute('src');
return src;
}
function getSupportingInfoFromElement() {
// Select all div elements with class 'supporting-info-statement'
const potentialSupportingInfoElements = document.querySelectorAll('div.supporting-info-statement');
// Loop through each element
for (const potentialSupportingInfoElement of potentialSupportingInfoElements) {
// Check if the element has the 'ng-star-inserted' class
if (potentialSupportingInfoElement.classList.contains('ng-star-inserted')) {
// Get the text content of the element
const supportingInfo = potentialSupportingInfoElement.textContent;
// Return the supporting info
return supportingInfo;
}
}
// Return null if no matching element is found
return null;
}
function getNearbyImages() {
// Get all the img elements inside the div
var images = document.querySelectorAll('.w-full.flex.overflow-x-auto.overflow-y-hidden.ng-star-inserted img');
// Create an empty array to store the image links
var imageLinks = [];
// Get the number of images to process
var numImages = Math.min(5, images.length);
// Loop through the first numImages img elements
for (let i = 0; i < numImages; i++) {
var img = images[i];
// Get the src attribute of the img element
var src = img.getAttribute('src');
// Add the image link to the array
imageLinks.push(src);
}
return imageLinks;
}
function getNonAppropriateReasons() {
const radioGroup = document.querySelector('.mat-radio-group');
if (!radioGroup) {
console.error("Radio group not found.");
return;
}
const radioButtons = radioGroup.querySelectorAll('mat-radio-button');
const texts = Array.from(radioButtons).map(button => button.innerText.trim());
return texts;
}
function getNonAccuracyReasons() {
const checkboxes = [];
// Use a DOMParser to parse the current document
const parser = new DOMParser();
const doc = parser.parseFromString(document.documentElement.outerHTML, 'text/html');
// Find all the checkboxes
const nodes = doc.querySelectorAll('mat-checkbox');
// Loop through the checkboxes and extract the label text
nodes.forEach(node => {
const label = node.querySelector('span.mat-checkbox-label').textContent.trim();
checkboxes.push(label);
});
return checkboxes;
}
async function clickAllButtonsAndGetText() {
// Get the number of buttons with class "question-subtitle-tooltip"
var buttons = document.querySelectorAll('button.question-subtitle-tooltip');
var numButtons = buttons.length;
if (numButtons === 0) {
console.log('No button found with class question-subtitle-tooltip');
return [];
}
var texts = [];
for (let i = 0; i < numButtons; i++) {
// Click the button at index i
var button = buttons[i];
button.dispatchEvent(new MouseEvent('click'));
// Wait for the modal to open
await new Promise(resolve => setTimeout(resolve, 200));
// Get the text from the modal
var modalElement = document.querySelector('app-question-card-tooltip-modal');
var text = modalElement.innerText.slice(0, -5);
texts.push(text);
// Close the modal after 0.2 seconds
setTimeout(() => {
// Get all buttons
var buttons = document.getElementsByTagName('button');
// Loop through all buttons
for (let i = 0; i < buttons.length; i++) {
var button = buttons[i];
// Check if the button contains the text "Close"
if (/Close/i.test(button.textContent)) {
// Dispatch a click event on the button
button.dispatchEvent(new MouseEvent('click'));
}
}
}, 200);
}
return texts;
}
function clickSelectNoAndEndButton() {
const buttons = document.getElementsByTagName('button');
for (let i = 0; i < buttons.length; i++) {
if (buttons[i].innerText === 'SELECT NO AND END') {
buttons[i].click();
return;
}
}
console.error('The expected "SELECT NO AND END" button was not found.');
}
function clickRadioInputAtIndex(index) {
var radioInputs = document.querySelectorAll('.mat-radio-input');
if (!radioInputs.length) {
console.error("No radio inputs found.");
return;
}
if (index < 0 || index >= radioInputs.length) {
console.error("Invalid index.");
return;
}
var radioInput = radioInputs[index];
if (radioInput) {
radioInput.click();
}
}
function clickNearbyImages(i) {
// Get all the img elements inside the div
var images = document.querySelectorAll('.w-full.flex.overflow-x-auto.overflow-y-hidden.ng-star-inserted img');
// Check if any images were found
if (images.length > 0) {
// Click on the first image
images[i].click();
}
}
function clickDuplicateButton(i) {
// Get all elements that contain the search string
var elements = document.querySelectorAll('*');
var matchingElements = [];
for (var j = 0; j < elements.length; j++) {
if (elements[j].innerText && elements[j].innerText.includes('Mark as duplicate')) {
matchingElements.push(elements[j]);
}
}
// Check if any matching elements were found
if (matchingElements.length === 0) {
console.log('No matching elements found');
return;
}
// Check if the passed `i` value is within the bounds of the matching buttons
if (i < 0 || i >= matchingElements.length) {
console.log('`i` value is out of bounds');
return;
}
// Find the `i`th button within the matching elements
var button = matchingElements[i].querySelector('.wf-button.wf-button--primary');
if (button) {
// Dispatch a 'click' event on the button
var clickEvent = new Event('click');
button.dispatchEvent(clickEvent);
} else {
console.log('No matching button found');
}
}
function clickThumbsUpButton(i) {
const buttons = document.querySelectorAll('.thumbs-button');
if (buttons[i * 2]) {
buttons[i * 2].click();
}
}
function clickThumbsDownButton(i) {
const buttons = document.querySelectorAll('.thumbs-button');
if (buttons[i * 2 + 1]) {
buttons[i * 2 + 1].click();
}
}
function clickDontKnowButton(i) {
const buttons = document.querySelectorAll('.dont-know-button');
if (i >= 0 && i < buttons.length) {
buttons[i].click();
}
}
function getPossibleCategories() {
// Select all elements with the same class as the div
var elements = document.querySelectorAll('.text-orange-500.flex.items-center.pl-2');
// Map over the elements and extract their text content
var categories = Array.from(elements).map(element => element.textContent);
return categories;
}
function clickYesButton(i) {
var button = document.querySelectorAll('.mat-button-toggle-label-content');
if (i >= 0 && i < button.length) {
button[i * 2].click();
}
}
function clickNoButton(i) {
var button = document.querySelectorAll('.mat-button-toggle-label-content');
if (i >= 0 && i < button.length) {
button[i * 2 + 1].click();
}
}
if (hasEditDiv()) {
console.log('Is Edit');
console.log('Title Text (Edit):', getTitleTextByClass());
console.log('Image Link (Edit):', getFirstImageLink());
var editImageMain = await urlsToBlobs([getFirstImageLink()])
if (getTextNodes().length > 0) {
if (isNthSlideToggleOn(1)) {
clickNthSlideToggle(1);
}
if (isNthSlideToggleOn(2)) {
clickNthSlideToggle(2);
}
var titles = [];
var descriptions = [];
var texts = getAllH4Texts();
if (typeof texts === 'object') {
var titleEdit = false;
var descriptionEdit = false;
for (let i = 0; i < texts.length; i++) {
if (texts[i] === "Best Title") {
console.log("Is Title Edit");
var editTitleBaseText = `You are a Niantic Wayfarer wayspot reviewer. Evaluate and decide on wayspot best title text, return a JSON review based on the schema. The first image is the image of the wayspot to be reviewed. Warp the result in markdown codeblock.\n`
titleEdit = true;
}
if (texts[i] === "Best Description") {
console.log("Is Description Edit");
var editDescriptionBaseText = `You are a Niantic Wayfarer wayspot reviewer. Evaluate and decide on wayspot best description text, return a JSON review based on the schema. The first image is the image of the wayspot to be reviewed. Warp the result in markdown codeblock.\n`
descriptionEdit = true;
}
}
if (texts.includes("Best Title") && texts.includes("Best Description")) {
function splitArray(arr) {
// Find the index of the first occurrence of "None of the above."
const index = arr.findIndex(item => item === "None of the above.");
// Split the array into two parts: before and after "None of the above."
const firstPart = arr.slice(0, index + 1);
const secondPart = arr.slice(index + 1);
// Return an array of the two parts
return [firstPart, secondPart];
}
console.log("Titles and Descriptions: ", splitArray(getTextNodes()));
titles = splitArray(getTextNodes())[0];
descriptions = splitArray(getTextNodes())[1];
} else {
if (titleEdit) {
titles = getTextNodes();
}
if (descriptionEdit) {
descriptions = getTextNodes();
}
}
if (titleEdit) {
console.log("Titles: ", titles);
var editTitleSchemaString = `{"$schema": "http://json-schema.org/draft-07/schema#", "type":"object","properties":{"bestTitle":{"type":"number","enum":`;
for (let e = 0; e < titles.length; e++) {
if (e !== 0) {
editTitleSchemaString += ",";
}
editTitleSchemaString += ` ${e}`;
}
editTitleSchemaString += "}}"
editTitleSchemaString += "\nThe titles: "
var titleDict = titles.reduce((acc, title, index) => {
acc[index] = title;
return acc;
}, {});
var editTitleTitlesString = JSON.stringify(titleDict);
var editTitleFinalString = editTitleBaseText + editTitleSchemaString + editTitleTitlesString + bottomText;
let titleIndex = await geminiCallAPI(editTitleFinalString, editImageMain);
await setTimeout(function() {
console.log(titleIndex.bestTitle);
let matchIndex;
if (getTextNodes()[titleIndex.bestTitle] !== undefined) {
console.log('Index found at', titleIndex.bestTitle);
clickRadioInputAtIndex(titleIndex.bestTitle);
if (!descriptionEdit) {
clickSubmitButton();
isSubmitted = true;
}
} else {
console.log('Index not found')
}
}, 50000);
}
if (descriptionEdit) {
console.log("Descriptions: ", descriptions);
var editDescriptionSchemaString = `{"$schema": "http://json-schema.org/draft-07/schema#", "type":"object","properties":{"bestDescription":{"type":"number","enum":`;
for (let e = 0; e < descriptions.length; e++) {
if (e !== 0) {
editDescriptionSchemaString += ",";
}
editDescriptionSchemaString += ` ${e + titles.length}`;
}
editDescriptionSchemaString += `}}`;
editDescriptionSchemaString += "\nThe descriptions: "
var descriptionDict = descriptions.reduce((acc, description, index) => {
acc[index + titles.length] = description;
return acc;
}, {});
var editDescriptionDescriptionsString = JSON.stringify(descriptionDict);
var editDescriptionFinalString = editDescriptionBaseText + editDescriptionSchemaString + editDescriptionDescriptionsString + bottomText
let descriptionIndex = await geminiCallAPI(editDescriptionFinalString, editImageMain);
await setTimeout(function() {
console.log(descriptionIndex.bestDescription);
let matchIndex;
if (getTextNodes()[descriptionIndex.bestDescription] !== undefined) {
console.log('Index found at', descriptionIndex.bestDescription);
clickRadioInputAtIndex(descriptionIndex.bestDescription);
clickSubmitButton();
isSubmitted = true;
} else {
console.log('Index not found');
}
}, 50000);
}
}
}
if (checkForBestLocation()) {
clickCheckboxWithLocationMessage();
console.log("Is Location Edit")
await setTimeout(function() {
new Promise(resolve => setTimeout(resolve, 50000));
if (getTextNodes().length === 0) {
clickSubmitButton();
isSubmitted = true;
}
}, 30000);
}
}
if (!hasCriteriaHeader() && !hasEditDiv() && !checkNGStar()) {
console.log('Is Review');
var title, description, supportInfo, mainPhoto, supportPhoto, nearbyImages, questionTitles, questionTexts, possibleCategories, questionResults;
title = getTitleFromElement();
description = getDescriptionFromElement();
supportInfo = getSupportingInfoFromElement();
mainPhoto = getMainPhoto();
supportPhoto = getSupportPhoto();
nearbyImages = getNearbyImages();
if (!questionTitles) {
questionTitles = getReviewQuestionTitles();
}
if (!questionTexts) {
questionTexts = getTexts();
}
console.log('Title Text (Review): ', title);
console.log('Description Text (Review): ', description);
console.log('Support Info Text (Review): ', supportInfo);
console.log('Main Image Link (Review): ', mainPhoto);
console.log('Support Image Link (Review): ', supportPhoto);
console.log('Nearby Image Link(s) (Review): ', nearbyImages);
console.log('Question Titles (Review): ', questionTitles);
console.log('Questions (Review): ', questionTexts);
if (!questionResults) {
questionResults = await clickAllButtonsAndGetText();
}
console.log('Question Informations (Review): ', questionResults);
await new Promise(resolve => setTimeout(resolve, 2000));
possibleCategories = getPossibleCategories();
if (possibleCategories) {
console.log('Possible Categories (Review): ', possibleCategories);
}
var reviewBaseText = `You are a Niantic Wayfarer wayspot reviewer. Evaluate and decide on wayspot nominations, returning a JSON review based on the schema, Wayspot Information:\ntitle:\n${title}\ndescription:${description}\nsupporting text:${supportInfo}\nthe first image is the image of the wayspot to be reviewed. Warp the result in markdown codeblock.\n`
var reviewDuplicateText = 'Determine if the 2 images shows the EXACT same item in the EXACT same location, by determining the shape/color/constrast/background, transcribe the texts in the images (without saying them outloud). Every detail is significant. Text being the same does not always mean they are the same place, but can be a reference point. Think internally first, then return true if you are 100% sure they are the EXACT SAME, if skeptical return false. { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "wayspot_duplicate_item": { "type": "boolean" } }, "required": ["wayspot_duplicate_item"] } \\n\nOnly return 1 result.'
var reviewImageMain = await urlsToBlobs([mainPhoto, supportPhoto])
var reviewDuplicateChoice = []
var numImages = nearbyImages.length;
var maxIndex = Math.min(numImages, 4) - 1;
if (maxIndex > -1) {
var duplicateCheckTitles = extractJsonObjects(4)
console.log('Duplicate Check Titles (Review): ', duplicateCheckTitles);
for (let i = 0; i <= maxIndex; i++) {
if (!isSubmitted) {
var reviewImageDuplicate = await urlsToBlobs([mainPhoto, nearbyImages[i]])
var reviewDuplicateTitle = `Title of First image: ${title}\nTitle of Second image: ${duplicateCheckTitles[i]}`;
var reviewDuplicateFinalString = reviewDuplicateText + reviewDuplicateTitle + bottomText
reviewDuplicateChoice[i] = await geminiCallAPI(reviewDuplicateFinalString, reviewImageDuplicate)
if (Object.values(reviewDuplicateChoice[i])[0] === true) {
clickNearbyImages(i)
await new Promise(resolve => setTimeout(resolve, 5000));
clickDuplicateButton(i);
await new Promise(resolve => setTimeout(resolve, 5000));
clickLastSubmitButton();
isSubmitted = true;
}
}
}
}
if (!isSubmitted) {
function getReviewQuestionStrings(questionResults) {
// Check if the arrays have the same length
if (questionTitles.length !== questionTexts.length || questionTexts.length !== questionResults.length) {
throw new Error("The arrays must have the same length");
}
// Create an array to hold the string representations of the JSON objects
var reviewQuestionStrings = [];
// Loop through the arrays and create the question strings
for (var i = 0; i < questionTitles.length; i++) {
// Create a string for each question
var questionString = `Question Title: ${questionTitles[i]}\nQuestion Text: ${questionTexts[i]}\nQuestion Info: ${questionResults[i]}\n\n`;
// Add the question string to the result array
reviewQuestionStrings.push(questionString);
}
// Return the array of strings
return reviewQuestionStrings;
}
var reviewQuestionTexts = getReviewQuestionStrings(questionResults)
var reviewQuestionFinalString = ''
var reviewQuestionChoice = []
for (let i = 0; i < getReviewQuestionStrings(questionResults).length; i++) {
if (!isSubmitted) {
var titleOfQuestion = questionTitles[i];
reviewQuestionTexts[i] = `{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"${titleOfQuestion}":{"type":"string","enum":["yes","no","unsure"]}},"required":["${titleOfQuestion}"]"}\n ${reviewQuestionTexts[i]}\nDetermine whether or not the wayspot fits the criteria.`
reviewQuestionFinalString = reviewBaseText + reviewQuestionTexts[i] + bottomText
reviewQuestionChoice[i] = await geminiCallAPI(reviewQuestionFinalString, reviewImageMain)
switch (Object.values(reviewQuestionChoice[i])[0]) {
case "yes":
clickThumbsUpButton(i);
break
case "no":
clickThumbsDownButton(i);
switch (Object.keys(reviewQuestionChoice[i])[0]) {
case "Appropriate":
var nonAppropriateReasons = getNonAppropriateReasons();
console.log(nonAppropriateReasons);
var reviewAppropriateTexts = `{"$schema": "http://json-schema.org/draft-07/schema#", "type":"object","properties":{"non_appropriate_reason":{"type":"string","enum":`;
for (let e = 0; e < nonAppropriateReasons.length; e++) {
if (e !== 0) {
reviewAppropriateTexts += ",";
}
reviewAppropriateTexts += `"${nonAppropriateReasons[e]}"`;
}
reviewAppropriateTexts += `}}`;
var reviewAppropriateBottom = "\nDetermine the reason why the wayspot is rejected by appropriacy. Select the most accurate reason.\n"
var reviewAppropriateFinalString = reviewBaseText + reviewAppropriateTexts + reviewAppropriateBottom + bottomText;
var appropriateChoice = await geminiCallAPI(reviewAppropriateFinalString, reviewImageMain);
console.log(appropriateChoice.non_appropriate_reason);
var appropriateIndex = nonAppropriateReasons.indexOf(appropriateChoice.non_appropriate_reason)
if (appropriateIndex != -1) {
clickRadioInputAtIndex(appropriateIndex);
await new Promise(resolve => setTimeout(resolve, 2500));
clickSubmitRejectButton();
isSubmitted = true;
break
} else {
console.error("appropriateIndex not found");
}
case "Accuracy":
var nonAccuracyReasons = getNonAccuracyReasons();
console.log(nonAccuracyReasons);
var reviewAccuracyTexts = `{"$schema": "http://json-schema.org/draft-07/schema#", "type":"object","properties":{"non_accuracy_reason":{"type":"string","enum":`;
for (let e = 0; e < (nonAccuracyReasons.length - 1); e++) {
if (e !== 0) {
reviewAccuracyTexts += ",";
}
reviewAccuracyTexts += `"${nonAccuracyReasons[e]}"`;
}
reviewAccuracyTexts += `}}`;
var reviewAccuracyBottom = "\nDetermine the reason why the wayspot is rejected by accuracy. Select the most accurate reason.\n"
var reviewAccuracyFinalString = reviewBaseText + reviewAccuracyTexts + reviewAccuracyBottom + bottomText;
var accuracyChoice = await geminiCallAPI(reviewAccuracyFinalString, reviewImageMain);
console.log(accuracyChoice.non_accuracy_reason);
var accuracyIndex = nonAccuracyReasons.indexOf(accuracyChoice.non_accuracy_reason)
if (accuracyIndex != -1) {
function clickSpecificNonAccuracy(index) {
var checkboxes = document.getElementsByClassName("mat-checkbox-input");
if(checkboxes.length > index) {
checkboxes[index].click();
}
}
clickSpecificNonAccuracy(accuracyIndex);
await new Promise(resolve => setTimeout(resolve, 2500));
clickSubmitRejectButton();
isSubmitted = true;
break
} else {
console.error("accuracyIndex not found");
}
case "Safe":
await new Promise(resolve => setTimeout(resolve, 2500));
clickSelectNoAndEndButton();
isSubmitted = true;
break
case "Permanent and Distinct":
await new Promise(resolve => setTimeout(resolve, 2500));
clickSelectNoAndEndButton();
isSubmitted = true;
break
}
break
case "unsure":
clickDontKnowButton(i);
break
}
}
}
if (possibleCategories.length > 0 && !isSubmitted) {
var reviewCategoryTexts = ''
var reviewCategoryFinalString = ''
var reviewCategoryChoice = []
for (let i = 0; i < possibleCategories.length; i++) {
var category = possibleCategories[i];
reviewCategoryTexts = `{"$schema":"http://json-schema/org/draft-07/schema#", "type":"object","properties":{"Is ${category}":{"type":boolean}},"required":"["${category}"]"}\n Determine whether or not the specified category within the schema is apropriate to the wayspot.`
reviewCategoryFinalString = reviewBaseText + reviewCategoryTexts + bottomText;
reviewCategoryChoice[i] = await geminiCallAPI(reviewCategoryFinalString, reviewImageMain);
switch (Object.values(reviewCategoryChoice[i])[0]) {
case true:
clickYesButton(i);
break
case false:
clickNoButton(i);
break
}
}
}
if (!isSubmitted) {
await new Promise(resolve => setTimeout(resolve, 2500));
clickSubmitButton();
isSubmitted = true;
}
}
}
} else {
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
})();