
There is a known regresion for the MESSAGE_CREATE event while it's not on screen, though I have not been able to replicate it while I'm looking for it. If you see this bug, please let me know the conditions it happens under
556 lines
21 KiB
JavaScript
556 lines
21 KiB
JavaScript
import { Guild } from "./guild.js";
|
|
import { Direct } from "./direct.js";
|
|
import { Voice } from "./audio.js";
|
|
import { User } from "./user.js";
|
|
import { markdown } from "./markdown.js";
|
|
import { Fullscreen } from "./fullscreen.js";
|
|
import { setTheme } from "./login.js";
|
|
class Localuser {
|
|
packets;
|
|
token;
|
|
userinfo;
|
|
serverurls;
|
|
initialized;
|
|
info;
|
|
headers;
|
|
usersettings;
|
|
ready;
|
|
guilds;
|
|
guildids;
|
|
user;
|
|
status;
|
|
channelfocus;
|
|
lookingguild;
|
|
guildhtml;
|
|
ws;
|
|
typing;
|
|
wsinterval;
|
|
constructor(userinfo) {
|
|
this.packets = 1;
|
|
this.token = userinfo.token;
|
|
this.userinfo = userinfo;
|
|
this.serverurls = this.userinfo.serverurls;
|
|
this.initialized = false;
|
|
this.info = this.serverurls;
|
|
this.headers = { "Content-type": "application/json; charset=UTF-8", Authorization: this.userinfo.token };
|
|
}
|
|
gottenReady(ready) {
|
|
this.usersettings = null;
|
|
this.initialized = true;
|
|
this.ready = ready;
|
|
this.guilds = [];
|
|
this.guildids = {};
|
|
this.user = new User(ready.d.user, this);
|
|
this.userinfo.username = this.user.username;
|
|
this.userinfo.pfpsrc = this.user.getpfpsrc();
|
|
this.status = this.ready.d.user_settings.status;
|
|
this.channelfocus = null;
|
|
this.lookingguild = null;
|
|
this.guildhtml = {};
|
|
const members = {};
|
|
for (const thing of ready.d.merged_members) {
|
|
members[thing[0].guild_id] = thing[0];
|
|
}
|
|
for (const thing of ready.d.guilds) {
|
|
const temp = new Guild(thing, this, members[thing.id]);
|
|
this.guilds.push(temp);
|
|
this.guildids[temp.id] = temp;
|
|
}
|
|
{
|
|
const temp = new Direct(ready.d.private_channels, this);
|
|
this.guilds.push(temp);
|
|
this.guildids[temp.id] = temp;
|
|
}
|
|
console.log(ready.d.user_guild_settings.entries);
|
|
for (const thing of ready.d.user_guild_settings.entries) {
|
|
this.guildids[thing.guild_id].notisetting(thing);
|
|
}
|
|
for (const thing of ready.d.read_state.entries) {
|
|
const guild = this.resolveChannelFromID(thing.id).guild;
|
|
if (guild === undefined) {
|
|
continue;
|
|
}
|
|
const guildid = guild.id;
|
|
this.guildids[guildid].channelids[thing.channel_id].readStateInfo(thing);
|
|
}
|
|
this.typing = [];
|
|
}
|
|
outoffocus() {
|
|
document.getElementById("servers").textContent = "";
|
|
document.getElementById("channels").textContent = "";
|
|
document.getElementById("messages").textContent = "";
|
|
this.lookingguild = null;
|
|
this.channelfocus = null;
|
|
}
|
|
unload() {
|
|
this.initialized = false;
|
|
clearInterval(this.wsinterval);
|
|
this.outoffocus();
|
|
this.guilds = [];
|
|
this.guildids = {};
|
|
this.ws.close(4000);
|
|
}
|
|
async initwebsocket() {
|
|
let returny = null;
|
|
const promise = new Promise((res) => { returny = res; });
|
|
this.ws = new WebSocket(this.serverurls.gateway.toString());
|
|
this.ws.addEventListener('open', (event) => {
|
|
console.log('WebSocket connected');
|
|
this.ws.send(JSON.stringify({
|
|
"op": 2,
|
|
"d": {
|
|
"token": this.token,
|
|
"capabilities": 16381,
|
|
"properties": {
|
|
"browser": "Jank Client",
|
|
"client_build_number": 0,
|
|
"release_channel": "Custom",
|
|
"browser_user_agent": navigator.userAgent
|
|
},
|
|
"compress": false,
|
|
"presence": {
|
|
"status": "online",
|
|
"since": new Date().getTime(),
|
|
"activities": [],
|
|
"afk": false
|
|
}
|
|
}
|
|
}));
|
|
});
|
|
this.ws.addEventListener('message', (event) => {
|
|
try {
|
|
const temp = JSON.parse(event.data);
|
|
console.log(temp);
|
|
if (temp.op == 0) {
|
|
switch (temp.t) {
|
|
case "MESSAGE_CREATE":
|
|
if (this.initialized) {
|
|
this.messageCreate(temp);
|
|
}
|
|
break;
|
|
case "MESSAGE_DELETE":
|
|
console.log(temp.d);
|
|
this.guildids[temp.d.guild_id].channelids[temp.d.channel_id].messageids[temp.d.id].deleteEvent();
|
|
break;
|
|
case "READY":
|
|
this.gottenReady(temp);
|
|
this.genusersettings();
|
|
returny();
|
|
break;
|
|
case "MESSAGE_UPDATE":
|
|
if (this.initialized) {
|
|
if (this.channelfocus.id === temp.d.channel_id) {
|
|
const find = temp.d.id;
|
|
const messagelist = document.getElementById("messages").children;
|
|
for (const message of messagelist) {
|
|
const all = message["all"];
|
|
if (all.id === find) {
|
|
all.content = temp.d.content;
|
|
message["txt"].innerHTML = markdown(temp.d.content).innerHTML;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
this.resolveChannelFromID(temp.d.channel_id).messages.find(e => e.id === temp.d.channel_id).content = temp.d.content;
|
|
}
|
|
}
|
|
break;
|
|
case "TYPING_START":
|
|
if (this.initialized) {
|
|
this.typeingStart(temp);
|
|
}
|
|
break;
|
|
case "USER_UPDATE":
|
|
if (this.initialized) {
|
|
const users = User.userids[temp.d.id];
|
|
console.log(users, temp.d.id);
|
|
if (users) {
|
|
users.userupdate(temp.d);
|
|
}
|
|
}
|
|
break;
|
|
case "CHANNEL_UPDATE":
|
|
if (this.initialized) {
|
|
this.updateChannel(temp.d);
|
|
}
|
|
break;
|
|
case "CHANNEL_CREATE":
|
|
if (this.initialized) {
|
|
this.createChannel(temp.d);
|
|
}
|
|
break;
|
|
case "CHANNEL_DELETE":
|
|
if (this.initialized) {
|
|
this.delChannel(temp.d);
|
|
}
|
|
break;
|
|
case "GUILD_DELETE":
|
|
{
|
|
const guildy = this.guildids[temp.d.id];
|
|
delete this.guildids[temp.d.id];
|
|
this.guilds.splice(this.guilds.indexOf(guildy), 1);
|
|
guildy.html.remove();
|
|
break;
|
|
}
|
|
case "GUILD_CREATE":
|
|
{
|
|
const guildy = new Guild(temp.d, this, this.user);
|
|
this.guilds.push(guildy);
|
|
this.guildids[guildy.id] = guildy;
|
|
document.getElementById("servers").insertBefore(guildy.generateGuildIcon(), document.getElementById("bottomseperator"));
|
|
}
|
|
}
|
|
}
|
|
else if (temp.op === 10) {
|
|
console.log("heartbeat down");
|
|
this.wsinterval = setInterval(_ => {
|
|
this.ws.send(JSON.stringify({ op: 1, d: this.packets }));
|
|
}, temp.d.heartbeat_interval);
|
|
this.packets = 1;
|
|
}
|
|
else if (temp.op != 11) {
|
|
this.packets++;
|
|
}
|
|
}
|
|
catch (error) {
|
|
console.error(error);
|
|
}
|
|
});
|
|
this.ws.addEventListener('close', (event) => {
|
|
clearInterval(this.wsinterval);
|
|
console.log('WebSocket closed');
|
|
console.warn(event);
|
|
if (event.code !== 4000 && this === this) {
|
|
this.unload();
|
|
document.getElementById("loading").classList.remove("doneloading");
|
|
document.getElementById("loading").classList.add("loading");
|
|
this.initwebsocket().then(_ => {
|
|
this.loaduser();
|
|
this.init();
|
|
document.getElementById("loading").classList.add("doneloading");
|
|
document.getElementById("loading").classList.remove("loading");
|
|
console.log("done loading");
|
|
});
|
|
}
|
|
});
|
|
await promise;
|
|
return;
|
|
}
|
|
resolveChannelFromID(ID) {
|
|
let resolve = this.guilds.find(guild => guild.channelids[ID]).channelids[ID];
|
|
resolve ??= undefined;
|
|
return resolve;
|
|
}
|
|
updateChannel(JSON) {
|
|
this.guildids[JSON.guild_id].updateChannel(JSON);
|
|
if (JSON.guild_id === this.lookingguild.id) {
|
|
this.loadGuild(JSON.guild_id);
|
|
}
|
|
}
|
|
createChannel(JSON) {
|
|
JSON.guild_id ??= "@me";
|
|
this.guildids[JSON.guild_id].createChannelpac(JSON);
|
|
if (JSON.guild_id === this.lookingguild.id) {
|
|
this.loadGuild(JSON.guild_id);
|
|
}
|
|
}
|
|
delChannel(JSON) {
|
|
JSON.guild_id ??= "@me";
|
|
this.guildids[JSON.guild_id].delChannel(JSON);
|
|
if (JSON.guild_id === this.lookingguild.id) {
|
|
this.loadGuild(JSON.guild_id);
|
|
}
|
|
}
|
|
init() {
|
|
const location = window.location.href.split("/");
|
|
if (location[3] === "channels") {
|
|
const guild = this.loadGuild(location[4]);
|
|
guild.loadChannel(location[5]);
|
|
this.channelfocus = guild.channelids[location[5]];
|
|
}
|
|
this.buildservers();
|
|
}
|
|
loaduser() {
|
|
document.getElementById("username").textContent = this.user.username;
|
|
document.getElementById("userpfp").src = this.user.getpfpsrc();
|
|
document.getElementById("status").textContent = this.status;
|
|
}
|
|
isAdmin() {
|
|
return this.lookingguild.isAdmin();
|
|
}
|
|
loadGuild(id) {
|
|
let guild = this.guildids[id];
|
|
if (!guild) {
|
|
guild = this.guildids["@me"];
|
|
}
|
|
this.lookingguild = guild;
|
|
document.getElementById("serverName").textContent = guild.properties.name;
|
|
//console.log(this.guildids,id)
|
|
document.getElementById("channels").innerHTML = "";
|
|
document.getElementById("channels").appendChild(guild.getHTML());
|
|
return guild;
|
|
}
|
|
buildservers() {
|
|
const serverlist = document.getElementById("servers"); //
|
|
const div = document.createElement("div");
|
|
div.textContent = "⌂";
|
|
div.classList.add("home", "servericon");
|
|
div["all"] = this.guildids["@me"];
|
|
serverlist.appendChild(div);
|
|
div.onclick = function () {
|
|
this["all"].loadGuild();
|
|
this["all"].loadChannel();
|
|
};
|
|
const sentdms = document.createElement("div");
|
|
sentdms.classList.add("sentdms");
|
|
serverlist.append(sentdms);
|
|
sentdms.id = "sentdms";
|
|
const br = document.createElement("hr");
|
|
br.classList.add("lightbr");
|
|
serverlist.appendChild(br);
|
|
for (const thing of this.guilds) {
|
|
if (thing instanceof Direct) {
|
|
thing.unreaddms();
|
|
continue;
|
|
}
|
|
const divy = thing.generateGuildIcon();
|
|
serverlist.append(divy);
|
|
}
|
|
{
|
|
const br = document.createElement("hr");
|
|
br.classList.add("lightbr");
|
|
serverlist.appendChild(br);
|
|
br.id = "bottomseperator";
|
|
const div = document.createElement("div");
|
|
div.textContent = "+";
|
|
div.classList.add("addserver", "servericon");
|
|
serverlist.appendChild(div);
|
|
div.onclick = _ => {
|
|
console.log("clicked :3");
|
|
this.createGuild();
|
|
};
|
|
}
|
|
this.unreads();
|
|
}
|
|
createGuild() {
|
|
let inviteurl = "";
|
|
const error = document.createElement("span");
|
|
const full = new Fullscreen(["tabs", [
|
|
["Join using invite", [
|
|
"vdiv",
|
|
["textbox",
|
|
"Invite Link/Code",
|
|
"",
|
|
function () {
|
|
console.log(this);
|
|
inviteurl = this.value;
|
|
}
|
|
],
|
|
["html", error],
|
|
["button",
|
|
"",
|
|
"Submit",
|
|
_ => {
|
|
let parsed = "";
|
|
if (inviteurl.includes("/")) {
|
|
parsed = inviteurl.split("/")[inviteurl.split("/").length - 1];
|
|
}
|
|
else {
|
|
parsed = inviteurl;
|
|
}
|
|
fetch(this.info.api.toString() + "/v9/invites/" + parsed, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
}).then(r => r.json()).then(_ => {
|
|
console.log(_);
|
|
if (_.message) {
|
|
error.textContent = _.message;
|
|
}
|
|
});
|
|
}
|
|
]
|
|
]],
|
|
["Create Server", [
|
|
"text", "Not currently implemented, sorry"
|
|
]]
|
|
]]);
|
|
full.show();
|
|
}
|
|
messageCreate(messagep) {
|
|
messagep.d.guild_id ??= "@me";
|
|
this.guildids[messagep.d.guild_id].channelids[messagep.d.channel_id].messageCreate(messagep);
|
|
this.unreads();
|
|
}
|
|
unreads() {
|
|
console.log(this.guildhtml);
|
|
for (const thing of this.guilds) {
|
|
if (thing.id === "@me") {
|
|
continue;
|
|
}
|
|
thing.unreads(this.guildhtml[thing.id]);
|
|
}
|
|
}
|
|
typeingStart(typing) {
|
|
if (this.channelfocus.id === typing.d.channel_id) {
|
|
const memb = typing.d.member;
|
|
let name;
|
|
if (memb.id === this.user.id) {
|
|
console.log("you is typing");
|
|
return;
|
|
}
|
|
console.log("user is typing and you should see it");
|
|
if (memb.nick) {
|
|
name = memb.nick;
|
|
}
|
|
else {
|
|
name = memb.user.username;
|
|
}
|
|
let already = false;
|
|
for (const thing of this.typing) {
|
|
if (thing[0] === name) {
|
|
thing[1] = new Date().getTime();
|
|
already = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!already) {
|
|
this.typing.push([name, new Date().getTime()]);
|
|
}
|
|
setTimeout(this.rendertyping.bind(this), 10000);
|
|
this.rendertyping();
|
|
}
|
|
}
|
|
updatepfp(file) {
|
|
var reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
console.log(this.headers);
|
|
reader.onload = () => {
|
|
fetch(this.info.api.toString() + "/v9/users/@me", {
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
avatar: reader.result,
|
|
})
|
|
});
|
|
console.log(reader.result);
|
|
};
|
|
}
|
|
updatepronouns(pronouns) {
|
|
fetch(this.info.api.toString() + "/v9/users/@me/profile", {
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
pronouns: pronouns,
|
|
})
|
|
});
|
|
}
|
|
updatebio(bio) {
|
|
fetch(this.info.api.toString() + "/v9/users/@me/profile", {
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
bio: bio,
|
|
})
|
|
});
|
|
}
|
|
rendertyping() {
|
|
const typingtext = document.getElementById("typing");
|
|
let build = "";
|
|
const array2 = [];
|
|
let showing = false;
|
|
let i = 0;
|
|
for (const thing of this.typing) {
|
|
i++;
|
|
if (thing[1] > new Date().getTime() - 5000) {
|
|
build += thing[0];
|
|
array2.push(thing);
|
|
showing = true;
|
|
if (i !== this.typing.length) {
|
|
build += ",";
|
|
}
|
|
}
|
|
}
|
|
if (i > 1) {
|
|
build += " are typing";
|
|
}
|
|
else {
|
|
build += " is typing";
|
|
}
|
|
console.log(typingtext.classList);
|
|
if (showing) {
|
|
typingtext.classList.remove("hidden");
|
|
document.getElementById("typingtext").textContent = build;
|
|
}
|
|
else {
|
|
typingtext.classList.add("hidden");
|
|
}
|
|
}
|
|
genusersettings() {
|
|
const hypothetcialprofie = document.createElement("div");
|
|
let file = null;
|
|
let newprouns = null;
|
|
let newbio = null;
|
|
let hypouser = new User(this.user, this, true);
|
|
function regen() {
|
|
hypothetcialprofie.textContent = "";
|
|
const hypoprofile = hypouser.buildprofile(-1, -1);
|
|
hypothetcialprofie.appendChild(hypoprofile);
|
|
}
|
|
regen();
|
|
this.usersettings = new Fullscreen(["hdiv",
|
|
["vdiv",
|
|
["fileupload", "upload pfp:", function (e) {
|
|
console.log(this.files[0]);
|
|
file = this.files[0];
|
|
const blob = URL.createObjectURL(this.files[0]);
|
|
hypouser.avatar = blob;
|
|
hypouser.hypotheticalpfp = true;
|
|
regen();
|
|
}],
|
|
["textbox", "Pronouns:", this.user.pronouns, function (e) {
|
|
console.log(this.value);
|
|
hypouser.pronouns = this.value;
|
|
newprouns = this.value;
|
|
regen();
|
|
}],
|
|
["mdbox", "Bio:", this.user.bio, function (e) {
|
|
console.log(this.value);
|
|
hypouser.bio = this.value;
|
|
newbio = this.value;
|
|
regen();
|
|
}],
|
|
["button", "update user content:", "submit", function () {
|
|
if (file !== null) {
|
|
this.updatepfp(file);
|
|
}
|
|
if (newprouns !== null) {
|
|
this.updatepronouns(newprouns);
|
|
}
|
|
if (newbio !== null) {
|
|
this.updatebio(newbio);
|
|
}
|
|
}],
|
|
["select", "Theme:", ["Dark", "Light", "WHITE"], e => {
|
|
localStorage.setItem("theme", ["Dark", "Light", "WHITE"][e.target.selectedIndex]);
|
|
setTheme();
|
|
}, ["Dark", "Light", "WHITE"].indexOf(localStorage.getItem("theme"))],
|
|
["select", "Notification sound:", Voice.sounds, e => {
|
|
Voice.setNotificationSound(Voice.sounds[e.target.selectedIndex]);
|
|
Voice.noises(Voice.sounds[e.target.selectedIndex]);
|
|
}, Voice.sounds.indexOf(Voice.getNotificationSound())]
|
|
],
|
|
["vdiv",
|
|
["html", hypothetcialprofie]
|
|
]
|
|
], _ => { }, function () {
|
|
console.log(this);
|
|
hypouser = new User(this.user, this);
|
|
regen();
|
|
file = null;
|
|
newprouns = null;
|
|
newbio = null;
|
|
}.bind(this));
|
|
}
|
|
}
|
|
export { Localuser };
|