emoji history, better decay and varrious fixes

This commit is contained in:
MathMan05
2025-10-01 17:00:33 -05:00
parent e3f1156a99
commit b35b8a89c4
9 changed files with 301 additions and 36 deletions
+28 -1
View File
@@ -1,6 +1,7 @@
import {Contextmenu} from "./contextmenu.js";
import {Guild} from "./guild.js";
import {Hover} from "./hover.js";
import {I18n} from "./i18n.js";
import {emojijson} from "./jsontypes.js";
import {Localuser} from "./localuser.js";
import {BinRead} from "./utils/binaryUtils.js";
@@ -300,10 +301,36 @@ class Emoji {
});
}
let i = 0;
Contextmenu.declareMenu(menu);
document.body.append(menu);
Contextmenu.keepOnScreen(menu);
let i = 0;
const recent = localuser?.favorites.emojiFreq();
if (recent && recent.length >= 15 && localuser) {
const select = document.createElement("div");
select.classList.add("svg-history");
selection.append(select);
console.log("appended");
select.onclick = () => {
title.textContent = I18n.recentEmoji();
body.innerHTML = "";
for (const [emj] of recent) {
const emoji = Emoji.getEmojiFromIDOrString(emj, localuser);
const emojihtml = document.createElement("div");
emojihtml.classList.add("emojiSelect");
emojihtml.append(emoji.getHTML());
body.append(emojihtml);
emojihtml.onclick = (_) => {
res(emoji);
Contextmenu.declareMenu();
};
}
};
select.click();
i++;
}
for (const thing of Emoji.emojis) {
const select = document.createElement("div");
select.textContent = thing.emojis[0].emoji;
+150 -33
View File
@@ -13,11 +13,14 @@ interface permStore {
emojiFrecency: favandfreq["emojiFrecency"]["emojis"];
emojiReactionFrecency: favandfreq["emojiReactionFrecency"]["emojis"];
guildAndChannelFrecency: favandfreq["guildAndChannelFrecency"]["guildAndChannels"];
favorite_stickers: string[];
sticker_frecency: favandfreq["sticker_frecency"]["stickers"];
favorite_emojis: string[];
};
needsSave: saveImportance;
lastSave: number;
lastDecay: number;
old: favandfreq;
old: favandfreqimp;
}
type RecursivePartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[]
@@ -26,6 +29,16 @@ type RecursivePartial<T> = {
? RecursivePartial<T[P]>
: T[P];
};
type favandfreqimp = Omit<
favandfreq,
| "application_command_frecency"
| "favorite_soundboard_sounds"
| "application_frecency"
| "heard_sound_frecency"
| "played_sound_frecency"
>;
export class Favorites {
owner: Localuser;
// ----- stuff that needs to store ----
@@ -33,10 +46,13 @@ export class Favorites {
private emojiFrecency: favandfreq["emojiFrecency"]["emojis"] = {};
private emojiReactionFrecency: favandfreq["emojiReactionFrecency"]["emojis"] = {};
private guildAndChannelFrecency: favandfreq["guildAndChannelFrecency"]["guildAndChannels"] = {};
private favorite_stickers: string[] = [];
private sticker_frecency: favandfreq["sticker_frecency"]["stickers"] = {};
private favorite_emojis: string[] = [];
needsSave: saveImportance = 0;
lastSave = 0;
lastDecay = 0;
old!: favandfreq;
old!: favandfreqimp;
// ----- end of stuff that needs to store ----
get info() {
return this.owner.info;
@@ -61,7 +77,14 @@ export class Favorites {
store.current.emojiFrecency ||= {};
store.current.emojiReactionFrecency ||= {};
store.current.guildAndChannelFrecency ||= {};
const old: Partial<favandfreq> = (store.old ||= {}) as Partial<favandfreq>;
store.current.sticker_frecency ||= {};
store.current.favorite_stickers = [];
store.current.favorite_emojis = [];
function deapClone(clone: any) {
const val = JSON.parse(JSON.stringify(clone));
return val;
}
const old = deapClone((store.old = {})) as Partial<favandfreqimp>;
old.favoriteGifs ||= {
gifs: {},
hideTooltip: false,
@@ -75,6 +98,14 @@ export class Favorites {
old.guildAndChannelFrecency ||= {
guildAndChannels: {},
};
old.favorite_emojis ||= [];
old.favorite_stickers ||= [];
old.sticker_frecency ||= {
stickers: {},
};
store.old = old;
store.needsSave ||= 0;
store.lastSave ||= Date.now();
store.lastDecay ||= Date.now();
@@ -82,12 +113,12 @@ export class Favorites {
this.needsSave = store.needsSave;
this.lastSave = store.lastSave;
this.lastDecay = store.lastDecay;
function deapClone(clone: any) {
const val = JSON.parse(JSON.stringify(clone));
return val;
}
this.old = deapClone(store.old) as favandfreq;
this.old = old as favandfreq;
this.favorite_emojis = deapClone(store.current.favorite_emojis);
this.sticker_frecency = deapClone(store.current.sticker_frecency);
this.favorite_stickers = deapClone(store.current.favorite_stickers);
this.gifs = deapClone(store.current.gifs);
this.emojiFrecency = deapClone(store.current.emojiFrecency);
this.emojiReactionFrecency = deapClone(store.current.emojiReactionFrecency);
@@ -101,6 +132,9 @@ export class Favorites {
gifs: this.gifs,
emojiReactionFrecency: this.emojiReactionFrecency,
guildAndChannelFrecency: this.guildAndChannelFrecency,
favorite_stickers: this.favorite_stickers,
sticker_frecency: this.sticker_frecency,
favorite_emojis: this.favorite_emojis,
},
lastDecay: this.lastDecay,
lastSave: this.lastSave,
@@ -110,22 +144,29 @@ export class Favorites {
}
async saveNetwork() {
await this.startSync(false);
const body: favandfreq & {versions: any} = {
versions: {clientVersion: 10, serverVersion: 0, dataVersion: 60},
favoriteGifs: {
gifs: this.gifs,
hideTooltip: false,
},
emojiFrecency: {
emojis: this.emojiFrecency,
},
emojiReactionFrecency: {
emojis: this.emojiReactionFrecency,
},
guildAndChannelFrecency: {
guildAndChannels: this.guildAndChannelFrecency,
const body: {settings: favandfreqimp} = {
settings: {
favoriteGifs: {
gifs: this.gifs,
hideTooltip: false,
},
emojiFrecency: {
emojis: this.emojiFrecency,
},
emojiReactionFrecency: {
emojis: this.emojiReactionFrecency,
},
guildAndChannelFrecency: {
guildAndChannels: this.guildAndChannelFrecency,
},
favorite_stickers: this.favorite_stickers,
sticker_frecency: {
stickers: this.sticker_frecency,
},
favorite_emojis: this.favorite_emojis,
},
};
console.warn(body);
const res = await fetch(this.info.api + "/users/@me/settings-proto/2/json", {
method: "PATCH",
headers: this.headers,
@@ -137,8 +178,8 @@ export class Favorites {
const sat = fetch(this.info.api + "/users/@me/settings-proto/2/json", {
headers: this.headers,
});
const res: Partial<favandfreq> = await (await sat).json();
this.saveDifs(res, save);
const res: {settings: Partial<favandfreq>} = await (await sat).json();
this.saveDifs(res.settings, save);
}
async setup() {
try {
@@ -149,10 +190,10 @@ export class Favorites {
}
console.log(this);
}
getOld(): favandfreq {
getOld(): favandfreqimp {
return this.old;
}
setOld(newold: favandfreq) {
setOld(newold: favandfreqimp) {
this.old = newold;
this.saveLocal();
return;
@@ -203,7 +244,7 @@ export class Favorites {
}
saveDifs(diffs: Partial<favandfreq>, save = true) {
const old = this.getOld();
if (diffs.favoriteGifs) {
if (diffs.favoriteGifs?.gifs) {
const oldKeys = new Set(Object.keys(old.favoriteGifs.gifs));
const newKeys = new Set(Object.keys(this.gifs));
const removedKeys = oldKeys.difference(newKeys);
@@ -216,7 +257,34 @@ export class Favorites {
}
old.favoriteGifs.gifs = this.gifs = diffs.favoriteGifs.gifs;
}
if (diffs.emojiFrecency) {
if (diffs.favorite_stickers) {
const oldKeys = new Set(Object.keys(old.favorite_stickers));
const newKeys = new Set(Object.keys(this.favorite_stickers));
const removedKeys = oldKeys.difference(newKeys);
const addedKeys = newKeys.difference(oldKeys);
diffs.favorite_stickers = diffs.favorite_stickers.filter((_) => !removedKeys.has(_));
diffs.favorite_stickers = [...new Set([...diffs.favorite_stickers, ...addedKeys])];
old.favorite_stickers = this.favorite_stickers = diffs.favorite_stickers;
}
if (diffs.favorite_emojis) {
const oldKeys = new Set(Object.keys(old.favorite_emojis));
const newKeys = new Set(Object.keys(this.favorite_emojis));
const removedKeys = oldKeys.difference(newKeys);
const addedKeys = newKeys.difference(oldKeys);
diffs.favorite_emojis = diffs.favorite_emojis.filter((_) => !removedKeys.has(_));
diffs.favorite_emojis = [...new Set([...diffs.favorite_emojis, ...addedKeys])];
old.favorite_emojis = this.favorite_emojis = diffs.favorite_emojis;
}
if (diffs.emojiFrecency?.emojis) {
this.mixNewOldNetwork(
this.emojiFrecency,
old.emojiFrecency.emojis,
@@ -224,7 +292,7 @@ export class Favorites {
);
old.emojiFrecency.emojis = this.emojiFrecency = diffs.emojiFrecency.emojis;
}
if (diffs.emojiReactionFrecency) {
if (diffs.emojiReactionFrecency?.emojis) {
this.mixNewOldNetwork(
this.emojiReactionFrecency,
old.emojiReactionFrecency.emojis,
@@ -233,7 +301,7 @@ export class Favorites {
old.emojiReactionFrecency.emojis = this.emojiReactionFrecency =
diffs.emojiReactionFrecency.emojis;
}
if (diffs.guildAndChannelFrecency) {
if (diffs.guildAndChannelFrecency?.guildAndChannels) {
this.mixNewOldNetwork(
this.guildAndChannelFrecency,
old.guildAndChannelFrecency.guildAndChannels,
@@ -243,6 +311,15 @@ export class Favorites {
diffs.guildAndChannelFrecency.guildAndChannels;
}
if (diffs.sticker_frecency?.stickers) {
this.mixNewOldNetwork(
this.sticker_frecency,
old.sticker_frecency.stickers,
diffs.sticker_frecency.stickers,
);
old.sticker_frecency.stickers = this.sticker_frecency = diffs.sticker_frecency.stickers;
}
this.setOld(old);
this.decayScore(save);
@@ -250,15 +327,22 @@ export class Favorites {
favoriteGifs() {
return structuredClone(Object.values(this.gifs).sort((a, b) => a.order - b.order));
}
favoriteStickers() {
return structuredClone(this.favorite_stickers);
}
stickersFreq() {
return Object.entries(this.sticker_frecency).sort((a, b) => b[1].score - a[1].score);
}
favoriteEmojis() {
return structuredClone(this.favorite_emojis);
}
emojiFreq() {
return Object.values(this.emojiFrecency).sort((a, b) => b.score - a.score);
return Object.entries(this.emojiFrecency).sort((a, b) => b[1].score - a[1].score);
}
emojiReactFreq() {
return Object.entries(this.emojiReactionFrecency).sort((a, b) => b[1].score - a[1].score);
}
async addEmoji(nameOrID: string) {
console.log(this.emojiReactionFrecency === this.guildAndChannelFrecency);
const obj = (this.emojiFrecency[nameOrID] ??= {
totalUses: 0,
recentUses: [],
@@ -271,6 +355,19 @@ export class Favorites {
obj.score += 100;
await this.save(saveImportance.low);
}
async addStickerFreq(id: string) {
const obj = (this.sticker_frecency[id] ??= {
totalUses: 0,
recentUses: [],
frecency: -1,
score: 0,
});
obj.totalUses++;
obj.recentUses.unshift(Math.floor(Date.now()) + "");
obj.recentUses = obj.recentUses.splice(0, 20);
obj.score += 100;
await this.save(saveImportance.low);
}
async addReactEmoji(nameOrID: string) {
const obj = (this.emojiReactionFrecency[nameOrID] ??= {
totalUses: 0,
@@ -301,6 +398,25 @@ export class Favorites {
delete this.gifs[name];
await this.save(saveImportance.high);
}
async favoriteSticker(id: string) {
this.favorite_stickers.push(id);
await this.save(saveImportance.high);
}
async unfavoriteSticker(id: string) {
this.favorite_stickers = this.favorite_stickers.filter((_) => _ !== id);
await this.save(saveImportance.high);
}
async favoriteEmoji(idorname: string) {
this.favorite_emojis.push(idorname);
await this.save(saveImportance.high);
}
async unfavoriteEmoji(idorname: string) {
this.favorite_emojis = this.favorite_emojis.filter((_) => _ !== idorname);
await this.save(saveImportance.high);
}
async favoriteGif(
name: string,
gif: Omit<favandfreq["favoriteGifs"]["gifs"][""], "order" | "format">,
@@ -338,6 +454,7 @@ export class Favorites {
thing.score *= decay;
thing.score ^= 0;
});
this.lastDecay = Date.now();
if (save) await this.save(saveImportance.low);
}
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="180"
height="180"
viewBox="0 0 180 180"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="history.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="px"
inkscape:zoom="2.05"
inkscape:cx="90"
inkscape:cy="90"
inkscape:window-width="1366"
inkscape:window-height="740"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
sodipodi:type="spiral"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:20;stroke-linecap:round;stroke-dasharray:none"
id="path1"
sodipodi:cx="75.609756"
sodipodi:cy="59.024391"
sodipodi:expansion="0"
sodipodi:revolution="1.05"
sodipodi:radius="65.954758"
sodipodi:argument="-6.3931794"
sodipodi:t0="0.2"
d="M 98.925474,120.72048 C 65.317611,133.42131 26.614496,115.94797 13.913668,82.340108 1.2128395,48.732246 18.686177,10.02913 52.294039,-2.6716978 85.901902,-15.372526 124.60502,2.1008115 137.30585,35.708674 141.71,47.36258 142.7206,60.197098 140.19468,72.396681"
transform="rotate(135.51113,76.177272,76.137124)" />
<path
style="fill:#000000;stroke:#000000;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 21.550599,30.500375 5.56604,33.198708 32.201771,-5.398895"
id="path2" />
<path
style="fill:none;stroke:#000000;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 88.292683,55.170732 88.780488,95.658537 109.75609,77.609756"
id="path3"
sodipodi:nodetypes="ccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+33
View File
@@ -141,6 +141,11 @@ export interface freq {
}
//https://docs.discord.food/resources/user-settings-proto#frecency-user-settings-object
export interface favandfreq {
versions?: {
client_version: number;
server_version: number;
data_version: number;
};
favoriteGifs: {
gifs: {
[key: string]: {
@@ -168,6 +173,34 @@ export interface favandfreq {
[key: string]: freq;
};
};
favorite_stickers: string[];
sticker_frecency: {
stickers: {
[key: string]: freq;
};
};
favorite_emojis: string[];
application_command_frecency: {
application_commands: {
[key: string]: freq;
};
};
favorite_soundboard_sounds: string[];
application_frecency: {
applications: {
[key: string]: freq;
};
};
heard_sound_frecency: {
heard_sounds: {
[key: string]: freq;
};
};
played_sound_frecency: {
played_sounds: {
[key: string]: freq;
};
};
}
interface readySuplemental {
op: 0;
-1
View File
@@ -3590,7 +3590,6 @@ class Localuser {
emoji.getHTML(),
() => {
this.favorites.addEmoji(emoji.id || (emoji.emoji as string));
this.favorites.save(4);
},
];
});
-1
View File
@@ -933,7 +933,6 @@ class Message extends SnowFlake {
.emojiReactFreq()
.slice(0, 6)
.filter(([emoji]) => {
console.log(emoji, this.reactions);
return !this.reactions.find(
(_) => _.emoji.id === emoji || _.emoji.emoji === emoji || _.emoji.name === emoji,
)?.me;
+11
View File
@@ -1,6 +1,7 @@
import {Contextmenu} from "./contextmenu.js";
import {Guild} from "./guild.js";
import {Hover} from "./hover.js";
import {I18n} from "./i18n.js";
import {stickerJson} from "./jsontypes.js";
import {Localuser} from "./localuser.js";
import {SnowFlake} from "./snowflake.js";
@@ -106,6 +107,16 @@ class Sticker extends SnowFlake {
]
.filter((guild) => guild !== undefined)
.filter((guild) => guild.id != "@me" && guild.stickers.length > 0);
if (guilds.length === 0) {
const title = document.createElement("h2");
title.textContent = I18n.noStickers();
topBar.append(title);
menu.append(topBar);
Contextmenu.declareMenu(menu);
document.body.append(menu);
Contextmenu.keepOnScreen(menu);
return new Promise(() => {});
}
const title = document.createElement("h2");
title.textContent = guilds[0].properties.name;
title.classList.add("emojiTitle");
+13
View File
@@ -604,6 +604,19 @@ textarea {
display: block;
mask-size: cover !important;
}
.svg-history {
width: 36px;
height: 36px;
background: var(--black);
mask: url(/icons/history.svg);
mask-size: cover !important;
flex-shrink: 0;
cursor: pointer;
&:hover {
background: var(--black);
}
}
.svg-folder {
mask: url(/icons/folder.svg);
background-color: var(--folder-color, var(--accent-color));
+2
View File
@@ -13,6 +13,8 @@
"joiningStream": "Joining stream..."
},
"favoriteGifs":"Favorited Gifs",
"recentEmoji":"Recent Emoji",
"noStickers":"You seem to be in an odd spot, there are no stickers for you to use!",
"readableName": "English",
"pinMessage": "Pin Message",
"unableToPin": "Unable to pin message",