From 1502dbec17ac30940fa510794ea98ade59ecde15 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Fri, 6 Sep 2024 15:43:11 -0500 Subject: [PATCH] get invite embeds working --- .dist/embed.js | 135 ++++++++++++++++++++++++++++++++++++++++++ .dist/guild.js | 55 ++++++++++++----- .dist/login.js | 13 +++-- webpage/embed.ts | 136 ++++++++++++++++++++++++++++++++++++++++++- webpage/guild.ts | 55 +++++++++++------ webpage/jsontypes.ts | 70 +++++++++++++++++++++- webpage/login.ts | 20 +++++-- webpage/style.css | 62 +++++++++++++++++++- 8 files changed, 500 insertions(+), 46 deletions(-) diff --git a/.dist/embed.js b/.dist/embed.js index d5ef133..f4ffb4d 100644 --- a/.dist/embed.js +++ b/.dist/embed.js @@ -1,5 +1,7 @@ import { Dialog } from "./dialog.js"; import { MarkDown } from "./markdown.js"; +import { getapiurls, getInstances } from "./login.js"; +import { Guild } from "./guild.js"; class Embed { type; owner; @@ -8,8 +10,40 @@ class Embed { this.type = this.getType(json); this.owner = owner; this.json = json; + console.log(this); } getType(json) { + const instances = getInstances(); + if (instances && json.type === "link" && json.url && URL.canParse(json.url)) { + const Url = new URL(json.url); + for (const instance of instances) { + if (instance.url && URL.canParse(instance.url)) { + const IUrl = new URL(instance.url); + const params = new URLSearchParams(Url.search); + let host; + if (params.has("instance")) { + const url = params.get("instance"); + if (URL.canParse(url)) { + host = new URL(url).host; + } + else { + host = Url.host; + } + } + else { + host = Url.host; + } + if (IUrl.host === host) { + const code = Url.pathname.split("/")[Url.pathname.split("/").length - 1]; + json.invite = { + url: instance.url, + code + }; + return "invite"; + } + } + } + } return json.type || "rich"; } generateHTML() { @@ -18,6 +52,8 @@ class Embed { return this.generateRich(); case "image": return this.generateImage(); + case "invite": + return this.generateInvite(); case "link": return this.generateLink(); case "video": @@ -185,6 +221,105 @@ class Embed { table.append(bottomtr); return table; } + invcache; + generateInvite() { + if (this.invcache && (!this.json.invite || !this.localuser)) { + return this.generateLink(); + } + const div = document.createElement("div"); + div.classList.add("embed", "inviteEmbed", "flexttb"); + const json1 = this.json.invite; + (async () => { + let json; + let info; + if (!this.invcache) { + if (!json1) { + div.append(this.generateLink()); + return; + } + const tempinfo = await getapiurls(json1.url); + ; + if (!tempinfo) { + div.append(this.generateLink()); + return; + } + info = tempinfo; + const res = await fetch(info.api + "/invites/" + json1.code); + if (!res.ok) { + div.append(this.generateLink()); + } + json = await res.json(); + this.invcache = [json, info]; + } + else { + [json, info] = this.invcache; + } + if (!json) { + div.append(this.generateLink()); + return; + } + if (json.guild.banner) { + const banner = document.createElement("img"); + banner.src = this.localuser.info.cdn + "/icons/" + json.guild.id + "/" + json.guild.banner + ".png?size=256"; + banner.classList.add("banner"); + div.append(banner); + } + const guild = json.guild; + guild.info = info; + const icon = Guild.generateGuildIcon(guild); + const iconrow = document.createElement("div"); + iconrow.classList.add("flexltr", "flexstart"); + iconrow.append(icon); + { + const guildinfo = document.createElement("div"); + guildinfo.classList.add("flexttb", "invguildinfo"); + const name = document.createElement("b"); + name.textContent = guild.name; + guildinfo.append(name); + const members = document.createElement("span"); + members.innerText = "#" + json.channel.name + " • Members: " + guild.member_count; + guildinfo.append(members); + members.classList.add("subtext"); + iconrow.append(guildinfo); + } + div.append(iconrow); + const h2 = document.createElement("h2"); + h2.textContent = `You've been invited by ${json.inviter.username}`; + div.append(h2); + const button = document.createElement("button"); + button.textContent = "Accept"; + if (this.localuser.info.api.startsWith(info.api)) { + if (this.localuser.guildids.has(guild.id)) { + button.textContent = "Already joined"; + button.disabled = true; + } + } + button.classList.add("acceptinvbutton"); + div.append(button); + button.onclick = _ => { + if (this.localuser.info.api.startsWith(info.api)) { + fetch(this.localuser.info.api + "/invites/" + json.code, { + method: "POST", + headers: this.localuser.headers, + }).then(r => r.json()).then(_ => { + if (_.message) { + alert(_.message); + } + }); + } + else { + if (this.json.invite) { + const params = new URLSearchParams(""); + params.set("instance", this.json.invite.url); + const encoded = params.toString(); + const url = `${location.origin}/invite/${this.json.invite.code}?${encoded}`; + window.open(url, "_blank"); + } + } + }; + })(); + return div; + } generateArticle() { const colordiv = document.createElement("div"); colordiv.style.backgroundColor = "#000000"; diff --git a/.dist/guild.js b/.dist/guild.js index 27d3b9f..89a48c5 100644 --- a/.dist/guild.js +++ b/.dist/guild.js @@ -264,39 +264,62 @@ class Guild extends SnowFlake { return a.position - b.position; }); } - generateGuildIcon() { + static generateGuildIcon(guild) { const divy = document.createElement("div"); divy.classList.add("servernoti"); const noti = document.createElement("div"); noti.classList.add("unread"); divy.append(noti); - this.localuser.guildhtml.set(this.id, divy); - if (this.properties.icon != null) { + if (guild instanceof Guild) { + guild.localuser.guildhtml.set(guild.id, divy); + } + let icon; + if (guild instanceof Guild) { + icon = guild.properties.icon; + } + else { + icon = guild.icon; + } + if (icon !== null) { const img = document.createElement("img"); img.classList.add("pfp", "servericon"); - img.src = this.info.cdn + "/icons/" + this.properties.id + "/" + this.properties.icon + ".png"; + img.src = guild.info.cdn + "/icons/" + guild.id + "/" + icon + ".png"; divy.appendChild(img); - img.onclick = () => { - console.log(this.loadGuild); - this.loadGuild(); - this.loadChannel(); - }; - Guild.contextmenu.bindContextmenu(img, this, undefined); + if (guild instanceof Guild) { + img.onclick = () => { + console.log(guild.loadGuild); + guild.loadGuild(); + guild.loadChannel(); + }; + Guild.contextmenu.bindContextmenu(img, guild, undefined); + } } else { const div = document.createElement("div"); - const build = this.properties.name.replace(/'s /g, " ").replace(/\w+/g, word => word[0]).replace(/\s/g, ""); + let name; + if (guild instanceof Guild) { + name = guild.properties.name; + } + else { + name = guild.name; + } + const build = name.replace(/'s /g, " ").replace(/\w+/g, word => word[0]).replace(/\s/g, ""); div.textContent = build; div.classList.add("blankserver", "servericon"); divy.appendChild(div); - div.onclick = () => { - this.loadGuild(); - this.loadChannel(); - }; - Guild.contextmenu.bindContextmenu(div, this, undefined); + if (guild instanceof Guild) { + div.onclick = () => { + guild.loadGuild(); + guild.loadChannel(); + }; + Guild.contextmenu.bindContextmenu(div, guild, undefined); + } } return divy; } + generateGuildIcon() { + return Guild.generateGuildIcon(this); + } confirmDelete() { let confirmname = ""; const full = new Dialog([ diff --git a/.dist/login.js b/.dist/login.js index 7f869f6..43c25a8 100644 --- a/.dist/login.js +++ b/.dist/login.js @@ -8,6 +8,7 @@ function setTheme() { } document.body.className = name + "-theme"; } +let instances; setTheme(); function getBulkUsers() { const json = getBulkInfo(); @@ -418,8 +419,12 @@ trimswitcher(); export { mobile, getBulkUsers, getBulkInfo, setTheme, Specialuser, getapiurls, adduser }; const datalist = document.getElementById("instances"); console.warn(datalist); -if (datalist) { - fetch("/instances.json").then(_ => _.json()).then((json) => { +export function getInstances() { + return instances; +} +fetch("/instances.json").then(_ => _.json()).then((json) => { + instances = json; + if (datalist) { console.warn(json); if (instancein && instancein.value === "") { instancein.value = json[0].name; @@ -452,5 +457,5 @@ if (datalist) { datalist.append(option); } checkInstance(""); - }); -} + } +}); diff --git a/webpage/embed.ts b/webpage/embed.ts index 437dbb5..6010ba0 100644 --- a/webpage/embed.ts +++ b/webpage/embed.ts @@ -1,7 +1,9 @@ import{Dialog}from"./dialog.js"; import{Message}from"./message.js"; import{MarkDown}from"./markdown.js"; -import{ embedjson }from"./jsontypes.js"; +import{ embedjson,guildjson, invitejson }from"./jsontypes.js"; +import { getapiurls, getInstances } from "./login.js"; +import { Guild } from "./guild.js"; class Embed{ type:string; @@ -11,8 +13,38 @@ class Embed{ this.type=this.getType(json); this.owner=owner; this.json=json; + console.log(this); } getType(json:embedjson){ + const instances=getInstances(); + if(instances&&json.type==="link"&&json.url&&URL.canParse(json.url)){ + const Url=new URL(json.url); + for(const instance of instances){ + if(instance.url&&URL.canParse(instance.url)){ + const IUrl=new URL(instance.url); + const params=new URLSearchParams(Url.search); + let host:string; + if(params.has("instance")){ + const url=params.get("instance") as string; + if(URL.canParse(url)){ + host=new URL(url).host; + }else{ + host=Url.host; + } + }else{ + host=Url.host; + } + if(IUrl.host===host){ + const code=Url.pathname.split("/")[Url.pathname.split("/").length-1]; + json.invite={ + url:instance.url, + code + } + return "invite"; + } + } + } + } return json.type||"rich"; } generateHTML(){ @@ -21,6 +53,8 @@ class Embed{ return this.generateRich(); case"image": return this.generateImage(); + case"invite": + return this.generateInvite(); case"link": return this.generateLink(); case "video": @@ -192,6 +226,106 @@ class Embed{ table.append(bottomtr); return table; } + invcache:[invitejson,{cdn:string,api:string}]|undefined; + generateInvite(){ + if(this.invcache&&(!this.json.invite||!this.localuser)){ + return this.generateLink(); + } + const div=document.createElement("div"); + div.classList.add("embed","inviteEmbed","flexttb"); + const json1=this.json.invite; + (async ()=>{ + let json:invitejson; + let info:{cdn:string,api:string}; + if(!this.invcache){ + if(!json1){ + div.append(this.generateLink()); + return; + } + const tempinfo=await getapiurls(json1.url);; + + if(!tempinfo){ + div.append(this.generateLink()); + return; + } + info=tempinfo; + const res=await fetch(info.api+"/invites/"+json1.code) + if(!res.ok){ + div.append(this.generateLink()); + } + json=await res.json() as invitejson; + this.invcache=[json,info]; + }else{ + [json,info]=this.invcache; + } + if(!json){ + div.append(this.generateLink()); + return; + } + if(json.guild.banner){ + const banner=document.createElement("img"); + banner.src=this.localuser.info.cdn+"/icons/"+json.guild.id+"/"+json.guild.banner+".png?size=256"; + banner.classList.add("banner"); + div.append(banner); + } + const guild:invitejson["guild"] & {info?:{cdn:string}}=json.guild; + guild.info=info; + const icon=Guild.generateGuildIcon(guild as invitejson["guild"] & {info:{cdn:string}}) + const iconrow=document.createElement("div"); + iconrow.classList.add("flexltr","flexstart"); + iconrow.append(icon); + { + const guildinfo=document.createElement("div"); + guildinfo.classList.add("flexttb","invguildinfo"); + const name=document.createElement("b"); + name.textContent=guild.name; + guildinfo.append(name); + + + const members=document.createElement("span"); + members.innerText="#"+json.channel.name+" • Members: "+guild.member_count + guildinfo.append(members); + members.classList.add("subtext"); + iconrow.append(guildinfo); + } + + div.append(iconrow); + const h2=document.createElement("h2"); + h2.textContent=`You've been invited by ${json.inviter.username}`; + div.append(h2); + const button=document.createElement("button"); + button.textContent="Accept"; + if(this.localuser.info.api.startsWith(info.api)){ + if(this.localuser.guildids.has(guild.id)){ + button.textContent="Already joined"; + button.disabled=true; + } + } + button.classList.add("acceptinvbutton"); + div.append(button); + button.onclick=_=>{ + if(this.localuser.info.api.startsWith(info.api)){ + fetch(this.localuser.info.api+"/invites/"+json.code,{ + method: "POST", + headers: this.localuser.headers, + }).then(r=>r.json()).then(_=>{ + if(_.message){ + alert(_.message); + } + }); + }else{ + if(this.json.invite){ + const params=new URLSearchParams(""); + params.set("instance",this.json.invite.url); + const encoded=params.toString(); + const url=`${location.origin}/invite/${this.json.invite.code}?${encoded}`; + window.open(url,"_blank"); + } + } + } + })() + return div; + } generateArticle(){ const colordiv=document.createElement("div"); colordiv.style.backgroundColor="#000000"; diff --git a/webpage/guild.ts b/webpage/guild.ts index 9a8311f..8fc90e4 100644 --- a/webpage/guild.ts +++ b/webpage/guild.ts @@ -7,7 +7,7 @@ import{Member}from"./member.js"; import{Settings}from"./settings.js"; import{Permissions}from"./permissions.js"; import{ SnowFlake }from"./snowflake.js"; -import{ channeljson, guildjson, emojijson, memberjson }from"./jsontypes.js"; +import{ channeljson, guildjson, emojijson, memberjson, invitejson }from"./jsontypes.js"; import{ User }from"./user.js"; class Guild extends SnowFlake{ @@ -274,39 +274,60 @@ class Guild extends SnowFlake{ return a.position-b.position; }); } - generateGuildIcon(){ + static generateGuildIcon(guild:Guild|(invitejson["guild"] & {info:{cdn:string}})){ const divy=document.createElement("div"); divy.classList.add("servernoti"); const noti=document.createElement("div"); noti.classList.add("unread"); divy.append(noti); - this.localuser.guildhtml.set(this.id,divy); - if(this.properties.icon!=null){ + if(guild instanceof Guild){ + guild.localuser.guildhtml.set(guild.id,divy); + } + let icon:string|null + if(guild instanceof Guild){ + icon=guild.properties.icon; + }else{ + icon=guild.icon; + } + if(icon!==null){ const img=document.createElement("img"); img.classList.add("pfp","servericon"); - img.src=this.info.cdn+"/icons/"+this.properties.id+"/"+this.properties.icon+".png"; + img.src=guild.info.cdn+"/icons/"+guild.id+"/"+icon+".png"; divy.appendChild(img); - img.onclick=()=>{ - console.log(this.loadGuild); - this.loadGuild(); - this.loadChannel(); - }; - Guild.contextmenu.bindContextmenu(img,this,undefined); + if(guild instanceof Guild){ + img.onclick=()=>{ + console.log(guild.loadGuild); + guild.loadGuild(); + guild.loadChannel(); + }; + Guild.contextmenu.bindContextmenu(img,guild,undefined); + } }else{ const div=document.createElement("div"); - const build=this.properties.name.replace(/'s /g, " ").replace(/\w+/g, word=>word[0]).replace(/\s/g, ""); + let name:string + if(guild instanceof Guild){ + name=guild.properties.name; + }else{ + name=guild.name; + } + const build=name.replace(/'s /g, " ").replace(/\w+/g, word=>word[0]).replace(/\s/g, ""); div.textContent=build; div.classList.add("blankserver","servericon"); divy.appendChild(div); - div.onclick=()=>{ - this.loadGuild(); - this.loadChannel(); - }; - Guild.contextmenu.bindContextmenu(div,this,undefined); + if(guild instanceof Guild){ + div.onclick=()=>{ + guild.loadGuild(); + guild.loadChannel(); + }; + Guild.contextmenu.bindContextmenu(div,guild,undefined); + } } return divy; } + generateGuildIcon(){ + return Guild.generateGuildIcon(this); + } confirmDelete(){ let confirmname=""; const full= new Dialog([ diff --git a/webpage/jsontypes.ts b/webpage/jsontypes.ts index 1f7a822..8dcb3ad 100644 --- a/webpage/jsontypes.ts +++ b/webpage/jsontypes.ts @@ -333,13 +333,79 @@ type embedjson={ provider:{ name:string, }, - video:{ + video?:{ url: string, width?: number|null, height?: number|null, proxy_url?: string + }, + invite?:{ + url:string, + code:string } } +type invitejson={ + code: string, + temporary: boolean, + uses: number, + max_use: number, + max_age: number, + created_at: string, + expires_at: string, + guild_id: string, + channel_id: string, + inviter_id: string, + target_user_id: string|null, + target_user_type: string|null, + vanity_url: string|null, + flags: number, + guild: { + id: string, + afk_channel_id: string|null, + afk_timeout: number, + banner: string|null, + default_message_notifications: number, + description: string|null, + discovery_splash: string|null, + explicit_content_filter: number, + features: [], + primary_category_id: string|null, + icon: string|null, + large: boolean, + max_members: number, + max_presences: number, + max_video_channel_users: number, + member_count: number, + presence_count: number, + template_id: string|null, + mfa_level: number, + name: string, + owner_id: string, + preferred_locale: string, + premium_subscription_count: number, + premium_tier: number, + public_updates_channel_id: string|null, + rules_channel_id: string|null, + region: string|null, + splash: string|null, + system_channel_id: string|null, + system_channel_flags: number, + verification_level: number, + welcome_screen: { + enabled: boolean, + description: string, + welcome_channels: string[] + }, + widget_channel_id: string|null, + widget_enabled: boolean, + nsfw_level: number, + nsfw: boolean, + parent: string|null, + premium_progress_bar_enabled: boolean + }, + channel: channeljson, + inviter: userjson +} type presencejson={ status: string, since: number|null, @@ -426,4 +492,4 @@ type memberChunk={ chunk_count: number, not_found: string[] } -export{readyjson,dirrectjson,channeljson,guildjson,rolesjson,userjson,memberjson,mainuserjson,messagejson,filejson,embedjson,emojijson,presencejson,wsjson,messageCreateJson,memberChunk}; +export{readyjson,dirrectjson,channeljson,guildjson,rolesjson,userjson,memberjson,mainuserjson,messagejson,filejson,embedjson,emojijson,presencejson,wsjson,messageCreateJson,memberChunk,invitejson}; diff --git a/webpage/login.ts b/webpage/login.ts index 7e822e4..218ec64 100644 --- a/webpage/login.ts +++ b/webpage/login.ts @@ -10,6 +10,11 @@ function setTheme(){ } document.body.className=name+"-theme"; } +let instances:{name:string,description?:string,descriptionLong?:string,image?:string,url?:string,display?:boolean,online?:boolean, + uptime:{alltime:number,daytime:number,weektime:number}, + urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[]|null; + + setTheme(); function getBulkUsers(){ const json=getBulkInfo(); @@ -407,8 +412,15 @@ export{mobile, getBulkUsers,getBulkInfo,setTheme,Specialuser,getapiurls,adduser} const datalist=document.getElementById("instances"); console.warn(datalist); -if(datalist){ - fetch("/instances.json").then(_=>_.json()).then((json:{name:string,online:boolean,description?:string,src?:string,url?:string,display?:boolean,urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{ +export function getInstances(){ + return instances; +} + +fetch("/instances.json").then(_=>_.json()).then((json:{name:string,description?:string,descriptionLong?:string,image?:string,url?:string,display?:boolean,online?:boolean, +uptime:{alltime:number,daytime:number,weektime:number}, +urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{ + instances=json; + if(datalist){ console.warn(json); if(instancein&&instancein.value===""){ instancein.value=json[0].name; @@ -438,5 +450,5 @@ if(datalist){ datalist.append(option); } checkInstance(""); - }); -} + } +}); diff --git a/webpage/style.css b/webpage/style.css index 02ba44c..80bc3f4 100644 --- a/webpage/style.css +++ b/webpage/style.css @@ -1478,7 +1478,6 @@ span { width: 100%; flex-direction: column; max-height:100in; - justify-content: flex-end; } #connection-container, #app-list-container { @@ -1876,7 +1875,6 @@ form div{ } .banner{ position:absolute; - z-index: 0; top:0; left:0; width:100%; @@ -2132,3 +2130,63 @@ form div{ background:var(--message-bg-hover); } } +.acceptinvbutton{ + background:var(--green); + width: 95%; + text-align:center; + font-size:.25in; + box-sizing: border-box; + margin: .1in; +} +.acceptinvbutton:hover{ + background:color-mix(in hsl,var(--green) 80%,var(--black)); +} +.acceptinvbutton:disabled:hover{ + background:color-mix(in hsl,var(--green) 80%,var(--black)); +} +.acceptinvbutton:disabled{ + background:color-mix(in hsl,var(--green) 80%,var(--black)); +} +.inviteEmbed{ + border: solid .035in var(--black); + gap: .075in; + min-width:4in; + max-width:6in; + min-height: 1.5in; + display: flex; + align-items: center; + position: relative; + .banner{ + height: .5in; + position: relative; + } +; + padding: 0in; +} +.inviteEmbed .flexltr { + display: flex; + flex-wrap: nowrap; +} +.inviteEmbed .invguildinfo{ + display:flex; + justify-content: center; + margin-left: .15in; + background: var(--channels-bg); + border: solid .03in var(--black); + border-radius: .1in; + padding: .07in; + flex-grow: 1; + height: fit-content; + backdrop-filter: blur(5px); + margin-right: .15in; +} +.subtext{ + color:color-mix(in srgb, var(--black),var(--primary-text) 65%); +} +.flexstart{ + align-self:start; + z-index: 1; + margin-left: .04in; + margin-top: .04in; + width: 100%; +}