diff --git a/.dist/channel.js b/.dist/channel.js index e6e4641..5c0e566 100644 --- a/.dist/channel.js +++ b/.dist/channel.js @@ -60,6 +60,62 @@ class Channel { this.contextmenu.addbutton("Edit channel", function () { this.editChannel(this); }, null, _ => { return _.isAdmin(); }); + this.contextmenu.addbutton("Make invite", function () { + this.createInvite(); + }, null, (_) => { + return _.hasPermission("CREATE_INSTANT_INVITE") && _.type !== 4; + }); + } + createInvite() { + const div = document.createElement("div"); + div.classList.add("invitediv"); + const text = document.createElement("span"); + div.append(text); + let uses = 0; + let expires = 1800; + const copycontainer = document.createElement("div"); + copycontainer.classList.add("copycontainer"); + const copy = document.createElement("img"); + copy.src = "/icons/copy.svg"; + copy.classList.add("copybutton", "svgtheme"); + copycontainer.append(copy); + copycontainer.onclick = _ => { + navigator.clipboard.writeText(text.textContent); + }; + div.append(copycontainer); + const update = () => { + fetch(`${this.info.api}/channels/${this.id}/invites`, { + method: "POST", + headers: this.headers, + body: JSON.stringify({ + flags: 0, + target_type: null, + target_user_id: null, + max_age: expires + "", + max_uses: uses, + temporary: uses !== 0 + }) + }).then(_ => _.json()).then(json => { + const params = new URLSearchParams(""); + params.set("instance", this.info.wellknown); + const encoded = params.toString(); + text.textContent = `${window.location.protocol}//${window.location.host}/invite/${json.code}?${encoded}`; + }); + }; + update(); + new Dialog(["vdiv", + ["title", "Invite people"], + ["text", `to #${this.name} in ${this.guild.properties.name}`], + ["select", "Expire after:", ["30 Minutes", "1 Hour", "6 Hours", "12 Hours", "1 Day", "7 Days", "30 Days", "Never"], function (e) { + expires = [1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0][e.srcElement.selectedIndex]; + update(); + }, 0], + ["select", "Max uses:", ["No limit", "1 use", "5 uses", "10 uses", "25 uses", "50 uses", "100 uses"], function (e) { + uses = [0, 1, 5, 10, 25, 50, 100][e.srcElement.selectedIndex]; + update(); + }, 0], + ["html", div] + ]).show(); } generateSettings() { this.sortPerms(); diff --git a/.dist/invite.js b/.dist/invite.js new file mode 100644 index 0000000..837fcd8 --- /dev/null +++ b/.dist/invite.js @@ -0,0 +1,116 @@ +import { getBulkUsers, getapiurls } from "./login.js"; +const users = getBulkUsers(); +const well = new URLSearchParams(window.location.search).get("instance"); +const joinable = []; +for (const thing in users.users) { + const user = users.users[thing]; + if (user.serverurls.wellknown.includes(well)) { + joinable.push(user); + } + console.log(users.users[thing]); +} +let urls; +if (!joinable.length) { + const out = await getapiurls(well); + if (out) { + urls = out; + for (const thing in users.users) { + const user = users.users[thing]; + if (user.serverurls.api.includes(out.api)) { + joinable.push(user); + } + console.log(users.users[thing]); + } + } + else { + throw Error("someone needs to handle the case where the servers don't exist"); + } +} +else { + urls = joinable[0].serverurls; +} +if (!joinable.length) { + document.getElementById("AcceptInvite").textContent = "Create an account to accept the invite"; +} +const code = window.location.pathname.split("/")[2]; +let guildinfo; +fetch(`${urls.api}/invites/${code}`, { + method: "GET" +}).then(_ => _.json()).then(json => { + const guildjson = json.guild; + guildinfo = guildjson; + document.getElementById("invitename").textContent = guildjson.name; + document.getElementById("invitedescription").textContent = + `${json.inviter.username} invited you to join ${guildjson.name}`; + if (guildjson.icon) { + const img = document.createElement("img"); + img.src = `${urls.cdn}/icons/${guildjson.id}/${guildjson.icon}.png`; + img.classList.add("inviteGuild"); + document.getElementById("inviteimg").append(img); + } + else { + const txt = guildjson.name.replace(/'s /g, " ").replace(/\w+/g, word => word[0]).replace(/\s/g, ""); + const div = document.createElement("div"); + div.textContent = txt; + div.classList.add("inviteGuild"); + document.getElementById("inviteimg").append(div); + } +}); +function showAccounts() { + const table = document.createElement("dialog"); + for (const thing of Object.values(joinable)) { + const specialuser = thing; + console.log(specialuser.pfpsrc); + const userinfo = document.createElement("div"); + userinfo.classList.add("flexltr", "switchtable"); + const pfp = document.createElement("img"); + userinfo.append(pfp); + const user = document.createElement("div"); + userinfo.append(user); + user.append(specialuser.username); + user.append(document.createElement("br")); + const span = document.createElement("span"); + span.textContent = specialuser.serverurls.wellknown.replace("https://", "").replace("http://", ""); + user.append(span); + user.classList.add("userinfo"); + span.classList.add("serverURL"); + pfp.src = specialuser.pfpsrc; + pfp.classList.add("pfp"); + table.append(userinfo); + userinfo.addEventListener("click", _ => { + console.log(thing); + fetch(`${urls.api}/invites/${code}`, { + method: "POST", + headers: { + Authorization: thing.token + } + }).then(_ => { + users["currentuser"] = specialuser.uid; + localStorage.setItem("userinfos", JSON.stringify(users)); + window.location.href = "/channels/" + guildinfo.id; + }); + }); + } + { + const td = document.createElement("div"); + td.classList.add("switchtable"); + td.append("Login or create an account ⇌"); + td.addEventListener("click", _ => { + const l = new URLSearchParams("?"); + l.set("goback", window.location.href); + l.set("instance", well); + window.location.href = "/login?" + l.toString(); + }); + if (!joinable.length) { + const l = new URLSearchParams("?"); + l.set("goback", window.location.href); + l.set("instance", well); + window.location.href = "/login?" + l.toString(); + } + table.append(td); + } + table.classList.add("accountSwitcher"); + console.log(table); + document.body.append(table); +} +document.getElementById("AcceptInvite").addEventListener("click", showAccounts); diff --git a/.dist/login.js b/.dist/login.js index be2f697..4b82337 100644 --- a/.dist/login.js +++ b/.dist/login.js @@ -1,11 +1,12 @@ import { Dialog } from "./dialog.js"; const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); -export { mobile, getBulkUsers, getBulkInfo, setTheme, Specialuser }; +export { mobile, getBulkUsers, getBulkInfo, setTheme, Specialuser, getapiurls, adduser }; function setTheme() { - const name = localStorage.getItem("theme"); + let name = localStorage.getItem("theme"); if (!name) { document.body.className = "Dark-theme"; localStorage.setItem("theme", "Dark"); + name = "Dark"; } document.body.className = name + "-theme"; } @@ -116,13 +117,38 @@ function adduser(user) { info.users[user.uid] = user; info.currentuser = user.uid; localStorage.setItem("userinfos", JSON.stringify(info)); + return user; } const instancein = document.getElementById("instancein"); let timeout; let instanceinfo; +async function getapiurls(str) { + if (str[str.length - 1] !== "/") { + str += "/"; + } + let api; + try { + const info = await fetch(`${str}/.well-known/spacebar`).then((x) => x.json()); + api = info.api; + } + catch { + return false; + } + const url = new URL(api); + try { + const info = await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then((x) => x.json()); + return { + api: info.apiEndpoint, + gateway: info.gateway, + cdn: info.cdn, + wellknown: str, + }; + } + catch { + } +} async function checkInstance(e) { const verify = document.getElementById("verify"); - ; try { verify.textContent = "Checking Instance"; const instanceinfo = await setInstance(instancein.value); @@ -219,16 +245,30 @@ async function login(username, password, captcha) { alert(response.message); } else { - adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: username, token: response.token }); - window.location.href = '/channels/@me'; + console.warn(response); + adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: username, token: response.token }).username = username; + const redir = new URLSearchParams(window.location.search).get("goback"); + if (redir) { + window.location.href = redir; + } + else { + window.location.href = '/channels/@me'; + } } }); }]]).show(); } else { - adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: username, token: response.token }); - window.location.href = '/channels/@me'; - return response.token; + console.warn(response); + adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: username, token: response.token }).username = username; + const redir = new URLSearchParams(window.location.search).get("goback"); + if (redir) { + window.location.href = redir; + } + else { + window.location.href = '/channels/@me'; + } + return ""; } } }); @@ -296,4 +336,14 @@ if ("serviceWorker" in navigator){ }) } */ +const switchurl = document.getElementById("switch"); +if (switchurl) { + switchurl.href += window.location.search; + const instance = new URLSearchParams(window.location.search).get("instance"); + console.log(instance); + if (instance) { + instancein.value = instance; + checkInstance(""); + } +} export { checkInstance }; diff --git a/.dist/register.js b/.dist/register.js index 176197c..aa351a8 100644 --- a/.dist/register.js +++ b/.dist/register.js @@ -1,4 +1,4 @@ -import { checkInstance } from "./login.js"; +import { checkInstance, adduser } from "./login.js"; if (document.getElementById("register")) { document.getElementById("register").addEventListener("submit", registertry); } @@ -67,8 +67,15 @@ async function registertry(e) { } } else { + adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: email, token: e.token }).username = username; localStorage.setItem("token", e.token); - window.location.href = '/channels/@me'; + const redir = new URLSearchParams(window.location.search).get("goback"); + if (redir) { + window.location.href = redir; + } + else { + window.location.href = '/channels/@me'; + } } }); }); diff --git a/.dist/user.js b/.dist/user.js index ea0a61e..2ff96dc 100644 --- a/.dist/user.js +++ b/.dist/user.js @@ -46,7 +46,7 @@ class User { static contextmenu = new Contextmenu("User Menu"); static setUpContextMenu() { this.contextmenu.addbutton("Copy user id", function () { - navigator.clipboard.writeText(this.id.id); + navigator.clipboard.writeText(this.id); }); this.contextmenu.addbutton("Message user", function () { fetch(this.info.api + "/users/@me/channels", { method: "POST", diff --git a/index.js b/index.js index 333c123..de42afd 100755 --- a/index.js +++ b/index.js @@ -18,11 +18,100 @@ app.use("/getupdates",(req, res) => { res.send(out.mtimeMs+""); }); let debugging=true;//Do not turn this off, the service worker is all kinds of jank as is, it'll really mess your day up if you disable this -app.use('/', (req, res) => { +function isembed(str){ + return str===("Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)")||str===("Mozilla/5.0 (compatible; Spacebar/1.0; +https://github.com/spacebarchat/server)"); +} +async function getapiurls(str){ + if(str[str.length-1]!=="/"){ + str+="/" + } + let api; + try{ + const info=await fetch(`${str}/.well-known/spacebar`).then((x) => x.json()); + api=info.api; + }catch{ + return false + } + const url = new URL(api); + try{ + + const info=await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then((x) => x.json()); + return { + api: info.apiEndpoint, + gateway: info.gateway, + cdn: info.cdn, + wellknown: str, + }; + }catch{ + return false; + } +} + +async function inviteres(req,res){ + //console.log(req.rawHeaders); + try{ + let embed=false; + for(const i in req.rawHeaders){ + if(req.rawHeaders[i]==="User-Agent"){ + embed=isembed(req.rawHeaders[1+ +i]); + } + } + + if(!embed){return false}; + const code=req.path.split("/")[2]; + let title=""; + let description=""; + let icon=""; + const urls=await getapiurls(req.query.instance); + await fetch(`${urls.api}/invites/${code}`,{ + method:"GET" + }).then(_=>_.json()).then(json=>{ + title=json.guild.name; + description=json.inviter.username+" Has invited you to "+json.guild.name+(json.guild.description?json.guild.description+"\n":""); + if(json.guild.icon){ + icon=`${urls.cdn}/icons/${json.guild.id}/${json.guild.icon}.png`; + } + }); + + function htmlEnc(s) {//https://stackoverflow.com/a/11561642 + return s.replaceAll(/&/g, '&') + .replaceAll(//g, '>') + .replaceAll(/'/g, ''') + .replaceAll(/"/g, '"'); + } + function strEscape(s){ + return JSON.stringify(s); + } + html=` +
+ +Someone invited you to Server Name
+ +