Last active
October 28, 2024 14:11
-
-
Save pckv/42acc362705dcbc2abbf51d09c18310d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Requires AutoHotkey v2 | |
AutoBurn := false | |
#b:: | |
{ | |
global AutoBurn | |
InputBoxObj := InputBox("Paste card IDs as copied from the Hifumin Tools userscript. If you have AutoBurn enabled, position your cursor in the center of the BIG PLUS BUTTON to the left of the Discord message field and press Enter instead of clicking OK. KEEP THE CURSOR OVER THE PLUS BUTTON UNTIL IT WRITES 'done'", "Burn cards") | |
if InputBoxObj.Result != "OK" | |
{ | |
return | |
} | |
cards := StrSplit(InputBoxObj.Value, ",") | |
for card in cards | |
{ | |
if AutoBurn | |
{ | |
MouseMove 76, 0, 0, "R" | |
Click | |
} | |
Send '/burn ' card '{Enter}' | |
if AutoBurn | |
{ | |
MouseMove -76, 0, 0, "R" | |
} | |
Sleep 2300 | |
if AutoBurn | |
{ | |
MouseMove 76, -80, 0, "R" | |
Click | |
MouseMove -76, 80, 0, "R" | |
} | |
Sleep 1200 | |
} | |
if AutoBurn | |
{ | |
MouseMove 76, 0, 0, "R" | |
Click | |
} | |
Send 'done' | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name hifumin burn assistant | |
// @namespace http://tampermonkey.net/ | |
// @version 2024-10-28 | |
// @description hifumin burn assistant | |
// @author You | |
// @match https://hifumin.osucad.com/ | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=osucad.com | |
// @grant none | |
// ==/UserScript== | |
(function () { | |
"use strict"; | |
let selecting = true; | |
const cards = []; | |
let selectedCardIds = []; | |
let lastSelectedId = null; | |
const isSelected = (id) => selectedCardIds.includes(id); | |
const getConditionFromName = (name) => { | |
if (name.includes("💥")) { | |
return "badlyDamaged"; | |
} | |
if (name.includes("⚠️")) { | |
return "poor"; | |
} | |
if (name.includes("✔️")) { | |
return "good"; | |
} | |
if (name.includes("💎")) { | |
return "mint"; | |
} | |
}; | |
const conditionToEmoji = (condition) => | |
condition === "mint" | |
? "🤩" | |
: condition === "good" | |
? "🙂" | |
: condition === "poor" | |
? "😕" | |
: "😡" | |
const getCondition = (card) => { | |
const name = card.querySelector(".name-box").innerText; | |
return getConditionFromName(name); | |
}; | |
const getName = (card) => | |
card | |
.querySelector(".name-box") | |
.innerText.replace(/💥|⚠️|✔️|💎/g, "") | |
.trim(); | |
const getId = (card) => card.querySelector(".card-id").innerText; | |
const getValue = (card) => | |
Number(card.querySelector(".value").innerText.replace(/💰/, "")); | |
const getStar = (card) => | |
Number([...card.classList].find((c) => c.startsWith("star")).slice(-1)) - 1; | |
const getFoil = (card) => card.classList.contains("foil"); | |
const getCardData = (card) => ({ | |
id: getId(card), | |
name: getName(card), | |
value: getValue(card), | |
condition: getCondition(card), | |
star: getStar(card), | |
foil: getFoil(card), | |
}); | |
const getCardDataOfIds = (cardIds) => cards | |
.filter(card => cardIds.includes(getId(card))) | |
.map(card => getCardData(card)); | |
const history = []; | |
const redoHistory = []; | |
const push = (action, skipHistory = false) => { | |
if (action.type === "add") { | |
selectedCardIds.push(...action.cards.map(getId)); | |
action.cards.forEach((card) => updateSelectedStyle(card, true)); | |
} else if (action.type === "remove") { | |
const removedIds = action.cards.map(getId); | |
selectedCardIds = selectedCardIds.filter( | |
(id) => !removedIds.includes(id) | |
); | |
action.cards.forEach((card) => updateSelectedStyle(card, false)); | |
} | |
if (!skipHistory) { | |
history.push(action); | |
if (redoHistory.length) redoHistory.length = 0; | |
} | |
}; | |
const revert = (action, skipHistory = false) => { | |
if (action.type === "add") { | |
push({ ...action, type: "remove" }, skipHistory); | |
} else if (action.type === "remove") { | |
push({ ...action, type: "add" }, skipHistory); | |
} | |
}; | |
const setAllAs = (selected, filter) => { | |
const filtered = filter ? cards.filter(filter) : [...cards]; | |
if (!selected) { | |
push({ type: "remove", cards: filtered.filter( | |
(card) => isSelected(getId(card)) | |
) }); | |
} else { | |
const unselectedCards = filtered.filter( | |
(card) => !isSelected(getId(card)) | |
); | |
push({ type: "add", cards: unselectedCards }); | |
} | |
} | |
const toggleSelectAll = (filter) => { | |
const filtered = filter ? cards.filter(filter) : [...cards]; | |
const areAllSelected = filtered.every((card) => isSelected(getId(card))); | |
if (areAllSelected) { | |
push({ type: "remove", cards: filtered }); | |
} else { | |
const unselectedCards = filtered.filter( | |
(card) => !isSelected(getId(card)) | |
); | |
push({ type: "add", cards: unselectedCards }); | |
} | |
}; | |
const selectRange = (fromId, toId) => { | |
const [start, end] = [fromId, toId] | |
.map((id) => cards.findIndex((card) => getId(card) === id)) | |
.sort((a, b) => a > b); | |
const cardsToSelect = cards.slice(start, end + 1); | |
push({ type: "add", cards: cardsToSelect }); | |
}; | |
const deselect = () => { | |
const selectedCards = cards.filter((card) => isSelected(getId(card))); | |
push({ type: "remove", cards: selectedCards }); | |
}; | |
const copy = () => { | |
navigator.clipboard.writeText(selectedCardIds.join(",")); | |
deselect(); | |
}; | |
const toSortable = (name) => name.replaceAll(/[ _-]*/g, "").toLocaleLowerCase() | |
const sortCard = (nameA, nameB) => toSortable(nameA).localeCompare(toSortable(nameB)) | |
const copyDetailed = () => { | |
const selectedCards = getCardDataOfIds(selectedCardIds); | |
const text = selectedCards | |
.toSorted((a, b) => sortCard(a.name, b.name)) | |
.map(c => `\`${c.id}\` · ${conditionToEmoji(c.condition)} · ${c.name} (${c.value} gold)${c.foil ? " · `foil`" : ""}` ) | |
.join("\n"); | |
navigator.clipboard.writeText(text); | |
deselect(); | |
} | |
const copyNames = () => { | |
const selectedCards = getCardDataOfIds(selectedCardIds); | |
const names = Array.from(new Set(selectedCards.map(c => c.name))).toSorted(sortCard); | |
const text = names | |
.map(c => c.replaceAll("_", "\\_").replaceAll("-", "\\-")) | |
.join(", ") | |
navigator.clipboard.writeText(text); | |
deselect(); | |
} | |
const undo = () => { | |
if (!history.length) return; | |
const action = history.pop(); | |
revert(action, true); | |
redoHistory.push(action); | |
}; | |
const redo = () => { | |
if (!redoHistory.length) return; | |
const action = redoHistory.pop(); | |
push(action, true); | |
history.push(action); | |
}; | |
document.body.addEventListener("keydown", (e) => { | |
if (!selecting) { | |
return; | |
} | |
if (e.code === "KeyC" && e.shiftKey) { | |
e.preventDefault(); | |
if (e.ctrlKey) copyNames(); | |
else copyDetailed(); | |
} | |
else if (e.code === "KeyC" && e.ctrlKey) { | |
e.preventDefault(); | |
copy(); | |
} | |
if (e.ctrlKey && !e.shiftKey && e.code === "KeyZ") { | |
e.preventDefault(); | |
undo(); | |
} | |
if ( | |
(e.ctrlKey && e.shiftKey && e.code === "KeyZ") || | |
(e.ctrlKey && !e.shiftKey && e.code === "KeyY") | |
) { | |
e.preventDefault(); | |
redo(); | |
} | |
if (e.ctrlKey && !e.shiftKey && e.code === "KeyA") { | |
e.preventDefault(); | |
toggleSelectAll(); | |
} | |
if (e.ctrlKey && !e.shiftKey && e.code === "KeyD") { | |
e.preventDefault(); | |
deselect(); | |
} | |
if (e.altKey && e.code === "Digit1") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => getStar(card) === 0); | |
} | |
if (e.altKey && e.code === "Digit2") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => getStar(card) === 1); | |
} | |
if (e.altKey && e.code === "Digit3") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => getStar(card) === 2); | |
} | |
if (e.altKey && e.code === "KeyF") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => getFoil(card)); | |
} | |
if (e.altKey && e.code === "KeyN") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => !getFoil(card)); | |
} | |
if (e.altKey && e.code === "KeyM") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => getCondition(card) === "mint"); | |
} | |
if (e.altKey && e.code === "KeyG") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => getCondition(card) === "good"); | |
} | |
if (e.altKey && e.code === "KeyP") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => getCondition(card) === "poor"); | |
} | |
if (e.altKey && e.code === "KeyB") { | |
e.preventDefault(); | |
setAllAs(!e.shiftKey, (card) => getCondition(card) === "badlyDamaged"); | |
} | |
}); | |
const results = document.querySelector("#results"); | |
const updateSelectedStyle = (card, selected) => { | |
card.style.boxShadow = selected ? "0 0 0 7px #007bff" : ""; | |
}; | |
const addSelect = (card) => { | |
const id = getId(card); | |
updateSelectedStyle(card, isSelected(id)); | |
const select = () => { | |
if (isSelected(id)) { | |
push({ type: "remove", cards: [card] }); | |
lastSelectedId = null; | |
} else { | |
push({ type: "add", cards: [card] }); | |
lastSelectedId = id; | |
console.log(getCardData(card)); | |
} | |
}; | |
const range = () => { | |
if (!lastSelectedId) return; | |
selectRange(lastSelectedId, id); | |
lastSelectedId = null; | |
}; | |
const img = card.querySelector("img"); | |
img.oncontextmenu = (e) => { | |
e.preventDefault(); | |
select(); | |
}; | |
const oldClick = img.onclick; | |
img.onclick = (e) => { | |
if (e.ctrlKey) { | |
e.preventDefault(); | |
select(); | |
} else if (e.shiftKey) { | |
e.preventDefault(); | |
range(); | |
} else oldClick(e); | |
}; | |
}; | |
const updateCards = () => { | |
Object.assign(cards, document.querySelectorAll(".card")); | |
cards.forEach((card) => { | |
addSelect(card); | |
}); | |
}; | |
const update = () => { | |
updateCards(); | |
}; | |
// Observe changes | |
let timer; | |
const observer = new MutationObserver((mutations) => { | |
if (!selecting) { | |
return; | |
} | |
mutations.forEach((mutation) => { | |
if (mutation.addedNodes.length > 0) { | |
clearTimeout(timer); | |
timer = setTimeout(update, 100); | |
} | |
}); | |
}); | |
console.log("hifumin burn assistant loaded"); | |
observer.observe(results, { childList: true }); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment