-
Notifications
You must be signed in to change notification settings - Fork 0
/
magic.js
101 lines (92 loc) · 2.86 KB
/
magic.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
let magicProm = null;
function toBase64(buffer) {
const buf = new Uint8Array(buffer);
return btoa(String.fromCharCode(...buf));
}
function fromBase64(str) {
return Uint8Array.from([...atob(str)].map((c) => c.charCodeAt(0)));
}
async function sha256(message) {
const msgBuffer = new TextEncoder().encode(message);
return await crypto.subtle.digest("SHA-256", msgBuffer);
}
async function aesCbcDecrypt(base64Bytes, key) {
const decKey = await crypto.subtle.importKey("raw", key, "AES-CBC", true, [
"decrypt",
]);
const fullBuf = fromBase64(base64Bytes);
const iv = fullBuf.slice(0, 16);
const ciphertext = fullBuf.slice(16);
return crypto.subtle.decrypt({ name: "AES-CBC", iv }, decKey, ciphertext);
}
async function loadMagic() {
if (!magicProm) {
magicProm = fetch("./magic.json")
.then((r) => {
if (!r.ok) {
throw new Error(`Failed to load magic.json`);
}
return r.json();
})
.then((data) => new Map(data));
}
return magicProm;
}
async function doSearch(search) {
const res = [];
const db = await loadMagic();
const ents = db.get(toBase64(await sha256("kk2024~" + search)));
if (!ents) return [];
let decKeyBits = new Array(search.length).fill(search).join("x");
const decKey = await sha256(decKeyBits);
for (const ent of ents) {
const plain = new TextDecoder().decode(await aesCbcDecrypt(ent, decKey));
const json = JSON.parse(plain);
if (json.im) {
json.im = json.im.replace("^se", "https://avatars.slack-edge.com/");
}
res.push(json);
}
return res;
}
function handleAddFromSearchResult(event) {
event.preventDefault();
const { im, rn, dn } = event.target.dataset;
const el = addCard();
el.querySelector(".username").innerText = dn ? `@${dn}` : "???";
el.querySelector(".nimi").innerText = rn || "???";
if (im) el.querySelector("img").src = im;
slideCardIn(el);
}
function rand(a, b) {
return a + Math.random() * (b - a);
}
async function handleSearch(event) {
const search = event.target.value.trim().toLowerCase();
document.getElementById("hakutulokset").innerHTML = "";
if (!search) return;
let results = await doSearch(search);
let i = 0;
for (const { im, rn, dn } of results) {
const el = document.createElement("a");
Object.assign(el.dataset, { im: im || "", rn: rn || "", dn: dn || "" });
el.innerText = [rn, dn].filter(Boolean).join(" / ");
el.onclick = handleAddFromSearchResult;
el.href = "#";
el.classList.add("hakutulos");
document.getElementById("hakutulokset").appendChild(el);
let transform = `rotate(${rand(-90, 90)}deg) translate(${rand(-50, 50)}%, ${rand(-50, 50)}%)`;
el.animate(
[
{ opacity: 0, transform },
{ opacity: 1, transform: "" },
],
{
duration: 300,
delay: i++ * 150,
fill: "both",
easing: "ease-out",
},
);
}
}