diff --git a/.dist/audio.js b/.dist/audio.js new file mode 100644 index 0000000..e98dad7 --- /dev/null +++ b/.dist/audio.js @@ -0,0 +1,138 @@ +import { getBulkInfo } from "./login.js"; +class Voice { + audioCtx; + info; + playing; + myArrayBuffer; + gainNode; + buffer; + source; + constructor(wave, freq, volume = 1) { + this.audioCtx = new (window.AudioContext)(); + this.info = { wave: wave, freq: freq }; + this.playing = false; + this.myArrayBuffer = this.audioCtx.createBuffer(1, this.audioCtx.sampleRate, this.audioCtx.sampleRate); + this.gainNode = this.audioCtx.createGain(); + this.gainNode.gain.value = volume; + this.gainNode.connect(this.audioCtx.destination); + this.buffer = this.myArrayBuffer.getChannelData(0); + this.source = this.audioCtx.createBufferSource(); + this.source.buffer = this.myArrayBuffer; + this.source.loop = true; + this.source.start(); + this.updateWave(); + } + get wave() { + return this.info.wave; + } + get freq() { + return this.info.freq; + } + set wave(wave) { + this.info.wave = wave; + this.updateWave(); + } + set freq(freq) { + this.info.freq = freq; + this.updateWave(); + } + updateWave() { + const func = this.waveFucnion(); + for (let i = 0; i < this.buffer.length; i++) { + this.buffer[i] = func(i / this.audioCtx.sampleRate, this.freq); + } + } + waveFucnion() { + if (typeof this.wave === 'function') { + return this.wave; + } + switch (this.wave) { + case "sin": + return (t, freq) => { + return Math.sin(t * Math.PI * 2 * freq); + }; + case "triangle": + return (t, freq) => { + return Math.abs((4 * t * freq) % 4 - 2) - 1; + }; + case "sawtooth": + return (t, freq) => { + return ((t * freq) % 1) * 2 - 1; + }; + case "square": + return (t, freq) => { + return (t * freq) % 2 < 1 ? 1 : -1; + }; + case "white": + return (_t, _freq) => { + return Math.random() * 2 - 1; + }; + case "noise": + return (_t, _freq) => { + return 0; + }; + } + } + play() { + if (this.playing) { + return; + } + this.source.connect(this.gainNode); + this.playing = true; + } + stop() { + if (this.playing) { + this.source.disconnect(); + this.playing = false; + } + } + static noises(noise) { + switch (noise) { + case "three": { + const voicy = new Voice("sin", 800); + voicy.play(); + setTimeout(_ => { voicy.freq = 1000; }, 50); + setTimeout(_ => { voicy.freq = 1300; }, 100); + setTimeout(_ => { voicy.stop(); }, 150); + break; + } + case "zip": { + const voicy = new Voice((t, freq) => { + return Math.sin(((t + 2) ** (Math.cos(t * 4))) * Math.PI * 2 * freq); + }, 700); + voicy.play(); + setTimeout(_ => { voicy.stop(); }, 150); + break; + } + case "square": { + const voicy = new Voice("square", 600, .4); + voicy.play(); + setTimeout(_ => { voicy.freq = 800; }, 50); + setTimeout(_ => { voicy.freq = 1000; }, 100); + setTimeout(_ => { voicy.stop(); }, 150); + break; + } + case "beep": { + const voicy = new Voice("sin", 800); + voicy.play(); + setTimeout(_ => { voicy.stop(); }, 50); + setTimeout(_ => { voicy.play(); }, 100); + setTimeout(_ => { voicy.stop(); }, 150); + break; + } + } + } + static get sounds() { + return ["three", "zip", "square", "beep"]; + } + static setNotificationSound(sound) { + let userinfos = getBulkInfo(); + userinfos.preferances.notisound = sound; + localStorage.setItem("userinfos", JSON.stringify(userinfos)); + } + static getNotificationSound() { + let userinfos = getBulkInfo(); + return userinfos.preferances.notisound; + } +} +export { Voice as Voice }; diff --git a/.dist/channel.js b/.dist/channel.js new file mode 100644 index 0000000..4bc2f27 --- /dev/null +++ b/.dist/channel.js @@ -0,0 +1,726 @@ +"use strict"; +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 { Permissions } from "./permissions.js"; +class Channel { + editing; + type; + owner; + headers; + messages; + name; + id; + parent_id; + parrent; + children; + guild_id; + messageids; + permission_overwrites; + topic; + nsfw; + position; + lastreadmessageid; + lastmessageid; + mentions; + lastpin; + move_id; + typing; + message_notifications; + allthewayup; + static contextmenu = new Contextmenu("channel menu"); + replyingto; + static setupcontextmenu() { + Channel.contextmenu.addbutton("Copy channel id", function () { + console.log(this); + navigator.clipboard.writeText(this.id); + }); + Channel.contextmenu.addbutton("Mark as read", function () { + console.log(this); + this.readbottom(); + }); + Channel.contextmenu.addbutton("Delete channel", function () { + console.log(this); + this.deleteChannel(); + }, null, _ => { console.log(_); return _.isAdmin(); }); + Channel.contextmenu.addbutton("Edit channel", function () { + this.editChannel(this); + }, null, _ => { return _.isAdmin(); }); + } + constructor(JSON, owner) { + if (JSON === -1) { + return; + } + this.editing; + this.type = JSON.type; + this.owner = owner; + this.headers = this.owner.headers; + this.messages = []; + this.name = JSON.name; + this.id = JSON.id; + this.parent_id = JSON.parent_id; + this.parrent = null; + this.children = []; + this.guild_id = JSON.guild_id; + this.messageids = {}; + this.permission_overwrites = {}; + for (const thing of JSON.permission_overwrites) { + this.permission_overwrites[thing.id] = new Permissions(thing.allow, thing.deny); + } + this.topic = JSON.topic; + this.nsfw = JSON.nsfw; + this.position = JSON.position; + this.lastreadmessageid = null; + this.lastmessageid = JSON.last_message_id; + } + isAdmin() { + return this.guild.isAdmin(); + } + get guild() { + return this.owner; + } + get localuser() { + return this.guild.localuser; + } + get info() { + return this.owner.info; + } + readStateInfo(json) { + this.lastreadmessageid = json.last_message_id; + this.mentions = json.mention_count; + this.mentions ??= 0; + this.lastpin = json.last_pin_timestamp; + } + get hasunreads() { + if (!this.hasPermission("VIEW_CHANNEL")) { + return false; + } + return this.lastmessageid !== this.lastreadmessageid && this.type !== 4; + } + 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].getPermision(name); + if (perm) { + return perm === 1; + } + } + if (thing.permissions.getPermision(name)) { + return true; + } + } + return false; + } + get canMessage() { + return this.hasPermission("SEND_MESSAGES"); + } + sortchildren() { + this.children.sort((a, b) => { return a.position - b.position; }); + } + resolveparent(guild) { + this.parrent = guild.channelids[this.parent_id]; + this.parrent ??= null; + if (this.parrent !== null) { + this.parrent.children.push(this); + } + return this.parrent === null; + } + calculateReorder() { + let position = -1; + let build = []; + for (const thing of this.children) { + const thisthing = { id: thing.id, position: undefined, parent_id: undefined }; + if (thing.position < position) { + thing.position = thisthing.position = position + 1; + } + position = thing.position; + if (thing.move_id && thing.move_id !== thing.parent_id) { + thing.parent_id = thing.move_id; + thisthing.parent_id = thing.parent_id; + thing.move_id = undefined; + console.log(this.guild.channelids[thisthing.parent_id]); + } + if (thisthing.position || thisthing.parent_id) { + build.push(thisthing); + } + } + return build; + } + static dragged = []; + createguildHTML(admin = false) { + const div = document.createElement("div"); + if (!this.hasPermission("VIEW_CHANNEL")) { + let quit = true; + for (const thing of this.children) { + if (thing.hasPermission("VIEW_CHANNEL")) { + quit = false; + } + } + if (quit) { + return div; + } + } + div["all"] = this; + div.draggable = admin; + div.addEventListener("dragstart", (e) => { Channel.dragged = [this, div]; e.stopImmediatePropagation(); }); + div.addEventListener("dragend", () => { Channel.dragged = []; }); + if (this.type === 4) { + this.sortchildren(); + const caps = document.createElement("div"); + const decdiv = document.createElement("div"); + const decoration = document.createElement("b"); + decoration.textContent = "â–ŧ"; + decdiv.appendChild(decoration); + const myhtml = document.createElement("p2"); + myhtml.textContent = this.name; + decdiv.appendChild(myhtml); + caps.appendChild(decdiv); + const childrendiv = document.createElement("div"); + if (admin) { + const addchannel = document.createElement("span"); + addchannel.textContent = "+"; + addchannel.classList.add("addchannel"); + caps.appendChild(addchannel); + addchannel.onclick = function () { + this.guild.createchannels(this.createChannel.bind(this)); + }.bind(this); + this.coatDropDiv(decdiv, childrendiv); + } + div.appendChild(caps); + caps.classList.add("capsflex"); + decdiv.classList.add("channeleffects"); + decdiv.classList.add("channel"); + Channel.contextmenu.bind(decdiv, this); + decdiv["all"] = this; + for (const channel of this.children) { + childrendiv.appendChild(channel.createguildHTML(admin)); + } + childrendiv.classList.add("channels"); + setTimeout(_ => { childrendiv.style.height = childrendiv.scrollHeight + 'px'; }, 100); + decdiv.onclick = function () { + if (decoration.textContent === "â–ŧ") { // + decoration.textContent = "▲"; + //childrendiv.classList.add("colapsediv"); + childrendiv.style.height = '0px'; + } + else { + decoration.textContent = "â–ŧ"; + //childrendiv.classList.remove("colapsediv") + childrendiv.style.height = childrendiv.scrollHeight + 'px'; + } + }; + div.appendChild(childrendiv); + } + else { + div.classList.add("channel"); + if (this.hasunreads) { + div.classList.add("cunread"); + } + Channel.contextmenu.bind(div, this); + if (admin) { + this.coatDropDiv(div); + } + div["all"] = this; + const myhtml = document.createElement("span"); + myhtml.textContent = this.name; + if (this.type === 0) { + const decoration = document.createElement("b"); + decoration.textContent = "#"; + div.appendChild(decoration); + decoration.classList.add("space"); + } + else if (this.type === 2) { // + const decoration = document.createElement("b"); + decoration.textContent = "đŸ•Ē"; + div.appendChild(decoration); + decoration.classList.add("spacee"); + } + else if (this.type === 5) { // + const decoration = document.createElement("b"); + decoration.textContent = "đŸ“Ŗ"; + div.appendChild(decoration); + decoration.classList.add("spacee"); + } + else { + console.log(this.type); + } + div.appendChild(myhtml); + div.onclick = _ => { + this.getHTML(); + }; + } + return div; + } + get myhtml() { + const search = document.getElementById("channels").children[0].children; + if (this.guild !== this.localuser.lookingguild) { + return null; + } + else if (this.parrent) { + for (const thing of search) { + if (thing["all"] === this.parrent) { + for (const thing2 of thing.children[1].children) { + if (thing2["all"] === this) { + return thing2; + } + } + } + } + } + else { + for (const thing of search) { + if (thing["all"] === this) { + return thing; + } + } + } + return null; + } + readbottom() { + if (!this.hasunreads) { + return; + } + fetch(this.info.api.toString() + "/v9/channels/" + this.id + "/messages/" + this.lastmessageid + "/ack", { + method: "POST", + headers: this.headers, + body: JSON.stringify({}) + }); + this.lastreadmessageid = this.lastmessageid; + this.guild.unreads(); + if (this.myhtml !== null) { + this.myhtml.classList.remove("cunread"); + } + } + coatDropDiv(div, container = false) { + div.addEventListener("dragenter", (event) => { + console.log("enter"); + event.preventDefault(); + }); + div.addEventListener("dragover", (event) => { + event.preventDefault(); + }); + div.addEventListener("drop", (event) => { + const that = Channel.dragged[0]; + event.preventDefault(); + if (container) { + that.move_id = this.id; + if (that.parrent) { + that.parrent.children.splice(that.parrent.children.indexOf(that), 1); + } + that.parrent = this; + container.prepend(Channel.dragged[1]); + this.children.unshift(that); + } + else { + console.log(this, Channel.dragged); + that.move_id = this.parent_id; + if (that.parrent) { + that.parrent.children.splice(that.parrent.children.indexOf(that), 1); + } + else { + this.guild.headchannels.splice(this.guild.headchannels.indexOf(that), 1); + } + that.parrent = this.parrent; + if (that.parrent) { + const build = []; + for (let i = 0; i < that.parrent.children.length; i++) { + build.push(that.parrent.children[i]); + if (that.parrent.children[i] === this) { + build.push(that); + } + } + that.parrent.children = build; + } + else { + const build = []; + for (let i = 0; i < this.guild.headchannels.length; i++) { + build.push(this.guild.headchannels[i]); + if (this.guild.headchannels[i] === this) { + build.push(that); + } + } + this.guild.headchannels = build; + } + div.after(Channel.dragged[1]); + } + this.guild.calculateReorder(); + }); + return div; + } + createChannel(name, type) { + fetch(this.info.api.toString() + "/guilds/" + this.guild.id + "/channels", { + method: "Post", + headers: this.headers, + body: JSON.stringify({ + name: name, + type: type, + parent_id: this.id, + permission_overwrites: [], + }) + }); + } + editChannel() { + let name = this.name; + let topic = this.topic; + let nsfw = this.nsfw; + const thisid = this.id; + const thistype = this.type; + const full = new Fullscreen(["hdiv", + ["vdiv", + ["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, { + method: "PATCH", + headers: this.headers, + body: JSON.stringify({ + "name": name, + "type": thistype, + "topic": topic, + "bitrate": 64000, + "user_limit": 0, + "nsfw": nsfw, + "flags": 0, + "rate_limit_per_user": 0 + }) + }); + console.log(full); + full.hide(); + }] + ] + ]); + full.show(); + console.log(full); + } + deleteChannel() { + fetch(this.info.api.toString() + "/v9/channels/" + this.id, { + method: "DELETE", + headers: this.headers + }); + } + setReplying(message) { + if (this.replyingto) { + this.replyingto.div.classList.remove("replying"); + } + this.replyingto = message; + console.log(message); + this.replyingto.div.classList.add("replying"); + this.makereplybox(); + } + makereplybox() { + const replybox = document.getElementById("replybox"); + if (this.replyingto) { + replybox.innerHTML = ""; + const span = document.createElement("span"); + span.textContent = "Replying to " + this.replyingto.author.username; + const X = document.createElement("button"); + X.onclick = _ => { + this.replyingto.div.classList.remove("replying"); + replybox.classList.add("hideReplyBox"); + this.replyingto = null; + replybox.innerHTML = ""; + }; + replybox.classList.remove("hideReplyBox"); + X.textContent = "âĻģ"; + X.classList.add("cancelReply"); + replybox.append(span); + replybox.append(X); + } + else { + replybox.classList.add("hideReplyBox"); + } + } + async getmessage(id) { + if (this.messageids[id]) { + return this.messageids[id]; + } + else { + const gety = await fetch(this.info.api.toString() + "/v9/channels/" + this.id + "/messages?limit=1&around=" + id, { headers: this.headers }); + const json = await gety.json(); + return new Message(json[0], this); + } + } + async getHTML() { + if (this.guild !== this.localuser.lookingguild) { + this.guild.loadGuild(); + } + if (this.localuser.channelfocus && this.localuser.channelfocus.myhtml) { + this.localuser.channelfocus.myhtml.classList.remove("viewChannel"); + } + this.myhtml.classList.add("viewChannel"); + this.guild.prevchannel = this; + this.localuser.channelfocus = this; + const prom = Message.wipeChanel(); + await this.putmessages(); + await prom; + this.makereplybox(); + this.buildmessages(); + 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; + } + async putmessages() { + if (this.messages.length >= 100 || this.allthewayup) { + return; + } + ; + const j = await fetch(this.info.api.toString() + "/channels/" + this.id + "/messages?limit=100", { + method: 'GET', + headers: this.headers, + }); + const responce = await j.json(); + if (responce.length !== 100) { + this.allthewayup = true; + } + for (const thing of responce) { + const messager = new Message(thing, this); + if (this.messageids[messager.id] === undefined) { + this.messageids[messager.id] = messager; + this.messages.push(messager); + } + } + } + delChannel(JSON) { + const build = []; + for (const thing of this.children) { + if (thing.id !== JSON.id) { + build.push(thing); + } + } + this.children = build; + } + async grabmoremessages() { + if (this.messages.length === 0 || this.allthewayup) { + return; + } + const out = this; + await fetch(this.info.api.toString() + "/channels/" + this.id + "/messages?before=" + this.messages[this.messages.length - 1].id + "&limit=100", { + method: "GET", + headers: this.headers + }).then((j) => { return j.json(); }).then(responce => { + //messages.innerHTML = ''; + //responce.reverse() + let next; + if (responce.length === 0) { + out.allthewayup = true; + } + for (const i in responce) { + let messager; + if (!next) { + messager = new Message(responce[i], this); + } + else { + messager = next; + } + if (responce[+i + 1] !== undefined) { + next = new Message(responce[+i + 1], this); + } + else { + next = undefined; + console.log("ohno", +i + 1); + } + if (out.messageids[messager.id] == undefined) { + out.messageids[messager.id] = messager; + out.buildmessage(messager, next); + out.messages.push(messager); + } + else { + console.log("How???"); + } + } + //out.buildmessages(); + }); + return; + } + buildmessage(message, next) { + const built = message.buildhtml(next); + document.getElementById("messages").prepend(built); + } + buildmessages() { + for (const i in this.messages) { + const prev = this.messages[(+i) + 1]; + const built = this.messages[i].buildhtml(prev); + document.getElementById("messages").prepend(built); + } + document.getElementById("messagecontainer").scrollTop = document.getElementById("messagecontainer").scrollHeight; + } + updateChannel(JSON) { + this.type = JSON.type; + this.name = JSON.name; + this.parent_id = JSON.parent_id; + this.parrent = null; + this.children = []; + this.guild_id = JSON.guild_id; + this.messageids = {}; + this.permission_overwrites = JSON.permission_overwrites; + this.topic = JSON.topic; + this.nsfw = JSON.nsfw; + } + typingstart() { + if (this.typing > new Date().getTime()) { + return; + } + this.typing = new Date().getTime() + 6000; + fetch(this.info.api.toString() + "/channels/" + this.id + "/typing", { + method: "POST", + headers: this.headers + }); + } + get notification() { + let notinumber = this.message_notifications; + if (+notinumber === 3) { + notinumber = null; + } + notinumber ??= this.guild.message_notifications; + switch (+notinumber) { + case 0: + return "all"; + case 1: + return "mentions"; + case 2: + return "none"; + case 3: + return "default"; + } + } + async sendMessage(content, { attachments = [], embeds = [], replyingto = null }) { + let replyjson; + if (replyingto) { + replyjson = + { + "guild_id": replyingto.guild.id, + "channel_id": replyingto.channel.id, + "message_id": replyingto.id, + }; + } + ; + if (attachments.length === 0) { + const body = { + content: content, + nonce: Math.floor(Math.random() * 1000000000), + message_reference: undefined + }; + if (replyjson) { + body.message_reference = replyjson; + } + console.log(body); + return await fetch(this.info.api.toString() + "/channels/" + this.id + "/messages", { + method: "POST", + headers: this.headers, + body: JSON.stringify(body) + }); + } + else { + const formData = new FormData(); + const body = { + content: content, + nonce: Math.floor(Math.random() * 1000000000), + message_reference: undefined + }; + if (replyjson) { + body.message_reference = replyjson; + } + formData.append('payload_json', JSON.stringify(body)); + for (const i in attachments) { + console.log(attachments[i]); + formData.append("files[" + i + "]", attachments[i]); + } + return await fetch(this.info.api.toString() + "/channels/" + this.id + "/messages", { + method: 'POST', + body: formData, + headers: { "Authorization": this.headers.Authorization } + }); + } + } + messageCreate(messagep) { + if (!this.hasPermission("VIEW_CHANNEL")) { + return; + } + const messagez = new Message(messagep.d, this); + this.lastmessageid = messagez.id; + if (messagez.author === this.localuser.user) { + this.lastreadmessageid = messagez.id; + if (this.myhtml) { + this.myhtml.classList.remove("cunread"); + } + } + else { + if (this.myhtml) { + this.myhtml.classList.add("cunread"); + } + } + this.guild.unreads(); + this.messages.unshift(messagez); + const scrolly = document.getElementById("messagecontainer"); + this.messageids[messagez.id] = messagez; + if (this.localuser.lookingguild.prevchannel === this) { + var shouldScroll = scrolly.scrollTop + scrolly.clientHeight > scrolly.scrollHeight - 20; + document.getElementById("messages").appendChild(messagez.buildhtml(this.messages[1])); + } + if (shouldScroll) { + scrolly.scrollTop = scrolly.scrollHeight; + } + if (messagez.author === this.localuser.user) { + return; + } + if (this.localuser.lookingguild.prevchannel === this && document.hasFocus()) { + return; + } + if (this.notification === "all") { + this.notify(messagez); + } + else if (this.notification === "mentions" && messagez.mentionsuser(this.localuser.user)) { + this.notify(messagez); + } + } + notititle(message) { + return message.author.username + " > " + this.guild.properties.name + " > " + this.name; + } + notify(message, deep = 0) { + Voice.noises(Voice.getNotificationSound()); + if (!("Notification" in window)) { + } + else if (Notification.permission === "granted") { + let noticontent = markdown(message.content).textContent; + if (message.embeds[0]) { + noticontent ||= message.embeds[0].json.title; + noticontent ||= markdown(message.embeds[0].json.description).textContent; + } + noticontent ||= "Blank Message"; + let imgurl = null; + const images = message.getimages(); + if (images.length) { + const image = images[0]; + imgurl ||= image.proxy_url; + imgurl ||= image.url; + } + const notification = new Notification(this.notititle(message), { + body: noticontent, + icon: message.author.getpfpsrc(), + image: imgurl, + }); + notification.addEventListener("click", _ => { + window.focus(); + this.getHTML(); + }); + } + else if (Notification.permission !== "denied") { + Notification.requestPermission().then(() => { + if (deep === 3) { + return; + } + ; + this.notify(message, deep + 1); + }); + } + } +} +Channel.setupcontextmenu(); +export { Channel }; diff --git a/.dist/contextmenu.js b/.dist/contextmenu.js new file mode 100644 index 0000000..17afb51 --- /dev/null +++ b/.dist/contextmenu.js @@ -0,0 +1,80 @@ +class Contextmenu { + static currentmenu; + name; + buttons; + div; + static setup() { + Contextmenu.currentmenu = ""; + document.addEventListener('click', function (event) { + if (Contextmenu.currentmenu == "") { + return; + } + if (!Contextmenu.currentmenu.contains(event.target)) { + Contextmenu.currentmenu.remove(); + Contextmenu.currentmenu = ""; + } + }); + } + constructor(name) { + this.name = name; + this.buttons = []; + } + addbutton(text, onclick, img = null, shown = _ => true, enabled = _ => true) { + this.buttons.push([text, onclick, img, shown, enabled]); + return {}; + } + makemenu(x, y, addinfo, obj) { + const div = document.createElement("table"); + div.classList.add("contextmenu"); + for (const thing of this.buttons) { + if (!thing[3](addinfo)) { + continue; + } + const textb = document.createElement("tr"); + const intext = document.createElement("button"); + intext.disabled = !thing[4](); + textb["button"] = intext; + intext.classList.add("contextbutton"); + intext.textContent = thing[0]; + textb.appendChild(intext); + console.log(thing); + intext.onclick = thing[1].bind(addinfo, obj); + div.appendChild(textb); + } + if (Contextmenu.currentmenu != "") { + Contextmenu.currentmenu.remove(); + } + div.style.top = y + 'px'; + div.style.left = x + 'px'; + document.body.appendChild(div); + Contextmenu.keepOnScreen(div); + console.log(div); + Contextmenu.currentmenu = div; + return this.div; + } + bind(obj, addinfo = undefined) { + const func = (event) => { + event.preventDefault(); + event.stopImmediatePropagation(); + this.makemenu(event.clientX, event.clientY, addinfo, obj); + }; + obj.addEventListener("contextmenu", func); + return func; + } + static keepOnScreen(obj) { + const html = document.documentElement.getBoundingClientRect(); + const docheight = html.height; + const docwidth = html.width; + const box = obj.getBoundingClientRect(); + console.log(box, docheight, docwidth); + if (box.right > docwidth) { + console.log("test"); + obj.style.left = docwidth - box.width + 'px'; + } + if (box.bottom > docheight) { + obj.style.top = docheight - box.height + 'px'; + } + } +} +Contextmenu.setup(); +export { Contextmenu as Contextmenu }; diff --git a/.dist/direct.js b/.dist/direct.js new file mode 100644 index 0000000..bfa5b6e --- /dev/null +++ b/.dist/direct.js @@ -0,0 +1,202 @@ +import { Guild } from "./guild.js"; +import { Channel } from "./channel.js"; +import { Message } from "./message.js"; +import { User } from "./user.js"; +class Direct extends Guild { + constructor(JSON, owner) { + super(-1, owner, null); + this.message_notifications = 0; + console.log(JSON); + this.owner = owner; + if (!this.localuser) { + console.error("Owner was not included, please fix"); + } + this.headers = this.localuser.headers; + this.channels = []; + this.channelids = {}; + this.id = "@me"; + this.properties = {}; + this.roles = []; + this.roleids = {}; + this.prevchannel = undefined; + this.properties.name = "Dirrect Messages"; + for (const thing of JSON) { + const temp = new Group(thing, this); + this.channels.push(temp); + this.channelids[temp.id] = temp; + } + this.headchannels = this.channels; + } + createChannelpac(JSON) { + const thischannel = new Group(JSON, this); + this.channelids[JSON.id] = thischannel; + this.channels.push(thischannel); + this.calculateReorder(); + this.printServers(); + } + sortchannels() { + this.headchannels.sort((a, b) => { + const result = (BigInt(a.lastmessageid) - BigInt(b.lastmessageid)); + return Number(-result); + }); + } + giveMember(member) { + console.error("not a real guild, can't give member object"); + } + getRole(ID) { + return null; + } + hasRole(r) { + return false; + } + isAdmin() { + return false; + } + unreaddms() { + for (const thing of this.channels) { + thing.unreads(); + } + } +} +class Group extends Channel { + user; + constructor(JSON, owner) { + super(-1, owner); + this.owner = owner; + this.headers = this.guild.headers; + this.messages = []; + this.name = JSON.recipients[0]?.username; + if (JSON.recipients[0]) { + this.user = new User(JSON.recipients[0], this.localuser); + } + else { + this.user = this.localuser.user; + } + this.name ??= this.localuser.user.username; + this.id = JSON.id; + this.parent_id = null; + this.parrent = null; + this.children = []; + this.guild_id = "@me"; + this.messageids = {}; + this.permission_overwrites = {}; + this.lastmessageid = JSON.last_message_id; + this.lastmessageid ??= "0"; + this.mentions = 0; + } + createguildHTML() { + const div = document.createElement("div"); + div.classList.add("channeleffects"); + const myhtml = document.createElement("span"); + myhtml.textContent = this.name; + div.appendChild(this.user.buildpfp()); + div.appendChild(myhtml); + div["myinfo"] = this; + div.onclick = _ => { + this.getHTML(); + }; + return div; + } + async getHTML() { + if (this.guild !== this.localuser.lookingguild) { + this.guild.loadGuild(); + } + const prom = Message.wipeChanel(); + this.guild.prevchannel = this; + this.localuser.channelfocus = this; + await this.putmessages(); + await prom; + this.buildmessages(); + history.pushState(null, null, "/channels/" + this.guild_id + "/" + this.id); + document.getElementById("channelname").textContent = "@" + this.name; + } + messageCreate(messagep) { + const messagez = new Message(messagep.d, this); + this.lastmessageid = messagez.id; + if (messagez.author === this.localuser.user) { + this.lastreadmessageid = messagez.id; + } + this.messages.unshift(messagez); + const scrolly = document.getElementById("messagecontainer"); + this.messageids[messagez.id] = messagez; + if (this.localuser.lookingguild.prevchannel === this) { + var shouldScroll = scrolly.scrollTop + scrolly.clientHeight > scrolly.scrollHeight - 20; + document.getElementById("messages").appendChild(messagez.buildhtml(this.messages[1])); + } + if (shouldScroll) { + scrolly.scrollTop = scrolly.scrollHeight; + } + console.log(document.getElementById("channels").children); + if (this.localuser.lookingguild === this.guild) { + const channellist = document.getElementById("channels").children[0]; + for (const thing of channellist.children) { + if (thing["myinfo"] === this) { + channellist.prepend(thing); + break; + } + } + } + this.unreads(); + if (messagez.author === this.localuser.user) { + return; + } + if (this.localuser.lookingguild.prevchannel === this && document.hasFocus()) { + return; + } + console.log(this.notification); + if (this.notification === "all") { + this.notify(messagez); + } + else if (this.notification === "mentions" && messagez.mentionsuser(this.localuser.user)) { + this.notify(messagez); + } + } + notititle(message) { + return message.author.username; + } + unreads() { + const sentdms = document.getElementById("sentdms"); + let current = null; + for (const thing of sentdms.children) { + if (thing["all"] === this) { + current = thing; + } + } + if (this.hasunreads) { + if (current) { + current.noti.textContent = this.mentions; + return; + } + const div = document.createElement("div"); + div.classList.add("servernoti"); + const noti = document.createElement("div"); + noti.classList.add("unread", "notiunread", "pinged"); + noti.textContent = "" + this.mentions; + console.log(this.mentions); + div["noti"] = noti; + div.append(noti); + const buildpfp = this.user.buildpfp(); + div["all"] = this; + buildpfp.classList.add("mentioned"); + console.log(this); + div.append(buildpfp); + sentdms.append(div); + div.onclick = function () { + this["noti"].guild.loadGuild(); + this["noti"].getHTML(); + }; + } + else if (current) { + current.remove(); + } + else { + } + } + isAdmin() { + return false; + } + hasPermission(name, member) { + return true; + } +} +export { Direct, Group }; diff --git a/.dist/embed.js b/.dist/embed.js new file mode 100644 index 0000000..cb0e10e --- /dev/null +++ b/.dist/embed.js @@ -0,0 +1,195 @@ +import { Fullscreen } from "./fullscreen.js"; +class Embed { + type; + owner; + json; + constructor(json, owner) { + this.type = this.getType(json); + this.owner = owner; + this.json = json; + } + getType(json) { + return json.type || "rich"; + } + generateHTML() { + switch (this.type) { + case "rich": + return this.generateRich(); + case "image": + return this.generateImage(); + case "link": + return this.generateLink(); + case "article": + return this.generateArticle(); + default: + console.warn(`unsupported embed type ${this.type}, please add support dev :3`, this.json); + return document.createElement("div"); //prevent errors by giving blank div + } + } + generateRich() { + console.log(this.json); + const div = document.createElement("div"); + if (this.json.color) { + div.style.backgroundColor = "#" + this.json.color.toString(16); + } + div.classList.add("embed-color"); + const embed = document.createElement("div"); + embed.classList.add("embed"); + div.append(embed); + if (this.json.author) { + const authorline = document.createElement("div"); + if (this.json.author.icon_url) { + const img = document.createElement("img"); + img.classList.add("embedimg"); + img.src = this.json.author.icon_url; + authorline.append(img); + } + const a = document.createElement("a"); + a.innerText = this.json.author.name; + if (this.json.author.url) { + a.href = this.json.author.url; + } + a.classList.add("username"); + authorline.append(a); + embed.append(authorline); + } + const title = document.createElement("a"); + title.textContent = this.json.title; + if (this.json.url) { + title.href = this.json.url; + } + title.classList.add("embedtitle"); + embed.append(title); + if (this.json.description) { + const p = document.createElement("p"); + p.textContent = this.json.description; + embed.append(p); + } + embed.append(document.createElement("br")); + if (this.json.fields) { + for (const thing of this.json.fields) { + const div = document.createElement("div"); + const b = document.createElement("b"); + b.textContent = thing.name; + div.append(b); + let p; + p = document.createElement("p"); + p.textContent = thing.value; + p.classList.add("embedp"); + div.append(p); + if (thing.inline) { + div.classList.add("inline"); + } + embed.append(div); + } + } + if (this.json.footer || this.json.timestamp) { + const footer = document.createElement("div"); + if (this.json?.footer?.icon_url) { + const img = document.createElement("img"); + img.src = this.json.footer.icon_url; + img.classList.add("embedicon"); + footer.append(img); + } + if (this.json?.footer?.text) { + const span = document.createElement("span"); + span.textContent = this.json.footer.text; + span.classList.add("spaceright"); + footer.append(span); + } + if (this.json?.footer && this.json?.timestamp) { + const span = document.createElement("span"); + span.textContent = "â€ĸ"; + span.classList.add("spaceright"); + footer.append(span); + } + if (this.json?.timestamp) { + const span = document.createElement("span"); + span.textContent = new Date(this.json.timestamp).toLocaleString(); + ; + footer.append(span); + } + embed.append(footer); + } + return div; + } + generateImage() { + const img = document.createElement("img"); + img.classList.add("messageimg"); + img.onclick = function () { + const full = new Fullscreen(["img", img.src, ["fit"]]); + full.show(); + }; + img.src = this.json.thumbnail.proxy_url; + return img; + } + generateLink() { + const table = document.createElement("table"); + table.classList.add("embed", "linkembed"); + const trtop = document.createElement("tr"); + table.append(trtop); + { + const td = document.createElement("td"); + const a = document.createElement("a"); + a.href = this.json.url; + a.textContent = this.json.title; + td.append(a); + trtop.append(td); + } + { + const td = document.createElement("td"); + const img = document.createElement("img"); + if (this.json.thumbnail) { + img.classList.add("embedimg"); + img.onclick = function () { + const full = new Fullscreen(["img", img.src, ["fit"]]); + full.show(); + }; + img.src = this.json.thumbnail.proxy_url; + td.append(img); + } + trtop.append(td); + } + const bottomtr = document.createElement("tr"); + const td = document.createElement("td"); + const span = document.createElement("span"); + span.textContent = this.json.description; + td.append(span); + bottomtr.append(td); + table.append(bottomtr); + return table; + } + generateArticle() { + const colordiv = document.createElement("div"); + colordiv.style.backgroundColor = "#000000"; + colordiv.classList.add("embed-color"); + const div = document.createElement("div"); + div.classList.add("embed"); + if (this.json.provider) { + const providor = document.createElement("p"); + providor.classList.add("provider"); + providor.textContent = this.json.provider.name; + div.append(providor); + } + const a = document.createElement("a"); + a.href = this.json.url; + a.textContent = this.json.title; + div.append(a); + const description = document.createElement("p"); + description.textContent = this.json.description; + div.append(description); + if (this.json.thumbnail) { + const img = document.createElement("img"); + img.classList.add("bigembedimg"); + img.onclick = function () { + const full = new Fullscreen(["img", img.src, ["fit"]]); + full.show(); + }; + img.src = this.json.thumbnail.proxy_url || this.json.thumbnail.url; + div.append(img); + } + colordiv.append(div); + return colordiv; + } +} +export { Embed }; diff --git a/.dist/file.js b/.dist/file.js new file mode 100644 index 0000000..04075fe --- /dev/null +++ b/.dist/file.js @@ -0,0 +1,127 @@ +import { Fullscreen } from "./fullscreen.js"; +class File { + owner; + id; + filename; + content_type; + width; + height; + proxy_url; + url; + size; + constructor(fileJSON, owner) { + console.log(fileJSON); + this.owner = owner; + this.id = fileJSON.id; + this.filename = fileJSON.filename; + this.content_type = fileJSON.content_type; + this.width = fileJSON.width; + this.height = fileJSON.height; + this.url = fileJSON.url; + this.proxy_url = fileJSON.proxy_url; + this.content_type = fileJSON.content_type; + this.size = fileJSON.size; + } + getHTML(temp = false) { + const src = this.proxy_url || this.url; + if (this.content_type.startsWith('image/')) { + const img = document.createElement("img"); + img.classList.add("messageimg"); + img.onclick = function () { + const full = new Fullscreen(["img", img.src, ["fit"]]); + full.show(); + }; + img.src = src; + img.height = this.height; + img.width = this.width; + return img; + } + else if (this.content_type.startsWith('video/')) { + const video = document.createElement("video"); + const source = document.createElement("source"); + source.src = src; + video.append(source); + source.type = this.content_type; + video.controls = !temp; + return video; + } + else if (this.content_type.startsWith('audio/')) { + const audio = document.createElement("audio"); + const source = document.createElement("source"); + source.src = src; + audio.append(source); + source.type = this.content_type; + audio.controls = !temp; + return audio; + } + else { + return this.createunknown(); + } + } + upHTML(files, file) { + const div = document.createElement("div"); + const contained = this.getHTML(true); + div.classList.add("containedFile"); + div.append(contained); + const controls = document.createElement("div"); + const garbage = document.createElement("button"); + garbage.textContent = "🗑"; + garbage.onclick = _ => { + div.remove(); + files.splice(files.indexOf(file), 1); + }; + controls.classList.add("controls"); + div.append(controls); + controls.append(garbage); + return div; + } + static initFromBlob(file) { + return new File({ + filename: file.name, + size: file.size, + id: null, + content_type: file.type, + width: undefined, + height: undefined, + url: URL.createObjectURL(file), + proxy_url: undefined + }, null); + } + createunknown() { + console.log("🗎"); + const src = this.proxy_url || this.url; + const div = document.createElement("table"); + div.classList.add("unknownfile"); + const nametr = document.createElement("tr"); + div.append(nametr); + const fileicon = document.createElement("td"); + nametr.append(fileicon); + fileicon.append("🗎"); + fileicon.classList.add("fileicon"); + fileicon.rowSpan = 2; + const nametd = document.createElement("td"); + if (src) { + const a = document.createElement("a"); + a.href = src; + a.textContent = this.filename; + nametd.append(a); + } + else { + nametd.textContent = this.filename; + } + nametd.classList.add("filename"); + nametr.append(nametd); + const sizetr = document.createElement("tr"); + const size = document.createElement("td"); + sizetr.append(size); + size.textContent = "Size:" + File.filesizehuman(this.size); + size.classList.add("filesize"); + div.appendChild(sizetr); + return div; + } + static filesizehuman(fsize) { + var i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024)); + return +((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes'][i]; + } +} +export { File }; diff --git a/.dist/fullscreen.js b/.dist/fullscreen.js new file mode 100644 index 0000000..3d271b0 --- /dev/null +++ b/.dist/fullscreen.js @@ -0,0 +1,247 @@ +export { Fullscreen }; +class Fullscreen { + layout; + onclose; + onopen; + html; + background; + constructor(layout, onclose = _ => { }, onopen = _ => { }) { + this.layout = layout; + this.onclose = onclose; + this.onopen = onopen; + const div = document.createElement("div"); + div.appendChild(this.tohtml(layout)); + this.html = div; + this.html.classList.add("centeritem"); + if (!(layout[0] === "img")) { + this.html.classList.add("nonimagecenter"); + } + } + tohtml(array) { + switch (array[0]) { + case "img": + const img = document.createElement("img"); + img.src = array[1]; + if (array[2] != undefined) { + if (array[2].length == 2) { + img.width = array[2][0]; + img.height = array[2][1]; + } + else if (array[2][0] == "fit") { + img.classList.add("imgfit"); + } + } + return img; + case "hdiv": + const hdiv = document.createElement("table"); + const tr = document.createElement("tr"); + hdiv.appendChild(tr); + for (const thing of array) { + if (thing === "hdiv") { + continue; + } + const td = document.createElement("td"); + td.appendChild(this.tohtml(thing)); + tr.appendChild(td); + } + return hdiv; + case "vdiv": + const vdiv = document.createElement("table"); + for (const thing of array) { + if (thing === "vdiv") { + continue; + } + const tr = document.createElement("tr"); + tr.appendChild(this.tohtml(thing)); + vdiv.appendChild(tr); + } + return vdiv; + case "checkbox": + { + const div = document.createElement("div"); + const checkbox = document.createElement('input'); + div.appendChild(checkbox); + const label = document.createElement("span"); + checkbox.value = array[2]; + label.textContent = array[1]; + div.appendChild(label); + checkbox.addEventListener("change", array[3]); + checkbox.type = "checkbox"; + return div; + } + case "button": + { + const div = document.createElement("div"); + const input = document.createElement('button'); + const label = document.createElement("span"); + input.textContent = array[2]; + label.textContent = array[1]; + div.appendChild(label); + div.appendChild(input); + input.addEventListener("click", array[3]); + return div; + } + case "mdbox": + { + const div = document.createElement("div"); + const input = document.createElement("textarea"); + input.value = array[2]; + const label = document.createElement("span"); + label.textContent = array[1]; + input.addEventListener("input", array[3]); + div.appendChild(label); + div.appendChild(document.createElement("br")); + div.appendChild(input); + return div; + } + case "textbox": + { + const div = document.createElement("div"); + const input = document.createElement("input"); + input.value = array[2]; + input.type = "text"; + const label = document.createElement("span"); + label.textContent = array[1]; + console.log(array[3]); + input.addEventListener("input", array[3]); + div.appendChild(label); + div.appendChild(input); + return div; + } + case "fileupload": + { + const div = document.createElement("div"); + const input = document.createElement("input"); + input.type = "file"; + const label = document.createElement("span"); + label.textContent = array[1]; + div.appendChild(label); + div.appendChild(input); + input.addEventListener("change", array[2]); + console.log(array); + return div; + } + case "text": { + const span = document.createElement("span"); + span.textContent = array[1]; + return span; + } + case "title": { + const span = document.createElement("span"); + span.classList.add("title"); + span.textContent = array[1]; + return span; + } + case "radio": { + const div = document.createElement("div"); + const fieldset = document.createElement("fieldset"); + fieldset.addEventListener("change", function () { + let i = -1; + for (const thing of fieldset.children) { + i++; + if (i === 0) { + continue; + } + const checkbox = thing.children[0].children[0]; + if (checkbox.checked) { + array[3](checkbox.value); + } + } + }); + const legend = document.createElement("legend"); + legend.textContent = array[1]; + fieldset.appendChild(legend); + let i = 0; + for (const thing of array[2]) { + const div = document.createElement("div"); + const input = document.createElement("input"); + input.classList.add("radio"); + input.type = "radio"; + input.name = array[1]; + input.value = thing; + if (i === array[4]) { + input.checked = true; + } + const label = document.createElement("label"); + label.appendChild(input); + const span = document.createElement("span"); + span.textContent = thing; + label.appendChild(span); + div.appendChild(label); + fieldset.appendChild(div); + i++; + } + div.appendChild(fieldset); + return div; + } + case "html": { + return array[1]; + } + case "select": { + const div = document.createElement("div"); + const label = document.createElement("label"); + const select = document.createElement("select"); + label.textContent = array[1]; + div.append(label); + div.appendChild(select); + for (const thing of array[2]) { + const option = document.createElement("option"); + option.textContent = thing; + select.appendChild(option); + } + select.selectedIndex = array[4]; + select.addEventListener("change", array[3]); + return div; + } + case "tabs": { + const table = document.createElement("table"); + const tabs = document.createElement("tr"); + tabs.classList.add("tabbed-head"); + table.appendChild(tabs); + const td = document.createElement("td"); + tabs.appendChild(td); + const content = document.createElement("tr"); + content.classList.add("tabbed-content"); + table.appendChild(content); + let shown; + for (const thing of array[1]) { + const button = document.createElement("button"); + button.textContent = thing[0]; + td.appendChild(button); + const tdcontent = document.createElement("td"); + tdcontent.colSpan = array[1].length; + tdcontent.appendChild(this.tohtml(thing[1])); + content.appendChild(tdcontent); + if (!shown) { + shown = tdcontent; + } + else { + tdcontent.hidden = true; + } + button.addEventListener("click", _ => { + shown.hidden = true; + tdcontent.hidden = false; + shown = tdcontent; + }); + } + return table; + } + default: + console.error("can't find element:" + array[0], " full element:" + array); + return; + } + } + show() { + this.onopen(); + console.log("fullscreen"); + this.background = document.createElement("div"); + this.background.classList.add("background"); + document.body.appendChild(this.background); + document.body.appendChild(this.html); + this.background.onclick = function () { this.hide(); }.bind(this); + } + hide() { + document.body.removeChild(this.background); + document.body.removeChild(this.html); + } +} diff --git a/.dist/guild.js b/.dist/guild.js new file mode 100644 index 0000000..dc7b712 --- /dev/null +++ b/.dist/guild.js @@ -0,0 +1,486 @@ +import { Channel } from "./channel.js"; +import { Contextmenu } from "./contextmenu.js"; +import { Role } from "./role.js"; +import { Fullscreen } from "./fullscreen.js"; +import { Member } from "./member.js"; +class Guild { + owner; + headers; + channels; + channelids; + id; + properties; + roles; + roleids; + prevchannel; + message_notifications; + headchannels; + position; + parent_id; + member; + html; + static contextmenu = new Contextmenu("guild menu"); + static setupcontextmenu() { + Guild.contextmenu.addbutton("Copy Guild id", function () { + console.log(this); + navigator.clipboard.writeText(this.id); + }); + Guild.contextmenu.addbutton("Mark as read", function () { + console.log(this); + this.markAsRead(); + }); + Guild.contextmenu.addbutton("Notifications", function () { + console.log(this); + this.setnotifcation(); + }); + Guild.contextmenu.addbutton("Leave guild", function () { + this.confirmleave(); + }, null, function (_) { return _.properties.owner_id !== _.member.user.id; }); + Guild.contextmenu.addbutton("Delete guild", function () { + this.confirmDelete(); + }, null, function (_) { return _.properties.owner_id === _.member.user.id; }); + Guild.contextmenu.addbutton("Create invite", function () { + console.log(this); + }, null, _ => true, _ => false); + /* -----things left for later----- + guild.contextmenu.addbutton("Leave Guild",function(){ + console.log(this) + this.deleteChannel(); + },null,_=>{return thisuser.isAdmin()}) + + guild.contextmenu.addbutton("Mute Guild",function(){ + editchannelf(this); + },null,_=>{return thisuser.isAdmin()}) + */ + } + 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.roles = []; + this.roleids = {}; + this.prevchannel = undefined; + this.message_notifications = 0; + for (const roley of JSON.roles) { + const roleh = new Role(roley, this); + this.roles.push(roleh); + this.roleids[roleh.id] = roleh; + } + Member.resolve(member, this).then(_ => this.member = _); + for (const thing of JSON.channels) { + const temp = new Channel(thing, this); + this.channels.push(temp); + this.channelids[temp.id] = temp; + } + this.headchannels = []; + for (const thing of this.channels) { + if (thing.resolveparent(this)) { + this.headchannels.push(thing); + } + } + } + notisetting(settings) { + this.message_notifications = settings.message_notifications; + } + setnotifcation() { + let noti = this.message_notifications; + const notiselect = new Fullscreen(["vdiv", + ["radio", "select notifications type", + ["all", "only mentions", "none"], + function (e) { + noti = ["all", "only mentions", "none"].indexOf(e); + }, + noti + ], + ["button", "", "submit", _ => { + fetch(this.info.api.toString() + "/v9/users/@me/guilds/settings", { + method: "PATCH", + headers: this.headers, + body: JSON.stringify({ + "guilds": { + [this.id]: { + "message_notifications": noti + } + } + }) + }); + this.message_notifications = noti; + }] + ]); + notiselect.show(); + } + confirmleave() { + const full = new Fullscreen([ + "vdiv", + ["title", + "Are you sure you want to leave?" + ], + ["hdiv", + ["button", + "", + "Yes, I'm sure", + _ => { + this.leave().then(_ => { + full.hide(); + }); + } + ], + ["button", + "", + "Nevermind", + _ => { + full.hide(); + } + ] + ] + ]); + full.show(); + } + async leave() { + return fetch(this.info.api.toString() + "/users/@me/guilds/" + this.id, { + method: "DELETE", + headers: this.headers + }); + } + printServers() { + let build = ""; + for (const thing of this.headchannels) { + build += (thing.name + ":" + thing.position) + "\n"; + for (const thingy of thing.children) { + build += (" " + thingy.name + ":" + thingy.position) + "\n"; + } + } + console.log(build); + } + calculateReorder() { + let position = -1; + let build = []; + for (const thing of this.headchannels) { + const thisthing = { id: thing.id, position: undefined, parent_id: undefined }; + if (thing.position <= position) { + thing.position = (thisthing.position = position + 1); + } + position = thing.position; + console.log(position); + if (thing.move_id && thing.move_id !== thing.parent_id) { + thing.parent_id = thing.move_id; + thisthing.parent_id = thing.parent_id; + thing.move_id = undefined; + } + if (thisthing.position || thisthing.parent_id) { + build.push(thisthing); + console.log(this.channelids[thisthing.parent_id]); + } + if (thing.children.length > 0) { + const things = thing.calculateReorder(); + for (const thing of things) { + build.push(thing); + } + } + } + console.log(build); + this.printServers(); + if (build.length === 0) { + return; + } + const serverbug = false; + if (serverbug) { + for (const thing of build) { + console.log(build, thing); + fetch(this.info.api.toString() + "/v9/guilds/" + this.id + "/channels", { + method: "PATCH", + headers: this.headers, + body: JSON.stringify([thing]) + }); + } + } + else { + fetch(this.info.api.toString() + "/v9/guilds/" + this.id + "/channels", { + method: "PATCH", + headers: this.headers, + body: JSON.stringify(build) + }); + } + } + get localuser() { + return this.owner; + } + get info() { + return this.owner.info; + } + sortchannels() { + this.headchannels.sort((a, b) => { return a.position - b.position; }); + } + generateGuildIcon() { + const divy = document.createElement("div"); + divy.classList.add("servernoti"); + const noti = document.createElement("div"); + noti.classList.add("unread"); + divy.append(noti); + this.localuser.guildhtml[this.id] = divy; + if (this.properties.icon != null) { + const img = document.createElement("img"); + img.classList.add("pfp", "servericon"); + img.src = this.info.cdn.toString() + "icons/" + this.properties.id + "/" + this.properties.icon + ".png"; + divy.appendChild(img); + img.onclick = () => { + console.log(this.loadGuild); + this.loadGuild(); + this.loadChannel(); + }; + Guild.contextmenu.bind(img, this); + } + else { + const div = document.createElement("div"); + let build = ""; + for (const char of this.properties.name.split(" ")) { + build += char[0]; + } + div.textContent = build; + div.classList.add("blankserver", "servericon"); + divy.appendChild(div); + div.onclick = () => { + this.loadGuild(); + this.loadChannel(); + }; + Guild.contextmenu.bind(div, this); + } + return divy; + } + confirmDelete() { + let confirmname = ""; + const full = new Fullscreen([ + "vdiv", + ["title", + "Are you sure you want to delete " + this.properties.name + "?" + ], + ["textbox", + "Name of server:", + "", + function () { + confirmname = this.value; + } + ], + ["hdiv", + ["button", + "", + "Yes, I'm sure", + _ => { + console.log(confirmname); + if (confirmname !== this.properties.name) { + return; + } + this.delete().then(_ => { + full.hide(); + }); + } + ], + ["button", + "", + "Nevermind", + _ => { + full.hide(); + } + ] + ] + ]); + full.show(); + } + async delete() { + return fetch(this.info.api.toString() + "/guilds/" + this.id + "/delete", { + method: "POST", + headers: this.headers, + }); + } + unreads(html = undefined) { + if (html) { + this.html = html; + } + else { + html = this.html; + } + let read = true; + for (const thing of this.channels) { + if (thing.hasunreads) { + console.log(thing); + read = false; + break; + } + } + if (!html) { + return; + } + if (read) { + html.children[0].classList.remove("notiunread"); + } + else { + html.children[0].classList.add("notiunread"); + } + } + getHTML() { + //this.printServers(); + this.sortchannels(); + this.printServers(); + const build = document.createElement("div"); + for (const thing of this.headchannels) { + build.appendChild(thing.createguildHTML(this.isAdmin())); + } + return build; + } + isAdmin() { + return this.member.isAdmin(); + } + async markAsRead() { + const build = { read_states: [] }; + for (const thing of this.channels) { + if (thing.hasunreads) { + build.read_states.push({ channel_id: thing.id, message_id: thing.lastmessageid, read_state_type: 0 }); + thing.lastreadmessageid = thing.lastmessageid; + thing.myhtml.classList.remove("cunread"); + } + } + this.unreads(); + fetch(this.info.api.toString() + "/v9/read-states/ack-bulk", { + method: "POST", + headers: this.headers, + 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; + } + return this.member.hasRole(r); + } + loadChannel(ID = undefined) { + if (ID && this.channelids[ID]) { + this.channelids[ID].getHTML(); + return; + } + if (this.prevchannel) { + console.log(this.prevchannel); + this.prevchannel.getHTML(); + return; + } + for (const thing of this.channels) { + if (thing.children.length === 0) { + thing.getHTML(); + return; + } + } + } + loadGuild() { + this.localuser.loadGuild(this.id); + } + updateChannel(JSON) { + this.channelids[JSON.id].updateChannel(JSON); + this.headchannels = []; + for (const thing of this.channels) { + thing.children = []; + } + for (const thing of this.channels) { + if (thing.resolveparent(this)) { + this.headchannels.push(thing); + } + } + this.printServers(); + } + createChannelpac(JSON) { + const thischannel = new Channel(JSON, this); + this.channelids[JSON.id] = thischannel; + this.channels.push(thischannel); + thischannel.resolveparent(this); + if (!thischannel.parrent) { + this.headchannels.push(thischannel); + } + this.calculateReorder(); + this.printServers(); + } + createchannels(func = this.createChannel) { + let name = ""; + let category = 0; + const channelselect = new Fullscreen(["vdiv", + ["radio", "select channel type", + ["voice", "text", "announcement"], + function (e) { + console.log(e); + category = { "text": 0, "voice": 2, "announcement": 5, "category": 4 }[e]; + }, + 1 + ], + ["textbox", "Name of channel", "", function () { + console.log(this); + name = this.value; + }], + ["button", "", "submit", function () { + console.log(name, category); + func(name, category); + channelselect.hide(); + }.bind(this)] + ]); + channelselect.show(); + } + createcategory() { + let name = ""; + let category = 4; + const channelselect = new Fullscreen(["vdiv", + ["textbox", "Name of category", "", function () { + console.log(this); + name = this.value; + }], + ["button", "", "submit", function () { + console.log(name, category); + this.createChannel(name, category); + channelselect.hide(); + }] + ]); + channelselect.show(); + } + delChannel(JSON) { + const channel = this.channelids[JSON.id]; + delete this.channelids[JSON.id]; + this.channels.splice(this.channels.indexOf(channel), 1); + const indexy = this.headchannels.indexOf(channel); + if (indexy !== -1) { + this.headchannels.splice(indexy, 1); + } + /* + const build=[]; + for(const thing of this.channels){ + console.log(thing.id); + if(thing!==channel){ + build.push(thing) + }else{ + console.log("fail"); + if(thing.parrent){ + thing.parrent.delChannel(JSON); + } + } + } + this.channels=build; + */ + this.printServers(); + } + createChannel(name, type) { + fetch(this.info.api.toString() + "/guilds/" + this.id + "/channels", { + method: "Post", + headers: this.headers, + body: JSON.stringify({ name: name, type: type }) + }); + } +} +Guild.setupcontextmenu(); +export { Guild }; diff --git a/.dist/index.js b/.dist/index.js new file mode 100644 index 0000000..a7aaf5b --- /dev/null +++ b/.dist/index.js @@ -0,0 +1,278 @@ +import { Localuser } from "./localuser.js"; +import { Contextmenu } from "./contextmenu.js"; +import { mobile, getBulkUsers, setTheme } from "./login.js"; +async function waitforload() { + let res; + new Promise(r => { res = r; }); + document.addEventListener("DOMContentLoaded", function () { + res(); + }); + await res; +} +await waitforload(); +function setDynamicHeight() { + var servertdHeight = document.getElementById('servertd').offsetHeight + document.getElementById('typediv').offsetHeight + document.getElementById('pasteimage').offsetHeight; + document.documentElement.style.setProperty('--servertd-height', servertdHeight + 'px'); +} +const resizeObserver = new ResizeObserver(() => { + setDynamicHeight(); +}); +resizeObserver.observe(document.getElementById('servertd')); +resizeObserver.observe(document.getElementById('replybox')); +resizeObserver.observe(document.getElementById('pasteimage')); +setDynamicHeight(); +const users = getBulkUsers(); +if (!users.currentuser) { + window.location.href = '/login.html'; +} +var info = users.users[users.currentuser].serverurls; +let token = users.users[users.currentuser].token; +let READY; +let thisuser = new Localuser(users.users[users.currentuser]); +thisuser.initwebsocket().then(_ => { + thisuser.loaduser(); + thisuser.init(); + document.getElementById("loading").classList.add("doneloading"); + document.getElementById("loading").classList.remove("loading"); + console.log("done loading"); +}); +{ + const userinfo = document.getElementById("userinfo"); + const userdock = document.getElementById("userdock"); + userinfo.addEventListener("click", function (event) { + const table = document.createElement("table"); + for (const thing of Object.values(users.users)) { + const specialuser = thing; + console.log(specialuser.pfpsrc); + const tr = document.createElement("tr"); + const td = document.createElement("td"); + const userinfo = document.createElement("table"); + userinfo.classList.add("switchtable"); + const row = document.createElement("tr"); + userinfo.append(row); + const pfpcell = document.createElement("td"); + row.append(pfpcell); + const pfp = document.createElement("img"); + pfpcell.append(pfp); + const usertd = document.createElement("td"); + row.append(usertd); + const user = document.createElement("div"); + usertd.append(user); + user.append(specialuser.username); + user.append(document.createElement("br")); + const span = document.createElement("span"); + span.textContent = specialuser.serverurls.wellknown.hostname; + user.append(span); + span.classList.add("serverURL"); + pfp.src = specialuser.pfpsrc; + pfp.classList.add("pfp"); + td.append(userinfo); + tr.append(td); + table.append(tr); + tr.addEventListener("click", _ => { + thisuser.unload(); + document.getElementById("loading").classList.remove("doneloading"); + document.getElementById("loading").classList.add("loading"); + thisuser = new Localuser(specialuser); + users["currentuser"] = specialuser.uid; + localStorage.setItem("userinfos", JSON.stringify(users)); + thisuser.initwebsocket().then(_ => { + thisuser.loaduser(); + thisuser.init(); + document.getElementById("loading").classList.add("doneloading"); + document.getElementById("loading").classList.remove("loading"); + console.log("done loading"); + }); + }); + } + { + const tr = document.createElement("tr"); + const td = document.createElement("td"); + tr.append(td); + td.append("Switch accounts ⇌"); + td.addEventListener("click", _ => { + window.location.href = "/login.html"; + }); + table.append(tr); + } + table.classList.add("accountSwitcher"); + if (Contextmenu.currentmenu != "") { + Contextmenu.currentmenu.remove(); + } + Contextmenu.currentmenu = table; + console.log(table); + userdock.append(table); + event.stopImmediatePropagation(); + }); +} +{ + const menu = new Contextmenu("create rightclick"); + menu.addbutton("Create channel", function () { + thisuser.lookingguild.createchannels(); + }, null, _ => { return thisuser.isAdmin(); }); + menu.addbutton("Create category", function () { + thisuser.lookingguild.createcategory(); + }, null, _ => { return thisuser.isAdmin(); }); + menu.bind(document.getElementById("channels")); +} +function editchannelf(channel) { channel.editChannel(); } +const pasteimage = document.getElementById("pasteimage"); +let replyingto = null; +async function enter(event) { + const channel = thisuser.channelfocus; + channel.typingstart(); + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + if (channel.editing) { + channel.editing.edit((typebox).value); + channel.editing = null; + } + else { + replyingto = thisuser.channelfocus.replyingto; + let replying = replyingto; + if (replyingto) { + replyingto.div.classList.remove("replying"); + } + thisuser.channelfocus.replyingto = null; + channel.sendMessage(typebox.value, { + attachments: images, + replyingto: replying, + }); + thisuser.channelfocus.makereplybox(); + } + while (images.length != 0) { + images.pop(); + pasteimage.removeChild(imageshtml.pop()); + } + typebox.value = ""; + return; + } +} +const typebox = document.getElementById("typebox"); +typebox.addEventListener("keyup", enter); +typebox.addEventListener("keydown", event => { + if (event.key === "Enter" && !event.shiftKey) + event.preventDefault(); +}); +console.log(typebox); +typebox.onclick = console.log; +let serverz = 0; +let serverid = []; +let cchanel = 0; +function getguildinfo() { + const path = window.location.pathname.split("/"); + const channel = path[3]; + this.ws.send(JSON.stringify({ op: 14, d: { guild_id: path[2], channels: { [channel]: [[0, 99]] } } })); +} +const images = []; +const imageshtml = []; +function createunknown(fname, fsize) { + const div = document.createElement("table"); + div.classList.add("unknownfile"); + const nametr = document.createElement("tr"); + div.append(nametr); + const fileicon = document.createElement("td"); + nametr.append(fileicon); + fileicon.append("🗎"); + fileicon.classList.add("fileicon"); + fileicon.rowSpan = 2; + const nametd = document.createElement("td"); + { + nametd.textContent = fname; + } + nametd.classList.add("filename"); + nametr.append(nametd); + const sizetr = document.createElement("tr"); + const size = document.createElement("td"); + sizetr.append(size); + size.textContent = "Size:" + filesizehuman(fsize); + size.classList.add("filesize"); + div.appendChild(sizetr); + return div; +} +function filesizehuman(fsize) { + var i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024)); + return +((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes'][i]; +} +function createunknownfile(file) { + return createunknown(file.name, file.size); +} +function filetohtml(file) { + if (file.type.startsWith('image/')) { + const img = document.createElement('img'); + const blob = URL.createObjectURL(file); + img.src = blob; + return img; + } + else { + console.log(file.name); + return createunknownfile(file); + } +} +import { File } from "./file.js"; +document.addEventListener('paste', async (e) => { + Array.from(e.clipboardData.files).forEach(async (f) => { + const file = File.initFromBlob(f); + e.preventDefault(); + const html = file.upHTML(images, f); + pasteimage.appendChild(html); + images.push(f); + imageshtml.push(html); + }); +}); +setTheme(); +function userSettings() { + thisuser.usersettings.show(); +} +document.getElementById("settings").onclick = userSettings; +let triggered = false; +document.getElementById("messagecontainer").addEventListener("scroll", (e) => { + const messagecontainer = document.getElementById("messagecontainer"); + if (messagecontainer.scrollTop < 2000) { + if (!triggered) { + thisuser.lookingguild.prevchannel.grabmoremessages().then(() => { + triggered = false; + if (messagecontainer.scrollTop === 0) { + messagecontainer.scrollTop = 1; + } + }); + } + triggered = true; + } + else { + if (Math.abs(messagecontainer.scrollHeight - messagecontainer.scrollTop - messagecontainer.clientHeight) < 3) { + thisuser.lookingguild.prevchannel.readbottom(); + } + } + // +}); +if (mobile) { + document.getElementById("channelw").onclick = function () { + document.getElementById("channels").parentNode.classList.add("collapse"); + document.getElementById("servertd").classList.add("collapse"); + document.getElementById("servers").classList.add("collapse"); + }; + document.getElementById("mobileback").textContent = "#"; + document.getElementById("mobileback").onclick = function () { + document.getElementById("channels").parentNode.classList.remove("collapse"); + document.getElementById("servertd").classList.remove("collapse"); + document.getElementById("servers").classList.remove("collapse"); + }; +} +/* +{ + const messages=document.getElementById("messages"); + let height=messages.clientHeight; + // + const resizeObserver=new ResizeObserver(()=>{ + console.log(messages.scrollTop,messages.clientHeight-height-messages.scrollHeight); + messages.scrollTop-=height-messages.scrollHeight; + console.log(messages.scrollTop) + //if(shouldsnap){ + // document.getElementById("messagecontainer").scrollTop = document.getElementById("messagecontainer").scrollHeight; + //} + height=messages.scrollHeight; + }) + resizeObserver.observe(messages) +} +*/ diff --git a/.dist/localuser.js b/.dist/localuser.js new file mode 100644 index 0000000..ea9751c --- /dev/null +++ b/.dist/localuser.js @@ -0,0 +1,575 @@ +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 channel = this.resolveChannelFromID(thing.id); + if (!channel) { + continue; + } + const guild = channel.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.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]); + if (resolve) { + return resolve.channelids[ID]; + } + return undefined; + } + 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("/"); + this.buildservers(); + if (location[3] === "channels") { + const guild = this.loadGuild(location[4]); + guild.loadChannel(location[5]); + this.channelfocus = guild.channelids[location[5]]; + } + } + 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"]; + } + if (this.lookingguild) { + this.lookingguild.html.classList.remove("serveropen"); + } + if (guild.html) { + guild.html.classList.add("serveropen"); + } + 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 outdiv = document.createElement("div"); + const div = document.createElement("div"); + div.textContent = "⌂"; + div.classList.add("home", "servericon"); + div["all"] = this.guildids["@me"]; + this.guildids["@me"].html = outdiv; + const unread = document.createElement("div"); + unread.classList.add("unread"); + outdiv.append(unread); + outdiv.appendChild(div); + outdiv.classList.add("servernoti"); + serverlist.append(outdiv); + 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 }; diff --git a/.dist/login.js b/.dist/login.js new file mode 100644 index 0000000..0d02490 --- /dev/null +++ b/.dist/login.js @@ -0,0 +1,237 @@ +const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); +export { mobile, getBulkUsers, getBulkInfo, setTheme, Specialuser }; +function setTheme() { + const name = localStorage.getItem("theme"); + if (!name) { + document.body.className = "Dark-theme"; + localStorage.setItem("theme", "Dark"); + } + document.body.className = name + "-theme"; +} +setTheme(); +function getBulkUsers() { + const json = getBulkInfo(); + for (const thing in json.users) { + json.users[thing] = new Specialuser(json.users[thing]); + } + return json; +} +function getBulkInfo() { + return JSON.parse(localStorage.getItem("userinfos")); +} +function setDefaults() { + let userinfos = getBulkInfo(); + if (!userinfos) { + localStorage.setItem("userinfos", JSON.stringify({ + currentuser: null, + users: {}, + preferances: { + theme: "Dark", + notifcations: false, + notisound: "three", + }, + })); + } + if (userinfos.users === undefined) { + userinfos.users = {}; + } + if (userinfos.preferances === undefined) { + userinfos.preferances = { + theme: "Dark", + notifcations: false, + notisound: "three", + }; + } + if (userinfos.preferances && (userinfos.preferances.notisound === undefined)) { + userinfos.preferances.notisound = "three"; + } + localStorage.setItem("userinfos", JSON.stringify(userinfos)); +} +setDefaults(); +class Specialuser { + serverurls; + email; + token; + loggedin; + json; + constructor(json) { + if (json instanceof Specialuser) { + console.error("specialuser can't construct from another specialuser"); + } + this.serverurls = json.serverurls; + this.serverurls.api = new URL(this.serverurls.api); + this.serverurls.cdn = new URL(this.serverurls.cdn); + this.serverurls.gateway = new URL(this.serverurls.gateway); + this.serverurls.wellknown = new URL(this.serverurls.wellknown); + this.email = json.email; + this.token = json.token; + this.loggedin = json.loggedin; + this.json = json; + if (!this.serverurls || !this.email || !this.token) { + console.error("There are fundamentally missing pieces of info missing from this user"); + } + } + set pfpsrc(e) { + console.log("this ran fr"); + this.json.pfpsrc = e; + this.updateLocal(); + } + get pfpsrc() { + return this.json.pfpsrc; + } + set username(e) { + this.json.username = e; + this.updateLocal(); + } + get username() { + return this.json.username; + } + get uid() { + return this.email + this.serverurls.wellknown; + } + toJSON() { + return this.json; + } + updateLocal() { + const info = getBulkInfo(); + info.users[this.uid] = this.toJSON(); + localStorage.setItem("userinfos", JSON.stringify(info)); + } +} +function adduser(user) { + user = new Specialuser(user); + const info = getBulkInfo(); + info.users[user.uid] = user; + info.currentuser = user.uid; + localStorage.setItem("userinfos", JSON.stringify(info)); +} +const instancein = document.getElementById("instancein"); +let timeout; +let instanceinfo; +async function checkInstance(e) { + const verify = document.getElementById("verify"); + ; + try { + verify.textContent = "Checking Instance"; + const instanceinfo = await setInstance(instancein.value); + localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo)); + verify.textContent = "Instance is all good"; + if (checkInstance["alt"]) { + checkInstance["alt"](); + } + setTimeout(_ => { + console.log(verify.textContent); + verify.textContent = ""; + }, 3000); + } + catch (e) { + console.log("catch"); + verify.textContent = "Invalid Instance, try again"; + } +} +if (instancein) { + console.log(instancein); + instancein.addEventListener("keydown", e => { + const verify = document.getElementById("verify"); + verify.textContent = "Waiting to check Instance"; + clearTimeout(timeout); + timeout = setTimeout(checkInstance, 1000); + }); + if (localStorage.getItem("instanceinfo")) { + instancein.value = JSON.parse(localStorage.getItem("instanceinfo")).wellknown; + } + else { + checkInstance("https://spacebar.chat/"); + } +} +async function login(username, password) { + const options = { + method: "POST", + body: JSON.stringify({ + "login": username, + "password": password, + "undelete": false + }), + headers: { + "Content-type": "application/json; charset=UTF-8", + } + }; + try { + const info = JSON.parse(localStorage.getItem("instanceinfo")); + const url = new URL(info.login); + return await fetch(url.origin + '/api/auth/login', options).then(responce => responce.json()) + .then((response) => { + console.log(response, response.message); + if ("Invalid Form Body" === response.message) { + return response.errors.login._errors[0].message; + console.log("test"); + } + //this.serverurls||!this.email||!this.token + adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: username, token: response.token }); + window.location.href = '/channels/@me'; + return response.token; + }); + } + catch (error) { + console.error('Error:', error); + } + ; +} +async function setInstance(url) { + url = new URL(url); + async function attempt(aurl) { + const info = await fetch(`${aurl.toString()}${aurl.pathname.includes("api") ? "" : "api"}/policies/instance/domains`) + .then((x) => x.json()); + return { + api: info.apiEndpoint, + gateway: info.gateway, + cdn: info.cdn, + wellknown: url, + login: aurl.toString() + }; + } + try { + return await attempt(url); + } + catch (e) { + } + const wellKnown = await fetch(`${url.origin}/.well-known/spacebar`) + .then((x) => x.json()) + .then((x) => new URL(x.api)); + return await attempt(wellKnown); +} +async function check(e) { + e.preventDefault(); + let h = await login(e.srcElement[1].value, e.srcElement[2].value); + document.getElementById("wrong").textContent = h; + console.log(h); +} +if (document.getElementById("form")) { + document.getElementById("form").addEventListener("submit", check); +} +//Service workers :3 +if ("serviceWorker" in navigator) { + navigator.serviceWorker.register("/service.js", { + scope: "/", + }).then((registration) => { + let serviceWorker; + if (registration.installing) { + serviceWorker = registration.installing; + console.log("installing"); + } + else if (registration.waiting) { + serviceWorker = registration.waiting; + console.log("waiting"); + } + else if (registration.active) { + serviceWorker = registration.active; + console.log("active"); + } + if (serviceWorker) { + console.log(serviceWorker.state); + serviceWorker.addEventListener("statechange", (e) => { + console.log(e.target.state); + }); + } + }); +} diff --git a/.dist/markdown.js b/.dist/markdown.js new file mode 100644 index 0000000..f247258 --- /dev/null +++ b/.dist/markdown.js @@ -0,0 +1,362 @@ +export { markdown }; +function markdown(text, { keep = false, stdsize = false } = {}) { + let txt; + if ((typeof txt) === "string") { + txt = text.split(""); + } + else { + txt = text; + } + const span = document.createElement("span"); + let current = document.createElement("span"); + function appendcurrent() { + if (current.innerHTML !== "") { + span.append(current); + current = document.createElement("span"); + } + } + for (let i = 0; i < txt.length; i++) { + if (txt[i] === "\n" || i === 0) { + const first = i === 0; + if (first) { + i--; + } + let element = null; + let keepys = ""; + if (txt[i + 1] === "#") { + console.log("test"); + if (txt[i + 2] === "#") { + if (txt[i + 3] === "#" && txt[i + 4] === " ") { + element = document.createElement("h3"); + keepys = "### "; + i += 5; + } + else if (txt[i + 3] === " ") { + element = document.createElement("h2"); + element.classList.add("h2md"); + keepys = "## "; + i += 4; + } + } + else if (txt[i + 2] === " ") { + element = document.createElement("h1"); + keepys = "# "; + i += 3; + } + } + else if (txt[i + 1] === ">" && txt[i + 2] === " ") { + element = document.createElement("div"); + const line = document.createElement("div"); + line.classList.add("quoteline"); + element.append(line); + element.classList.add("quote"); + keepys = "> "; + i += 3; + } + if (keepys) { + appendcurrent(); + if (!first && !stdsize) { + span.appendChild(document.createElement("br")); + } + const build = []; + for (; txt[i] !== "\n" && txt[i] !== undefined; i++) { + build.push(txt[i]); + } + if (stdsize) { + element = document.createElement("span"); + } + if (keep) { + element.append(keepys); + } + element.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + span.append(element); + i--; + continue; + } + if (first) { + i++; + } + } + if (txt[i] === "\n") { + if (!stdsize) { + appendcurrent(); + span.append(document.createElement("br")); + } + continue; + } + if (txt[i] === "`") { + let count = 1; + if (txt[i + 1] === "`") { + count++; + if (txt[i + 2] === "`") { + count++; + } + } + let build = ""; + if (keep) { + build += "`".repeat(count); + } + let find = 0; + let j = i + count; + let init = true; + for (; txt[j] !== undefined && (txt[j] !== "\n" || count === 3) && find !== count; j++) { + if (txt[j] === "`") { + find++; + } + else { + if (find !== 0) { + build += "`".repeat(find); + find = 0; + } + if (init && count === 3) { + if (txt[j] === " " || txt[j] === "\n") { + init = false; + } + if (keep) { + build += txt[j]; + } + continue; + } + build += txt[j]; + } + } + if (find === count) { + appendcurrent(); + i = j; + if (keep) { + build += "`".repeat(find); + } + if (count !== 3 && !stdsize) { + const samp = document.createElement("samp"); + samp.textContent = build; + span.appendChild(samp); + } + else { + const pre = document.createElement("pre"); + if (build[build.length - 1] === "\n") { + build = build.substring(0, build.length - 1); + } + if (txt[i] === "\n") { + i++; + } + pre.textContent = build; + span.appendChild(pre); + } + i--; + continue; + } + } + if (txt[i] === "*") { + let count = 1; + if (txt[i + 1] === "*") { + count++; + if (txt[i + 2] === "*") { + count++; + } + } + let build = []; + let find = 0; + let j = i + count; + for (; txt[j] !== undefined && find !== count; j++) { + if (txt[j] === "*") { + find++; + } + else { + build.push(txt[j]); + if (find !== 0) { + build = build.concat(new Array(find).fill("*")); + find = 0; + } + } + } + if (find === count && (count != 1 || txt[i + 1] !== " ")) { + appendcurrent(); + i = j; + const stars = "*".repeat(count); + if (count === 1) { + const i = document.createElement("i"); + if (keep) { + i.append(stars); + } + i.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + if (keep) { + i.append(stars); + } + span.appendChild(i); + } + else if (count === 2) { + const b = document.createElement("b"); + if (keep) { + b.append(stars); + } + b.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + if (keep) { + b.append(stars); + } + span.appendChild(b); + } + else { + const b = document.createElement("b"); + const i = document.createElement("i"); + if (keep) { + b.append(stars); + } + b.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + if (keep) { + b.append(stars); + } + i.appendChild(b); + span.appendChild(i); + } + i--; + continue; + } + } + if (txt[i] === "_") { + let count = 1; + if (txt[i + 1] === "_") { + count++; + if (txt[i + 2] === "_") { + count++; + } + } + let build = []; + let find = 0; + let j = i + count; + for (; txt[j] !== undefined && find !== count; j++) { + if (txt[j] === "_") { + find++; + } + else { + build.push(txt[j]); + if (find !== 0) { + build = build.concat(new Array(find).fill("_")); + find = 0; + } + } + } + if (find === count && (count != 1 || (txt[j + 1] === " " || txt[j + 1] === "\n" || txt[j + 1] === undefined))) { + appendcurrent(); + i = j; + const underscores = "_".repeat(count); + if (count === 1) { + const i = document.createElement("i"); + if (keep) { + i.append(underscores); + } + i.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + if (keep) { + i.append(underscores); + } + span.appendChild(i); + } + else if (count === 2) { + const u = document.createElement("u"); + if (keep) { + u.append(underscores); + } + u.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + if (keep) { + u.append(underscores); + } + span.appendChild(u); + } + else { + const u = document.createElement("u"); + const i = document.createElement("i"); + if (keep) { + i.append(underscores); + } + i.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + if (keep) { + i.append(underscores); + } + u.appendChild(i); + span.appendChild(u); + } + i--; + continue; + } + } + if (txt[i] === "~" && txt[i + 1] === "~") { + let count = 2; + let build = []; + let find = 0; + let j = i + 2; + for (; txt[j] !== undefined && find !== count; j++) { + if (txt[j] === "~") { + find++; + } + else { + build.push(txt[j]); + if (find !== 0) { + build = build.concat(new Array(find).fill("~")); + find = 0; + } + } + } + if (find === count) { + appendcurrent(); + i = j; + const underscores = "~~"; + if (count === 2) { + const s = document.createElement("s"); + if (keep) { + s.append(underscores); + } + s.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + if (keep) { + s.append(underscores); + } + span.appendChild(s); + } + continue; + } + } + if (txt[i] === "|" && txt[i + 1] === "|") { + let count = 2; + let build = []; + let find = 0; + let j = i + 2; + for (; txt[j] !== undefined && find !== count; j++) { + if (txt[j] === "|") { + find++; + } + else { + build.push(txt[j]); + if (find !== 0) { + build = build.concat(new Array(find).fill("~")); + find = 0; + } + } + } + if (find === count) { + appendcurrent(); + i = j; + const underscores = "||"; + if (count === 2) { + const j = document.createElement("j"); + if (keep) { + j.append(underscores); + } + j.appendChild(markdown(build, { keep: keep, stdsize: stdsize })); + j.classList.add("spoiler"); + j.onclick = markdown.unspoil; + if (keep) { + j.append(underscores); + } + span.appendChild(j); + } + continue; + } + } + current.textContent += txt[i]; + } + appendcurrent(); + return span; +} +markdown.unspoil = function (e) { + //console.log("undone") + e.target.classList.remove("spoiler"); + e.target.classList.add("unspoiled"); +}; diff --git a/.dist/member.js b/.dist/member.js new file mode 100644 index 0000000..74a9c69 --- /dev/null +++ b/.dist/member.js @@ -0,0 +1,119 @@ +import { User } from "./user.js"; +import { Guild } from "./guild.js"; +class Member { + static already = {}; + owner; + user; + roles; + error; + constructor(memberjson, owner, error = false) { + this.error = error; + this.owner = owner; + let membery = memberjson; + this.roles = []; + if (!error) { + if (memberjson.guild_member) { + membery = memberjson.guild_member; + this.user = memberjson.user; + } + } + for (const thing of Object.keys(membery)) { + if (thing === "guild") { + continue; + } + if (thing === "owner") { + continue; + } + if (thing === "roles") { + for (const strrole of membery["roles"]) { + const role = this.guild.getRole(strrole); + this.roles.push(role); + } + continue; + } + this[thing] = membery[thing]; + } + if (error) { + this.user = memberjson; + } + else { + this.user = new User(this.user, owner.localuser); + } + } + get guild() { + return this.owner; + } + get localuser() { + return this.guild.localuser; + } + get info() { + return this.owner.info; + } + static async resolve(unkown, guild) { + if (!(guild instanceof Guild)) { + console.error(guild); + } + let user; + if (unkown instanceof User) { + user = unkown; + } + 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][user.id]) { + const memb = Member.already[guild.id][user.id]; + if (memb instanceof Promise) { + return await memb; + } + return memb; + } + const promoise = fetch(guild.info.api.toString() + "/v9/users/" + user.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][user.id] = memb; + console.log("resolved"); + return memb; + }); + Member.already[guild.id][user.id] = promoise; + try { + return await promoise; + } + catch (_) { + const memb = new Member(user, guild, true); + Member.already[guild.id][user.id] = memb; + return memb; + } + } + hasRole(ID) { + console.log(this.roles, ID); + for (const thing of this.roles) { + if (thing.id === ID) { + return true; + } + } + return false; + } + getColor() { + for (const thing of this.roles) { + const color = thing.getColor(); + if (color) { + return color; + } + } + return ""; + } + isAdmin() { + for (const role of this.roles) { + if (role.permissions.getPermision("ADMINISTRATOR")) { + return true; + } + } + return this.guild.properties.owner_id === this.user.id; + } +} +export { Member }; diff --git a/.dist/message.js b/.dist/message.js new file mode 100644 index 0000000..121a872 --- /dev/null +++ b/.dist/message.js @@ -0,0 +1,384 @@ +import { Contextmenu } from "./contextmenu.js"; +import { User } from "./user.js"; +import { Member } from "./member.js"; +import { markdown } from "./markdown.js"; +import { Embed } from "./embed.js"; +import { File } from "./file.js"; +class Message { + static contextmenu = new Contextmenu("message menu"); + owner; + headers; + embeds; + author; + mentions; + mention_roles; + attachments; //probably should be its own class tbh, should be Attachments[] + id; + message_reference; + type; + timestamp; + content; + static del; + static resolve; + div; + static setup() { + this.del = new Promise(_ => { this.resolve = _; }); + Message.setupcmenu(); + } + static async wipeChanel() { + this.resolve(); + document.getElementById("messages").innerHTML = ""; + await Promise.allSettled([this.resolve]); + this.del = new Promise(_ => { this.resolve = _; }); + } + static setupcmenu() { + Message.contextmenu.addbutton("Copy raw text", function () { + navigator.clipboard.writeText(this.content); + }); + Message.contextmenu.addbutton("Reply", function (div) { + this.channel.setReplying(this); + }); + Message.contextmenu.addbutton("Copy message id", function () { + navigator.clipboard.writeText(this.id); + }); + Message.contextmenu.addbutton("Copy user id", function () { + navigator.clipboard.writeText(this.author.id); + }); + Message.contextmenu.addbutton("Message user", function () { + fetch(this.info.api.toString() + "/v9/users/@me/channels", { method: "POST", + body: JSON.stringify({ "recipients": [this.author.id] }), + headers: this.headers + }); + }); + Message.contextmenu.addbutton("Edit", function () { + this.channel.editing = this; + document.getElementById("typebox").value = this.content; + }, null, _ => { return _.author.id === _.localuser.user.id; }); + Message.contextmenu.addbutton("Delete message", function () { + this.delete(); + }, null, _ => { return _.canDelete(); }); + } + constructor(messagejson, owner) { + this.owner = owner; + this.headers = this.owner.headers; + for (const thing of Object.keys(messagejson)) { + if (thing === "attachments") { + this.attachments = []; + for (const thing of messagejson.attachments) { + this.attachments.push(new File(thing, this)); + } + continue; + } + this[thing] = messagejson[thing]; + } + for (const thing in this.embeds) { + console.log(thing, this.embeds); + this.embeds[thing] = new Embed(this.embeds[thing], this); + } + this.author = new User(this.author, this.localuser); + for (const thing in this.mentions) { + this.mentions[thing] = new User(this.mentions[thing], this.localuser); + } + if (this.mentions.length || this.mention_roles.length) { //currently mention_roles isn't implemented on the spacebar servers + console.log(this.mentions, this.mention_roles); + } + if (this.mentionsuser(this.localuser.user)) { + console.log(this); + } + } + canDelete() { + return this.channel.hasPermission("MANAGE_MESSAGES") || this.author.id === this.localuser.user.id; + } + get channel() { + return this.owner; + } + get guild() { + return this.owner.guild; + } + get localuser() { + return this.owner.localuser; + } + get info() { + return this.owner.info; + } + messageevents(obj) { + const func = Message.contextmenu.bind(obj, this); + this.div = obj; + Message.del.then(_ => { + obj.removeEventListener("click", func); + this.div = null; + }); + obj.classList.add("messagediv"); + } + mentionsuser(userd) { + if (userd instanceof User) { + return this.mentions.includes(userd); + } + else if (userd instanceof Member) { + return this.mentions.includes(userd.user); + } + } + getimages() { + const build = []; + for (const thing of this.attachments) { + if (thing.content_type.startsWith('image/')) { + build.push(thing); + } + } + return build; + } + async edit(content) { + return await fetch(this.info.api.toString() + "/channels/" + this.channel.id + "/messages/" + this.id, { + method: "PATCH", + headers: this.headers, + body: JSON.stringify({ content: content }) + }); + } + delete() { + fetch(`${this.info.api.toString()}/channels/${this.channel.id}/messages/${this.id}`, { + headers: this.headers, + method: "DELETE", + }); + } + deleteEvent() { + if (this.div) { + this.div.innerHTML = ""; + this.div = null; + } + const index = this.channel.messages.indexOf(this); + this.channel.messages.splice(this.channel.messages.indexOf(this), 1); + delete this.channel.messageids[this.id]; + const regen = this.channel.messages[index - 1]; + if (regen) { + regen.generateMessage(); + } + } + generateMessage(premessage = null) { + if (!premessage) { + premessage = this.channel.messages[this.channel.messages.indexOf(this) + 1]; + } + const div = this.div; + if (this === this.channel.replyingto) { + div.classList.add("replying"); + } + div.innerHTML = ""; + const build = document.createElement('table'); + if (this.message_reference) { + const replyline = document.createElement("div"); + const line = document.createElement("hr"); + const minipfp = document.createElement("img"); + minipfp.classList.add("replypfp"); + replyline.appendChild(line); + replyline.appendChild(minipfp); + const username = document.createElement("span"); + replyline.appendChild(username); + const reply = document.createElement("div"); + username.classList.add("username"); + Member.resolve(this.author, this.guild).then(_ => { + if (!_) { + return; + } + ; + console.log(_.error); + if (_.error) { + username.textContent += "Error"; + alert("Should've gotten here"); + const error = document.createElement("span"); + error.textContent = "!"; + error.classList.add("membererror"); + username.after(error); + return; + } + username.style.color = _.getColor(); + }).catch(_ => { + console.log(_); + }); + reply.classList.add("replytext"); + replyline.appendChild(reply); + const line2 = document.createElement("hr"); + replyline.appendChild(line2); + line2.classList.add("reply"); + line.classList.add("startreply"); + replyline.classList.add("replyflex"); + this.channel.getmessage(this.message_reference.message_id).then(message => { + const author = message.author; + reply.appendChild(markdown(message.content, { stdsize: true })); + minipfp.src = author.getpfpsrc(); + author.profileclick(minipfp); + username.textContent = author.username; + author.profileclick(username); + }); + div.appendChild(replyline); + } + this.messageevents(div); + build.classList.add("message"); + div.appendChild(build); + if ({ 0: true, 19: true }[this.type] || this.attachments.length !== 0) { + const pfpRow = document.createElement('th'); + let pfpparent, current; + if (premessage != null) { + pfpparent ??= premessage; + let pfpparent2 = pfpparent.all; + pfpparent2 ??= pfpparent; + const old = (new Date(pfpparent2.timestamp).getTime()) / 1000; + const newt = (new Date(this.timestamp).getTime()) / 1000; + current = (newt - old) > 600; + } + const combine = (premessage?.author?.id != this.author.id) || (current) || this.message_reference; + if (combine) { + const pfp = this.author.buildpfp(); + this.author.profileclick(pfp); + pfpRow.appendChild(pfp); + } + else { + div["pfpparent"] = pfpparent; + } + pfpRow.classList.add("pfprow"); + build.appendChild(pfpRow); + const text = document.createElement("th"); + const texttxt = document.createElement("table"); + texttxt.classList.add("commentrow"); + text.appendChild(texttxt); + if (combine) { + const username = document.createElement("span"); + username.classList.add("username"); + this.author.profileclick(username); + Member.resolve(this.author, this.guild).then(_ => { + if (!_) { + return; + } + ; + if (_.error) { + const error = document.createElement("span"); + error.textContent = "!"; + error.classList.add("membererror"); + username.after(error); + return; + } + username.style.color = _.getColor(); + }); + username.textContent = this.author.username; + const userwrap = document.createElement("tr"); + userwrap.appendChild(username); + if (this.author.bot) { + const username = document.createElement("span"); + username.classList.add("bot"); + username.textContent = "BOT"; + userwrap.appendChild(username); + } + const time = document.createElement("span"); + time.textContent = " " + formatTime(new Date(this.timestamp)); + time.classList.add("timestamp"); + userwrap.appendChild(time); + texttxt.appendChild(userwrap); + } + const messaged = markdown(this.content); + div["txt"] = messaged; + const messagedwrap = document.createElement("tr"); + messagedwrap.appendChild(messaged); + texttxt.appendChild(messagedwrap); + build.appendChild(text); + if (this.attachments.length) { + console.log(this.attachments); + const attatch = document.createElement("tr"); + for (const thing of this.attachments) { + attatch.appendChild(thing.getHTML()); + } + messagedwrap.appendChild(attatch); + } + if (this.embeds.length) { + const embeds = document.createElement("tr"); + for (const thing of this.embeds) { + embeds.appendChild(thing.generateHTML()); + } + messagedwrap.appendChild(embeds); + } + // + } + else if (this.type === 7) { + const text = document.createElement("th"); + const texttxt = document.createElement("table"); + text.appendChild(texttxt); + build.appendChild(text); + const messaged = document.createElement("p"); + div["txt"] = messaged; + messaged.textContent = "welcome: " + this.author.username; + const messagedwrap = document.createElement("tr"); + messagedwrap.appendChild(messaged); + const time = document.createElement("span"); + time.textContent = " " + formatTime(new Date(this.timestamp)); + time.classList.add("timestamp"); + messagedwrap.append(time); + texttxt.appendChild(messagedwrap); + } + div["all"] = this; + return (div); + } + buildhtml(premessage) { + if (this.div) { + console.error(`HTML for ${this} already exists, aborting`); + return; + } + //premessage??=messages.lastChild; + const div = document.createElement("div"); + this.div = div; + return this.generateMessage(premessage); + } + createunknown(fname, fsize, src) { + const div = document.createElement("table"); + div.classList.add("unknownfile"); + const nametr = document.createElement("tr"); + div.append(nametr); + const fileicon = document.createElement("td"); + nametr.append(fileicon); + fileicon.append("🗎"); + fileicon.classList.add("fileicon"); + fileicon.rowSpan = 2; + const nametd = document.createElement("td"); + if (src) { + const a = document.createElement("a"); + a.href = src; + a.textContent = fname; + nametd.append(a); + } + else { + nametd.textContent = fname; + } + nametd.classList.add("filename"); + nametr.append(nametd); + const sizetr = document.createElement("tr"); + const size = document.createElement("td"); + sizetr.append(size); + size.textContent = "Size:" + this.filesizehuman(fsize); + size.classList.add("filesize"); + div.appendChild(sizetr); + return div; + } + filesizehuman(fsize) { + var i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024)); + return +((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes'][i]; + } +} +function formatTime(date) { + const now = new Date(); + const sameDay = date.getDate() === now.getDate() && + date.getMonth() === now.getMonth() && + date.getFullYear() === now.getFullYear(); + const yesterday = new Date(now); + yesterday.setDate(now.getDate() - 1); + const isYesterday = date.getDate() === yesterday.getDate() && + date.getMonth() === yesterday.getMonth() && + date.getFullYear() === yesterday.getFullYear(); + const formatTime = date => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + if (sameDay) { + return `Today at ${formatTime(date)}`; + } + else if (isYesterday) { + return `Yesterday at ${formatTime(date)}`; + } + else { + return `${date.toLocaleDateString()} at ${formatTime(date)}`; + } +} +Message.setup(); +export { Message }; diff --git a/.dist/permissions.js b/.dist/permissions.js new file mode 100644 index 0000000..76d7bd9 --- /dev/null +++ b/.dist/permissions.js @@ -0,0 +1,246 @@ +export { Permissions }; +class Permissions { + allow; + deny; + constructor(allow, deny = "") { + this.allow = BigInt(allow); + this.deny = BigInt(deny); + } + getPermisionbit(b, big) { + return Boolean((big >> BigInt(b)) & 1n); + } + setPermisionbit(b, state, big) { + const bit = 1n << BigInt(b); + return (big & ~bit) | (BigInt(state) << BigInt(b)); //thanks to geotale for this code :3 + } + static map; + static info; + static makeMap() { + Permissions.info = [ + { + name: "CREATE_INSTANT_INVITE", + readableName: "Create instance invite", + description: "Allows the user to create invites for the guild" + }, + { + name: "KICK_MEMBERS", + readableName: "Kick members", + description: "Allows the user to kick members from the guild" + }, + { + name: "BAN_MEMBERS", + readableName: "Ban members", + description: "Allows the user to ban members from the guild" + }, + { + name: "ADMINISTRATOR", + readableName: "Administrator", + description: "Allows all permissions and bypasses channel permission overwrites" + }, + { + name: "MANAGE_CHANNELS", + readableName: "Manage channels", + description: "Allows the user to manage and edit channels" + }, + { + name: "MANAGE_GUILD", + readableName: "Manage guild", + description: "Allows management and editing of the guild" + }, + { + name: "ADD_REACTIONS", + readableName: "Add reactions", + description: "Allows user to add reactions to messages" + }, + { + name: "VIEW_AUDIT_LOG", + readableName: "View audit log", + description: "Allows the user to view the audit log" + }, + { + name: "PRIORITY_SPEAKER", + readableName: "Priority speaker", + description: "Allows for using priority speaker in a voice channel" + }, + { + name: "STREAM", + readableName: "Stream", + description: "Allows the user to stream" + }, + { + name: "VIEW_CHANNEL", + readableName: "View channel", + description: "Allows the user to view the channel" + }, + { + name: "SEND_MESSAGES", + readableName: "Send Messages", + description: "Allows user to send messages" + }, + { + name: "SEND_TTS_MESSAGES", + readableName: "Send text-to-speech messages", + description: "Allows the user to send text-to-speech messages" + }, + { + name: "MANAGE_MESSAGES", + readableName: "Manager messages", + description: "Allows the user to delete messages that aren't their own" + }, + { + name: "EMBED_LINKS", + readableName: "Embed links", + description: "Allow links sent by this user to auto-embed" + }, + { + name: "ATTACH_FILES", + readableName: "Attach files", + description: "Allows the user to attach files" + }, + { + name: "READ_MESSAGE_HISTORY", + readableName: "Read message history", + description: "Allows user to read the message history" + }, + { + name: "MENTION_EVERYONE", + readableName: "Mention everyone", + description: "Allows the user to mention everyone" + }, + { + name: "USE_EXTERNAL_EMOJIS", + readableName: "Use external emojis", + description: "Allows the user to use external emojis" + }, + { + name: "VIEW_GUILD_INSIGHTS", + readableName: "View guild insights", + description: "Allows the user to see guild insights" + }, + { + name: "CONNECT", + readableName: "Connect", + description: "Allows the user to connect to a voice channel" + }, + { + name: "SPEAK", + readableName: "Speak", + description: "Allows the user to speak in a voice channel" + }, + { + name: "MUTE_MEMBERS", + readableName: "Mute members", + description: "Allows user to mute other members" + }, + { + name: "DEAFEN_MEMBERS", + readableName: "Deafen members", + description: "Allows user to deafen other members" + }, + { + name: "MOVE_MEMBERS", + readableName: "Move members", + description: "Allows the user to move members between voice channels" + }, + { + name: "USE_VAD", + readableName: "use voice-activity-detection", + description: "Allows user to use voice-activity-detection" + }, + { + name: "CHANGE_NICKNAME", + readableName: "Change nickname", + description: "Allows the user to change their own nickname" + }, + { + name: "MANAGE_NICKNAMES", + readableName: "Manage nicknames", + description: "Allows user to change nicknames of other members" + }, + { + name: "MANAGE_ROLES", + readableName: "Manage roles", + description: "Allows user to edit and manage roles" + }, + { + name: "MANAGE_WEBHOOKS", + readableName: "Manage webhooks", + description: "Allows management and editing of webhooks" + }, + { + name: "MANAGE_GUILD_EXPRESSIONS", + readableName: "Manage guild expressions", + description: "Allows for managing emoji, stickers, and soundboards" + }, + { + name: "USE_APPLICATION_COMMANDS", + readableName: "Use application commands", + description: "Allows the user to use application commands" + }, + { + name: "REQUEST_TO_SPEAK", + readableName: "Request to speak", + description: "Allows user to request to speak in stage channel" + }, + { + name: "MANAGE_EVENTS", + readableName: "Manage events", + description: "Allows user to edit and manage events" + }, + { + name: "MANAGE_THREADS", + readableName: "Manage threads", + description: "Allows the user to delete and archive threads and view all private threads" + }, + { + name: "CREATE_PUBLIC_THREADS", + readableName: "Create public threads", + description: "Allows the user to create public threads" + }, + { + name: "CREATE_PRIVATE_THREADS", + readableName: "Create private threads", + description: "Allows the user to create private threads" + }, + { + name: "USE_EXTERNAL_STICKERS", + readableName: "Use external stickers", + description: "Allows user to use external stickers" + }, + { + name: "SEND_MESSAGES_IN_THREADS", + readableName: "Send messages in threads", + description: "Allows the user to send messages in threads" + }, + { + name: "USE_EMBEDDED_ACTIVITIES", + readableName: "Use embedded activities", + description: "Allows the user to use embedded activities" + }, + { + name: "MODERATE_MEMBERS", + readableName: "Moderate members", + description: "Allows the user to time out other users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels" + }, + ]; + Permissions.map = {}; + let i = 0; + for (const thing of Permissions.info) { + Permissions.map[i] = thing; + Permissions.map[thing.name] = i; + i++; + } + } + getPermision(name) { + if (this.getPermisionbit(Permissions.map[name], this.allow)) { + return 1; + } + else if (this.getPermisionbit(Permissions.map[name], this.deny)) { + return -1; + } + else { + return 0; + } + } +} +Permissions.makeMap(); diff --git a/.dist/role.js b/.dist/role.js new file mode 100644 index 0000000..8973e10 --- /dev/null +++ b/.dist/role.js @@ -0,0 +1,28 @@ +export { Role }; +import { Permissions } from "./permissions.js"; +class Role { + permissions; + owner; + color; + id; + constructor(JSON, owner) { + for (const thing of Object.keys(JSON)) { + this[thing] = JSON[thing]; + } + this.permissions = new Permissions(JSON.permissions); + this.owner = owner; + } + get guild() { + return this.owner; + } + get localuser() { + return this.guild.localuser; + } + getColor() { + if (this.color === 0) { + return null; + } + ; + return `#${this.color.toString(16)}`; + } +} diff --git a/.dist/service.js b/.dist/service.js new file mode 100644 index 0000000..789db17 --- /dev/null +++ b/.dist/service.js @@ -0,0 +1,93 @@ +function deleteoldcache() { + caches.delete("cache"); + console.log("this ran :P"); +} +async function putInCache(request, response) { + console.log(request, response); + const cache = await caches.open('cache'); + console.log("Grabbed"); + try { + console.log(await cache.put(request, response)); + } + catch (error) { + console.error(error); + } +} +; +console.log("test"); +let lastcache; +self.addEventListener("activate", async (event) => { + console.log("test2"); + checkCache(); +}); +async function checkCache() { + if (checkedrecently) { + return; + } + const promise = await caches.match("/getupdates"); + if (promise) { + lastcache = await promise.text(); + } + console.log(lastcache); + fetch("/getupdates").then(async (data) => { + const text = await data.clone().text(); + console.log(text, lastcache); + if (lastcache !== text) { + deleteoldcache(); + putInCache("/getupdates", data.clone()); + } + checkedrecently = true; + setTimeout(_ => { checkedrecently = false; }, 1000 * 60 * 30); + }); +} +var checkedrecently = false; +function samedomain(url) { + return new URL(url).origin === self.origin; +} +function isindexhtml(url) { + console.log(url); + if (new URL(url).pathname.startsWith("/channels")) { + return true; + } + return false; +} +async function getfile(event) { + checkCache(); + if (!samedomain(event.request.url)) { + return await fetch(event.request.clone()); + } + const responseFromCache = await caches.match(event.request.url); + console.log(responseFromCache, caches); + if (responseFromCache) { + console.log("cache hit"); + return responseFromCache; + } + if (isindexhtml(event.request.url)) { + console.log("is index.html"); + const responseFromCache = await caches.match("/index.html"); + if (responseFromCache) { + console.log("cache hit"); + return responseFromCache; + } + const responseFromNetwork = await fetch("/index.html"); + await putInCache("/index.html", responseFromNetwork.clone()); + return responseFromNetwork; + } + const responseFromNetwork = await fetch(event.request.clone()); + console.log(event.request.clone()); + await putInCache(event.request.clone(), responseFromNetwork.clone()); + try { + return responseFromNetwork; + } + catch (e) { + console.error(e); + } +} +self.addEventListener('fetch', (event) => { + try { + event.respondWith(getfile(event)); + } + catch (e) { + console.error(e); + } +}); diff --git a/.dist/user.js b/.dist/user.js new file mode 100644 index 0000000..5830f56 --- /dev/null +++ b/.dist/user.js @@ -0,0 +1,140 @@ +//const usercache={}; +import { Member } from "./member.js"; +import { markdown } from "./markdown.js"; +import { Contextmenu } from "./contextmenu.js"; +class User { + static userids = {}; + owner; + hypotheticalpfp; + id; + avatar; + username; + bio; + discriminator; + pronouns; + bot; + static checkuser(userjson, owner) { + if (User.userids[userjson.id]) { + return User.userids[userjson.id]; + } + else { + const tempuser = new User(userjson, owner, true); + User.userids[userjson.id] = tempuser; + return tempuser; + } + } + get info() { + return this.owner.info; + } + get localuser() { + return this.owner; + } + constructor(userjson, owner, dontclone = false) { + this.owner = owner; + if (!owner) { + console.error("missing localuser"); + } + if (dontclone) { + for (const thing of Object.keys(userjson)) { + this[thing] = userjson[thing]; + } + this.hypotheticalpfp = false; + } + else { + return User.checkuser(userjson, owner); + } + } + async resolvemember(guild) { + await Member.resolve(this, guild); + } + buildpfp() { + const pfp = document.createElement('img'); + pfp.src = this.getpfpsrc(); + pfp.classList.add("pfp"); + pfp.classList.add("userid:" + this.id); + return pfp; + } + userupdate(json) { + if (json.avatar !== this.avatar) { + console.log; + this.changepfp(json.avatar); + } + } + changepfp(update) { + this.avatar = update; + this.hypotheticalpfp = false; + const src = this.getpfpsrc(); + console.log(src); + for (const thing of document.getElementsByClassName("userid:" + this.id)) { + thing.src = src; + } + } + getpfpsrc() { + if (this.hypotheticalpfp) { + return this.avatar; + } + if (this.avatar != null) { + return this.info.cdn.toString() + "avatars/" + this.id + "/" + this.avatar + ".png"; + } + else { + return this.info.cdn.toString() + "embed/avatars/3.png"; + } + } + createjankpromises() { + new Promise(_ => { }); + } + buildprofile(x, y) { + if (Contextmenu.currentmenu != "") { + Contextmenu.currentmenu.remove(); + } + const div = document.createElement("table"); + if (x !== -1) { + div.style.left = x + "px"; + div.style.top = y + "px"; + div.classList.add("profile"); + } + else { + div.classList.add("hypoprofile"); + } + { + const pfp = this.buildpfp(); + const pfprow = document.createElement("tr"); + div.appendChild(pfprow); + pfprow.appendChild(pfp); + } + { + const userbody = document.createElement("tr"); + userbody.classList.add("infosection"); + div.appendChild(userbody); + const usernamehtml = document.createElement("h2"); + usernamehtml.textContent = this.username; + userbody.appendChild(usernamehtml); + const discrimatorhtml = document.createElement("h3"); + discrimatorhtml.classList.add("tag"); + discrimatorhtml.textContent = this.username + "#" + this.discriminator; + userbody.appendChild(discrimatorhtml); + const pronounshtml = document.createElement("p"); + pronounshtml.textContent = this.pronouns; + pronounshtml.classList.add("pronouns"); + userbody.appendChild(pronounshtml); + const rule = document.createElement("hr"); + userbody.appendChild(rule); + const biohtml = markdown(this.bio); + userbody.appendChild(biohtml); + } + console.log(div); + if (x !== -1) { + Contextmenu.currentmenu = div; + document.body.appendChild(div); + Contextmenu.keepOnScreen(div); + } + return div; + } + profileclick(obj) { + obj.onclick = e => { + this.buildprofile(e.clientX, e.clientY); + e.stopPropagation(); + }; + } +} +export { User }; diff --git a/Archive.tar.gz b/Archive.tar.gz new file mode 100644 index 0000000..ac47266 Binary files /dev/null and b/Archive.tar.gz differ diff --git a/index.js b/index.js index 887294c..42afe72 100755 --- a/index.js +++ b/index.js @@ -4,6 +4,13 @@ const express = require('express'); const fs = require('fs'); const app = express(); +const tsNode = require('ts-node'); + +tsNode.register({ + transpileOnly: true, + files: true +}); + app.use("/getupdates",(req, res) => { const out=fs.statSync(`${__dirname}/webpage`); res.send(out.mtimeMs+""); @@ -16,6 +23,10 @@ app.use('/', (req, res) => { } if(fs.existsSync(`${__dirname}/webpage${req.path}`)) { res.sendFile(`./webpage${req.path}`, {root: __dirname}); + }else if(req.path.endsWith(".js") && fs.existsSync(`${__dirname}/.dist${req.path}`)){ + const dir=`./.dist${req.path}`; + res.sendFile(dir, {root: __dirname}); + return; } else if(fs.existsSync(`${__dirname}/webpage${req.path}.html`)) { res.sendFile(`./webpage${req.path}.html`, {root: __dirname}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c32748b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,985 @@ +{ + "name": "jankclient", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "jankclient", + "version": "0.1.0", + "license": "GPL-3.0", + "dependencies": { + "express": "latest" + }, + "devDependencies": { + "ts-node": "^10.9.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.14.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", + "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", + "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json index b377d93..f992135 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,10 @@ "keywords": [], "author": "MathMan05", "license": "GPL-3.0", - "dependencies":{ - "express":"latest" + "dependencies": { + "express": "latest" + }, + "devDependencies": { + "ts-node": "^10.9.2" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7c0d445 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ + { + "compilerOptions": { + "target": "es2022", + "moduleResolution": "Bundler", + "module":"es2022", + "strict": false, + "esModuleInterop": true, + "outDir": "./.dist" + }, + "include": [ + "./webpage/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/webpage/audio.js b/webpage/audio.ts similarity index 71% rename from webpage/audio.js rename to webpage/audio.ts index 3a1f160..c006881 100644 --- a/webpage/audio.js +++ b/webpage/audio.ts @@ -1,6 +1,15 @@ -class voice{ - constructor(wave,freq,volume=1){ - this.audioCtx = new (window.AudioContext || window.webkitAudioContext)(); +import {getBulkInfo} from "./login.js"; + +class Voice{ + audioCtx:AudioContext; + info:{wave:string|Function,freq:number}; + playing:boolean; + myArrayBuffer:AudioBuffer; + gainNode:GainNode; + buffer:Float32Array; + source:AudioBufferSourceNode; + constructor(wave:string|Function,freq:number,volume=1){ + this.audioCtx = new (window.AudioContext)(); this.info={wave:wave,freq:freq} this.playing=false; this.myArrayBuffer=this.audioCtx.createBuffer( @@ -16,61 +25,61 @@ class voice{ this.source.buffer = this.myArrayBuffer; this.source.loop=true; this.source.start(); - this.updateWave(freq); + this.updateWave(); } - get wave(){ + get wave():string|Function{ return this.info.wave; } - get freq(){ + get freq():number{ return this.info.freq; } - set wave(wave){ + set wave(wave:string|Function){ this.info.wave=wave; - this.updateWave() + this.updateWave(); } - set freq(freq){ + set freq(freq:number){ this.info.freq=freq; - this.updateWave() + this.updateWave(); } - updateWave(){ + updateWave():void{ const func=this.waveFucnion(); for (let i = 0; i < this.buffer.length; i++) { this.buffer[i]=func(i/this.audioCtx.sampleRate,this.freq); } } - waveFucnion(){ + waveFucnion():Function{ if(typeof this.wave === 'function'){ return this.wave; } switch(this.wave){ case "sin": - return (t,freq)=>{ + return (t:number,freq:number)=>{ return Math.sin(t*Math.PI*2*freq); } case "triangle": - return (t,freq)=>{ + return (t:number,freq:number)=>{ return Math.abs((4*t*freq)%4-2)-1; } case "sawtooth": - return (t,freq)=>{ + return (t:number,freq:number)=>{ return ((t*freq)%1)*2-1; } case "square": - return (t,freq)=>{ + return (t:number,freq:number)=>{ return (t*freq)%2<1?1:-1; } case "white": - return (t,freq)=>{ + return (_t:number,_freq:number)=>{ return Math.random()*2-1; } case "noise": - return (t,freq)=>{ + return (_t:number,_freq:number)=>{ return 0; } } } - play(){ + play():void{ if(this.playing){ return; } @@ -78,16 +87,16 @@ class voice{ this.playing=true; } - stop(){ + stop():void{ if(this.playing){ this.source.disconnect(); this.playing=false; } } - static noises(noise){ + static noises(noise:string):void{ switch(noise){ case "three":{ - const voicy=new voice("sin",800); + const voicy=new Voice("sin",800); voicy.play(); setTimeout(_=>{voicy.freq=1000},50); setTimeout(_=>{voicy.freq=1300},100); @@ -95,7 +104,7 @@ class voice{ break; } case "zip":{ - const voicy=new voice((t,freq)=>{ + const voicy=new Voice((t:number,freq:number)=>{ return Math.sin(((t+2)**(Math.cos(t*4)))*Math.PI*2*freq); },700); voicy.play(); @@ -103,7 +112,7 @@ class voice{ break; } case "square":{ - const voicy=new voice("square",600,.4); + const voicy=new Voice("square",600,.4); voicy.play() setTimeout(_=>{voicy.freq=800},50); setTimeout(_=>{voicy.freq=1000},100); @@ -111,7 +120,7 @@ class voice{ break; } case "beep":{ - const voicy=new voice("sin",800); + const voicy=new Voice("sin",800); voicy.play(); setTimeout(_=>{voicy.stop()},50); setTimeout(_=>{voicy.play();},100); @@ -123,7 +132,7 @@ class voice{ static get sounds(){ return ["three","zip","square","beep"]; } - static setNotificationSound(sound){ + static setNotificationSound(sound:string){ let userinfos=getBulkInfo(); userinfos.preferances.notisound=sound; localStorage.setItem("userinfos",JSON.stringify(userinfos)); @@ -133,3 +142,4 @@ class voice{ return userinfos.preferances.notisound; } } +export {Voice as Voice}; diff --git a/webpage/channel.js b/webpage/channel.ts similarity index 67% rename from webpage/channel.js rename to webpage/channel.ts index 18f8aa8..5c9b330 100644 --- a/webpage/channel.js +++ b/webpage/channel.ts @@ -1,31 +1,71 @@ "use strict" -class channel{ - static contextmenu=new contextmenu("channel menu"); +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"; + +declare global { + interface NotificationOptions { + image?: string + } +} +class Channel{ + editing:Message; + type:number; + owner:Guild; + headers:Localuser["headers"]; + messages:Message[]; + name:string; + id:string; + parent_id:string; + parrent:Channel; + children:Channel[]; + guild_id:string; + messageids:{[key : string]:Message}; + permission_overwrites:{[key:string]:Permissions}; + topic:string; + nsfw:boolean; + position:number; + lastreadmessageid:string; + lastmessageid:string; + mentions:number; + lastpin:string; + move_id:string; + typing:number; + message_notifications:number; + allthewayup:boolean; + static contextmenu=new Contextmenu("channel menu"); + replyingto:Message; static setupcontextmenu(){ - channel.contextmenu.addbutton("Copy channel id",function(){ + Channel.contextmenu.addbutton("Copy channel id",function(){ console.log(this) navigator.clipboard.writeText(this.id); }); - channel.contextmenu.addbutton("Mark as read",function(){ + Channel.contextmenu.addbutton("Mark as read",function(){ console.log(this) this.readbottom(); }); - channel.contextmenu.addbutton("Delete channel",function(){ + Channel.contextmenu.addbutton("Delete channel",function(){ console.log(this) this.deleteChannel(); },null,_=>{console.log(_);return _.isAdmin()}); - channel.contextmenu.addbutton("Edit channel",function(){ - editchannelf(this); + Channel.contextmenu.addbutton("Edit channel",function(){ + this.editChannel(this); },null,_=>{return _.isAdmin()}); } - constructor(JSON,owner){ + constructor(JSON,owner:Guild){ if(JSON===-1){ return; } + this.editing; this.type=JSON.type; this.owner=owner; this.headers=this.owner.headers; @@ -37,7 +77,10 @@ class channel{ this.children=[]; this.guild_id=JSON.guild_id; this.messageids={}; - this.permission_overwrites=JSON.permission_overwrites; + this.permission_overwrites={}; + for(const thing of JSON.permission_overwrites){ + this.permission_overwrites[thing.id]=new Permissions(thing.allow,thing.deny); + } this.topic=JSON.topic; this.nsfw=JSON.nsfw; this.position=JSON.position; @@ -53,27 +96,43 @@ class channel{ get localuser(){ return this.guild.localuser; } + get info(){ + return this.owner.info; + } readStateInfo(json){ this.lastreadmessageid=json.last_message_id; this.mentions=json.mention_count; this.mentions??=0; this.lastpin=json.last_pin_timestamp; } - get hasunreads(){ + get hasunreads():boolean{ + if(!this.hasPermission("VIEW_CHANNEL")){return false;} return this.lastmessageid!==this.lastreadmessageid&&this.type!==4; } - get canMessage(){ - for(const thing of this.permission_overwrites){ - if(this.guild.hasRole(thing.id)&&thing.deny&(1<<11)){ - return false; + 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].getPermision(name); + if(perm){ + return perm===1; + } + } + if(thing.permissions.getPermision(name)){ + return true; } } - return true; + return false; + } + get canMessage():boolean{ + return this.hasPermission("SEND_MESSAGES"); } sortchildren(){ this.children.sort((a,b)=>{return a.position-b.position}); } - resolveparent(guild){ + resolveparent(guild:Guild){ this.parrent=guild.channelids[this.parent_id]; this.parrent??=null; if(this.parrent!==null){ @@ -85,7 +144,7 @@ class channel{ let position=-1; let build=[]; for(const thing of this.children){ - const thisthing={id:thing.id} + const thisthing={id:thing.id,position:undefined,parent_id:undefined}; if(thing.position{channel.dragged=[this,div];e.stopImmediatePropagation()}) - div.addEventListener("dragend",()=>{channel.dragged=[]}) + div.addEventListener("dragstart",(e)=>{Channel.dragged=[this,div];e.stopImmediatePropagation()}) + div.addEventListener("dragend",()=>{Channel.dragged=[]}) if(this.type===4){ this.sortchildren(); const caps=document.createElement("div"); @@ -129,17 +199,17 @@ class channel{ addchannel.classList.add("addchannel"); caps.appendChild(addchannel); addchannel.onclick=function(){ - createchannels(this.createChannel.bind(this)); + this.guild.createchannels(this.createChannel.bind(this)); }.bind(this); - this.coatDropDiv(decdiv,this,childrendiv); + this.coatDropDiv(decdiv,childrendiv); } div.appendChild(caps) caps.classList.add("capsflex") decdiv.classList.add("channeleffects"); decdiv.classList.add("channel"); - channel.contextmenu.bind(decdiv,this); - decdiv.all=this; + Channel.contextmenu.bind(decdiv,this); + decdiv["all"]=this; for(const channel of this.children){ @@ -164,9 +234,9 @@ class channel{ if(this.hasunreads){ div.classList.add("cunread"); } - channel.contextmenu.bind(div,this); - if(admin){this.coatDropDiv(div,this);} - div.all=this; + Channel.contextmenu.bind(div,this); + if(admin){this.coatDropDiv(div);} + div["all"]=this; const myhtml=document.createElement("span"); myhtml.textContent=this.name; if(this.type===0){ @@ -188,9 +258,8 @@ class channel{ console.log(this.type) } div.appendChild(myhtml); - div.myinfo=this; - div.onclick=function(){ - this.myinfo.getHTML(); + div.onclick=_=>{ + this.getHTML(); } } return div; @@ -201,9 +270,9 @@ class channel{ return null }else if(this.parrent){ for(const thing of search){ - if(thing.all===this.parrent){ + if(thing["all"]===this.parrent){ for(const thing2 of thing.children[1].children){ - if(thing2.all===this){ + if(thing2["all"]===this){ return thing2; } } @@ -211,7 +280,7 @@ class channel{ } }else{ for(const thing of search){ - if(thing.all===this){ + if(thing["all"]===this){ return thing; } } @@ -222,7 +291,7 @@ class channel{ if(!this.hasunreads){ return; } - fetch(info.api.toString()+"/v9/channels/"+this.id+"/messages/"+this.lastmessageid+"/ack",{ + fetch(this.info.api.toString()+"/v9/channels/"+this.id+"/messages/"+this.lastmessageid+"/ack",{ method:"POST", headers:this.headers, body:JSON.stringify({}) @@ -233,7 +302,7 @@ class channel{ this.myhtml.classList.remove("cunread"); } } - coatDropDiv(div,that,container=false){ + coatDropDiv(div:HTMLDivElement,container:HTMLElement|boolean=false){ div.addEventListener("dragenter", (event) => { console.log("enter") event.preventDefault(); @@ -244,7 +313,7 @@ class channel{ }); div.addEventListener("drop", (event) => { - const that=channel.dragged[0]; + const that=Channel.dragged[0]; event.preventDefault(); if(container){ that.move_id=this.id; @@ -252,10 +321,10 @@ class channel{ that.parrent.children.splice(that.parrent.children.indexOf(that),1); } that.parrent=this; - container.prepend(channel.dragged[1]); + (container as HTMLElement).prepend(Channel.dragged[1]); this.children.unshift(that); }else{ - console.log(this,channel.dragged); + console.log(this,Channel.dragged); that.move_id=this.parent_id; if(that.parrent){ that.parrent.children.splice(that.parrent.children.indexOf(that),1); @@ -282,15 +351,15 @@ class channel{ } this.guild.headchannels=build; } - div.after(channel.dragged[1]); + div.after(Channel.dragged[1]); } this.guild.calculateReorder() }); return div; } - createChannel(name,type){ - fetch(info.api.toString()+"/guilds/"+this.guild.id+"/channels",{ + createChannel(name:string,type:number){ + fetch(this.info.api.toString()+"/guilds/"+this.guild.id+"/channels",{ method:"Post", headers:this.headers, body:JSON.stringify({ @@ -307,14 +376,14 @@ class channel{ let nsfw=this.nsfw; const thisid=this.id; const thistype=this.type; - const full=new fullscreen( + const full=new Fullscreen( ["hdiv", ["vdiv", ["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(info.api.toString()+"/v9/channels/"+thisid,{ + fetch(this.info.api.toString()+"/v9/channels/"+thisid,{ method:"PATCH", headers:this.headers, body:JSON.stringify({ @@ -338,39 +407,89 @@ class channel{ console.log(full) } deleteChannel(){ - fetch(info.api.toString()+"/v9/channels/"+this.id,{ + fetch(this.info.api.toString()+"/v9/channels/"+this.id,{ method:"DELETE", headers:this.headers }) } - getHTML(){ + setReplying(message:Message){ + if(this.replyingto){ + this.replyingto.div.classList.remove("replying"); + } + this.replyingto=message; + console.log(message); + this.replyingto.div.classList.add("replying"); + this.makereplybox(); + + } + makereplybox(){ + const replybox=document.getElementById("replybox"); + if(this.replyingto){ + replybox.innerHTML=""; + const span=document.createElement("span"); + span.textContent="Replying to "+this.replyingto.author.username; + const X=document.createElement("button"); + X.onclick=_=>{ + this.replyingto.div.classList.remove("replying"); + replybox.classList.add("hideReplyBox"); + this.replyingto=null; + replybox.innerHTML=""; + } + replybox.classList.remove("hideReplyBox"); + X.textContent="âĻģ"; + X.classList.add("cancelReply"); + replybox.append(span); + replybox.append(X); + }else{ + replybox.classList.add("hideReplyBox"); + } + } + async getmessage(id:string):Promise{ + if(this.messageids[id]){ + return this.messageids[id]; + }else{ + const gety=await fetch(this.info.api.toString()+"/v9/channels/"+this.id+"/messages?limit=1&around="+id,{headers:this.headers}) + const json=await gety.json(); + return new Message(json[0],this); + } + } + async getHTML(){ if(this.guild!==this.localuser.lookingguild){ this.guild.loadGuild(); } + if(this.localuser.channelfocus&&this.localuser.channelfocus.myhtml){ + this.localuser.channelfocus.myhtml.classList.remove("viewChannel"); + } + this.myhtml.classList.add("viewChannel") this.guild.prevchannel=this; this.localuser.channelfocus=this; - this.putmessages(); + const prom=Message.wipeChanel(); + await this.putmessages(); + await prom; + this.makereplybox(); + this.buildmessages(); 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; } - putmessages(){ - const out=this; - fetch(info.api.toString()+"/channels/"+this.id+"/messages?limit=100",{ + async putmessages(){ + if(this.messages.length>=100||this.allthewayup){return}; + const j=await fetch(this.info.api.toString()+"/channels/"+this.id+"/messages?limit=100",{ method: 'GET', headers: this.headers, - }).then((j)=>{return j.json()}).then(responce=>{ - messages.innerHTML = ''; - //responce.reverse() - messagelist=[]; - for(const thing of responce){ - const messager=new cmessage(thing,this) - if(out.messageids[messager.id]==undefined){ - out.messageids[messager.id]=messager; - out.messages.push(messager); - } - } - out.buildmessages(); }) + const responce=await j.json(); + if(responce.length!==100){ + this.allthewayup=true; + } + for(const thing of responce){ + const messager=new Message(thing,this) + if(this.messageids[messager.id]===undefined){ + this.messageids[messager.id]=messager; + this.messages.push(messager); + } + } } delChannel(JSON){ const build=[]; @@ -387,25 +506,25 @@ class channel{ } const out=this; - await fetch(info.api.toString()+"/channels/"+this.id+"/messages?before="+this.messages[this.messages.length-1].id+"&limit=100",{ + await fetch(this.info.api.toString()+"/channels/"+this.id+"/messages?before="+this.messages[this.messages.length-1].id+"&limit=100",{ method:"GET", headers:this.headers }).then((j)=>{return j.json()}).then(responce=>{ //messages.innerHTML = ''; //responce.reverse() - let next + let next:Message; if(responce.length===0){ out.allthewayup=true; } for(const i in responce){ - let messager + let messager:Message; if(!next){ - messager=new cmessage(responce[i],this) + messager=new Message(responce[i],this) }else{ messager=next; } if(responce[+i+1]!==undefined){ - next=new cmessage(responce[+i+1],this); + next=new Message(responce[+i+1],this); }else{ next=undefined; console.log("ohno",+i+1) @@ -422,15 +541,15 @@ class channel{ }) return; } - buildmessage(message,next){ + buildmessage(message:Message,next:Message){ const built=message.buildhtml(next); - messages.prepend(built); + document.getElementById("messages").prepend(built); } buildmessages(){ for(const i in this.messages){ const prev=this.messages[(+i)+1]; const built=this.messages[i].buildhtml(prev); - messages.prepend(built); + document.getElementById("messages").prepend(built); } document.getElementById("messagecontainer").scrollTop = document.getElementById("messagecontainer").scrollHeight; @@ -452,7 +571,7 @@ class channel{ return; } this.typing=new Date().getTime()+6000; - fetch(info.api.toString()+"/channels/"+this.id+"/typing",{ + fetch(this.info.api.toString()+"/channels/"+this.id+"/typing",{ method:"POST", headers:this.headers }) @@ -472,8 +591,8 @@ class channel{ return "default"; } } - async sendMessage(content,{attachments=[],embeds=[],replyingto=false}){ - let replyjson=false; + async sendMessage(content:string,{attachments=[],embeds=[],replyingto=null}){ + let replyjson:any; if(replyingto){ replyjson= { @@ -481,17 +600,18 @@ class channel{ "channel_id": replyingto.channel.id, "message_id": replyingto.id, }; - } + }; if(attachments.length===0){ const body={ content:content, - nonce:Math.floor(Math.random()*1000000000) + nonce:Math.floor(Math.random()*1000000000), + message_reference:undefined }; if(replyjson){ body.message_reference=replyjson; } console.log(body) - return await fetch(info.api.toString()+"/channels/"+this.id+"/messages",{ + return await fetch(this.info.api.toString()+"/channels/"+this.id+"/messages",{ method:"POST", headers:this.headers, body:JSON.stringify(body) @@ -501,6 +621,7 @@ class channel{ const body={ content:content, nonce:Math.floor(Math.random()*1000000000), + message_reference:undefined } if(replyjson){ body.message_reference=replyjson; @@ -510,15 +631,16 @@ class channel{ console.log(attachments[i]) formData.append("files["+i+"]",attachments[i]); } - return await fetch(info.api.toString()+"/channels/"+this.id+"/messages", { + return await fetch(this.info.api.toString()+"/channels/"+this.id+"/messages", { method: 'POST', body: formData, headers:{"Authorization":this.headers.Authorization} }); } } - messageCreate(messagep,focus){ - const messagez=new cmessage(messagep.d,this); + messageCreate(messagep:any):void{ + if(!this.hasPermission("VIEW_CHANNEL")){return} + const messagez=new Message(messagep.d,this); this.lastmessageid=messagez.id; if(messagez.author===this.localuser.user){ this.lastreadmessageid=messagez.id; @@ -536,7 +658,7 @@ class channel{ this.messageids[messagez.id]=messagez; if(this.localuser.lookingguild.prevchannel===this){ var shouldScroll=scrolly.scrollTop+scrolly.clientHeight>scrolly.scrollHeight-20; - messages.appendChild(messagez.buildhtml(this.messages[1])); + document.getElementById("messages").appendChild(messagez.buildhtml(this.messages[1])); } if(shouldScroll){ scrolly.scrollTop = scrolly.scrollHeight; @@ -553,11 +675,11 @@ class channel{ this.notify(messagez); } } - notititle(message){ + notititle(message:Message):string{ return message.author.username+" > "+this.guild.properties.name+" > "+this.name; } - notify(message,deep=0){ - voice.noises(voice.getNotificationSound()); + notify(message:Message,deep=0){ + Voice.noises(Voice.getNotificationSound()); if (!("Notification" in window)) { } else if (Notification.permission === "granted") { @@ -584,11 +706,12 @@ class channel{ this.getHTML(); }) } else if (Notification.permission !== "denied") { - Notification.requestPermission().then((permission) => { + Notification.requestPermission().then(() => { if(deep===3){return}; this.notify(message,deep+1); }); } } } -channel.setupcontextmenu(); +Channel.setupcontextmenu(); +export {Channel}; diff --git a/webpage/contextmenu.js b/webpage/contextmenu.js deleted file mode 100644 index 932fc0c..0000000 --- a/webpage/contextmenu.js +++ /dev/null @@ -1,44 +0,0 @@ -class contextmenu{ - constructor(name){ - this.name=name; - this.buttons=[] - - } - addbutton(text,onclick,img=null,shown=_=>true,enabled=_=>true){ - this.buttons.push([text,onclick,img,shown,enabled]) - return {}; - } - makemenu(x,y,addinfo,obj){ - const div=document.createElement("table"); - div.classList.add("contextmenu"); - for(const thing of this.buttons){ - if(!thing[3](addinfo)){continue;} - const textb=document.createElement("tr"); - const intext=document.createElement("button") - intext.disabled=!thing[4](); - textb.button=intext; - intext.classList.add("contextbutton") - intext.textContent=thing[0] - textb.appendChild(intext) - console.log(thing) - intext.onclick=thing[1].bind(addinfo,obj); - div.appendChild(textb); - } - if(currentmenu!=""){ - currentmenu.remove(); - } - div.style.top = y+'px'; - div.style.left = x+'px'; - document.body.appendChild(div); - console.log(div) - currentmenu=div; - return this.div; - } - bind(obj,addinfo){ - obj.addEventListener("contextmenu", (event) => { - event.preventDefault(); - event.stopImmediatePropagation(); - this.makemenu(event.clientX,event.clientY,addinfo,obj) - }); - } -} diff --git a/webpage/contextmenu.ts b/webpage/contextmenu.ts new file mode 100644 index 0000000..aaf7a3f --- /dev/null +++ b/webpage/contextmenu.ts @@ -0,0 +1,78 @@ +class Contextmenu{ + static currentmenu; + name:string; + buttons:[string,Function,string,Function,Function][]; + div:HTMLDivElement; + static setup(){ + Contextmenu.currentmenu=""; + document.addEventListener('click', function(event) { + if(Contextmenu.currentmenu==""){ + return; + } + if (!Contextmenu.currentmenu.contains(event.target)) { + Contextmenu.currentmenu.remove(); + Contextmenu.currentmenu=""; + } + }); + } + constructor(name:string){ + this.name=name; + this.buttons=[] + } + addbutton(text:string,onclick:Function,img=null,shown=_=>true,enabled=_=>true){ + this.buttons.push([text,onclick,img,shown,enabled]) + return {}; + } + makemenu(x:number,y:number,addinfo:any,obj:HTMLElement){ + const div=document.createElement("table"); + div.classList.add("contextmenu"); + for(const thing of this.buttons){ + if(!thing[3](addinfo)){continue;} + const textb=document.createElement("tr"); + const intext=document.createElement("button") + intext.disabled=!thing[4](); + textb["button"]=intext; + intext.classList.add("contextbutton") + intext.textContent=thing[0] + textb.appendChild(intext) + console.log(thing) + intext.onclick=thing[1].bind(addinfo,obj); + div.appendChild(textb); + } + if(Contextmenu.currentmenu!=""){ + Contextmenu.currentmenu.remove(); + } + div.style.top = y+'px'; + div.style.left = x+'px'; + document.body.appendChild(div); + Contextmenu.keepOnScreen(div); + console.log(div) + Contextmenu.currentmenu=div; + return this.div; + } + bind(obj:HTMLElement,addinfo:any=undefined){ + const func=(event) => { + event.preventDefault(); + event.stopImmediatePropagation(); + this.makemenu(event.clientX,event.clientY,addinfo,obj) + } + obj.addEventListener("contextmenu", func); + return func; + } + static keepOnScreen(obj:HTMLElement){ + const html = document.documentElement.getBoundingClientRect(); + const docheight=html.height + const docwidth=html.width + const box=obj.getBoundingClientRect(); + console.log(box,docheight,docwidth); + if(box.right>docwidth){ + console.log("test") + obj.style.left = docwidth-box.width+'px'; + } + if(box.bottom>docheight){ + obj.style.top = docheight-box.height+'px'; + } + } +} +Contextmenu.setup(); +export {Contextmenu as Contextmenu} diff --git a/webpage/direct.js b/webpage/direct.ts similarity index 74% rename from webpage/direct.js rename to webpage/direct.ts index 68e7a85..98f17b9 100644 --- a/webpage/direct.js +++ b/webpage/direct.ts @@ -1,6 +1,13 @@ -class direct extends guild{ - constructor(JSON,owner){ - super(-1); +import {Guild} from "./guild.js"; +import { Channel } from "./channel.js"; +import { Message } from "./message.js"; +import { Localuser } from "./localuser.js"; +import {User} from "./user.js"; +import { Member } from "./member.js"; + +class Direct extends Guild{ + constructor(JSON,owner:Localuser){ + super(-1,owner,null); this.message_notifications=0; console.log(JSON); this.owner=owner; @@ -17,14 +24,14 @@ class direct extends guild{ this.prevchannel=undefined; this.properties.name="Dirrect Messages"; for(const thing of JSON){ - const temp=new group(thing,this); + const temp=new Group(thing,this); this.channels.push(temp); this.channelids[temp.id]=temp; } this.headchannels=this.channels; } createChannelpac(JSON){ - const thischannel=new group(JSON,this); + const thischannel=new Group(JSON,this); this.channelids[JSON.id]=thischannel; this.channels.push(thischannel); this.calculateReorder(); @@ -50,19 +57,20 @@ class direct extends guild{ } unreaddms(){ for(const thing of this.channels){ - thing.unreads(); + (thing as Group).unreads(); } } } -class group extends channel{ - constructor(JSON,owner){ - super(-1); +class Group extends Channel{ + user:User; + constructor(JSON,owner:Direct){ + super(-1,owner); this.owner=owner; this.headers=this.guild.headers; this.messages=[]; this.name=JSON.recipients[0]?.username; if(JSON.recipients[0]){ - this.user=new user(JSON.recipients[0]); + this.user=new User(JSON.recipients[0],this.localuser); }else{ this.user=this.localuser.user; } @@ -73,9 +81,9 @@ class group extends channel{ this.children=[]; this.guild_id="@me"; this.messageids={}; - this.permission_overwrites=[]; + this.permission_overwrites={}; this.lastmessageid=JSON.last_message_id; - this.lastmessageid??=0; + this.lastmessageid??="0"; this.mentions=0; } createguildHTML(){ @@ -85,21 +93,27 @@ class group extends channel{ myhtml.textContent=this.name; div.appendChild(this.user.buildpfp()); div.appendChild(myhtml); - div.myinfo=this; - div.onclick=function(){ - this.myinfo.getHTML(); + div["myinfo"]=this; + div.onclick=_=>{ + this.getHTML(); } return div; } - getHTML(){ + async getHTML(){ + if(this.guild!==this.localuser.lookingguild){ + this.guild.loadGuild(); + } + const prom=Message.wipeChanel(); this.guild.prevchannel=this; this.localuser.channelfocus=this; - this.putmessages(); + await this.putmessages(); + await prom; + this.buildmessages(); history.pushState(null, null,"/channels/"+this.guild_id+"/"+this.id); document.getElementById("channelname").textContent="@"+this.name; } - messageCreate(messagep,focus){ - const messagez=new cmessage(messagep.d,this); + messageCreate(messagep){ + const messagez=new Message(messagep.d,this); this.lastmessageid=messagez.id; if(messagez.author===this.localuser.user){ this.lastreadmessageid=messagez.id; @@ -109,7 +123,7 @@ class group extends channel{ this.messageids[messagez.id]=messagez; if(this.localuser.lookingguild.prevchannel===this){ var shouldScroll=scrolly.scrollTop+scrolly.clientHeight>scrolly.scrollHeight-20; - messages.appendChild(messagez.buildhtml(this.messages[1])); + document.getElementById("messages").appendChild(messagez.buildhtml(this.messages[1])); } if(shouldScroll){ scrolly.scrollTop = scrolly.scrollHeight; @@ -118,12 +132,10 @@ class group extends channel{ if(this.localuser.lookingguild===this.guild){ const channellist=document.getElementById("channels").children[0] for(const thing of channellist.children){ - if(thing.myinfo===this){ + if(thing["myinfo"]===this){ channellist.prepend(thing); - console.log(thing.myinfo); break; } - console.log(thing.myinfo,this,thing.myinfo===this); } } this.unreads(); @@ -147,8 +159,7 @@ class group extends channel{ const sentdms=document.getElementById("sentdms"); let current=null; for(const thing of sentdms.children){ - console.log(thing.all) - if(thing.all===this){ + if(thing["all"]===this){ current=thing; } } @@ -158,19 +169,19 @@ class group extends channel{ div.classList.add("servernoti"); const noti=document.createElement("div"); noti.classList.add("unread","notiunread","pinged"); - noti.textContent=this.mentions; + noti.textContent=""+this.mentions; console.log(this.mentions) - div.noti=noti; + div["noti"]=noti; div.append(noti) const buildpfp=this.user.buildpfp(); - div.all=this; + div["all"]=this; buildpfp.classList.add("mentioned"); console.log(this); div.append(buildpfp) sentdms.append(div); div.onclick=function(){ - this.all.guild.loadGuild(); - this.all.getHTML(); + this["noti"].guild.loadGuild(); + this["noti"].getHTML(); } }else if(current){ @@ -179,4 +190,11 @@ class group extends channel{ } } + isAdmin(): boolean { + return false; + } + hasPermission(name: string, member?: Member): boolean { + return true; + } } +export {Direct, Group}; diff --git a/webpage/embed.js b/webpage/embed.ts similarity index 92% rename from webpage/embed.js rename to webpage/embed.ts index 09eff97..db6c1ef 100644 --- a/webpage/embed.js +++ b/webpage/embed.ts @@ -1,6 +1,10 @@ -class embed{ - constructor(json, owner){ - +import {Fullscreen} from "./fullscreen.js"; +import {Message} from "./message.js"; +class Embed{ + type:string; + owner:Message; + json; + constructor(json, owner:Message){ this.type=this.getType(json); this.owner=owner; this.json=json; @@ -116,7 +120,7 @@ class embed{ const img=document.createElement("img"); img.classList.add("messageimg") img.onclick=function(){ - const full=new fullscreen(["img",img.src,["fit"]]); + const full=new Fullscreen(["img",img.src,["fit"]]); full.show(); } img.src=this.json.thumbnail.proxy_url; @@ -141,7 +145,7 @@ class embed{ if(this.json.thumbnail){ img.classList.add("embedimg"); img.onclick=function(){ - const full=new fullscreen(["img",img.src,["fit"]]); + const full=new Fullscreen(["img",img.src,["fit"]]); full.show(); } img.src=this.json.thumbnail.proxy_url; @@ -180,17 +184,18 @@ class embed{ description.textContent=this.json.description; div.append(description); - { + if(this.json.thumbnail){ const img=document.createElement("img"); img.classList.add("bigembedimg"); img.onclick=function(){ - const full=new fullscreen(["img",img.src,["fit"]]); + const full=new Fullscreen(["img",img.src,["fit"]]); full.show(); } - img.src=this.json.thumbnail.proxy_url; + img.src=this.json.thumbnail.proxy_url||this.json.thumbnail.url; div.append(img); } colordiv.append(div); return colordiv; } } +export {Embed}; diff --git a/webpage/favicon.ico b/webpage/favicon.ico new file mode 100644 index 0000000..f10fd44 Binary files /dev/null and b/webpage/favicon.ico differ diff --git a/webpage/file.ts b/webpage/file.ts new file mode 100644 index 0000000..cb83dd0 --- /dev/null +++ b/webpage/file.ts @@ -0,0 +1,126 @@ +import { Message } from "./message.js"; +import { Fullscreen } from "./fullscreen.js"; +type filejson= {id:string,filename:string,content_type:string,width:number,height:number,proxy_url:string|undefined,url:string,size:number}; +class File{ + owner:Message; + id:string; + filename:string; + content_type:string; + width:number; + height:number; + proxy_url:string; + url:string; + size:number; + constructor(fileJSON:filejson,owner:Message){ + console.log(fileJSON); + this.owner=owner; + this.id=fileJSON.id; + this.filename=fileJSON.filename; + this.content_type=fileJSON.content_type; + this.width=fileJSON.width; + this.height=fileJSON.height; + this.url=fileJSON.url; + this.proxy_url=fileJSON.proxy_url; + this.content_type=fileJSON.content_type; + this.size=fileJSON.size; + } + getHTML(temp:boolean=false):HTMLElement{ + const src=this.proxy_url||this.url; + if(this.content_type.startsWith('image/')){ + const img=document.createElement("img"); + img.classList.add("messageimg"); + img.onclick=function(){ + const full=new Fullscreen(["img",img.src,["fit"]]); + full.show(); + } + img.src=src; + img.height=this.height; + img.width=this.width; + return img; + }else if(this.content_type.startsWith('video/')){ + const video=document.createElement("video"); + const source=document.createElement("source"); + source.src=src; + video.append(source); + source.type=this.content_type; + video.controls=!temp; + return video; + }else if(this.content_type.startsWith('audio/')){ + const audio=document.createElement("audio"); + const source=document.createElement("source"); + source.src=src; + audio.append(source); + source.type=this.content_type; + audio.controls=!temp; + return audio; + }else{ + return this.createunknown(); + } + } + upHTML(files:Blob[],file:globalThis.File):HTMLElement{ + const div=document.createElement("div"); + const contained=this.getHTML(true); + div.classList.add("containedFile"); + div.append(contained); + const controls=document.createElement("div"); + const garbage=document.createElement("button"); + garbage.textContent="🗑"; + garbage.onclick=_=>{ + div.remove(); + files.splice(files.indexOf(file),1); + } + controls.classList.add("controls"); + div.append(controls); + controls.append(garbage); + return div; + } + static initFromBlob(file:globalThis.File){ + return new File({ + filename:file.name, + size:file.size, + id:null, + content_type:file.type, + width:undefined, + height:undefined, + url:URL.createObjectURL(file), + proxy_url:undefined + },null) + } + createunknown():HTMLElement{ + console.log("🗎") + const src=this.proxy_url||this.url; + const div=document.createElement("table"); + div.classList.add("unknownfile"); + const nametr=document.createElement("tr"); + div.append(nametr); + const fileicon=document.createElement("td"); + nametr.append(fileicon); + fileicon.append("🗎"); + fileicon.classList.add("fileicon"); + fileicon.rowSpan=2; + const nametd=document.createElement("td"); + if(src){ + const a=document.createElement("a"); + a.href=src; + a.textContent=this.filename; + nametd.append(a); + }else{ + nametd.textContent=this.filename; + } + + nametd.classList.add("filename"); + nametr.append(nametd); + const sizetr=document.createElement("tr"); + const size=document.createElement("td"); + sizetr.append(size); + size.textContent="Size:"+File.filesizehuman(this.size); + size.classList.add("filesize"); + div.appendChild(sizetr); + return div; + } + static filesizehuman(fsize:number){ + var i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024)); + return +((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes'][i]; + } +} +export{File} diff --git a/webpage/fullscreen.js b/webpage/fullscreen.ts similarity index 96% rename from webpage/fullscreen.js rename to webpage/fullscreen.ts index 60cf256..4868b9f 100644 --- a/webpage/fullscreen.js +++ b/webpage/fullscreen.ts @@ -1,4 +1,10 @@ -class fullscreen{ +export {Fullscreen}; +class Fullscreen{ + layout; + onclose: Function; + onopen: Function; + html:HTMLDivElement; + background: HTMLDivElement; constructor(layout,onclose=_=>{},onopen=_=>{}){ this.layout=layout; this.onclose=onclose; @@ -134,8 +140,9 @@ class fullscreen{ if(i===0){ continue; } - if(thing.children[0].children[0].checked){ - array[3](thing.children[0].children[0].value); + const checkbox = thing.children[0].children[0] as HTMLInputElement; + if(checkbox.checked){ + array[3](checkbox.value); } } }); diff --git a/webpage/guild.js b/webpage/guild.ts similarity index 70% rename from webpage/guild.js rename to webpage/guild.ts index 015a2f1..1db660c 100644 --- a/webpage/guild.js +++ b/webpage/guild.ts @@ -1,30 +1,52 @@ -class guild{ - static contextmenu=new contextmenu("guild menu"); +import { Channel } from "./channel.js"; +import { Localuser } from "./localuser.js"; +import {Contextmenu} from "./contextmenu.js"; +import {Role} from "./role.js"; +import {Fullscreen} from "./fullscreen.js"; +import {Member} from "./member.js"; + +class Guild{ + owner:Localuser; + headers:Localuser["headers"]; + channels:Channel[]; + channelids:{[key:string]:Channel}; + id:string; + properties + roles:Role[]; + roleids:{[key:string]:Role}; + prevchannel:Channel; + message_notifications:number; + headchannels:Channel[]; + position:number; + parent_id:string; + member:Member; + html:HTMLElement; + static contextmenu=new Contextmenu("guild menu"); static setupcontextmenu(){ - guild.contextmenu.addbutton("Copy Guild id",function(){ + Guild.contextmenu.addbutton("Copy Guild id",function(){ console.log(this) navigator.clipboard.writeText(this.id); }); - guild.contextmenu.addbutton("Mark as read",function(){ + Guild.contextmenu.addbutton("Mark as read",function(){ console.log(this) this.markAsRead(); }); - guild.contextmenu.addbutton("Notifications",function(){ + Guild.contextmenu.addbutton("Notifications",function(){ console.log(this) this.setnotifcation(); }); - guild.contextmenu.addbutton("Leave guild",function(){ + Guild.contextmenu.addbutton("Leave guild",function(){ this.confirmleave(); },null,function(_){return _.properties.owner_id!==_.member.user.id}); - guild.contextmenu.addbutton("Delete guild",function(){ + Guild.contextmenu.addbutton("Delete guild",function(){ this.confirmDelete(); },null,function(_){return _.properties.owner_id===_.member.user.id}); - guild.contextmenu.addbutton("Create invite",function(){ + Guild.contextmenu.addbutton("Create invite",function(){ console.log(this); },null,_=>true,_=>false); /* -----things left for later----- @@ -38,16 +60,13 @@ class guild{ },null,_=>{return thisuser.isAdmin()}) */ } - constructor(JSON,owner){ + constructor(JSON,owner:Localuser,member){ if(JSON===-1){ return; } this.owner=owner; this.headers=this.owner.headers; - if(!this.owner){ - console.error("localuser was not included, please fix") - } this.channels=[]; this.channelids={}; this.id=JSON.id; @@ -57,12 +76,13 @@ class guild{ this.prevchannel=undefined; this.message_notifications=0; for(const roley of JSON.roles){ - const roleh=new role(roley); + const roleh=new Role(roley,this); this.roles.push(roleh) this.roleids[roleh.id]=roleh; } + Member.resolve(member,this).then(_=>this.member=_); for(const thing of JSON.channels){ - const temp=new channel(thing,this); + const temp=new Channel(thing,this); this.channels.push(temp); this.channelids[temp.id]=temp; } @@ -78,7 +98,7 @@ class guild{ } setnotifcation(){ let noti=this.message_notifications - const notiselect=new fullscreen( + const notiselect=new Fullscreen( ["vdiv", ["radio","select notifications type", ["all","only mentions","none"], @@ -88,7 +108,7 @@ class guild{ noti ], ["button","","submit",_=>{ - fetch(info.api.toString()+"/v9/users/@me/guilds/settings",{ + fetch(this.info.api.toString()+"/v9/users/@me/guilds/settings",{ method:"PATCH", headers:this.headers, body:JSON.stringify({ @@ -105,7 +125,7 @@ class guild{ notiselect.show(); } confirmleave(){ - const full= new fullscreen([ + const full= new Fullscreen([ "vdiv", ["title", "Are you sure you want to leave?" @@ -133,7 +153,7 @@ class guild{ full.show(); } async leave(){ - return fetch(info.api.toString()+"/users/@me/guilds/"+this.id,{ + return fetch(this.info.api.toString()+"/users/@me/guilds/"+this.id,{ method:"DELETE", headers:this.headers }) @@ -152,7 +172,7 @@ class guild{ let position=-1; let build=[]; for(const thing of this.headchannels){ - const thisthing={id:thing.id} + const thisthing={id:thing.id,position:undefined,parent_id:undefined} if(thing.position<=position){ thing.position=(thisthing.position=position+1); } @@ -181,14 +201,14 @@ class guild{ if(serverbug){ for(const thing of build){ console.log(build,thing) - fetch(info.api.toString()+"/v9/guilds/"+this.id+"/channels",{ + fetch(this.info.api.toString()+"/v9/guilds/"+this.id+"/channels",{ method:"PATCH", headers:this.headers, body:JSON.stringify([thing]) }); } }else{ - fetch(info.api.toString()+"/v9/guilds/"+this.id+"/channels",{ + fetch(this.info.api.toString()+"/v9/guilds/"+this.id+"/channels",{ method:"PATCH", headers:this.headers, body:JSON.stringify(build) @@ -199,9 +219,8 @@ class guild{ get localuser(){ return this.owner; } - loadChannel(id){ - this.localuser.channelfocus=this.channelids[id]; - this.channelids[id].getHTML(); + get info(){ + return this.owner.info; } sortchannels(){ this.headchannels.sort((a,b)=>{return a.position-b.position;}); @@ -217,14 +236,14 @@ class guild{ if(this.properties.icon!=null){ const img=document.createElement("img"); img.classList.add("pfp","servericon"); - img.src=info.cdn.toString()+"icons/"+this.properties.id+"/"+this.properties.icon+".png"; + img.src=this.info.cdn.toString()+"icons/"+this.properties.id+"/"+this.properties.icon+".png"; divy.appendChild(img) img.onclick=()=>{ console.log(this.loadGuild) this.loadGuild(); this.loadChannel(); } - guild.contextmenu.bind(img,this); + Guild.contextmenu.bind(img,this); }else{ const div=document.createElement("div"); let build=""; @@ -238,13 +257,13 @@ class guild{ this.loadGuild(); this.loadChannel(); } - guild.contextmenu.bind(div,this) + Guild.contextmenu.bind(div,this) } return divy; } confirmDelete(){ let confirmname=""; - const full= new fullscreen([ + const full= new Fullscreen([ "vdiv", ["title", "Are you sure you want to delete "+this.properties.name+"?" @@ -284,12 +303,12 @@ class guild{ full.show(); } async delete(){ - return fetch(info.api.toString()+"/guilds/"+this.id+"/delete",{ + return fetch(this.info.api.toString()+"/guilds/"+this.id+"/delete",{ method:"POST", headers:this.headers, }) } - unreads(html){ + unreads(html=undefined){ if(html){ this.html=html; }else{ @@ -333,34 +352,24 @@ class guild{ } } this.unreads(); - fetch(info.api.toString()+"/v9/read-states/ack-bulk",{ + fetch(this.info.api.toString()+"/v9/read-states/ack-bulk",{ method:"POST", headers:this.headers, body:JSON.stringify(build) }) } - fillMember(member){ - const realroles=[]; - for(const thing of member.roles){ - realroles.push(this.getRole(thing)); - } - member.roles=realroles; - return member; - } - giveMember(member){ - this.fillMember(member); - this.member=member; - } - getRole(ID){ + getRole(ID:string):Role{ + if(!this.roleids[ID]){console.error(`role id ${ID} does not exist`,this.roleids)} return this.roleids[ID]; } - hasRole(r){ + hasRole(r:Role|string){ + console.log("this should run"); if((typeof r)!==(typeof "")){ - r=r.id; + r=(r as Role).id; } - return this.member.hasRole(r); + return this.member.hasRole(r as string); } - loadChannel(ID){ + loadChannel(ID:string=undefined){ if(ID&&this.channelids[ID]){ this.channelids[ID].getHTML(); return; @@ -394,7 +403,7 @@ class guild{ this.printServers(); } createChannelpac(JSON){ - const thischannel=new channel(JSON,this); + const thischannel=new Channel(JSON,this); this.channelids[JSON.id]=thischannel; this.channels.push(thischannel); thischannel.resolveparent(this); @@ -404,26 +413,82 @@ class guild{ this.calculateReorder(); this.printServers(); } + createchannels(func=this.createChannel){ + let name=""; + let category=0; + const channelselect=new Fullscreen( + ["vdiv", + ["radio","select channel type", + ["voice","text","announcement"], + function(e){ + console.log(e) + category={"text":0,"voice":2,"announcement":5,"category":4}[e] + }, + 1 + ], + ["textbox","Name of channel","",function(){ + console.log(this) + name=this.value + }], + ["button","","submit",function(){ + console.log(name,category) + func(name,category); + channelselect.hide(); + }.bind(this)] + ]); + channelselect.show(); + } + createcategory(){ + let name=""; + let category=4; + const channelselect=new Fullscreen( + ["vdiv", + ["textbox","Name of category","",function(){ + console.log(this); + name=this.value; + }], + ["button","","submit",function(){ + console.log(name,category) + this.createChannel(name,category); + channelselect.hide(); + }] + ]); + channelselect.show(); + } delChannel(JSON){ + const channel=this.channelids[JSON.id]; delete this.channelids[JSON.id]; + + this.channels.splice(this.channels.indexOf(channel),1); + const indexy=this.headchannels.indexOf(channel); + if(indexy!==-1){ + this.headchannels.splice(indexy,1); + } + + /* const build=[]; for(const thing of this.channels){ - if(thing.id!==JSON.id){ + console.log(thing.id); + if(thing!==channel){ build.push(thing) }else{ + console.log("fail"); if(thing.parrent){ thing.parrent.delChannel(JSON); } } } this.channels=build; + */ + this.printServers(); } - createChannel(name,type){ - fetch(info.api.toString()+"/guilds/"+this.id+"/channels",{ + createChannel(name:string,type:number){ + fetch(this.info.api.toString()+"/guilds/"+this.id+"/channels",{ method:"Post", headers:this.headers, body:JSON.stringify({name: name, type: type}) }) } } -guild.setupcontextmenu(); +Guild.setupcontextmenu(); +export { Guild }; diff --git a/webpage/index.html b/webpage/index.html index 7e895fd..8f3689d 100644 --- a/webpage/index.html +++ b/webpage/index.html @@ -10,21 +10,7 @@ - - - - - - - - - - - - - - - +

Jank Client is loading

This shouldn't take long

@@ -58,7 +44,7 @@
-

⚙

+

⚙

@@ -71,6 +57,7 @@
+
- + diff --git a/webpage/login.js b/webpage/login.ts similarity index 89% rename from webpage/login.js rename to webpage/login.ts index ad40880..f6d20c0 100644 --- a/webpage/login.js +++ b/webpage/login.ts @@ -1,4 +1,5 @@ -const mobile=isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); +const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); +export {mobile, getBulkUsers,getBulkInfo,setTheme,Specialuser} function setTheme(){ const name=localStorage.getItem("theme"); if(!name){ @@ -11,7 +12,7 @@ setTheme(); function getBulkUsers(){ const json=getBulkInfo() for(const thing in json.users){ - json.users[thing]=new specialuser(json.users[thing]); + json.users[thing]=new Specialuser(json.users[thing]); } return json; } @@ -48,9 +49,14 @@ function setDefaults(){ localStorage.setItem("userinfos",JSON.stringify(userinfos)); } setDefaults(); -class specialuser{ +class Specialuser{ + serverurls; + email:string; + token:string; + loggedin; + json; constructor(json){ - if(json instanceof specialuser){ + if(json instanceof Specialuser){ console.error("specialuser can't construct from another specialuser"); } this.serverurls=json.serverurls; @@ -94,21 +100,23 @@ class specialuser{ } } function adduser(user){ - user=new specialuser(user); + user=new Specialuser(user); const info=getBulkInfo(); info.users[user.uid]=user; info.currentuser=user.uid; localStorage.setItem("userinfos",JSON.stringify(info)); } const instancein=document.getElementById("instancein"); -let timeout=0; +let timeout; +let instanceinfo; async function checkInstance(e){ + const verify=document.getElementById("verify");; try{ verify.textContent="Checking Instance"; - instanceinfo=await setInstance(instancein.value); + const instanceinfo=await setInstance((instancein as HTMLInputElement).value); localStorage.setItem("instanceinfo",JSON.stringify(instanceinfo)); verify.textContent="Instance is all good" - if(checkInstance.alt){checkInstance.alt();} + if(checkInstance["alt"]){checkInstance["alt"]();} setTimeout(_=>{ console.log(verify.textContent) verify.textContent=""; @@ -128,7 +136,7 @@ if(instancein){ timeout=setTimeout(checkInstance,1000); }); if(localStorage.getItem("instanceinfo")){ - instancein.value=JSON.parse(localStorage.getItem("instanceinfo")).wellknown + (instancein as HTMLInputElement).value=JSON.parse(localStorage.getItem("instanceinfo")).wellknown }else{ checkInstance("https://spacebar.chat/"); } @@ -148,7 +156,7 @@ async function login(username, password){ }} try{ const info=JSON.parse(localStorage.getItem("instanceinfo")); - url=new URL(info.login); + const url=new URL(info.login); return await fetch(url.origin+'/api/auth/login',options).then(responce=>responce.json()) .then((response) => { console.log(response,response.message) diff --git a/webpage/markdown.js b/webpage/markdown.ts similarity index 86% rename from webpage/markdown.js rename to webpage/markdown.ts index efc9116..d6230e4 100644 --- a/webpage/markdown.js +++ b/webpage/markdown.ts @@ -1,7 +1,10 @@ -"use strict"; -function markdown(txt,keep=false){ - if((typeof txt)===(typeof "")){ - return markdown(txt.split(""),keep); +export {markdown}; +function markdown(text : string|string[],{keep=false,stdsize=false} = {}){ + let txt : string[]; + if((typeof txt)==="string"){ + txt=(text as string).split(""); + }else{ + txt=(text as string[]); } const span=document.createElement("span"); let current=document.createElement("span"); @@ -19,7 +22,7 @@ function markdown(txt,keep=false){ i--; } let element=null; - let keepys=false; + let keepys=""; if(txt[i+1]==="#"){ console.log("test"); @@ -50,17 +53,20 @@ function markdown(txt,keep=false){ } if(keepys){ appendcurrent(); - if(!first){ + if(!first&&!stdsize){ span.appendChild(document.createElement("br")); } const build=[]; for(;txt[i]!=="\n"&&txt[i]!==undefined;i++){ build.push(txt[i]); } + if(stdsize){ + element=document.createElement("span"); + } if(keep){ element.append(keepys); } - element.appendChild(markdown(build,keep)); + element.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); span.append(element); i--; continue; @@ -70,8 +76,11 @@ function markdown(txt,keep=false){ } } if(txt[i]==="\n"){ - appendcurrent(); - span.append(document.createElement("br")); + + if(!stdsize){ + appendcurrent(); + span.append(document.createElement("br")); + } continue; } if(txt[i]==="`"){ @@ -115,7 +124,7 @@ function markdown(txt,keep=false){ if(keep){ build+="`".repeat(find); } - if(count!==3){ + if(count!==3&&!stdsize){ const samp=document.createElement("samp"); samp.textContent=build; span.appendChild(samp); @@ -151,7 +160,7 @@ function markdown(txt,keep=false){ if(txt[j]==="*"){ find++; }else{ - build+=txt[j]; + build.push(txt[j]); if(find!==0){ build=build.concat(new Array(find).fill("*")); find=0; @@ -166,20 +175,20 @@ function markdown(txt,keep=false){ if(count===1){ const i=document.createElement("i"); if(keep){i.append(stars)} - i.appendChild(markdown(build,keep)); + i.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); if(keep){i.append(stars)} span.appendChild(i); }else if(count===2){ const b=document.createElement("b"); if(keep){b.append(stars)} - b.appendChild(markdown(build,keep)); + b.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); if(keep){b.append(stars)} span.appendChild(b); }else{ const b=document.createElement("b"); const i=document.createElement("i"); if(keep){b.append(stars)} - b.appendChild(markdown(build,keep)); + b.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); if(keep){b.append(stars)} i.appendChild(b); span.appendChild(i); @@ -205,7 +214,7 @@ function markdown(txt,keep=false){ if(txt[j]==="_"){ find++; }else{ - build+=txt[j]; + build.push(txt[j]); if(find!==0){ build=build.concat(new Array(find).fill("_")); find=0; @@ -219,20 +228,20 @@ function markdown(txt,keep=false){ if(count===1){ const i=document.createElement("i"); if(keep){i.append(underscores)} - i.appendChild(markdown(build,keep)); + i.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); if(keep){i.append(underscores)} span.appendChild(i); }else if(count===2){ const u=document.createElement("u"); if(keep){u.append(underscores)} - u.appendChild(markdown(build,keep)); + u.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); if(keep){u.append(underscores)} span.appendChild(u); }else{ const u=document.createElement("u"); const i=document.createElement("i"); if(keep){i.append(underscores)} - i.appendChild(markdown(build,keep)); + i.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); if(keep){i.append(underscores)} u.appendChild(i) span.appendChild(u); @@ -251,7 +260,7 @@ function markdown(txt,keep=false){ if(txt[j]==="~"){ find++; }else{ - build+=txt[j]; + build.push(txt[j]); if(find!==0){ build=build.concat(new Array(find).fill("~")); find=0; @@ -265,7 +274,7 @@ function markdown(txt,keep=false){ if(count===2){ const s=document.createElement("s"); if(keep){s.append(underscores)} - s.appendChild(markdown(build,keep)); + s.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); if(keep){s.append(underscores)} span.appendChild(s); } @@ -281,7 +290,7 @@ function markdown(txt,keep=false){ if(txt[j]==="|"){ find++; }else{ - build+=txt[j]; + build.push(txt[j]); if(find!==0){ build=build.concat(new Array(find).fill("~")); find=0; @@ -295,7 +304,7 @@ function markdown(txt,keep=false){ if(count===2){ const j=document.createElement("j"); if(keep){j.append(underscores)} - j.appendChild(markdown(build,keep)); + j.appendChild(markdown(build,{keep:keep,stdsize:stdsize})); j.classList.add("spoiler"); j.onclick=markdown.unspoil; if(keep){j.append(underscores)} @@ -309,8 +318,9 @@ function markdown(txt,keep=false){ appendcurrent(); return span; } -markdown.unspoil=function(e){ +markdown.unspoil=function(e:any) : void{ //console.log("undone") e.target.classList.remove("spoiler") e.target.classList.add("unspoiled") } + diff --git a/webpage/member.js b/webpage/member.js deleted file mode 100644 index 17f334a..0000000 --- a/webpage/member.js +++ /dev/null @@ -1,64 +0,0 @@ -class member{ - static already={}; - constructor(memberjson,owner){ - if(!owner){console.error("Guild not included in the creation of a member object")} - this.owner=owner; - let membery=memberjson; - if(memberjson.guild_member){ - membery=memberjson.guild_member; - this.user=memberjson.user; - } - for(const thing of Object.keys(membery)){ - if(thing==="guild"){continue} - this[thing]=membery[thing]; - } - this.user=new user(this.user); - } - get guild(){ - return this.owner; - } - get localuser(){ - return this.guild.localuser; - } - static async resolve(user,guild){ - if(!member.already[guild.id]){ - member.already[guild.id]={}; - }else if(member.already[guild.id][user.id]){ - const memb=member.already[guild.id][user.id] - if(memb instanceof Promise){ - return await memb; - } - return memb; - } - const promoise= fetch(info.api.toString()+"/v9/users/"+user.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][user.id]=memb; - guild.fillMember(memb); - console.log("resolved") - return memb - }); - member.already[guild.id][user.id]=promoise; - return await promoise; - } - hasRole(ID){ - for(const thing of this.roles){ - if(thing.id===ID){ - return true; - } - } - return false; - } - getColor(){ - for(const thing of this.roles){ - const color=thing.getColor(); - if(color){ - return color; - } - } - return ""; - } - isAdmin(){ - return this.guild.properties.owner_id===this.user.id; - - } -} diff --git a/webpage/member.ts b/webpage/member.ts new file mode 100644 index 0000000..6b5a3c4 --- /dev/null +++ b/webpage/member.ts @@ -0,0 +1,111 @@ +import {User} from "./user.js"; +import {Role} from "./role.js"; +import {Guild} from "./guild.js"; +class Member{ + static already={}; + owner:Guild; + user:User; + roles:Role[]; + error:boolean; + constructor(memberjson,owner:Guild,error=false){ + this.error=error; + this.owner=owner; + let membery=memberjson; + this.roles=[]; + if(!error){ + if(memberjson.guild_member){ + membery=memberjson.guild_member; + this.user=memberjson.user; + } + } + for(const thing of Object.keys(membery)){ + if(thing==="guild"){continue} + if(thing==="owner"){continue} + if(thing==="roles"){ + for(const strrole of membery["roles"]){ + const role=this.guild.getRole(strrole); + this.roles.push(role); + } + continue; + } + this[thing]=membery[thing]; + } + if(error){ + this.user=memberjson as User; + }else{ + this.user=new User(this.user,owner.localuser); + } + } + get guild(){ + return this.owner; + } + get localuser(){ + return this.guild.localuser; + } + get info(){ + return this.owner.info; + } + static async resolve(unkown:User|object,guild:Guild):Promise{ + if(!(guild instanceof Guild)){ + console.error(guild) + } + let user:User; + if(unkown instanceof User){ + user=unkown as User; + }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][user.id]){ + const memb=Member.already[guild.id][user.id] + if(memb instanceof Promise){ + return await memb; + } + return memb; + } + const promoise= fetch(guild.info.api.toString()+"/v9/users/"+user.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][user.id]=memb; + console.log("resolved") + return memb + }) + Member.already[guild.id][user.id]=promoise; + try{ + return await promoise + }catch(_){ + const memb=new Member(user,guild,true); + Member.already[guild.id][user.id]=memb; + return memb; + } + } + hasRole(ID:string){ + console.log(this.roles,ID); + for(const thing of this.roles){ + if(thing.id===ID){ + return true; + } + } + return false; + } + getColor(){ + for(const thing of this.roles){ + const color=thing.getColor(); + if(color){ + return color; + } + } + return ""; + } + isAdmin(){ + for(const role of this.roles){ + if(role.permissions.getPermision("ADMINISTRATOR")){ + return true; + } + } + return this.guild.properties.owner_id===this.user.id; + + } +} +export {Member}; diff --git a/webpage/message.js b/webpage/message.js deleted file mode 100644 index 2f590a6..0000000 --- a/webpage/message.js +++ /dev/null @@ -1,271 +0,0 @@ -class cmessage{ - static contextmenu=new contextmenu("message menu"); - static setupcmenu(){ - cmessage.contextmenu.addbutton("Copy raw text",function(){ - navigator.clipboard.writeText(this.content); - }); - cmessage.contextmenu.addbutton("Reply",function(div){ - if(replyingto){ - replyingto.classList.remove("replying"); - } - replyingto=div; - console.log(div); - replyingto.classList.add("replying"); - }); - cmessage.contextmenu.addbutton("Copy message id",function(){ - navigator.clipboard.writeText(this.id); - }); - cmessage.contextmenu.addbutton("Copy user id",function(){ - navigator.clipboard.writeText(this.author.id); - }); - cmessage.contextmenu.addbutton("Message user",function(){ - fetch(info.api.toString()+"/v9/users/@me/channels", - {method:"POST", - body:JSON.stringify({"recipients":[this.author.id]}), - headers: {"Content-type": "application/json; charset=UTF-8",Authorization:token} - }); - }) - cmessage.contextmenu.addbutton("Edit",function(){ - editing=this; - document.getElementById("typebox").value=this.content; - },null,_=>{return _.author.id==READY.d.user.id}); - } - constructor(messagejson,owner){ - this.owner=owner; - this.headers=this.owner.headers; - for(const thing of Object.keys(messagejson)){ - this[thing]=messagejson[thing]; - } - for(const thing in this.embeds){ - console.log(thing,this.embeds) - this.embeds[thing]=new embed(this.embeds[thing],this); - } - this.author=new user(this.author); - for(const thing in this.mentions){ - this.mentions[thing]=new user(this.mentions[thing]); - } - if(this.mentions.length||this.mention_roles.length){//currently mention_roles isn't implemented on the spacebar servers - console.log(this.mentions,this.mention_roles) - } - if(this.mentionsuser(this.localuser.user)){ - console.log(this); - } - } - get channel(){ - return this.owner; - } - get guild(){ - return this.owner.guild; - } - get localuser(){ - return this.owner.localuser; - } - messageevents(obj){ - cmessage.contextmenu.bind(obj,this) - obj.classList.add("messagediv") - } - mentionsuser(userd){ - if(userd instanceof user){ - return this.mentions.includes(userd); - }else if(userd instanceof member){ - return this.mentions.includes(userd.user); - } - } - getimages(){ - const build=[]; - for(const thing of this.attachments){ - if(thing.content_type.startsWith('image/')){ - build.push(thing); - } - } - return build; - } - async edit(content){ - return await fetch(info.api.toString()+"/channels/"+this.channel.id+"/messages/"+this.id,{ - method: "PATCH", - headers: this.headers, - body:JSON.stringify({content:content}) - }); - } - buildhtml(premessage){ - //premessage??=messages.lastChild; - const build = document.createElement('table'); - const div=document.createElement("div"); - - if(this.message_reference){ - const replyline=document.createElement("div"); - const line=document.createElement("hr"); - const minipfp=document.createElement("img") - minipfp.classList.add("replypfp"); - replyline.appendChild(line); - replyline.appendChild(minipfp); - const username=document.createElement("span"); - replyline.appendChild(username); - const reply=document.createElement("div"); - username.classList.add("username"); - - member.resolve(this.author,this.guild).then(_=>{ - username.style.color=_.getColor(); - }); - - reply.classList.add("replytext"); - replyline.appendChild(reply); - const line2=document.createElement("hr"); - replyline.appendChild(line2); - line2.classList.add("reply"); - line.classList.add("startreply"); - replyline.classList.add("replyflex") - fetch(info.api.toString()+"/v9/channels/"+this.message_reference.channel_id+"/messages?limit=1&around="+this.message_reference.message_id,{headers:this.headers}).then(responce=>responce.json()).then(responce=>{ - const author=new user(responce[0].author); - - reply.appendChild(markdown(responce[0].content)); - - minipfp.src=author.getpfpsrc() - profileclick(minipfp,author) - username.textContent=author.username; - profileclick(username,author) - }); - div.appendChild(replyline); - } - - this.messageevents(div); - messagelist.push(div) - build.classList.add("message"); - div.appendChild(build); - if({0:true,19:true}[this.type]||this.attachments.length!==0){ - const pfpRow = document.createElement('th'); - - let pfpparent, current - if(premessage!=null){ - pfpparent=premessage.pfpparent; - pfpparent??=premessage; - let pfpparent2=pfpparent.all; - pfpparent2??=pfpparent; - const old=(new Date(pfpparent2.timestamp).getTime())/1000; - const newt=(new Date(this.timestamp).getTime())/1000; - current=(newt-old)>600; - } - const combine=(premessage?.userid!=this.author.id&premessage?.author?.id!=this.author.id)||(current)||this.message_reference - if(combine){ - const pfp=this.author.buildpfp(); - profileclick(pfp,this.author); - pfpRow.appendChild(pfp); - }else{ - div.pfpparent=pfpparent; - } - pfpRow.classList.add("pfprow") - build.appendChild(pfpRow); - const text=document.createElement("th"); - - const texttxt=document.createElement("table"); - texttxt.classList.add("commentrow") - text.appendChild(texttxt); - if(combine){ - const username=document.createElement("span"); - username.classList.add("username") - profileclick(username,this.author); - member.resolve(this.author,this.guild).then(_=>{ - username.style.color=_.getColor(); - }) - username.textContent=this.author.username; - const userwrap=document.createElement("tr") - userwrap.appendChild(username) - if(this.author.bot){ - const username=document.createElement("span"); - username.classList.add("bot") - username.textContent="BOT"; - userwrap.appendChild(username) - } - const time=document.createElement("span"); - time.textContent=" "+formatTime(new Date(this.timestamp)); - time.classList.add("timestamp") - userwrap.appendChild(time); - - texttxt.appendChild(userwrap) - } - const messaged=markdown(this.content); - div.txt=messaged; - const messagedwrap=document.createElement("tr") - messagedwrap.appendChild(messaged) - texttxt.appendChild(messagedwrap) - - build.appendChild(text) - if(this.attachments.length){ - const attatch = document.createElement("tr") - for(const thing of this.attachments){ - const array=thing.url.split("/");array.shift();array.shift();array.shift(); - const src=info.cdn.toString()+array.join("/"); - if(thing.content_type.startsWith('image/')){ - const img=document.createElement("img"); - img.classList.add("messageimg") - img.onclick=function(){ - const full=new fullscreen(["img",img.src,["fit"]]); - full.show(); - } - img.src=src; - attatch.appendChild(img) - }else{ - attatch.appendChild(createunknown(thing.filename,thing.size,src)) - } - - } - messagedwrap.appendChild(attatch) - } - if(this.embeds.length){ - const embeds = document.createElement("tr") - for(const thing of this.embeds){ - embeds.appendChild(thing.generateHTML()); - } - messagedwrap.appendChild(embeds) - } - // - }else if(this.type===7){ - - const text=document.createElement("th"); - - const texttxt=document.createElement("table"); - text.appendChild(texttxt); - build.appendChild(text) - - const messaged=document.createElement("p"); - div.txt=messaged; - messaged.textContent="welcome: "+this.author.username; - const messagedwrap=document.createElement("tr") - messagedwrap.appendChild(messaged); - - const time=document.createElement("span"); - time.textContent=" "+formatTime(new Date(this.timestamp)); - time.classList.add("timestamp"); - messagedwrap.append(time); - - texttxt.appendChild(messagedwrap) - } - div.userid=this.author.id; - div.all=this; - return(div) - } -} - -function formatTime(date) { - const now = new Date(); - const sameDay = date.getDate() === now.getDate() && - date.getMonth() === now.getMonth() && - date.getFullYear() === now.getFullYear(); - - const yesterday = new Date(now); - yesterday.setDate(now.getDate() - 1); - const isYesterday = date.getDate() === yesterday.getDate() && - date.getMonth() === yesterday.getMonth() && - date.getFullYear() === yesterday.getFullYear(); - - const formatTime = date => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); - - if (sameDay) { - return `Today at ${formatTime(date)}`; - } else if (isYesterday) { - return `Yesterday at ${formatTime(date)}`; - } else { - return `${date.toLocaleDateString()} at ${formatTime(date)}`; - } -} -cmessage.setupcmenu(); diff --git a/webpage/message.ts b/webpage/message.ts new file mode 100644 index 0000000..5d2bd28 --- /dev/null +++ b/webpage/message.ts @@ -0,0 +1,394 @@ +import {Contextmenu} from "./contextmenu.js"; +import {User} from "./user.js"; +import {Member} from "./member.js"; +import {markdown} from "./markdown.js"; +import {Embed} from "./embed.js"; +import {Fullscreen} from "./fullscreen.js"; +import { Channel } from "./channel.js"; +import {Localuser} from "./localuser.js"; +import { Role } from "./role.js"; +import {File} from "./file.js"; + +class Message{ + static contextmenu=new Contextmenu("message menu"); + owner:Channel; + headers:Localuser["headers"]; + embeds:Embed[]; + author:User; + mentions:User[]; + mention_roles:Role[]; + attachments:File[];//probably should be its own class tbh, should be Attachments[] + id:string; + message_reference; + type:number; + timestamp:number; + content:string; + static del:Promise; + static resolve:Function; + div:HTMLDivElement; + static setup(){ + this.del=new Promise(_=>{this.resolve=_}); + Message.setupcmenu(); + } + static async wipeChanel(){ + this.resolve(); + document.getElementById("messages").innerHTML=""; + await Promise.allSettled([this.resolve]); + this.del=new Promise(_=>{this.resolve=_}) + } + static setupcmenu(){ + Message.contextmenu.addbutton("Copy raw text",function(){ + navigator.clipboard.writeText(this.content); + }); + Message.contextmenu.addbutton("Reply",function(this:Message,div:HTMLDivElement){ + this.channel.setReplying(this); + }); + Message.contextmenu.addbutton("Copy message id",function(){ + navigator.clipboard.writeText(this.id); + }); + Message.contextmenu.addbutton("Copy user id",function(){ + navigator.clipboard.writeText(this.author.id); + }); + Message.contextmenu.addbutton("Message user",function(){ + fetch(this.info.api.toString()+"/v9/users/@me/channels", + {method:"POST", + body:JSON.stringify({"recipients":[this.author.id]}), + headers: this.headers + }); + }) + Message.contextmenu.addbutton("Edit",function(){ + this.channel.editing=this; + (document.getElementById("typebox") as HTMLInputElement).value=this.content; + },null,_=>{return _.author.id===_.localuser.user.id}); + Message.contextmenu.addbutton("Delete message",function(){ + this.delete(); + },null,_=>{return _.canDelete()}) + } + constructor(messagejson,owner:Channel){ + this.owner=owner; + this.headers=this.owner.headers; + for(const thing of Object.keys(messagejson)){ + if(thing==="attachments"){ + this.attachments=[]; + for(const thing of messagejson.attachments){ + this.attachments.push(new File(thing,this)); + } + continue; + } + this[thing]=messagejson[thing]; + } + for(const thing in this.embeds){ + console.log(thing,this.embeds) + this.embeds[thing]=new Embed(this.embeds[thing],this); + } + this.author=new User(this.author,this.localuser); + for(const thing in this.mentions){ + this.mentions[thing]=new User(this.mentions[thing],this.localuser); + } + if(this.mentions.length||this.mention_roles.length){//currently mention_roles isn't implemented on the spacebar servers + console.log(this.mentions,this.mention_roles) + } + if(this.mentionsuser(this.localuser.user)){ + console.log(this); + } + } + canDelete(){ + return this.channel.hasPermission("MANAGE_MESSAGES")||this.author.id===this.localuser.user.id; + } + get channel(){ + return this.owner; + } + get guild(){ + return this.owner.guild; + } + get localuser(){ + return this.owner.localuser; + } + get info(){ + return this.owner.info; + } + messageevents(obj:HTMLDivElement){ + const func=Message.contextmenu.bind(obj,this); + this.div=obj; + Message.del.then(_=>{ + obj.removeEventListener("click",func); + this.div=null; + }) + obj.classList.add("messagediv"); + } + mentionsuser(userd:User|Member){ + if(userd instanceof User){ + return this.mentions.includes(userd); + }else if(userd instanceof Member){ + return this.mentions.includes(userd.user); + } + } + getimages(){ + const build=[]; + for(const thing of this.attachments){ + if(thing.content_type.startsWith('image/')){ + build.push(thing); + } + } + return build; + } + async edit(content){ + return await fetch(this.info.api.toString()+"/channels/"+this.channel.id+"/messages/"+this.id,{ + method: "PATCH", + headers: this.headers, + body:JSON.stringify({content:content}) + }); + } + delete(){ + fetch(`${this.info.api.toString()}/channels/${this.channel.id}/messages/${this.id}`,{ + headers:this.headers, + method:"DELETE", + }) + } + deleteEvent(){ + if(this.div){ + this.div.innerHTML=""; + this.div=null; + } + const index=this.channel.messages.indexOf(this); + this.channel.messages.splice(this.channel.messages.indexOf(this),1); + delete this.channel.messageids[this.id]; + const regen=this.channel.messages[index-1] + if(regen){ + regen.generateMessage(); + } + } + generateMessage(premessage:Message=null){ + if(!premessage){ + premessage=this.channel.messages[this.channel.messages.indexOf(this)+1]; + } + const div=this.div; + if(this===this.channel.replyingto){ + div.classList.add("replying"); + } + div.innerHTML=""; + const build = document.createElement('table'); + if(this.message_reference){ + const replyline=document.createElement("div"); + const line=document.createElement("hr"); + const minipfp=document.createElement("img") + minipfp.classList.add("replypfp"); + replyline.appendChild(line); + replyline.appendChild(minipfp); + const username=document.createElement("span"); + replyline.appendChild(username); + const reply=document.createElement("div"); + username.classList.add("username"); + + Member.resolve(this.author,this.guild).then(_=>{ + if(!_) {return}; + console.log(_.error); + if(_.error){ + username.textContent+="Error"; + alert("Should've gotten here") + const error=document.createElement("span"); + error.textContent="!"; + error.classList.add("membererror"); + username.after(error); + + return; + } + username.style.color=_.getColor(); + }).catch(_=>{ + console.log(_) + }); + + reply.classList.add("replytext"); + replyline.appendChild(reply); + const line2=document.createElement("hr"); + replyline.appendChild(line2); + line2.classList.add("reply"); + line.classList.add("startreply"); + replyline.classList.add("replyflex") + this.channel.getmessage(this.message_reference.message_id).then(message=>{ + const author=message.author; + reply.appendChild(markdown(message.content,{stdsize:true})); + minipfp.src=author.getpfpsrc() + author.profileclick(minipfp) + username.textContent=author.username; + author.profileclick(username) + }); + div.appendChild(replyline); + } + + this.messageevents(div); + build.classList.add("message"); + div.appendChild(build); + if({0:true,19:true}[this.type]||this.attachments.length!==0){ + const pfpRow = document.createElement('th'); + + let pfpparent, current + if(premessage!=null){ + pfpparent??=premessage; + let pfpparent2=pfpparent.all; + pfpparent2??=pfpparent; + const old=(new Date(pfpparent2.timestamp).getTime())/1000; + const newt=(new Date(this.timestamp).getTime())/1000; + current=(newt-old)>600; + } + const combine=(premessage?.author?.id!=this.author.id)||(current)||this.message_reference + if(combine){ + const pfp=this.author.buildpfp(); + this.author.profileclick(pfp); + pfpRow.appendChild(pfp); + }else{ + div["pfpparent"]=pfpparent; + } + pfpRow.classList.add("pfprow") + build.appendChild(pfpRow); + const text=document.createElement("th"); + + const texttxt=document.createElement("table"); + texttxt.classList.add("commentrow") + text.appendChild(texttxt); + if(combine){ + const username=document.createElement("span"); + username.classList.add("username") + this.author.profileclick(username); + Member.resolve(this.author,this.guild).then(_=>{ + if(!_) {return}; + if(_.error){ + const error=document.createElement("span"); + error.textContent="!"; + error.classList.add("membererror"); + username.after(error); + + return; + } + username.style.color=_.getColor(); + }) + username.textContent=this.author.username; + const userwrap=document.createElement("tr") + userwrap.appendChild(username) + if(this.author.bot){ + const username=document.createElement("span"); + username.classList.add("bot") + username.textContent="BOT"; + userwrap.appendChild(username) + } + const time=document.createElement("span"); + time.textContent=" "+formatTime(new Date(this.timestamp)); + time.classList.add("timestamp") + userwrap.appendChild(time); + + texttxt.appendChild(userwrap) + } + const messaged=markdown(this.content); + div["txt"]=messaged; + const messagedwrap=document.createElement("tr") + messagedwrap.appendChild(messaged) + texttxt.appendChild(messagedwrap) + + build.appendChild(text) + if(this.attachments.length){ + console.log(this.attachments) + const attatch = document.createElement("tr") + for(const thing of this.attachments){ + attatch.appendChild(thing.getHTML()) + } + messagedwrap.appendChild(attatch) + } + if(this.embeds.length){ + const embeds = document.createElement("tr") + for(const thing of this.embeds){ + embeds.appendChild(thing.generateHTML()); + } + messagedwrap.appendChild(embeds) + } + // + }else if(this.type===7){ + + const text=document.createElement("th"); + + const texttxt=document.createElement("table"); + text.appendChild(texttxt); + build.appendChild(text) + + const messaged=document.createElement("p"); + div["txt"]=messaged; + messaged.textContent="welcome: "+this.author.username; + const messagedwrap=document.createElement("tr") + messagedwrap.appendChild(messaged); + + const time=document.createElement("span"); + time.textContent=" "+formatTime(new Date(this.timestamp)); + time.classList.add("timestamp"); + messagedwrap.append(time); + + texttxt.appendChild(messagedwrap) + } + div["all"]=this; + return(div) + } + buildhtml(premessage:Message){ + if(this.div){console.error(`HTML for ${this} already exists, aborting`);return;} + //premessage??=messages.lastChild; + const div=document.createElement("div"); + this.div=div; + return this.generateMessage(premessage); + } + createunknown(fname,fsize,src){ + const div=document.createElement("table"); + div.classList.add("unknownfile"); + const nametr=document.createElement("tr"); + div.append(nametr); + const fileicon=document.createElement("td"); + nametr.append(fileicon); + fileicon.append("🗎"); + fileicon.classList.add("fileicon"); + fileicon.rowSpan=2; + const nametd=document.createElement("td"); + if(src){ + const a=document.createElement("a"); + a.href=src; + a.textContent=fname; + nametd.append(a); + }else{ + nametd.textContent=fname; + } + + nametd.classList.add("filename"); + nametr.append(nametd); + const sizetr=document.createElement("tr"); + const size=document.createElement("td"); + sizetr.append(size); + size.textContent="Size:"+this.filesizehuman(fsize); + size.classList.add("filesize"); + div.appendChild(sizetr) + return div; + } + filesizehuman(fsize){ + var i = fsize == 0 ? 0 : Math.floor(Math.log(fsize) / Math.log(1024)); + return +((fsize / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes'][i]; + } +} + +function formatTime(date) { + const now = new Date(); + const sameDay = date.getDate() === now.getDate() && + date.getMonth() === now.getMonth() && + date.getFullYear() === now.getFullYear(); + + const yesterday = new Date(now); + yesterday.setDate(now.getDate() - 1); + const isYesterday = date.getDate() === yesterday.getDate() && + date.getMonth() === yesterday.getMonth() && + date.getFullYear() === yesterday.getFullYear(); + + const formatTime = date => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + + if (sameDay) { + return `Today at ${formatTime(date)}`; + } else if (isYesterday) { + return `Yesterday at ${formatTime(date)}`; + } else { + return `${date.toLocaleDateString()} at ${formatTime(date)}`; + } +} +Message.setup(); +export { Message }; diff --git a/webpage/permissions.js b/webpage/permissions.ts similarity index 87% rename from webpage/permissions.js rename to webpage/permissions.ts index d844fce..9144527 100644 --- a/webpage/permissions.js +++ b/webpage/permissions.ts @@ -1,18 +1,24 @@ -class permissions{ - constructor(b){ - this.permissions=BigInt(b); +export {Permissions}; +class Permissions{ + allow:bigint; + deny:bigint; + constructor(allow:string,deny:string=""){ + this.allow=BigInt(allow); + this.deny=BigInt(deny); } - getPermisionbit(b){ - return Boolean((this.permissions>>BigInt(b))&1n); + getPermisionbit(b:number,big:bigint) : boolean{ + return Boolean((big>>BigInt(b))&1n); } - setPermisionbit(b,state){ + setPermisionbit(b:number,state:boolean,big:bigint) : bigint{ const bit=1n<{ - text=await data.clone().text(); + const text=await data.clone().text(); console.log(text,lastcache) if(lastcache!==text){ deleteoldcache(); @@ -80,8 +80,8 @@ async function getfile(event){ return responseFromNetwork; }catch(e){console.error(e)} } -self.addEventListener('fetch', (event) => { +self.addEventListener('fetch', (event:any) => { try{ - event.respondWith(getfile(event)); + event.respondWith(getfile(event)); }catch(e){console.error(e)} }) diff --git a/webpage/style.css b/webpage/style.css index 45c7622..0aed969 100644 --- a/webpage/style.css +++ b/webpage/style.css @@ -8,6 +8,10 @@ body { margin: 0; padding: 0; } +video{ + max-width: 3in; + max-height: 4in; +} .collapse{ width:0px !important; overflow: hidden; @@ -57,7 +61,12 @@ th { .messagediv:hover { background-color: var(--message-bg-hover); } - +.messagediv{ + overflow: hidden; + max-width:100%; + /* width: 9%; */ + /* display: inline-block; */ +} pre { background-color: var(--code-bg); width: 100%; @@ -138,7 +147,7 @@ h2 { .pfp { border-radius: 50%; - width: .5in; + width: 0.5in; height: .5in; user-select: none; cursor: pointer; @@ -147,11 +156,14 @@ h2 { .servericon { transition: border-radius .2s; position: relative; + margin: .0in 0in 0.03in 0; } .servericon:hover { border-radius: 30%; } - +.serveropen .servericon{ + border-radius: 30%; +} .contextbutton:hover { background-color: var(--primary-bg); } @@ -172,6 +184,7 @@ h2 { vertical-align: top; height: 100dvh; overflow-x: hidden; + padding: 0.02in .05in 0.0in .05in; } #servers::-webkit-scrollbar { @@ -225,10 +238,11 @@ img { height: 100%; width: 100%; display: inline-block; + max-width: 100%; } #messages { - width: 100%; + max-width: 100%; } p { @@ -390,6 +404,7 @@ p { /* Move down out of view */ opacity: 0; /* Fade out */ + display: none; } @keyframes fade { @@ -448,15 +463,25 @@ p { } .replyflex { + overflow: hidden; display: flex; align-items: center; + max-width: 100%; + flex-direction: row; + /* width: 00; */ + flex-wrap: nowrap; + justify-content: space-between; } .reply { - display: inline-block; + display: inline; vertical-align: middle; flex-grow: 1; border-color: var(--reply-border); + /* flex: 1; */ + min-width: 0px; + /* max-width: 0px; */ + /* grid-column-end: 1; */ } .startreply { @@ -477,8 +502,20 @@ p { } .replytext { - padding: .05in; + padding: 0 .05in; color: var(--reply-text); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + max-width: fit-content; + /* display: block; */ + /* flex-grow: 1; */ + flex: 1 1 auto; + width: fit-content; + min-width: 0; + /* display: inline-block !important; */ + width: 25vw; + grid-column: 2; } ::-webkit-scrollbar { @@ -528,8 +565,12 @@ textarea { .channel { user-select: none; cursor: pointer; + transition: font-weight .1s; +} +.viewChannel{ + font-weight:900; + background: color-mix(in srgb, var(--channel-hover) 60%, transparent) } - #servername { margin-top: .1in; margin-bottom: .1in; @@ -599,9 +640,12 @@ textarea { #settings { cursor: pointer; user-select: none; - border-radius: .1in; + border-radius: .3in; transition: background 1s; text-align: center; + font-size: .25in; + width: .3in; + height: .3in; } #settings:hover { @@ -762,6 +806,9 @@ input[type="checkbox"] { span { word-wrap: break-word; word-break: break-word; + /* overflow: clip; */ + /* width: 2in; */ + /* max-width: 100%; */ } #loading { @@ -811,17 +858,24 @@ span { background-color: var(--primary-text); height: .075in; width: .075in; - transition: transform .2s, background .2s, height .2s, width .2s; - transform: translate(-.20in, .2in); + transition: transform .2s, background .2s, height .3s, width .2s; + transform: translate(-.14in, .2in); z-index: 10; - border-radius: 50%; + border-radius: .2in; border: solid; border-width: .02in; border-color: var(--black); } - +.servernoti:hover .unread{ + transform: translate(-.1in, 0.15in); + height:.2in; +} +.serveropen .unread{ + transform: translate(-.1in, 0.1in) !important; + height:.3in !important; +} .notiunread { - transform: translate(0, .2in); + transform: translate(-.1in, .2in); } .pinged { @@ -975,3 +1029,57 @@ span { .spaceright{ margin-right:.1in; } +.membererror{ + display:inline-block; + background:#656500; + height:.15in; + width:.15in; + border-radius:.1in; + text-align:center; + border:solid black .03in; + margin-left:.025in; +} +.replyflex span{ + /* display: inline-block; */ + text-overflow:ellipsis; + overflow: hidden; + max-width: 100%; +} +.controls{ + position:absolute; + top:0px; + right:0px; +} +.containedFile{ + position:relative !important; + width: fit-content; + box-shadow:.02in .02in .05in black; +} +#replybox{ + display:flex; + background:var(--reply-bg); + height:.25in; + transition: height .2s, padding .2s; + border-radius:.1in .1in 0 0; + padding-left:.05in; + justify-content: space-between; + flex-direction: row; + align-items: flex-end; + padding-top: .05in; +} +#replybox span{ + align-self:stretch; +} +.cancelReply{ + align-self:flex-start; + color:red; + margin-right: .1in; + height: .25in; + width: .25in; + text-align: center; + padding: 0in; +} +#replybox.hideReplyBox{ + height: 0in; + padding: 0in; +} diff --git a/webpage/user.js b/webpage/user.js deleted file mode 100644 index 0d45200..0000000 --- a/webpage/user.js +++ /dev/null @@ -1,61 +0,0 @@ -//const usercache={}; -class user{ - static userids={}; - static checkuser(userjson){ - if(user.userids[userjson.id]){ - return user.userids[userjson.id]; - }else{ - const tempuser=new user(userjson,true) - user.userids[userjson.id]=tempuser; - return tempuser; - } - } - constructor(userjson,dontclone=false){ - if(dontclone){ - for(const thing of Object.keys(userjson)){ - this[thing]=userjson[thing]; - } - this.hypotheticalpfp=false; - }else{ - return user.checkuser(userjson); - } - } - async resolvemember(guild){ - await member.resolve(this,guild); - } - buildpfp(){ - const pfp=document.createElement('img'); - pfp.src=this.getpfpsrc(this.id,this.avatar); - pfp.classList.add("pfp"); - pfp.classList.add("userid:"+this.id); - return pfp; - } - userupdate(json){ - if(json.avatar!==this.avatar){ - console.log - this.changepfp(json.avatar); - } - } - changepfp(update){ - this.avatar=update; - this.hypotheticalpfp=false; - const src=this.getpfpsrc(); - console.log(src) - for(thing of document.getElementsByClassName("userid:"+this.id)){ - thing.src=src; - } - } - getpfpsrc(){ - if(this.hypotheticalpfp){ - return this.avatar; - } - if(this.avatar!=null){ - return info.cdn.toString()+"avatars/"+this.id+"/"+this.avatar+".png"; - }else{ - return info.cdn.toString()+"embed/avatars/3.png"; - } - } - createjankpromises(){ - new Promise(_=>{}) - } -} diff --git a/webpage/user.ts b/webpage/user.ts new file mode 100644 index 0000000..530b9c7 --- /dev/null +++ b/webpage/user.ts @@ -0,0 +1,143 @@ +//const usercache={}; +import {Member} from "./member.js"; +import {markdown} from "./markdown.js"; +import {Contextmenu} from "./contextmenu.js"; +import {Localuser} from "./localuser.js"; +import {Guild} from "./guild.js"; +class User{ + static userids={}; + owner:Localuser; + hypotheticalpfp:boolean; + id:string; + avatar:string; + username:string; + bio:string; + discriminator:string; + pronouns:string; + bot:boolean; + static checkuser(userjson,owner:Localuser){ + if(User.userids[userjson.id]){ + return User.userids[userjson.id]; + }else{ + const tempuser=new User(userjson,owner,true) + User.userids[userjson.id]=tempuser; + return tempuser; + } + } + get info(){ + return this.owner.info; + } + get localuser(){ + return this.owner; + } + constructor(userjson,owner:Localuser,dontclone=false){ + this.owner=owner; + if(!owner){console.error("missing localuser")} + if(dontclone){ + for(const thing of Object.keys(userjson)){ + this[thing]=userjson[thing]; + } + this.hypotheticalpfp=false; + }else{ + return User.checkuser(userjson,owner); + } + } + async resolvemember(guild:Guild){ + await Member.resolve(this,guild); + } + buildpfp(){ + const pfp=document.createElement('img'); + pfp.src=this.getpfpsrc(); + pfp.classList.add("pfp"); + pfp.classList.add("userid:"+this.id); + return pfp; + } + userupdate(json){ + if(json.avatar!==this.avatar){ + console.log + this.changepfp(json.avatar); + } + } + changepfp(update:string){ + this.avatar=update; + this.hypotheticalpfp=false; + const src=this.getpfpsrc(); + console.log(src) + for(const thing of document.getElementsByClassName("userid:"+this.id)){ + (thing as HTMLImageElement).src=src; + } + } + getpfpsrc(){ + if(this.hypotheticalpfp){ + return this.avatar; + } + if(this.avatar!=null){ + return this.info.cdn.toString()+"avatars/"+this.id+"/"+this.avatar+".png"; + }else{ + return this.info.cdn.toString()+"embed/avatars/3.png"; + } + } + createjankpromises(){ + new Promise(_=>{}) + } + buildprofile(x:number,y:number){ + if(Contextmenu.currentmenu!=""){ + Contextmenu.currentmenu.remove(); + } + + + const div=document.createElement("table"); + if(x!==-1){ + div.style.left=x+"px"; + div.style.top=y+"px"; + div.classList.add("profile"); + }else{ + div.classList.add("hypoprofile"); + } + + { + const pfp=this.buildpfp(); + const pfprow=document.createElement("tr"); + div.appendChild(pfprow); + pfprow.appendChild(pfp); + } + { + const userbody=document.createElement("tr"); + userbody.classList.add("infosection"); + div.appendChild(userbody); + const usernamehtml=document.createElement("h2"); + usernamehtml.textContent=this.username; + userbody.appendChild(usernamehtml); + + const discrimatorhtml=document.createElement("h3"); + discrimatorhtml.classList.add("tag"); + discrimatorhtml.textContent=this.username+"#"+this.discriminator; + userbody.appendChild(discrimatorhtml) + + const pronounshtml=document.createElement("p"); + pronounshtml.textContent=this.pronouns; + pronounshtml.classList.add("pronouns"); + userbody.appendChild(pronounshtml) + + const rule=document.createElement("hr"); + userbody.appendChild(rule); + const biohtml=markdown(this.bio); + userbody.appendChild(biohtml); + } + console.log(div); + + if(x!==-1){ + Contextmenu.currentmenu=div; + document.body.appendChild(div) + Contextmenu.keepOnScreen(div); + } + return div; + } + profileclick(obj:HTMLElement){ + obj.onclick=e=>{ + this.buildprofile(e.clientX,e.clientY); + e.stopPropagation(); + } + } +} +export {User};