Source code
// ==UserScript==
// @name Neopets - Wishing Well Auto-Wisher
// @namespace https://www.scriptneo.com/
// @license GPL3
// @version 0.0.2
// @description Automatically makes wishes at wishing well (supports cycling through a list of items)
// @author Karla (edited)
// @match *://*.neopets.com/wishing.phtml*
// @grant GM_setValue
// @grant GM_getValue
// @downloadURL https://www.scriptneo.com/scripts/download.php?id=11
// @updateURL https://www.scriptneo.com/scripts/download.php?id=11
// ==/UserScript==
let item = GM_getValue("item") || ""; // kept for backward compatibility
let items_text = GM_getValue("items_text") || item || "";
let item_index = parseInt(GM_getValue("item_index") || "0", 10);
let np = GM_getValue("np") || "21";
let auto = GM_getValue("auto_ww") || false;
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
const random_in_range = (start, end) => {
return Math.floor(Math.random() * (end - start + 1) + start);
};
function parse_items(text) {
const lines = (text || "")
.split(/\r?\n/)
.map((s) => s.trim())
.filter(Boolean);
const items = [];
for (const line of lines) {
// Support: "Item Name x3" or "Item Name * 3"
const m = line.match(/^(.*?)(?:\s*[x\*]\s*(\d+))?\s*$/i);
if (!m) continue;
const name = (m[1] || "").trim();
if (!name) continue;
const qty = m[2] ? Math.max(1, parseInt(m[2], 10) || 1) : 1;
for (let i = 0; i < qty; i++) items.push(name);
}
return items;
}
function get_current_item_and_advance() {
const list = parse_items(items_text);
if (list.length === 0) return { chosen: "", list_len: 0 };
if (!Number.isFinite(item_index) || item_index < 0) item_index = 0;
if (item_index >= list.length) item_index = 0;
const chosen = list[item_index];
item_index = (item_index + 1) % list.length;
GM_setValue("item_index", String(item_index));
return { chosen, list_len: list.length };
}
async function makeWish() {
const match = document.body.innerHTML.match(/Wish Count:\s*(\d+)/);
if (match) {
const wishCount = parseInt(match[1], 10);
console.log(`Making wish ${wishCount + 1}`);
} else if (document.body.innerHTML.includes("Thanks for your donation")) {
console.log("Wish done");
return;
} else {
console.log("Making wish 1");
}
const donationInput = document.querySelector('[name="donation"]');
const wishInput = document.querySelector('[name="wish"]');
const wishButton = document.querySelector('[value="Make a Wish"]');
if (!donationInput || !wishInput || !wishButton) {
console.log("Wishing Well inputs not found on this page.");
return;
}
const picked = get_current_item_and_advance();
if (!picked.chosen) {
console.log("No items in list. Add at least 1 item to the textarea.");
return;
}
await sleep(random_in_range(500, 900));
donationInput.value = np;
await sleep(random_in_range(500, 900));
wishInput.value = picked.chosen;
console.log(`Wishing with: "${picked.chosen}" (list size: ${picked.list_len})`);
await sleep(random_in_range(500, 900));
wishButton.click();
}
(function () {
"use strict";
try {
const div = document.createElement("div");
div.style.marginTop = "20px";
div.innerHTML = `
<div>-- Wishing Well Auto Wisher --</div>
<small>Enter one item per line. Optional: "xN" or "* N" to repeat an item.</small>
<br/><br/>
<label style="display:block; margin-bottom:6px;">Items list:</label>
<textarea id="k-items" rows="6" style="width: 320px; max-width: 100%;" ${auto ? "disabled" : ""}></textarea>
<div style="margin-top:8px; display:flex; gap:10px; align-items:center; flex-wrap:wrap;">
<label>Np: <input id="k-np" type="number" min="21" ${auto ? "disabled" : ""} style="width: 90px;" /></label>
<button id="k-toggle">${auto ? "Stop" : "Start"}</button>
<button id="k-reset" type="button" ${auto ? "disabled" : ""}>Reset cycle</button>
</div>
<div style="margin-top:6px; font-size: 12px; opacity: 0.9;">
<span id="k-status"></span>
</div>
`;
const itemsArea = div.querySelector("#k-items");
const npInput = div.querySelector("#k-np");
const startButton = div.querySelector("#k-toggle");
const resetButton = div.querySelector("#k-reset");
const status = div.querySelector("#k-status");
function refreshStatus() {
const list = parse_items(items_text);
const idx = Number.isFinite(item_index) ? item_index : 0;
if (list.length === 0) {
status.textContent = "Status: add items above to enable wishing.";
return;
}
const nextIdx = idx >= list.length ? 0 : idx;
status.textContent = `Status: ${list.length} item(s) in cycle. Next up: "${list[nextIdx]}" (position ${nextIdx + 1}/${list.length}).`;
}
itemsArea.value = items_text;
itemsArea.addEventListener("input", function (event) {
items_text = event.target.value || "";
GM_setValue("items_text", items_text);
// keep old single-item key in sync with first valid line for compatibility
const list = parse_items(items_text);
item = list[0] || "";
GM_setValue("item", item);
// if index is out of bounds after edits, clamp
if (item_index >= list.length) {
item_index = 0;
GM_setValue("item_index", "0");
}
refreshStatus();
});
npInput.value = np;
npInput.addEventListener("change", function (event) {
np = event.target.value;
GM_setValue("np", np);
});
resetButton.addEventListener("click", function () {
item_index = 0;
GM_setValue("item_index", "0");
refreshStatus();
});
startButton.addEventListener("click", function () {
auto = !auto;
GM_setValue("auto_ww", auto);
if (auto) {
startButton.textContent = "Stop";
itemsArea.disabled = true;
npInput.disabled = true;
resetButton.disabled = true;
refreshStatus();
makeWish();
} else {
startButton.textContent = "Start";
itemsArea.removeAttribute("disabled");
npInput.removeAttribute("disabled");
resetButton.removeAttribute("disabled");
refreshStatus();
}
});
const anchorImg = document.querySelector('[src="//images.neopets.com/images/wishingwell.gif"]');
const anchor = anchorImg ? anchorImg.parentNode : null;
if (anchor) {
anchor.append(div);
} else {
// fallback if Neopets changes the page
document.body.append(div);
}
refreshStatus();
if (auto) {
makeWish();
}
} catch (e) {
console.log(e);
}
})();