snowflake and cleaning up classes

This commit is contained in:
MathMan05 2024-07-23 23:01:45 -05:00
parent 14d1c69c7d
commit 7eb3ff6cab
21 changed files with 584 additions and 361 deletions

View file

@ -6,7 +6,7 @@ import { Fullscreen } from "./fullscreen.js";
import { Permissions } from "./permissions.js";
import { Settings, RoleList } from "./settings.js";
import { InfiniteScroller } from "./infiniteScroller.js";
Settings;
import { SnowFlake } from "./snowflake.js";
class Channel {
editing;
type;
@ -35,8 +35,8 @@ class Channel {
static contextmenu = new Contextmenu("channel menu");
replyingto;
infinite;
idToPrev = {};
idToNext = {};
idToPrev = new Map();
idToNext = new Map();
static setupcontextmenu() {
this.contextmenu.addbutton("Copy channel id", function () {
console.log(this);
@ -73,22 +73,24 @@ class Channel {
setUpInfiniteScroller() {
const ids = {};
this.infinite = new InfiniteScroller(async function (id, offset) {
const snowflake = SnowFlake.getSnowFlakeFromID(id, Message);
if (offset === 1) {
if (this.idToPrev[id]) {
return this.idToPrev[id];
if (this.idToPrev.get(snowflake)) {
return this.idToPrev.get(snowflake)?.id;
}
else {
await this.grabmoremessages(id);
return this.idToPrev[id];
await this.grabBefore(id);
return this.idToPrev.get(snowflake)?.id;
}
}
else {
return this.idToNext[id];
return this.idToNext.get(snowflake)?.id;
}
}.bind(this), function (id) {
let res;
const promise = new Promise(_ => { res = _; });
const html = this.messageids[id].buildhtml(this.messageids[this.idToPrev[id]], promise);
const snowflake = SnowFlake.getSnowFlakeFromID(id, Message);
const html = this.messageids.get(snowflake).buildhtml(this.messageids.get(this.idToPrev.get(snowflake)), promise);
ids[id] = res;
return html;
}.bind(this), async function (id) {
@ -106,28 +108,27 @@ class Channel {
this.owner = owner;
this.headers = this.owner.headers;
this.name = JSON.name;
this.id = JSON.id;
this.parent_id = JSON.parent_id;
this.id = new SnowFlake(JSON.id, this);
this.parent_id = new SnowFlake(JSON.parent_id, undefined);
this.parent = null;
this.children = [];
this.guild_id = JSON.guild_id;
this.messageids = {};
this.permission_overwrites = {};
this.messageids = new Map();
this.permission_overwrites = new Map();
this.permission_overwritesar = [];
for (const thing of JSON.permission_overwrites) {
console.log(thing);
if (thing.id === "1182819038095799904" || thing.id === "1182820803700625444") {
continue;
}
;
this.permission_overwrites[thing.id] = new Permissions(thing.allow, thing.deny);
this.permission_overwritesar.push([thing.id, this.permission_overwrites[thing.id]]);
this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny));
this.permission_overwritesar.push([thing.id, this.permission_overwrites.get(thing.id)]);
}
this.topic = JSON.topic;
this.nsfw = JSON.nsfw;
this.position = JSON.position;
this.lastreadmessageid = null;
this.lastmessageid = JSON.last_message_id;
this.lastmessageid = SnowFlake.getSnowFlakeFromID(JSON.last_message_id, Message);
this.setUpInfiniteScroller();
}
isAdmin() {
@ -143,7 +144,7 @@ class Channel {
return this.owner.info;
}
readStateInfo(json) {
this.lastreadmessageid = json.last_message_id;
this.lastreadmessageid = SnowFlake.getSnowFlakeFromID(json.last_message_id, Message);
this.mentions = json.mention_count;
this.mentions ??= 0;
this.lastpin = json.last_pin_timestamp;
@ -152,15 +153,15 @@ class Channel {
if (!this.hasPermission("VIEW_CHANNEL")) {
return false;
}
return this.lastmessageid !== this.lastreadmessageid && this.type !== 4;
return this.lastmessageid !== this.lastreadmessageid && this.type !== 4 && !!this.lastmessageid.id;
}
hasPermission(name, member = this.guild.member) {
if (member.isAdmin()) {
return true;
}
for (const thing of member.roles) {
if (this.permission_overwrites[thing.id]) {
let perm = this.permission_overwrites[thing.id].getPermission(name);
if (this.permission_overwrites.get(thing.id.id)) {
let perm = this.permission_overwrites.get(thing.id.id).getPermission(name);
if (perm) {
return perm === 1;
}
@ -181,7 +182,7 @@ class Channel {
this.children.sort((a, b) => { return a.position - b.position; });
}
resolveparent(guild) {
this.parent = guild.channelids[this.parent_id];
this.parent = guild.channelids[this.parent_id?.id];
this.parent ??= null;
if (this.parent !== null) {
this.parent.children.push(this);
@ -343,7 +344,7 @@ class Channel {
if (!this.hasunreads) {
return;
}
fetch(this.info.api.toString() + "/v9/channels/" + this.id + "/messages/" + this.lastmessageid + "/ack", {
fetch(this.info.api.toString() + "/channels/" + this.id + "/messages/" + this.lastmessageid + "/ack", {
method: "POST",
headers: this.headers,
body: JSON.stringify({})
@ -433,8 +434,8 @@ class Channel {
["textbox", "Channel name:", this.name, function () { name = this.value; }],
["mdbox", "Channel topic:", this.topic, function () { topic = this.value; }],
["checkbox", "NSFW Channel", this.nsfw, function () { nsfw = this.checked; }],
["button", "", "submit", function () {
fetch(this.info.api.toString() + "/v9/channels/" + thisid, {
["button", "", "submit", () => {
fetch(this.info.api.toString() + "/channels/" + thisid, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
@ -457,7 +458,7 @@ class Channel {
console.log(full);
}
deleteChannel() {
fetch(this.info.api.toString() + "/v9/channels/" + this.id, {
fetch(this.info.api.toString() + "/channels/" + this.id, {
method: "DELETE",
headers: this.headers
});
@ -495,11 +496,12 @@ class Channel {
}
}
async getmessage(id) {
if (this.messageids[id]) {
return this.messageids[id];
const snowflake = SnowFlake.getSnowFlakeFromID(id, Message);
if (snowflake.getObject()) {
return snowflake.getObject();
}
else {
const gety = await fetch(this.info.api.toString() + "/v9/channels/" + this.id + "/messages?limit=1&around=" + id, { headers: this.headers });
const gety = await fetch(this.info.api.toString() + "/channels/" + this.id + "/messages?limit=1&around=" + id, { headers: this.headers });
const json = await gety.json();
return new Message(json[0], this);
}
@ -527,8 +529,9 @@ class Channel {
history.pushState(null, null, "/channels/" + this.guild_id + "/" + this.id);
document.getElementById("channelname").textContent = "#" + this.name;
console.log(this);
document.getElementById("typebox").disabled = !this.canMessage;
document.getElementById("typebox").contentEditable = "" + this.canMessage;
}
lastmessage;
async putmessages() {
if (this.allthewayup) {
return;
@ -545,12 +548,15 @@ class Channel {
for (const thing of response) {
const message = new Message(thing, this);
if (prev) {
this.idToNext[message.id] = prev.id;
this.idToPrev[prev.id] = message.id;
this.idToNext.set(message.id, prev.id);
this.idToPrev.set(prev.id, message.id);
}
else {
this.lastmessage = message;
}
prev = message;
if (this.messageids[message.id] === undefined) {
this.messageids[message.id] = message;
if (this.messageids.get(message.id) === undefined) {
this.messageids.set(message.id, message);
}
}
}
@ -563,7 +569,7 @@ class Channel {
}
this.children = build;
}
async grabmoremessages(id) {
async grabBefore(id) {
if (this.allthewayup) {
return;
}
@ -574,7 +580,7 @@ class Channel {
if (response.length === 0) {
this.allthewayup = true;
}
let previd = id;
let previd = SnowFlake.getSnowFlakeFromID(id, Message);
for (const i in response) {
let messager;
if (!next) {
@ -590,11 +596,11 @@ class Channel {
next = undefined;
console.log("ohno", +i + 1);
}
if (this.messageids[messager.id] === undefined) {
this.idToNext[messager.id] = previd;
this.idToPrev[previd] = messager.id;
if (this.messageids.get(messager.id) === undefined) {
this.idToNext.set(messager.id, previd);
this.idToPrev.set(previd, messager.id);
previd = messager.id;
this.messageids[messager.id] = messager;
this.messageids.set(messager.id, messager);
}
else {
console.log("How???");
@ -611,17 +617,47 @@ class Channel {
buildmessages() {
const messages = document.getElementById("channelw");
messages.innerHTML = "";
messages.append(this.infinite.getDiv(this.lastmessageid));
let id;
if (this.messageids.get(this.lastreadmessageid)) {
id = this.lastreadmessageid;
}
else if (this.lastmessage.id) {
id = this.goBackIds(this.lastmessage.id, 50);
console.log("shouldn't");
}
messages.append(this.infinite.getDiv(id.id));
}
goBackIds(id, back) {
while (back !== 0) {
const nextid = this.idToPrev.get(id);
if (nextid) {
id = nextid;
console.log(id);
back--;
}
else {
break;
}
}
return id;
}
updateChannel(JSON) {
this.type = JSON.type;
this.name = JSON.name;
this.parent_id = JSON.parent_id;
this.parent_id = new SnowFlake(JSON.parent_id, undefined);
this.parent = null;
this.children = [];
this.guild_id = JSON.guild_id;
this.messageids = {};
this.permission_overwrites = JSON.permission_overwrites;
this.messageids = new Map();
this.permission_overwrites = new Map();
for (const thing of JSON.permission_overwrites) {
if (thing.id === "1182819038095799904" || thing.id === "1182820803700625444") {
continue;
}
;
this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny));
this.permission_overwritesar.push([thing.id, this.permission_overwrites.get(thing.id)]);
}
this.topic = JSON.topic;
this.nsfw = JSON.nsfw;
}
@ -706,10 +742,11 @@ class Channel {
return;
}
const messagez = new Message(messagep.d, this);
this.idToNext[this.lastmessageid] = messagez.id;
this.idToPrev[messagez.id] = this.lastmessageid;
console.log(this.lastmessageid, messagez.id, ":3");
this.idToNext.set(this.lastmessageid, messagez.id);
this.idToPrev.set(messagez.id, this.lastmessageid);
this.lastmessageid = messagez.id;
this.messageids[messagez.id] = messagez;
this.messageids.set(messagez.id, messagez);
if (messagez.author === this.localuser.user) {
this.lastreadmessageid = messagez.id;
if (this.myhtml) {
@ -789,11 +826,11 @@ class Channel {
})
});
const perm = new Permissions("0", "0");
this.permission_overwrites[role.id] = perm;
this.permission_overwrites.set(role.id.id, perm);
this.permission_overwritesar.push([role.id, perm]);
}
async updateRolePermissions(id, perms) {
const permission = this.permission_overwrites[id];
const permission = this.permission_overwrites.get(id);
permission.allow = perms.allow;
permission.deny = perms.deny;
await fetch(this.info.api.toString() + "/channels/" + this.id + "/permissions/" + id, {

View file

@ -2,6 +2,7 @@ import { Guild } from "./guild.js";
import { Channel } from "./channel.js";
import { Message } from "./message.js";
import { User } from "./user.js";
import { SnowFlake } from "./snowflake.js";
class Direct extends Guild {
constructor(JSON, owner) {
super(-1, owner, null);
@ -14,16 +15,16 @@ class Direct extends Guild {
this.headers = this.localuser.headers;
this.channels = [];
this.channelids = {};
this.id = "@me";
this.id = new SnowFlake("@me", this);
this.properties = {};
this.roles = [];
this.roleids = {};
this.roleids = new Map();
this.prevchannel = undefined;
this.properties.name = "Direct Messages";
for (const thing of JSON) {
const temp = new Group(thing, this);
this.channels.push(temp);
this.channelids[temp.id] = temp;
this.channelids[temp.id.id] = temp;
}
this.headchannels = this.channels;
}
@ -36,7 +37,7 @@ class Direct extends Guild {
}
sortchannels() {
this.headchannels.sort((a, b) => {
const result = (BigInt(a.lastmessageid) - BigInt(b.lastmessageid));
const result = (a.lastmessageid.getUnixTime() - b.lastmessageid.getUnixTime());
return Number(-result);
});
}
@ -72,15 +73,15 @@ class Group extends Channel {
this.user = this.localuser.user;
}
this.name ??= this.localuser.user.username;
this.id = JSON.id;
this.id = new SnowFlake(JSON.id, this);
this.parent_id = null;
this.parent = null;
this.children = [];
this.guild_id = "@me";
this.messageids = {};
this.permission_overwrites = {};
this.lastmessageid = JSON.last_message_id;
this.lastmessageid ??= "0";
this.messageids = new Map();
this.permission_overwrites = new Map();
this.lastmessageid = SnowFlake.getSnowFlakeFromID(JSON.last_message_id, Message);
this.lastmessageid ??= new SnowFlake("0", undefined);
this.mentions = 0;
this.setUpInfiniteScroller();
}
@ -116,10 +117,10 @@ class Group extends Channel {
}
messageCreate(messagep) {
const messagez = new Message(messagep.d, this);
this.idToNext[this.lastmessageid] = messagez.id;
this.idToPrev[messagez.id] = this.lastmessageid;
this.idToNext.set(this.lastmessageid, messagez.id);
this.idToPrev.set(messagez.id, this.lastmessageid);
this.lastmessageid = messagez.id;
this.messageids[messagez.id] = messagez;
this.messageids.set(messagez.id, messagez);
if (messagez.author === this.localuser.user) {
this.lastreadmessageid = messagez.id;
if (this.myhtml) {

View file

@ -4,6 +4,7 @@ import { Role } from "./role.js";
import { Fullscreen } from "./fullscreen.js";
import { Member } from "./member.js";
import { Settings, RoleList } from "./settings.js";
import { SnowFlake } from "./snowflake.js";
class Guild {
owner;
headers;
@ -67,30 +68,30 @@ class Guild {
s1.options.push(new RoleList(permlist, this, this.updateRolePermissions.bind(this)));
settings.show();
}
constructor(JSON, owner, member) {
if (JSON === -1) {
constructor(json, owner, member) {
if (json === -1) {
return;
}
this.owner = owner;
this.headers = this.owner.headers;
this.channels = [];
this.channelids = {};
this.id = JSON.id;
this.properties = JSON.properties;
this.id = new SnowFlake(json.id, this);
this.properties = json.properties;
this.roles = [];
this.roleids = {};
this.roleids = new Map();
this.prevchannel = undefined;
this.message_notifications = 0;
for (const roley of JSON.roles) {
for (const roley of json.roles) {
const roleh = new Role(roley, this);
this.roles.push(roleh);
this.roleids[roleh.id] = roleh;
this.roleids.set(roleh.id, roleh);
}
Member.resolve(member, this).then(_ => this.member = _);
for (const thing of JSON.channels) {
for (const thing of json.channels) {
const temp = new Channel(thing, this);
this.channels.push(temp);
this.channelids[temp.id] = temp;
this.channelids[temp.id.id] = temp;
}
this.headchannels = [];
for (const thing of this.channels) {
@ -118,7 +119,7 @@ class Guild {
headers: this.headers,
body: JSON.stringify({
"guilds": {
[this.id]: {
[this.id.id]: {
"message_notifications": noti
}
}
@ -237,7 +238,7 @@ class Guild {
const noti = document.createElement("div");
noti.classList.add("unread");
divy.append(noti);
this.localuser.guildhtml[this.id] = divy;
this.localuser.guildhtml[this.id.id] = divy;
if (this.properties.icon != null) {
const img = document.createElement("img");
img.classList.add("pfp", "servericon");
@ -366,16 +367,10 @@ class Guild {
body: JSON.stringify(build)
});
}
getRole(ID) {
if (!this.roleids[ID]) {
console.error(`role id ${ID} does not exist`, this.roleids);
}
return this.roleids[ID];
}
hasRole(r) {
console.log("this should run");
if ((typeof r) !== (typeof "")) {
r = r.id;
if (r instanceof Role) {
r = r.id.id;
}
return this.member.hasRole(r);
}
@ -397,10 +392,10 @@ class Guild {
}
}
loadGuild() {
this.localuser.loadGuild(this.id);
this.localuser.loadGuild(this.id.id);
}
updateChannel(JSON) {
this.channelids[JSON.id].updateChannel(JSON);
SnowFlake.getSnowFlakeFromID(JSON.id, Channel).getObject().updateChannel(JSON);
this.headchannels = [];
for (const thing of this.channels) {
thing.children = [];
@ -507,7 +502,7 @@ class Guild {
});
const json = await fetched.json();
const role = new Role(json, this);
this.roleids[role.id] = role;
this.roleids[role.id.id] = role;
this.roles.push(role);
return role;
}

View file

@ -4,6 +4,8 @@ import { Voice } from "./audio.js";
import { User } from "./user.js";
import { Fullscreen } from "./fullscreen.js";
import { setTheme } from "./login.js";
import { SnowFlake } from "./snowflake.js";
import { Message } from "./message.js";
const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]);
class Localuser {
packets;
@ -43,14 +45,14 @@ class Localuser {
this.initialized = true;
this.ready = ready;
this.guilds = [];
this.guildids = {};
this.guildids = new Map();
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 = {};
this.guildhtml = new Map();
const members = {};
for (const thing of ready.d.merged_members) {
members[thing[0].guild_id] = thing[0];
@ -58,12 +60,12 @@ class Localuser {
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;
this.guildids[temp.id.id] = temp;
}
{
const temp = new Direct(ready.d.private_channels, this);
this.guilds.push(temp);
this.guildids[temp.id] = temp;
this.guildids[temp.id.id] = temp;
}
console.log(ready.d.user_guild_settings.entries);
for (const thing of ready.d.user_guild_settings.entries) {
@ -79,7 +81,7 @@ class Localuser {
continue;
}
const guildid = guild.id;
this.guildids[guildid].channelids[thing.channel_id].readStateInfo(thing);
this.guildids[guildid.id].channelids[thing.channel_id].readStateInfo(thing);
}
this.typing = [];
}
@ -97,7 +99,7 @@ class Localuser {
clearInterval(this.wsinterval);
this.outoffocus();
this.guilds = [];
this.guildids = {};
this.guildids = new Map();
this.ws.close(4000);
}
async initwebsocket() {
@ -128,90 +130,85 @@ class Localuser {
}));
});
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);
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);
SnowFlake.getSnowFlakeFromID(temp.d.id, Message).getObject().deleteEvent();
break;
case "READY":
this.gottenReady(temp);
this.genusersettings();
returny();
break;
case "MESSAGE_UPDATE":
const message = SnowFlake.getSnowFlakeFromID(temp.d.id, Message).getObject();
message.giveData(temp.d);
break;
case "TYPING_START":
if (this.initialized) {
this.typingStart(temp);
}
break;
case "USER_UPDATE":
if (this.initialized) {
const users = SnowFlake.getSnowFlakeFromID(temp.d.id, User).getObject();
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 "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":
const message = this.resolveChannelFromID(temp.d.channel_id).messageids[temp.d.id];
message.giveData(temp.d);
break;
case "TYPING_START":
if (this.initialized) {
this.typingStart(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("bottomseparator"));
}
}
}
else if (temp.op === 10) {
console.log("heartbeat down");
this.wsinterval = setInterval(_ => {
if (this.connectionSucceed === 0)
this.connectionSucceed = Date.now();
this.ws.send(JSON.stringify({ op: 1, d: this.packets }));
}, temp.d.heartbeat_interval);
this.packets = 1;
}
else if (temp.op != 11) {
this.packets++;
}
case "GUILD_CREATE":
{
const guildy = new Guild(temp.d, this, this.user);
this.guilds.push(guildy);
this.guildids[guildy.id.id] = guildy;
document.getElementById("servers").insertBefore(guildy.generateGuildIcon(), document.getElementById("bottomseparator"));
}
}
}
catch (error) {
console.error(error);
else if (temp.op === 10) {
console.log("heartbeat down");
this.wsinterval = setInterval(_ => {
if (this.connectionSucceed === 0)
this.connectionSucceed = Date.now();
this.ws.send(JSON.stringify({ op: 1, d: this.packets }));
}, temp.d.heartbeat_interval);
this.packets = 1;
}
else if (temp.op != 11) {
this.packets++;
}
});
this.ws.addEventListener("close", event => {
@ -253,15 +250,15 @@ class Localuser {
return undefined;
}
updateChannel(JSON) {
this.guildids[JSON.guild_id].updateChannel(JSON);
if (JSON.guild_id === this.lookingguild.id) {
SnowFlake.getSnowFlakeFromID(JSON.guild_id, Guild).getObject().updateChannel(JSON);
if (JSON.guild_id === this.lookingguild.id.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) {
SnowFlake.getSnowFlakeFromID(JSON.guild_id, Guild).getObject().createChannelpac(JSON);
if (JSON.guild_id === this.lookingguild.id.id) {
this.loadGuild(JSON.guild_id);
}
}
@ -469,10 +466,10 @@ class Localuser {
unreads() {
console.log(this.guildhtml);
for (const thing of this.guilds) {
if (thing.id === "@me") {
if (thing.id.id === "@me") {
continue;
}
thing.unreads(this.guildhtml[thing.id]);
thing.unreads(this.guildhtml[thing.id.id]);
}
}
typingStart(typing) {

View file

@ -1,6 +1,8 @@
import { User } from "./user.js";
import { Role } from "./role.js";
import { Guild } from "./guild.js";
import { Contextmenu } from "./contextmenu.js";
import { SnowFlake } from "./snowflake.js";
class Member {
static already = {};
owner;
@ -39,7 +41,7 @@ class Member {
}
if (thing === "roles") {
for (const strrole of membery["roles"]) {
const role = this.guild.getRole(strrole);
const role = SnowFlake.getSnowFlakeFromID(strrole, Role).getObject();
this.roles.push(role);
}
continue;
@ -67,50 +69,50 @@ class Member {
console.error(guild);
}
let user;
let id = "";
let id;
if (unkown instanceof User) {
user = unkown;
id = user.id;
}
else if (typeof unkown === typeof "") {
id = unkown;
id = new SnowFlake(unkown, undefined);
}
else {
return new Member(unkown, guild);
}
if (guild.id === "@me") {
if (guild.id.id === "@me") {
return null;
}
if (!Member.already[guild.id]) {
Member.already[guild.id] = {};
if (!Member.already[guild.id.id]) {
Member.already[guild.id.id] = {};
}
else if (Member.already[guild.id][id]) {
const memb = Member.already[guild.id][id];
else if (Member.already[guild.id.id][id]) {
const memb = Member.already[guild.id.id][id];
if (memb instanceof Promise) {
return await memb;
}
return memb;
}
const promoise = fetch(guild.info.api.toString() + "/v9/users/" + id + "/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id=" + guild.id, { headers: guild.headers }).then(_ => _.json()).then(json => {
const promoise = fetch(guild.info.api.toString() + "/users/" + id + "/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id=" + guild.id, { headers: guild.headers }).then(_ => _.json()).then(json => {
const memb = new Member(json, guild);
Member.already[guild.id][id] = memb;
Member.already[guild.id.id][id] = memb;
console.log("resolved");
return memb;
});
Member.already[guild.id][id] = promoise;
Member.already[guild.id.id][id] = promoise;
try {
return await promoise;
}
catch (_) {
const memb = new Member(user, guild, true);
Member.already[guild.id][id] = memb;
Member.already[guild.id.id][id] = memb;
return memb;
}
}
hasRole(ID) {
console.log(this.roles, ID);
for (const thing of this.roles) {
if (thing.id === ID) {
if (thing.id.id === ID) {
return true;
}
}
@ -131,7 +133,7 @@ class Member {
return true;
}
}
return this.guild.properties.owner_id === this.user.id;
return this.guild.properties.owner_id === this.user.id.id;
}
bind(html) {
if (html.tagName === "SPAN") {

View file

@ -4,6 +4,7 @@ import { Member } from "./member.js";
import { MarkDown } from "./markdown.js";
import { Embed } from "./embed.js";
import { File } from "./file.js";
import { SnowFlake } from "./snowflake.js";
class Message {
static contextmenu = new Contextmenu("message menu");
owner;
@ -39,7 +40,7 @@ class Message {
this.channel.setReplying(this);
});
Message.contextmenu.addbutton("Copy message id", function () {
navigator.clipboard.writeText(this.id);
navigator.clipboard.writeText(this.id.id);
});
Message.contextmenu.addbutton("Edit", function () {
this.channel.editing = this;
@ -69,6 +70,10 @@ class Message {
this.content = new MarkDown(messagejson[thing], this.channel);
continue;
}
else if (thing === "id") {
this.id = new SnowFlake(messagejson.id, this);
continue;
}
this[thing] = messagejson[thing];
}
for (const thing in this.embeds) {
@ -132,14 +137,14 @@ class Message {
return build;
}
async edit(content) {
return await fetch(this.info.api.toString() + "/channels/" + this.channel.id + "/messages/" + this.id, {
return await fetch(this.info.api.toString() + "/channels/" + this.channel.id + "/messages/" + this.id.id, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({ content: content })
});
}
delete() {
fetch(`${this.info.api.toString()}/channels/${this.channel.id}/messages/${this.id}`, {
fetch(`${this.info.api.toString()}/channels/${this.channel.id}/messages/${this.id.id}`, {
headers: this.headers,
method: "DELETE",
});
@ -149,19 +154,22 @@ class Message {
this.div.innerHTML = "";
this.div = null;
}
const prev = this.channel.idToPrev[this.id];
const next = this.channel.idToNext[this.id];
const prev = this.channel.idToPrev[this.id.id];
const next = this.channel.idToNext[this.id.id];
this.channel.idToNext[prev] = next;
this.channel.idToPrev[next] = prev;
delete this.channel.messageids[this.id];
delete this.channel.messageids[this.id.id];
const regen = this.channel.messageids[prev];
if (regen) {
regen.generateMessage();
}
if (this.channel.lastmessage === this) {
this.channel.lastmessage = this.channel.messageids[prev];
}
}
generateMessage(premessage = null) {
if (!premessage) {
premessage = this.channel.messageids[this.channel.idToNext[this.id]];
premessage = this.channel.messageids[this.channel.idToNext[this.id.id]];
}
const div = this.div;
if (this === this.channel.replyingto) {

View file

@ -1,5 +1,6 @@
export { Role };
import { Permissions } from "./permissions.js";
import { SnowFlake } from "./snowflake.js";
class Role {
permissions;
owner;
@ -16,6 +17,10 @@ class Role {
this.headers = owner.headers;
this.info = owner.info;
for (const thing of Object.keys(JSON)) {
if (thing === "id") {
this.id = new SnowFlake(JSON.id, this);
continue;
}
this[thing] = JSON[thing];
}
this.permissions = new Permissions(JSON.permissions);

View file

@ -1,4 +1,6 @@
import { Permissions } from "./permissions.js";
import { SnowFlake } from "./snowflake.js";
import { Role } from "./role.js";
class Buttons {
name;
buttons;
@ -154,16 +156,17 @@ class RoleList extends Buttons {
options.addPermissionToggle(thing, this.permission); //
}
for (const i of permissions) {
this.buttons.push([guild.getRole(i[0]).name, i[0]]); //
console.log(i);
this.buttons.push([i[0].getObject().name, i[0].id]); //
}
this.options = options;
}
handleString(str) {
this.curid = str;
const perm = this.permissions.find(_ => _[0] === str)[1];
const perm = this.permissions.find(_ => _[0].id === str)[1];
this.permission.deny = perm.deny;
this.permission.allow = perm.allow;
this.options.name = this.guild.getRole(str).name;
this.options.name = SnowFlake.getSnowFlakeFromID(str, Role).getObject().name;
this.options.haschanged = false;
return this.options.generateHTML();
}

51
.dist/snowflake.js Normal file
View file

@ -0,0 +1,51 @@
class SnowFlake {
id;
static SnowFlakes = new Map();
static FinalizationRegistry = new FinalizationRegistry((id) => {
SnowFlake.SnowFlakes.delete(id);
});
obj;
constructor(id, obj) {
if (!obj) {
this.id = id;
return;
}
if (!SnowFlake.SnowFlakes.get(obj.constructor)) {
SnowFlake.SnowFlakes.set(obj.constructor, new Map());
}
if (SnowFlake.SnowFlakes.get(obj.constructor).get(id)) {
const snowflake = SnowFlake.SnowFlakes.get(obj.constructor).get(id).deref();
snowflake.obj = obj;
return snowflake;
}
this.id = id;
SnowFlake.SnowFlakes.get(obj.constructor).set(id, new WeakRef(this));
SnowFlake.FinalizationRegistry.register(this, id);
this.obj = obj;
}
static getSnowFlakeFromID(id, type) {
if (!SnowFlake.SnowFlakes.get(type)) {
SnowFlake.SnowFlakes.set(type, new Map());
}
const snowflake = SnowFlake.SnowFlakes.get(type).get(id);
if (snowflake) {
return snowflake.deref();
}
{
const snowflake = new SnowFlake(id, undefined);
SnowFlake.SnowFlakes.get(type).set(id, new WeakRef(snowflake));
SnowFlake.FinalizationRegistry.register(snowflake, id);
return snowflake;
}
}
getUnixTime() {
return Number((BigInt(this.id) >> 22n) + 1420070400000n);
}
toString() {
return this.id;
}
getObject() {
return this.obj;
}
}
export { SnowFlake };

View file

@ -2,6 +2,7 @@
import { Member } from "./member.js";
import { MarkDown } from "./markdown.js";
import { Contextmenu } from "./contextmenu.js";
import { SnowFlake } from "./snowflake.js";
class User {
static userids = {};
owner;
@ -16,12 +17,12 @@ class User {
static contextmenu = new Contextmenu("User Menu");
static setUpContextMenu() {
this.contextmenu.addbutton("Copy user id", function () {
navigator.clipboard.writeText(this.id);
navigator.clipboard.writeText(this.id.id);
});
this.contextmenu.addbutton("Message user", function () {
fetch(this.info.api.toString() + "/v9/users/@me/channels", { method: "POST",
body: JSON.stringify({ "recipients": [this.id] }),
headers: this.headers
body: JSON.stringify({ "recipients": [this.id.id] }),
headers: this.localuser.headers
});
});
}
@ -52,6 +53,10 @@ class User {
this.bio = new MarkDown(userjson[thing], this.localuser);
continue;
}
if (thing === "id") {
this.id = new SnowFlake(userjson[thing], this);
continue;
}
this[thing] = userjson[thing];
}
this.hypotheticalpfp = false;
@ -67,7 +72,7 @@ class User {
const pfp = document.createElement('img');
pfp.src = this.getpfpsrc();
pfp.classList.add("pfp");
pfp.classList.add("userid:" + this.id);
pfp.classList.add("userid:" + this.id.id);
return pfp;
}
userupdate(json) {
@ -77,7 +82,7 @@ class User {
}
}
bind(html, guild = null) {
if (guild && guild.id !== "@me") {
if (guild && guild.id.id !== "@me") {
Member.resolve(this, guild).then(_ => {
_.bind(html);
}).catch(_ => {
@ -96,7 +101,7 @@ class User {
this.hypotheticalpfp = false;
const src = this.getpfpsrc();
console.log(src);
for (const thing of document.getElementsByClassName("userid:" + this.id)) {
for (const thing of document.getElementsByClassName("userid:" + this.id.id)) {
thing.src = src;
}
}
@ -105,7 +110,7 @@ class User {
return this.avatar;
}
if (this.avatar != null) {
return this.info.cdn.toString() + "avatars/" + this.id + "/" + this.avatar + ".png";
return this.info.cdn.toString() + "avatars/" + this.id.id + "/" + this.avatar + ".png";
}
else {
return this.info.cdn.toString() + "embed/avatars/3.png";

View file

@ -3,14 +3,14 @@ import { Message } from "./message.js";
import {Voice} from "./audio.js";
import {Contextmenu} from "./contextmenu.js";
import {Fullscreen} from "./fullscreen.js";
import {MarkDown} from "./markdown.js";
import {Guild} from "./guild.js";
import { Localuser } from "./localuser.js";
import { Permissions } from "./permissions.js";
import { Settings, RoleList } from "./settings.js";
import { Role } from "./role.js";
import {InfiniteScroller} from "./infiniteScroller.js"
Settings;
import {InfiniteScroller} from "./infiniteScroller.js";
import { SnowFlake } from "./snowflake.js";
declare global {
interface NotificationOptions {
image?: string
@ -22,30 +22,30 @@ class Channel{
owner:Guild;
headers:Localuser["headers"];
name:string;
id:string;
parent_id:string;
id:SnowFlake<Channel>;
parent_id:SnowFlake<Channel>;
parent:Channel;
children:Channel[];
guild_id:string;
messageids:{[key : string]:Message};
permission_overwrites:{[key:string]:Permissions};
permission_overwritesar:[string,Permissions][]
messageids:Map<SnowFlake<Message>,Message>;
permission_overwrites:Map<string,Permissions>;
permission_overwritesar:[SnowFlake<Role>,Permissions][]
topic:string;
nsfw:boolean;
position:number;
lastreadmessageid:string;
lastmessageid:string;
lastreadmessageid:SnowFlake<Message>;
lastmessageid:SnowFlake<Message>;
mentions:number;
lastpin:string;
move_id:string;
move_id:SnowFlake<Channel>;
typing:number;
message_notifications:number;
allthewayup:boolean;
static contextmenu=new Contextmenu("channel menu");
replyingto:Message;
infinite:InfiniteScroller;
idToPrev:{[key:string]:string}={};
idToNext:{[key:string]:string}={};
idToPrev:Map<SnowFlake<Message>,SnowFlake<Message>>=new Map();
idToNext:Map<SnowFlake<Message>,SnowFlake<Message>>=new Map();
static setupcontextmenu(){
this.contextmenu.addbutton("Copy channel id",function(){
console.log(this)
@ -87,26 +87,28 @@ class Channel{
}
setUpInfiniteScroller(){
const ids:{[key:string]:Function}={};
this.infinite=new InfiniteScroller(async function(id:string,offset:number){
this.infinite=new InfiniteScroller(async function(this:Channel,id:string,offset:number):Promise<string>{
const snowflake=SnowFlake.getSnowFlakeFromID(id,Message) as SnowFlake<Message>;
if(offset===1){
if(this.idToPrev[id]){
return this.idToPrev[id];
if(this.idToPrev.get(snowflake)){
return this.idToPrev.get(snowflake)?.id;
}else{
await this.grabmoremessages(id);
return this.idToPrev[id];
await this.grabBefore(id);
return this.idToPrev.get(snowflake)?.id;
}
}else{
return this.idToNext[id];
return this.idToNext.get(snowflake)?.id;
}
}.bind(this),
function(this:Channel,id:string){
let res:Function;
const promise=new Promise(_=>{res=_;}) as Promise<void>;
const html=this.messageids[id].buildhtml(this.messageids[this.idToPrev[id]],promise);
const snowflake=SnowFlake.getSnowFlakeFromID(id,Message) as SnowFlake<Message>;
const html=this.messageids.get(snowflake).buildhtml(this.messageids.get(this.idToPrev.get(snowflake)),promise);
ids[id]=res;
return html;
}.bind(this),
async function(id:string){
async function(this:Channel,id:string){
ids[id]();
delete ids[id];
return true;
@ -125,26 +127,25 @@ class Channel{
this.owner=owner;
this.headers=this.owner.headers;
this.name=JSON.name;
this.id=JSON.id;
this.parent_id=JSON.parent_id;
this.id=new SnowFlake(JSON.id,this);
this.parent_id=new SnowFlake(JSON.parent_id,undefined);
this.parent=null;
this.children=[];
this.guild_id=JSON.guild_id;
this.messageids={};
this.permission_overwrites={};
this.messageids=new Map();
this.permission_overwrites=new Map();
this.permission_overwritesar=[];
for(const thing of JSON.permission_overwrites){
console.log(thing);
if(thing.id==="1182819038095799904"||thing.id==="1182820803700625444"){continue;};
this.permission_overwrites[thing.id]=new Permissions(thing.allow,thing.deny);
this.permission_overwritesar.push([thing.id,this.permission_overwrites[thing.id]]);
this.permission_overwrites.set(thing.id,new Permissions(thing.allow,thing.deny));
this.permission_overwritesar.push([thing.id,this.permission_overwrites.get(thing.id)]);
}
this.topic=JSON.topic;
this.nsfw=JSON.nsfw;
this.position=JSON.position;
this.lastreadmessageid=null;
this.lastmessageid=JSON.last_message_id;
this.lastmessageid=SnowFlake.getSnowFlakeFromID(JSON.last_message_id,Message);
this.setUpInfiniteScroller();
}
isAdmin(){
@ -160,22 +161,22 @@ class Channel{
return this.owner.info;
}
readStateInfo(json){
this.lastreadmessageid=json.last_message_id;
this.lastreadmessageid=SnowFlake.getSnowFlakeFromID(json.last_message_id,Message);
this.mentions=json.mention_count;
this.mentions??=0;
this.lastpin=json.last_pin_timestamp;
}
get hasunreads():boolean{
if(!this.hasPermission("VIEW_CHANNEL")){return false;}
return this.lastmessageid!==this.lastreadmessageid&&this.type!==4;
return this.lastmessageid!==this.lastreadmessageid&&this.type!==4&&!!this.lastmessageid.id;
}
hasPermission(name:string,member=this.guild.member):boolean{
if(member.isAdmin()){
return true;
}
for(const thing of member.roles){
if(this.permission_overwrites[thing.id]){
let perm=this.permission_overwrites[thing.id].getPermission(name);
if(this.permission_overwrites.get(thing.id.id)){
let perm=this.permission_overwrites.get(thing.id.id).getPermission(name);
if(perm){
return perm===1;
}
@ -196,7 +197,7 @@ class Channel{
this.children.sort((a,b)=>{return a.position-b.position});
}
resolveparent(guild:Guild){
this.parent=guild.channelids[this.parent_id];
this.parent=guild.channelids[this.parent_id?.id];
this.parent??=null;
if(this.parent!==null){
this.parent.children.push(this);
@ -354,7 +355,7 @@ class Channel{
if(!this.hasunreads){
return;
}
fetch(this.info.api.toString()+"/v9/channels/"+this.id+"/messages/"+this.lastmessageid+"/ack",{
fetch(this.info.api.toString()+"/channels/"+this.id+"/messages/"+this.lastmessageid+"/ack",{
method:"POST",
headers:this.headers,
body:JSON.stringify({})
@ -445,8 +446,8 @@ class Channel{
["textbox","Channel name:",this.name,function(){name=this.value}],
["mdbox","Channel topic:",this.topic,function(){topic=this.value}],
["checkbox","NSFW Channel",this.nsfw,function(){nsfw=this.checked}],
["button","","submit",function(){
fetch(this.info.api.toString()+"/v9/channels/"+thisid,{
["button","","submit",()=>{
fetch(this.info.api.toString()+"/channels/"+thisid,{
method:"PATCH",
headers:this.headers,
body:JSON.stringify({
@ -470,7 +471,7 @@ class Channel{
console.log(full)
}
deleteChannel(){
fetch(this.info.api.toString()+"/v9/channels/"+this.id,{
fetch(this.info.api.toString()+"/channels/"+this.id,{
method:"DELETE",
headers:this.headers
})
@ -508,10 +509,11 @@ class Channel{
}
}
async getmessage(id:string):Promise<Message>{
if(this.messageids[id]){
return this.messageids[id];
const snowflake=SnowFlake.getSnowFlakeFromID(id,Message) as SnowFlake<Message>;
if(snowflake.getObject()){
return snowflake.getObject();
}else{
const gety=await fetch(this.info.api.toString()+"/v9/channels/"+this.id+"/messages?limit=1&around="+id,{headers:this.headers})
const gety=await fetch(this.info.api.toString()+"/channels/"+this.id+"/messages?limit=1&around="+id,{headers:this.headers})
const json=await gety.json();
return new Message(json[0],this);
}
@ -539,13 +541,15 @@ class Channel{
history.pushState(null, null,"/channels/"+this.guild_id+"/"+this.id);
document.getElementById("channelname").textContent="#"+this.name;
console.log(this);
(document.getElementById("typebox") as HTMLInputElement).disabled=!this.canMessage;
(document.getElementById("typebox") as HTMLInputElement).contentEditable=""+this.canMessage;
}
lastmessage:Message;
async putmessages(){
if(this.allthewayup){return};
const j=await fetch(this.info.api.toString()+"/channels/"+this.id+"/messages?limit=100",{
headers: this.headers,
})
});
const response=await j.json();
if(response.length!==100){
this.allthewayup=true;
@ -554,12 +558,14 @@ class Channel{
for(const thing of response){
const message=new Message(thing,this);
if(prev){
this.idToNext[message.id]=prev.id;
this.idToPrev[prev.id]=message.id;
this.idToNext.set(message.id,prev.id);
this.idToPrev.set(prev.id,message.id);
}else{
this.lastmessage=message;
}
prev=message;
if(this.messageids[message.id]===undefined){
this.messageids[message.id]=message;
if(this.messageids.get(message.id)===undefined){
this.messageids.set(message.id,message);
}
}
}
@ -572,7 +578,7 @@ class Channel{
}
this.children=build;
}
async grabmoremessages(id:string){
async grabBefore(id:string){
if(this.allthewayup){
return;
}
@ -584,7 +590,7 @@ class Channel{
if(response.length===0){
this.allthewayup=true;
}
let previd=id;
let previd=SnowFlake.getSnowFlakeFromID(id,Message) as SnowFlake<Message>;
for(const i in response){
let messager:Message;
if(!next){
@ -598,11 +604,11 @@ class Channel{
next=undefined;
console.log("ohno",+i+1);
}
if(this.messageids[messager.id]===undefined){
this.idToNext[messager.id]=previd;
this.idToPrev[previd]=messager.id;
if(this.messageids.get(messager.id)===undefined){
this.idToNext.set(messager.id,previd);
this.idToPrev.set(previd,messager.id);
previd=messager.id;
this.messageids[messager.id]=messager;
this.messageids.set(messager.id,messager);
}else{
console.log("How???")
}
@ -618,17 +624,42 @@ class Channel{
buildmessages(){
const messages=document.getElementById("channelw");
messages.innerHTML="";
messages.append(this.infinite.getDiv(this.lastmessageid));
let id:SnowFlake<Message>;
if(this.messageids.get(this.lastreadmessageid)){
id=this.lastreadmessageid;
}else if(this.lastmessage.id){
id=this.goBackIds(this.lastmessage.id,50);
console.log("shouldn't")
}
messages.append(this.infinite.getDiv(id.id));
}
private goBackIds(id:SnowFlake<Message>,back:number):SnowFlake<Message>{
while(back!==0){
const nextid=this.idToPrev.get(id);
if(nextid){
id=nextid;
console.log(id);
back--;
}else{
break;
}
}
return id;
}
updateChannel(JSON){
this.type=JSON.type;
this.name=JSON.name;
this.parent_id=JSON.parent_id;
this.parent_id=new SnowFlake(JSON.parent_id,undefined);
this.parent=null;
this.children=[];
this.guild_id=JSON.guild_id;
this.messageids={};
this.permission_overwrites=JSON.permission_overwrites;
this.messageids=new Map();
this.permission_overwrites=new Map();
for(const thing of JSON.permission_overwrites){
if(thing.id==="1182819038095799904"||thing.id==="1182820803700625444"){continue;};
this.permission_overwrites.set(thing.id,new Permissions(thing.allow,thing.deny));
this.permission_overwritesar.push([thing.id,this.permission_overwrites.get(thing.id)]);
}
this.topic=JSON.topic;
this.nsfw=JSON.nsfw;
}
@ -707,10 +738,11 @@ class Channel{
messageCreate(messagep:any):void{
if(!this.hasPermission("VIEW_CHANNEL")){return}
const messagez=new Message(messagep.d,this);
this.idToNext[this.lastmessageid]=messagez.id;
this.idToPrev[messagez.id]=this.lastmessageid;
console.log(this.lastmessageid,messagez.id,":3");
this.idToNext.set(this.lastmessageid,messagez.id);
this.idToPrev.set(messagez.id,this.lastmessageid);
this.lastmessageid=messagez.id;
this.messageids[messagez.id]=messagez;
this.messageids.set(messagez.id,messagez);
if(messagez.author===this.localuser.user){
this.lastreadmessageid=messagez.id;
if(this.myhtml){
@ -785,11 +817,11 @@ class Channel{
})
})
const perm=new Permissions("0","0");
this.permission_overwrites[role.id]=perm;
this.permission_overwrites.set(role.id.id,perm);
this.permission_overwritesar.push([role.id,perm]);
}
async updateRolePermissions(id:string,perms:Permissions){
const permission=this.permission_overwrites[id];
const permission=this.permission_overwrites.get(id);
permission.allow=perms.allow;
permission.deny=perms.deny;
await fetch(this.info.api.toString()+"/channels/"+this.id+"/permissions/"+id,{

View file

@ -4,6 +4,7 @@ import { Message } from "./message.js";
import { Localuser } from "./localuser.js";
import {User} from "./user.js";
import { Member } from "./member.js";
import { SnowFlake } from "./snowflake.js";
class Direct extends Guild{
constructor(JSON,owner:Localuser){
@ -17,16 +18,16 @@ class Direct extends Guild{
this.headers=this.localuser.headers;
this.channels=[];
this.channelids={};
this.id="@me";
this.id=new SnowFlake("@me",this);
this.properties={};
this.roles=[];
this.roleids={};
this.roleids=new Map();
this.prevchannel=undefined;
this.properties.name="Direct Messages";
for(const thing of JSON){
const temp=new Group(thing,this);
this.channels.push(temp);
this.channelids[temp.id]=temp;
this.channelids[temp.id.id]=temp;
}
this.headchannels=this.channels;
}
@ -39,7 +40,7 @@ class Direct extends Guild{
}
sortchannels(){
this.headchannels.sort((a,b)=>{
const result=(BigInt(a.lastmessageid)-BigInt(b.lastmessageid));
const result=(a.lastmessageid.getUnixTime()-b.lastmessageid.getUnixTime());
return Number(-result);
});
}
@ -74,15 +75,15 @@ class Group extends Channel{
this.user=this.localuser.user;
}
this.name??=this.localuser.user.username;
this.id=JSON.id;
this.id=new SnowFlake(JSON.id,this);
this.parent_id=null;
this.parent=null;
this.children=[];
this.guild_id="@me";
this.messageids={};
this.permission_overwrites={};
this.lastmessageid=JSON.last_message_id;
this.lastmessageid??="0";
this.messageids=new Map();
this.permission_overwrites=new Map();
this.lastmessageid=SnowFlake.getSnowFlakeFromID(JSON.last_message_id,Message);
this.lastmessageid??=new SnowFlake("0",undefined);
this.mentions=0;
this.setUpInfiniteScroller();
}
@ -118,10 +119,10 @@ class Group extends Channel{
}
messageCreate(messagep){
const messagez=new Message(messagep.d,this);
this.idToNext[this.lastmessageid]=messagez.id;
this.idToPrev[messagez.id]=this.lastmessageid;
this.idToNext.set(this.lastmessageid,messagez.id);
this.idToPrev.set(messagez.id,this.lastmessageid);
this.lastmessageid=messagez.id;
this.messageids[messagez.id]=messagez;
this.messageids.set(messagez.id,messagez);
if(messagez.author===this.localuser.user){
this.lastreadmessageid=messagez.id;
if(this.myhtml){

View file

@ -6,15 +6,16 @@ import {Fullscreen} from "./fullscreen.js";
import {Member} from "./member.js";
import {Settings,RoleList} from "./settings.js";
import {Permissions} from "./permissions.js";
import { SnowFlake } from "./snowflake.js";
class Guild{
owner:Localuser;
headers:Localuser["headers"];
channels:Channel[];
channelids:{[key:string]:Channel};
id:string;
id:SnowFlake<Guild>;
properties
roles:Role[];
roleids:{[key:string]:Role};
roleids:Map<SnowFlake<Role>,Role>;
prevchannel:Channel;
message_notifications:number;
headchannels:Channel[];
@ -75,30 +76,30 @@ class Guild{
s1.options.push(new RoleList(permlist,this,this.updateRolePermissions.bind(this)));
settings.show();
}
constructor(JSON,owner:Localuser,member){
if(JSON===-1){
constructor(json,owner:Localuser,member){
if(json===-1){
return;
}
this.owner=owner;
this.headers=this.owner.headers;
this.channels=[];
this.channelids={};
this.id=JSON.id;
this.properties=JSON.properties;
this.id=new SnowFlake(json.id,this);
this.properties=json.properties;
this.roles=[];
this.roleids={};
this.roleids=new Map();
this.prevchannel=undefined;
this.message_notifications=0;
for(const roley of JSON.roles){
for(const roley of json.roles){
const roleh=new Role(roley,this);
this.roles.push(roleh)
this.roleids[roleh.id]=roleh;
this.roleids.set(roleh.id,roleh);
}
Member.resolve(member,this).then(_=>this.member=_);
for(const thing of JSON.channels){
for(const thing of json.channels){
const temp=new Channel(thing,this);
this.channels.push(temp);
this.channelids[temp.id]=temp;
this.channelids[temp.id.id]=temp;
}
this.headchannels=[];
for(const thing of this.channels){
@ -128,7 +129,7 @@ class Guild{
headers:this.headers,
body:JSON.stringify({
"guilds":{
[this.id]:{
[this.id.id]:{
"message_notifications": noti
}
}
@ -247,7 +248,7 @@ class Guild{
const noti=document.createElement("div");
noti.classList.add("unread");
divy.append(noti);
this.localuser.guildhtml[this.id]=divy;
this.localuser.guildhtml[this.id.id]=divy;
if(this.properties.icon!=null){
const img=document.createElement("img");
img.classList.add("pfp","servericon");
@ -373,16 +374,12 @@ class Guild{
body:JSON.stringify(build)
})
}
getRole(ID:string):Role{
if(!this.roleids[ID]){console.error(`role id ${ID} does not exist`,this.roleids)}
return this.roleids[ID];
}
hasRole(r:Role|string){
console.log("this should run");
if((typeof r)!==(typeof "")){
r=(r as Role).id;
if(r instanceof Role){
r=r.id.id;
}
return this.member.hasRole(r as string);
return this.member.hasRole(r);
}
loadChannel(ID:string=undefined){
if(ID&&this.channelids[ID]){
@ -402,10 +399,10 @@ class Guild{
}
}
loadGuild(){
this.localuser.loadGuild(this.id);
this.localuser.loadGuild(this.id.id);
}
updateChannel(JSON){
this.channelids[JSON.id].updateChannel(JSON);
SnowFlake.getSnowFlakeFromID(JSON.id,Channel).getObject().updateChannel(JSON);
this.headchannels=[];
for(const thing of this.channels){
thing.children=[];
@ -516,7 +513,7 @@ class Guild{
})
const json=await fetched.json();
const role=new Role(json,this);
this.roleids[role.id]=role;
this.roleids[role.id.id]=role;
this.roles.push(role);
return role;
}

View file

@ -7,6 +7,8 @@ import {Member} from "./member.js";
import {MarkDown} from "./markdown.js";
import {Fullscreen} from "./fullscreen.js";
import {setTheme, Specialuser} from "./login.js";
import { SnowFlake } from "./snowflake.js";
import { Message } from "./message.js";
const wsCodesRetry=new Set([4000,4003,4005,4007,4008,4009]);
@ -23,12 +25,12 @@ class Localuser{
devPortal:Fullscreen;
ready;
guilds:Guild[];
guildids:{ [key: string]: Guild };
guildids:Map<string,Guild>;
user:User;
status:string;
channelfocus:Channel;
lookingguild:Guild;
guildhtml:Record<string, HTMLDivElement>;
guildhtml:Map<string, HTMLDivElement>;
ws:WebSocket;
typing:[string,number][];
wsinterval:NodeJS.Timeout;
@ -48,14 +50,14 @@ class Localuser{
this.initialized=true;
this.ready=ready;
this.guilds=[];
this.guildids={};
this.guildids=new Map();
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={};
this.guildhtml=new Map();
const members={};
for(const thing of ready.d.merged_members){
members[thing[0].guild_id]=thing[0];
@ -64,12 +66,12 @@ class Localuser{
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;
this.guildids[temp.id.id]=temp;
}
{
const temp=new Direct(ready.d.private_channels,this);
this.guilds.push(temp);
this.guildids[temp.id]=temp;
this.guildids[temp.id.id]=temp;
}
console.log(ready.d.user_guild_settings.entries);
@ -86,7 +88,7 @@ class Localuser{
continue
}
const guildid=guild.id;
this.guildids[guildid].channelids[thing.channel_id].readStateInfo(thing);
this.guildids[guildid.id].channelids[thing.channel_id].readStateInfo(thing);
}
this.typing=[];
}
@ -104,7 +106,7 @@ class Localuser{
clearInterval(this.wsinterval);
this.outoffocus();
this.guilds=[];
this.guildids={};
this.guildids=new Map();
this.ws.close(4000)
}
async initwebsocket():Promise<void>{
@ -138,7 +140,7 @@ class Localuser{
this.ws.addEventListener('message', (event) => {
try{
const temp=JSON.parse(event.data);
console.log(temp)
if(temp.op==0){
@ -150,7 +152,7 @@ class Localuser{
break;
case "MESSAGE_DELETE":
console.log(temp.d);
this.guildids[temp.d.guild_id].channelids[temp.d.channel_id].messageids[temp.d.id].deleteEvent();
SnowFlake.getSnowFlakeFromID(temp.d.id,Message).getObject().deleteEvent();
break;
case "READY":
this.gottenReady(temp);
@ -158,7 +160,7 @@ class Localuser{
returny();
break;
case "MESSAGE_UPDATE":
const message=this.resolveChannelFromID(temp.d.channel_id).messageids[temp.d.id];
const message=SnowFlake.getSnowFlakeFromID(temp.d.id,Message).getObject();
message.giveData(temp.d);
break;
case "TYPING_START":
@ -168,7 +170,7 @@ class Localuser{
break;
case "USER_UPDATE":
if(this.initialized){
const users=User.userids[temp.d.id];
const users=SnowFlake.getSnowFlakeFromID(temp.d.id,User).getObject() as User;
console.log(users,temp.d.id)
if(users){
users.userupdate(temp.d);
@ -202,7 +204,7 @@ class Localuser{
{
const guildy=new Guild(temp.d,this,this.user);
this.guilds.push(guildy);
this.guildids[guildy.id]=guildy;
this.guildids[guildy.id.id]=guildy;
document.getElementById("servers").insertBefore(guildy.generateGuildIcon(),document.getElementById("bottomseparator"));
}
}
@ -218,9 +220,7 @@ class Localuser{
}else if(temp.op!=11){
this.packets++
}
}catch(error){
console.error(error)
}
});
@ -264,15 +264,15 @@ class Localuser{
return undefined;
}
updateChannel(JSON):void{
this.guildids[JSON.guild_id].updateChannel(JSON);
if(JSON.guild_id===this.lookingguild.id){
SnowFlake.getSnowFlakeFromID(JSON.guild_id,Guild).getObject().updateChannel(JSON);
if(JSON.guild_id===this.lookingguild.id.id){
this.loadGuild(JSON.guild_id);
}
}
createChannel(JSON):void{
JSON.guild_id??="@me";
this.guildids[JSON.guild_id].createChannelpac(JSON);
if(JSON.guild_id===this.lookingguild.id){
SnowFlake.getSnowFlakeFromID(JSON.guild_id,Guild).getObject().createChannelpac(JSON);
if(JSON.guild_id===this.lookingguild.id.id){
this.loadGuild(JSON.guild_id);
}
}
@ -497,8 +497,8 @@ class Localuser{
unreads():void{
console.log(this.guildhtml)
for(const thing of this.guilds){
if(thing.id==="@me"){continue;}
thing.unreads(this.guildhtml[thing.id]);
if(thing.id.id==="@me"){continue;}
thing.unreads(this.guildhtml[thing.id.id]);
}
}
typingStart(typing):void{

View file

@ -2,6 +2,7 @@ import {User} from "./user.js";
import {Role} from "./role.js";
import {Guild} from "./guild.js";
import { Contextmenu } from "./contextmenu.js";
import { SnowFlake } from "./snowflake.js";
class Member{
static already={};
@ -38,7 +39,7 @@ class Member{
if(thing==="owner"){continue}
if(thing==="roles"){
for(const strrole of membery["roles"]){
const role=this.guild.getRole(strrole);
const role=SnowFlake.getSnowFlakeFromID(strrole,Role).getObject();
this.roles.push(role);
}
continue;
@ -65,44 +66,45 @@ class Member{
console.error(guild)
}
let user:User;
let id="";
let id:SnowFlake<User>;
if(unkown instanceof User){
user=unkown as User;
id=user.id;
}else if(typeof unkown===typeof ""){
id=unkown as string;
id=new SnowFlake(unkown as string,undefined);
}else{
return new Member(unkown,guild);
}
if(guild.id==="@me"){return null}
if(!Member.already[guild.id]){
Member.already[guild.id]={};
}else if(Member.already[guild.id][id]){
const memb=Member.already[guild.id][id]
if(guild.id.id==="@me"){return null}
if(!Member.already[guild.id.id]){
Member.already[guild.id.id]={};
}else if(Member.already[guild.id.id][id]){
const memb=Member.already[guild.id.id][id]
if(memb instanceof Promise){
return await memb;
}
return memb;
}
const promoise= fetch(guild.info.api.toString()+"/v9/users/"+id+"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id="+guild.id,{headers:guild.headers}).then(_=>_.json()).then(json=>{
const promoise= fetch(guild.info.api.toString()+"/users/"+id+"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id="+guild.id,{headers:guild.headers}).then(_=>_.json()).then(json=>{
const memb=new Member(json,guild);
Member.already[guild.id][id]=memb;
Member.already[guild.id.id][id]=memb;
console.log("resolved")
return memb
})
Member.already[guild.id][id]=promoise;
Member.already[guild.id.id][id]=promoise;
try{
return await promoise
}catch(_){
const memb=new Member(user,guild,true);
Member.already[guild.id][id]=memb;
Member.already[guild.id.id][id]=memb;
return memb;
}
}
hasRole(ID:string){
console.log(this.roles,ID);
for(const thing of this.roles){
if(thing.id===ID){
if(thing.id.id===ID){
return true;
}
}
@ -123,7 +125,7 @@ class Member{
return true;
}
}
return this.guild.properties.owner_id===this.user.id;
return this.guild.properties.owner_id===this.user.id.id;
}
bind(html:HTMLElement){
if(html.tagName==="SPAN"){

View file

@ -7,6 +7,7 @@ import { Channel } from "./channel.js";
import {Localuser} from "./localuser.js";
import { Role } from "./role.js";
import {File} from "./file.js";
import { SnowFlake } from "./snowflake.js";
class Message{
static contextmenu=new Contextmenu("message menu");
@ -17,7 +18,7 @@ class Message{
mentions:User[];
mention_roles:Role[];
attachments:File[];//probably should be its own class tbh, should be Attachments[]
id:string;
id:SnowFlake<Message>;
message_reference;
type:number;
timestamp:number;
@ -43,7 +44,7 @@ class Message{
this.channel.setReplying(this);
});
Message.contextmenu.addbutton("Copy message id",function(){
navigator.clipboard.writeText(this.id);
navigator.clipboard.writeText(this.id.id);
});
Message.contextmenu.addbutton("Edit",function(){
this.channel.editing=this;
@ -72,6 +73,9 @@ class Message{
}else if(thing==="content"){
this.content=new MarkDown(messagejson[thing],this.channel);
continue;
}else if(thing ==="id"){
this.id=new SnowFlake(messagejson.id,this);
continue;
}
this[thing]=messagejson[thing];
}
@ -135,14 +139,14 @@ class Message{
return build;
}
async edit(content){
return await fetch(this.info.api.toString()+"/channels/"+this.channel.id+"/messages/"+this.id,{
return await fetch(this.info.api.toString()+"/channels/"+this.channel.id+"/messages/"+this.id.id,{
method: "PATCH",
headers: this.headers,
body:JSON.stringify({content:content})
});
}
delete(){
fetch(`${this.info.api.toString()}/channels/${this.channel.id}/messages/${this.id}`,{
fetch(`${this.info.api.toString()}/channels/${this.channel.id}/messages/${this.id.id}`,{
headers:this.headers,
method:"DELETE",
})
@ -152,19 +156,22 @@ class Message{
this.div.innerHTML="";
this.div=null;
}
const prev=this.channel.idToPrev[this.id];
const next=this.channel.idToNext[this.id];
const prev=this.channel.idToPrev[this.id.id];
const next=this.channel.idToNext[this.id.id];
this.channel.idToNext[prev]=next;
this.channel.idToPrev[next]=prev;
delete this.channel.messageids[this.id];
delete this.channel.messageids[this.id.id];
const regen=this.channel.messageids[prev]
if(regen){
regen.generateMessage();
}
if(this.channel.lastmessage===this){
this.channel.lastmessage=this.channel.messageids[prev];
}
}
generateMessage(premessage:Message=null){
if(!premessage){
premessage=this.channel.messageids[this.channel.idToNext[this.id]];
premessage=this.channel.messageids[this.channel.idToNext[this.id.id]];
}
const div=this.div;
if(this===this.channel.replyingto){

View file

@ -2,11 +2,12 @@ export {Role};
import {Permissions} from "./permissions.js";
import {Localuser} from "./localuser.js";
import {Guild} from "./guild.js";
import { SnowFlake } from "./snowflake.js";
class Role{
permissions:Permissions;
owner:Guild;
color:number;
id:string;
readonly id:SnowFlake<Role>;
name:string;
info:Guild["info"];
hoist:boolean;
@ -18,6 +19,10 @@ class Role{
this.headers=owner.headers;
this.info=owner.info;
for(const thing of Object.keys(JSON)){
if(thing==="id"){
this.id=new SnowFlake(JSON.id,this);
continue;
}
this[thing]=JSON[thing];
}
this.permissions=new Permissions(JSON.permissions);

View file

@ -1,5 +1,7 @@
import { Permissions } from "./permissions.js";
import { Guild } from "./guild.js";
import { SnowFlake } from "./snowflake.js";
import { Role } from "./role.js";
class Buttons{
readonly name:string;
@ -126,7 +128,7 @@ class PermissionToggle{
}
}
class RoleList extends Buttons{
readonly permissions:[string,Permissions][];
readonly permissions:[SnowFlake<Role>,Permissions][];
permission:Permissions;
readonly guild:Guild;
readonly channel:boolean;
@ -134,7 +136,7 @@ class RoleList extends Buttons{
readonly options:Options;
onchange:Function;
curid:string;
constructor(permissions:[string,Permissions][],guild:Guild,onchange:Function,channel=false){
constructor(permissions:[SnowFlake<Role>,Permissions][],guild:Guild,onchange:Function,channel=false){
super("Roles");
this.guild=guild;
this.permissions=permissions;
@ -150,16 +152,17 @@ class RoleList extends Buttons{
options.addPermissionToggle(thing,this.permission);//
}
for(const i of permissions){
this.buttons.push([guild.getRole(i[0]).name,i[0]])//
console.log(i);
this.buttons.push([i[0].getObject().name,i[0].id])//
}
this.options=options;
}
handleString(str:string):HTMLElement{
this.curid=str;
const perm=this.permissions.find(_=>_[0]===str)[1];
const perm=this.permissions.find(_=>_[0].id===str)[1];
this.permission.deny=perm.deny;
this.permission.allow=perm.allow;
this.options.name=this.guild.getRole(str).name;
this.options.name=SnowFlake.getSnowFlakeFromID(str,Role).getObject().name;
this.options.haschanged=false;
return this.options.generateHTML();
}

53
webpage/snowflake.ts Normal file
View file

@ -0,0 +1,53 @@
class SnowFlake<x>{
public readonly id:string;
private static readonly SnowFlakes:Map<any,Map<string,WeakRef<SnowFlake<any>>>>=new Map();
private static readonly FinalizationRegistry=new FinalizationRegistry((id:string)=>{
SnowFlake.SnowFlakes.delete(id);
});
private obj:x;
constructor(id:string,obj:x){
if(!obj){
this.id=id;
return;
}
if(!SnowFlake.SnowFlakes.get(obj.constructor)){
SnowFlake.SnowFlakes.set(obj.constructor,new Map());
}
if(SnowFlake.SnowFlakes.get(obj.constructor).get(id)){
const snowflake=SnowFlake.SnowFlakes.get(obj.constructor).get(id).deref();
snowflake.obj=obj;
return snowflake;
}
this.id=id;
SnowFlake.SnowFlakes.get(obj.constructor).set(id,new WeakRef(this));
SnowFlake.FinalizationRegistry.register(this,id);
this.obj=obj;
}
static getSnowFlakeFromID(id:string,type:any):SnowFlake<any>{
if(!SnowFlake.SnowFlakes.get(type)){
SnowFlake.SnowFlakes.set(type,new Map());
}
const snowflake=SnowFlake.SnowFlakes.get(type).get(id);
if(snowflake){
return snowflake.deref();
}
{
const snowflake=new SnowFlake(id,undefined);
SnowFlake.SnowFlakes.get(type).set(id,new WeakRef(snowflake));
SnowFlake.FinalizationRegistry.register(snowflake,id);
return snowflake;
}
}
getUnixTime():number{
return Number((BigInt(this.id)>>22n)+1420070400000n);
}
toString(){
return this.id;
}
getObject():x{
return this.obj;
}
}
export {SnowFlake};

View file

@ -115,12 +115,17 @@ samp {
}
.infosection {
hr{
width:100%;
}
display: inline-block;
background-color: var(--profile-info-bg);
border-radius: 10%;
padding: .3cm;
width: calc(100% - .6cm);
height: calc(100% - .75in);
display: flex;
flex-direction: column;
}
.profile {
@ -1390,3 +1395,12 @@ span {
width:22px;
height:22px;
}
#typebox[contenteditable=false]{
cursor:not-allowed;
}
#typebox[contenteditable=false]:before{
content:'You can\'t chat here';
color:color-mix(in hsl, var(--primary-bg),var(--primary-text));
}

View file

@ -4,12 +4,13 @@ import {MarkDown} from "./markdown.js";
import {Contextmenu} from "./contextmenu.js";
import {Localuser} from "./localuser.js";
import {Guild} from "./guild.js";
import { SnowFlake } from "./snowflake.js";
class User{
static userids={};
owner:Localuser;
hypotheticalpfp:boolean;
id:string;
id:SnowFlake<User>;
avatar:string;
username:string;
bio:MarkDown;
@ -19,13 +20,13 @@ class User{
static contextmenu:Contextmenu=new Contextmenu("User Menu");
static setUpContextMenu(){
this.contextmenu.addbutton("Copy user id",function(){
navigator.clipboard.writeText(this.id);
navigator.clipboard.writeText(this.id.id);
});
this.contextmenu.addbutton("Message user",function(){
fetch(this.info.api.toString()+"/v9/users/@me/channels",
{method:"POST",
body:JSON.stringify({"recipients":[this.id]}),
headers: this.headers
body:JSON.stringify({"recipients":[this.id.id]}),
headers: this.localuser.headers
});
})
}
@ -53,6 +54,10 @@ class User{
this.bio=new MarkDown(userjson[thing],this.localuser);
continue;
}
if(thing === "id"){
this.id=new SnowFlake(userjson[thing],this);
continue;
}
this[thing]=userjson[thing];
}
this.hypotheticalpfp=false;
@ -67,7 +72,7 @@ class User{
const pfp=document.createElement('img');
pfp.src=this.getpfpsrc();
pfp.classList.add("pfp");
pfp.classList.add("userid:"+this.id);
pfp.classList.add("userid:"+this.id.id);
return pfp;
}
userupdate(json){
@ -77,7 +82,7 @@ class User{
}
}
bind(html:HTMLElement,guild:Guild=null){
if(guild&&guild.id!=="@me"){
if(guild&&guild.id.id!=="@me"){
Member.resolve(this,guild).then(_=>{
_.bind(html);
}).catch(_=>{
@ -98,7 +103,7 @@ class User{
this.hypotheticalpfp=false;
const src=this.getpfpsrc();
console.log(src)
for(const thing of document.getElementsByClassName("userid:"+this.id)){
for(const thing of document.getElementsByClassName("userid:"+this.id.id)){
(thing as HTMLImageElement).src=src;
}
}
@ -107,7 +112,7 @@ class User{
return this.avatar;
}
if(this.avatar!=null){
return this.info.cdn.toString()+"avatars/"+this.id+"/"+this.avatar+".png";
return this.info.cdn.toString()+"avatars/"+this.id.id+"/"+this.avatar+".png";
}else{
return this.info.cdn.toString()+"embed/avatars/3.png";
}