From bf0b80e954ed67f2575b9db369fa2b23fb0d09fd Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Thu, 18 Sep 2025 23:49:40 -0500 Subject: [PATCH] role and everyone pings --- src/webpage/channel.ts | 1 + src/webpage/direct.ts | 14 +++-- src/webpage/localuser.ts | 123 ++++++++++++++++++++++++++------------- src/webpage/markdown.ts | 70 ++++++++++++++++------ src/webpage/message.ts | 22 ++++--- src/webpage/role.ts | 13 +++++ src/webpage/user.ts | 18 ++++++ 7 files changed, 187 insertions(+), 74 deletions(-) diff --git a/src/webpage/channel.ts b/src/webpage/channel.ts index 8b0d0c5..ad4143a 100644 --- a/src/webpage/channel.ts +++ b/src/webpage/channel.ts @@ -1439,6 +1439,7 @@ class Channel extends SnowFlake { } const typebox = document.getElementById("typebox") as CustomHTMLDivElement; const md = typebox.markdown; + md.owner = this; typebox.textContent = this.textSave; md.boxupdate(Infinity); this.localuser.fileExtange(this.files, this.htmls); diff --git a/src/webpage/direct.ts b/src/webpage/direct.ts index a2cecd3..25b8746 100644 --- a/src/webpage/direct.ts +++ b/src/webpage/direct.ts @@ -353,7 +353,9 @@ dmPermissions.setPermission("STREAM", 1); dmPermissions.setPermission("USE_VAD", 1); //@ts-ignore No clue how to fix this dumb bug lol class Group extends Channel { + //TODO remove user user: User; + users: User[]; static contextmenu = new Contextmenu("channel menu"); static setupcontextmenu() { this.contextmenu.addButton( @@ -395,11 +397,13 @@ class Group extends Channel { this.owner = owner; this.headers = this.guild.headers; 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; - } + + const userSet = new Set(json.recipients.map((user) => new User(user, this.localuser))); + userSet.add(this.localuser.user); + this.users = [...userSet]; + + this.user = this.users[0]; + this.name ??= this.localuser.user.username; this.parent_id!; this.parent!; diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index b2f979b..17e3e9e 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -1,6 +1,6 @@ import {Guild} from "./guild.js"; import {Channel} from "./channel.js"; -import {Direct} from "./direct.js"; +import {Direct, Group} from "./direct.js"; import {AVoice} from "./audio/voice.js"; import {User} from "./user.js"; import {createImg, getapiurls, getBulkUsers, installPGet, SW} from "./utils/utils.js"; @@ -2947,18 +2947,55 @@ class Localuser { return new User(await (await fetch(this.info.api + "/users/" + id)).json(), this); } MDFineMentionGen(name: string, original: string, box: HTMLDivElement, typebox: MarkDown) { - let members: [Member, number][] = []; - if (this.lookingguild) { - for (const member of this.lookingguild.members) { - const rank = member.compare(name); - if (rank > 0) { - members.push([member, rank]); + let members: [Member | Role | User | "@everyone", number][] = []; + if (this.lookingguild && name !== "everyone") { + if (this.lookingguild.id === "@me") { + const dirrect = this.channelfocus as Group; + + for (const user of dirrect.users) { + const rank = user.compare(name); + if (rank > 0) { + members.push([user, rank]); + } + } + } else { + for (const member of this.lookingguild.members) { + const rank = member.compare(name); + if (rank > 0) { + members.push([member, rank]); + } + } + for (const role of this.lookingguild.roles.filter((_) => _.id !== this.lookingguild?.id)) { + const rank = role.compare(name); + if (rank > 0) { + members.push([role, rank]); + } } } + function similar(str2: string | null | undefined) { + if (!str2) return 0; + const strl = Math.max(name.length, 1); + if (str2.includes(name)) { + return strl / str2.length; + } else if (str2.toLowerCase().includes(name.toLowerCase())) { + return strl / str2.length / 1.2; + } + return 0; + } + const everyoneScore = similar("everyone"); + if (everyoneScore) members.push(["@everyone", everyoneScore]); } members.sort((a, b) => b[1] - a[1]); this.MDSearchOptions( - members.map((a) => ["@" + a[0].name, `<@${a[0].id}> `, undefined]), + members.map((a) => [ + a[0] === "@everyone" ? "@everyone" : "@" + a[0].name, + a[0] instanceof Role + ? `<@&${a[0].id}> ` + : a[0] === "@everyone" + ? "@everyone " + : `<@${a[0].id}> `, + undefined, + ]), original, box, typebox, @@ -2969,43 +3006,45 @@ class Localuser { this.MDFineMentionGen(name, original, box, typebox); const nonce = Math.floor(Math.random() * 10 ** 8) + ""; if (this.lookingguild.member_count <= this.lookingguild.members.size) return; - this.ws.send( - JSON.stringify({ - op: 8, - d: { - guild_id: [this.lookingguild.id], - query: name, - limit: 8, - presences: true, - nonce, - }, - }), - ); - this.searchMap.set(nonce, async (e) => { - console.log(e); - if (e.members && e.members[0]) { - if (e.members[0].user) { - for (const thing of e.members) { - await Member.new(thing, this.lookingguild as Guild); - } - } else { - const prom1: Promise[] = []; - for (const thing of e.members) { - prom1.push(this.getUser(thing.id)); - } - Promise.all(prom1); - for (const thing of e.members) { - if (!this.userMap.has(thing.id)) { - console.warn("Dumb server bug for this member", thing); - continue; + if (this.lookingguild.id !== "@me") { + this.ws.send( + JSON.stringify({ + op: 8, + d: { + guild_id: [this.lookingguild.id], + query: name, + limit: 8, + presences: true, + nonce, + }, + }), + ); + this.searchMap.set(nonce, async (e) => { + console.log(e); + if (e.members && e.members[0]) { + if (e.members[0].user) { + for (const thing of e.members) { + await Member.new(thing, this.lookingguild as Guild); + } + } else { + const prom1: Promise[] = []; + for (const thing of e.members) { + prom1.push(this.getUser(thing.id)); + } + Promise.all(prom1); + for (const thing of e.members) { + if (!this.userMap.has(thing.id)) { + console.warn("Dumb server bug for this member", thing); + continue; + } + await Member.new(thing, this.lookingguild as Guild); } - await Member.new(thing, this.lookingguild as Guild); } + if (!typebox.rawString.startsWith(original)) return; + this.MDFineMentionGen(name, original, box, typebox); } - if (!typebox.rawString.startsWith(original)) return; - this.MDFineMentionGen(name, original, box, typebox); - } - }); + }); + } } } findEmoji(search: string, orginal: string, box: HTMLDivElement, typebox: MarkDown) { diff --git a/src/webpage/markdown.ts b/src/webpage/markdown.ts index febef28..df60b05 100644 --- a/src/webpage/markdown.ts +++ b/src/webpage/markdown.ts @@ -526,10 +526,33 @@ class MarkDown { continue; } } + if (txt[i] === "@") { + let j = i + 1; + let everyone = true; + for (const char of "everyone") { + if (char !== txt[j]) { + everyone = false; + break; + } + j++; + } + if (everyone) { + i = j - 1; + const mention = document.createElement("span"); + mention.classList.add("mentionMD"); + mention.contentEditable = "false"; + mention.textContent = "@everyone"; + appendcurrent(); + span.appendChild(mention); + mention.setAttribute("real", `@everyone`); + continue; + } + } if (txt[i] === "<") { if ((txt[i + 1] === "@" || txt[i + 1] === "#") && this.localuser) { let id = ""; - let j = i + 2; + const role = txt[i + 1] === "@" && txt[i + 2] === "&"; + let j = i + 2 + +role; const numbers = new Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); for (; txt[j] !== undefined; j++) { const char = txt[j]; @@ -555,25 +578,36 @@ class MarkDown { i = j; switch (char) { case "@": - const user = this.localuser.userMap.get(id); - if (user) { - mention.textContent = `@${user.name}`; - let guild: null | Guild = null; + if (role) { if (this.owner instanceof Channel) { - guild = this.owner.guild; - } - if (!keep) { - user.bind(mention, guild); - } - if (guild) { - Member.resolveMember(user, guild).then((member) => { - if (member) { - mention.textContent = `@${member.name}`; - } - }); + const role = this.owner.guild.roleids.get(id); + if (role) { + mention.textContent = `@${role.name}`; + } else { + mention.textContent = "@unknown-role"; + } } } else { - mention.textContent = "@unknown"; + const user = this.localuser.userMap.get(id); + if (user) { + mention.textContent = `@${user.name}`; + let guild: null | Guild = null; + if (this.owner instanceof Channel) { + guild = this.owner.guild; + } + if (!keep) { + user.bind(mention, guild); + } + if (guild) { + Member.resolveMember(user, guild).then((member) => { + if (member) { + mention.textContent = `@${member.name}`; + } + }); + } + } else { + mention.textContent = "@unknown"; + } } break; case "#": @@ -592,7 +626,7 @@ class MarkDown { break; } span.appendChild(mention); - mention.setAttribute("real", `<${char}${id}>`); + mention.setAttribute("real", `<${char}${role ? "&" : ""}${id}>`); continue; } } else { diff --git a/src/webpage/message.ts b/src/webpage/message.ts index a336835..201e111 100644 --- a/src/webpage/message.ts +++ b/src/webpage/message.ts @@ -272,15 +272,18 @@ class Message extends SnowFlake { for (const thing in messagejson.mentions) { this.mentions[thing] = new User(messagejson.mentions[thing], this.localuser); } + + this.mention_roles = messagejson.mention_roles + .map((role: string | {id: string}) => { + return this.guild.roleids.get(role instanceof Object ? role.id : role); + }) + .filter((_) => _ !== undefined); + if (!this.member && this.guild.id !== "@me") { this.author.resolvemember(this.guild).then((_) => { this.member = _; }); } - 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); } @@ -345,14 +348,16 @@ class Message extends SnowFlake { console.error(e); } } + mention_everyone!: boolean; mentionsuser(userd: User | Member) { + if (this.mention_everyone) return true; if (userd instanceof User) { return this.mentions.includes(userd); } else if (userd instanceof Member) { if (this.mentions.includes(userd.user)) { return true; } else { - return !new Set(this.mentions).isDisjointFrom(new Set(userd.roles)); //if the message mentions a role the user has + return !new Set(this.mention_roles).isDisjointFrom(new Set(userd.roles)); //if the message mentions a role the user has } } else { return false; @@ -459,11 +464,10 @@ class Message extends SnowFlake { if (!premessage && !dupe) { premessage = this.channel.messages.get(this.channel.idToPrev.get(this.id) as string); } - for (const user of this.mentions) { - if (user === this.localuser.user) { - div.classList.add("mentioned"); - } + if (this.mentionsuser(this.guild.member)) { + div.classList.add("mentioned"); } + if (this === this.channel.replyingto) { div.classList.add("replying"); } diff --git a/src/webpage/role.ts b/src/webpage/role.ts index 2d87ba5..b0838c2 100644 --- a/src/webpage/role.ts +++ b/src/webpage/role.ts @@ -61,6 +61,19 @@ class Role extends SnowFlake { document.body.style.setProperty(`--role-${this.id}`, this.getColor()); this.permissions.allow = BigInt(json.permissions); } + compare(str: string) { + function similar(str2: string | null | undefined) { + if (!str2) return 0; + const strl = Math.max(str.length, 1); + if (str2.includes(str)) { + return strl / str2.length; + } else if (str2.toLowerCase().includes(str.toLowerCase())) { + return strl / str2.length / 1.2; + } + return 0; + } + return Math.max(similar(this.name), similar(this.id) / 1.5); + } get guild(): Guild { return this.owner; } diff --git a/src/webpage/user.ts b/src/webpage/user.ts index 22bff6d..f3f1cd4 100644 --- a/src/webpage/user.ts +++ b/src/webpage/user.ts @@ -60,6 +60,24 @@ class User extends SnowFlake { return User.checkuser(userjson, owner); } } + compare(str: string) { + function similar(str2: string | null | undefined) { + if (!str2) return 0; + const strl = Math.max(str.length, 1); + if (str2.includes(str)) { + return strl / str2.length; + } else if (str2.toLowerCase().includes(str.toLowerCase())) { + return strl / str2.length / 1.2; + } + return 0; + } + return Math.max( + similar(this.name), + similar(this.nickname), + similar(this.username), + similar(this.id) / 1.5, + ); + } /** * function is meant to check if userjson contains too much information IE non-public stuff *