0% found this document useful (0 votes)
45 views7 pages

Message 2

This plugin allows users to animate their Discord status by cycling through multiple status messages. It provides functions to load configuration data, start and stop the animation loop, and generate a settings panel to edit animation steps and timing options. The settings panel includes an editor to define multiple status messages and their properties, with options to add or remove steps.

Uploaded by

calebmhill4321
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
45 views7 pages

Message 2

This plugin allows users to animate their Discord status by cycling through multiple status messages. It provides functions to load configuration data, start and stop the animation loop, and generate a settings panel to edit animation steps and timing options. The settings panel includes an editor to define multiple status messages and their properties, with options to add or remove steps.

Uploaded by

calebmhill4321
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

//META{"name":"AnimatedStatus","source":"https://raw.githubusercontent.

com/
toluschr/BetterDiscord-Animated-Status/master/
Animated_Status.plugin.js","website":"https://github.com/toluschr/BetterDiscord-
Animated-Status"}*//

class AnimatedStatus {
/* BD functions */
getName() { return "Animated Status"; }
getVersion() { return "0.13.2"; }
getAuthor() { return "toluschr"; }
getDescription() { return "Animate your Discord status"; }

SetData(key, value) {
BdApi.setData("AnimatedStatus", key, value);
}

GetData(key) {
return BdApi.getData("AnimatedStatus", key);
}

/* Code related to Animations */


load() {
this.kSpacing = "15px";
this.kMinTimeout = 2900;
this.cancel = undefined;

this.animation = this.GetData("animation") || [];


this.timeout = this.GetData("timeout") || this.kMinTimeout;
this.randomize = this.GetData("randomize") || false;

// Import Older Config Files


if (typeof this.timeout == "string")
this.timeout = parseInt(this.timeout);
if (this.animation.length > 0 && Array.isArray(this.animation[0]))
this.animation = this.animation.map(em =>
this.ConfigObjectFromArray(em));

Status.authToken = BdApi.findModule(m => m.default &&


m.default.getToken).default.getToken();
this.currentUser = BdApi.findModule(m => m.default &&
m.default.getCurrentUser).default.getCurrentUser();
}

start() {
if (this.animation.length == 0)
BdApi.showToast("Animated Status: No status set. Go to
Settings>Plugins to set a custom animation!");
else
this.AnimationLoop();
}

stop() {
if (this.cancel) {
this.cancel();
} else {
console.assert(this.loop != undefined);
clearTimeout(this.loop);
}
Status.Set(null);
}

ConfigObjectFromArray(arr) {
let data = {};
if (arr[0] !== undefined && arr[0].length > 0) data.text =
arr[0];
if (arr[1] !== undefined && arr[1].length > 0) data.emoji_name =
arr[1];
if (arr[2] !== undefined && arr[2].length > 0) data.emoji_id =
arr[2];
if (arr[3] !== undefined && arr[3].length > 0) data.timeout =
parseInt(arr[3]);
return data;
}

async ResolveStatusField(text = "") {


let evalPrefix = "eval ";
if (!text.startsWith(evalPrefix)) return text;

try {
return eval(text.substr(evalPrefix.length));
} catch (e) {
BdApi.showToast(e, {type: "error"});
return "";
}
}

AnimationLoop(i = 0) {
i %= this.animation.length;

// Every loop needs its own shouldContinue variable, otherwise there


// is the possibility of multiple loops running simultaneously
let shouldContinue = true;
this.loop = undefined;
this.cancel = () => { shouldContinue = false; };

Promise.all([this.ResolveStatusField(this.animation[i].text),
this.ResolveStatusField(this.animation[i].emoji_name),

this.ResolveStatusField(this.animation[i].emoji_id)]).then(p => {
Status.Set(this.ConfigObjectFromArray(p));
this.cancel = undefined;

if (shouldContinue) {
let timeout = this.animation[i].timeout || this.timeout;
this.loop = setTimeout(() => {
if (this.randomize) {
i += Math.floor(Math.random() *
(this.animation.length - 2));
}
this.AnimationLoop(i + 1);
}, timeout);
}
});
}

NewEditorRow({text, emoji_name, emoji_id, timeout} = {}) {


let hbox = GUI.newHBox();
hbox.style.marginBottom = this.kSpacing;
let textWidget = hbox.appendChild(GUI.newInput(text, "Text"));
textWidget.style.marginRight = this.kSpacing;

let emojiWidget = hbox.appendChild(GUI.newInput(emoji_name, "👍" +


(this.currentUser.premiumType ? " / Nitro Name" : "")));
emojiWidget.style.marginRight = this.kSpacing;
emojiWidget.style.width = "140px";

let optNitroIdWidget = hbox.appendChild(GUI.newInput(emoji_id, "Nitro


ID"));
if (!this.currentUser.premiumType) optNitroIdWidget.style.display =
"none";
optNitroIdWidget.style.marginRight = this.kSpacing;
optNitroIdWidget.style.width = "140px";

let optTimeoutWidget = hbox.appendChild(GUI.newNumericInput(timeout,


this.kMinTimeout, "Time"));
optTimeoutWidget.style.width = "75px";

hbox.onkeydown = (e) => {


let activeContainer = document.activeElement.parentNode;
let activeIndex =
Array.from(activeContainer.children).indexOf(document.activeElement);

let keymaps = {
"Delete": [
[[false, true], () => {
activeContainer = hbox.nextSibling ||
hbox.previousSibling;
hbox.parentNode.removeChild(hbox);
}],
],

"ArrowDown": [
[[true, true], () => {
activeContainer = this.NewEditorRow();
hbox.parentNode.insertBefore(activeContainer,
hbox.nextSibling);
}],
[[false, true], () => {
let next = hbox.nextSibling;
if (next != undefined) {
next.replaceWith(hbox);
hbox.parentNode.insertBefore(next, hbox);
}
}],
[[false, false], () => {
activeContainer = hbox.nextSibling;
}],
],

"ArrowUp": [
[[true, true], () => {
activeContainer = this.NewEditorRow();
hbox.parentNode.insertBefore(activeContainer,
hbox);
}],
[[false, true], () => {
let prev = hbox.previousSibling;
if (prev != undefined) {
prev.replaceWith(hbox);
hbox.parentNode.insertBefore(prev,
hbox.nextSibling);
}
}],
[[false, false], () => {
activeContainer = hbox.previousSibling;
}],
],
};

let letter = keymaps[e.key];


if (letter == undefined) return;

for (let i = 0; i < letter.length; i++) {


if (letter[i][0][0] != e.ctrlKey || letter[i][0][1] !=
e.shiftKey)
continue;

letter[i][1]();
if (activeContainer)
activeContainer.children[activeIndex].focus();
e.preventDefault();
return;
}
};
return hbox;
}

EditorFromJSON(json) {
let out = document.createElement("div");
for (let i = 0; i < json.length; i++) {
out.appendChild(this.NewEditorRow(json[i]));
}
return out;
}

JSONFromEditor(editor) {
return Array.prototype.slice.call(editor.childNodes).map(row => {
return
this.ConfigObjectFromArray(Array.prototype.slice.call(row.childNodes).map(e =>
e.value));
});
}

// Settings
getSettingsPanel() {
let settings = document.createElement("div");
settings.style.padding = "10px";

// timeout
settings.appendChild(GUI.newLabel("Step-Duration (3000: 3 seconds,
3500: 3.5 seconds, ...), overwritten by invididual steps"));
let timeout = settings.appendChild(GUI.newNumericInput(this.timeout,
this.kMinTimeout));
timeout.style.marginBottom = this.kSpacing;
// Animation Container
settings.appendChild(GUI.newLabel("Animation"));
let animationContainer =
settings.appendChild(document.createElement("div"));
animationContainer.marginBottom = this.kSpacing;

// Editor
let edit =
animationContainer.appendChild(this.EditorFromJSON(this.animation));

// Actions
let actions = settings.appendChild(GUI.newHBox());

// Add Step
let addStep = actions.appendChild(GUI.setSuggested(GUI.newButton("+",
false)));
addStep.title = "Add step to end";
addStep.onclick = () => edit.appendChild(this.NewEditorRow());

// Del Step
let delStep = actions.appendChild(GUI.setDestructive(GUI.newButton("-",
false)));
delStep.title = "Remove last step";
delStep.style.marginLeft = this.kSpacing;
delStep.onclick = () =>
edit.removeChild(edit.childNodes[edit.childNodes.length - 1]);

// Move save to the right (XXX make use of flexbox)


actions.appendChild(GUI.setExpand(document.createElement("div"), 2));

// Save
let save = actions.appendChild(GUI.newButton("Save"));
GUI.setSuggested(save, true);
save.onclick = () => {
try {
// Set timeout
this.SetData("randomize", this.randomize);
this.SetData("timeout", parseInt(timeout.value));
this.SetData("animation", this.JSONFromEditor(edit));
} catch (e) {
BdApi.showToast(e, {type: "error"});
return;
}

// Show Toast
BdApi.showToast("Settings were saved!", {type: "success"});

// Restart
this.stop();
this.load();
this.start();
};

// End
return settings;
}
}

/* Status API */
const Status = {
strerror: (req) => {
if (req.status < 400) return undefined;
if (req.status == 401) return "Invalid AuthToken";

// Discord _sometimes_ returns an error message


let json = JSON.parse(req.response);
for (const s of ["errors", "custom_status", "text", "_errors", 0,
"message"])
if ((json == undefined) || ((json = json[s]) == undefined))
return "Unknown error. Please report at
github.com/toluschr/BetterDiscord-Animated-Status";

return json;
},

Set: async (status) => {


let req = new XMLHttpRequest();
req.open("PATCH", "/api/v9/users/@me/settings", true);
req.setRequestHeader("authorization", Status.authToken);
req.setRequestHeader("content-type", "application/json");
req.onload = () => {
let err = Status.strerror(req);
if (err != undefined)
BdApi.showToast(`Animated Status: Error: ${err}`, {type:
"error"});
};
if (status === {}) status = null;
req.send(JSON.stringify({custom_status: status}));
},
};

// Used to easily style elements like the 'native' discord ones


const GUI = {
newInput: (text = "", placeholder = "") => {
let input = document.createElement("input");
input.className = "inputDefault-3FGxgL input-2g-os5";
input.value = String(text);
input.placeholder = String(placeholder);
return input;
},

newNumericInput: (text = "", minimum = 0, placeholder = "") => {


let out = GUI.newInput(text, placeholder);
out.setAttribute("type", "number");
out.addEventListener("focusout", () => {
if (parseInt(out.value) < minimum) {
out.value = String(minimum);
BdApi.showToast(`Value must not be lower than ${minimum}`,
{type: "error"});
}
});
return out;
},

newLabel: (text = "") => {


let label = document.createElement("h5");
label.className = "h5-2RwDNl";
label.innerText = String(text);
return label;
},

newButton: (text, filled = true) => {


let button = document.createElement("button");
button.className = "button-f2h6uQ colorBrand-I6CyqQ sizeSmall-wU2dO-
grow-2sR_-F";
if (filled) button.classList.add("lookFilled-yCfaCM");
else button.classList.add("lookOutlined-3yKVGo");
button.innerText = String(text);
return button;
},

newHBox: () => {
let hbox = document.createElement("div");
hbox.style.display = "flex";
hbox.style.flexDirection = "row";
return hbox;
},

setExpand: (element, value) => {


element.style.flexGrow = value;
return element;
},

setSuggested: (element, value = true) => {


if (value) element.classList.add("colorGreen-3y-Z79");
else element.classList.remove("colorGreen-3y-Z79");
return element;
},

setDestructive: (element, value = true) => {


if (value) element.classList.add("colorRed-rQXKgM");
else element.classList.remove("colorRed-rQXKgM");
return element;
}
};

You might also like