From 3a6e26ce62361e462c4b5f3aebd4dcb3eccb98bb Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Sun, 23 Mar 2025 18:40:00 -0500 Subject: [PATCH] webhook configuration fixes --- src/webpage/channel.ts | 124 +------------------------ src/webpage/guild.ts | 133 +-------------------------- src/webpage/jsontypes.ts | 2 +- src/webpage/settings.ts | 1 + src/webpage/webhooks.ts | 194 +++++++++++++++++++++++++++++++++++++++ translations/en.json | 9 +- translations/qqq.json | 9 +- 7 files changed, 212 insertions(+), 260 deletions(-) create mode 100644 src/webpage/webhooks.ts diff --git a/src/webpage/channel.ts b/src/webpage/channel.ts index 9f8a4aa..b6ae7d1 100644 --- a/src/webpage/channel.ts +++ b/src/webpage/channel.ts @@ -24,6 +24,7 @@ import {Voice} from "./voice.js"; import {User} from "./user.js"; import {I18n} from "./i18n.js"; import {mobile} from "./utils/utils.js"; +import {webhookMenu} from "./webhooks.js"; declare global { interface NotificationOptions { @@ -268,128 +269,7 @@ class Channel extends SnowFlake { ); const webhooks = settings.addButton(I18n.webhooks.base()); - (async () => { - const hooks = (await ( - await fetch(this.info.api + `/channels/${this.id}/webhooks`, {headers: this.headers}) - ).json()) as webhookType[]; - webhooks.addButtonInput("", I18n.webhooks.newWebHook(), () => { - const nameBox = new Dialog(I18n.webhooks.EnterWebhookName()); - const options = nameBox.float.options; - options.addTextInput(I18n.webhooks.name(), async (name) => { - const json = await ( - await fetch(`${this.info.api}/channels/${this.id}/webhooks/`, { - method: "POST", - headers: this.headers, - body: JSON.stringify({name}), - }) - ).json(); - makeHook(json); - }); - options.addButtonInput("", I18n.submit(), () => { - options.submit(); - nameBox.hide(); - }); - nameBox.show(); - }); - - const makeHook = (hook: webhookType) => { - const div = document.createElement("div"); - div.classList.add("flexltr", "webhookArea"); - const pfp = document.createElement("img"); - if (hook.avatar) { - pfp.src = `${this.info.cdn}/avatars/${hook.id}/${hook.avatar}`; - } else { - const int = Number((BigInt(hook.id) >> 22n) % 6n); - pfp.src = `${this.info.cdn}/embed/avatars/${int}.png`; - } - pfp.classList.add("webhookpfppreview"); - - const namePlate = document.createElement("div"); - namePlate.classList.add("flexttb"); - - const name = document.createElement("b"); - name.textContent = hook.name; - - const createdAt = document.createElement("span"); - createdAt.textContent = I18n.webhooks.createdAt( - new Intl.DateTimeFormat(I18n.lang).format(SnowFlake.stringToUnixTime(hook.id)), - ); - - namePlate.append(name, createdAt); - - const icon = document.createElement("span"); - icon.classList.add("svg-intoMenu", "svgicon"); - - div.append(pfp, namePlate, icon); - - div.onclick = () => { - const form = webhooks.addSubForm( - hook.name, - (e) => { - console.log(e); - }, - {traditionalSubmit: true}, - ); - form.addTextInput(I18n.webhooks.name(), "name", {initText: hook.name}); - form.addFileInput(I18n.webhooks.avatar(), "avatar", {clear: true}); - - const moveChannels = this.guild.channels.filter( - (_) => _.hasPermission("MANAGE_WEBHOOKS") && _.type !== 4, - ); - form.addSelect( - I18n.webhooks.channel(), - "channel_id", - moveChannels.map((_) => _.name), - { - defaultIndex: moveChannels.findIndex((_) => _.id === this.id), - }, - moveChannels.map((_) => _.id), - ); - - form.addMDText(I18n.webhooks.token(hook.token)); - form.addMDText(I18n.webhooks.url(hook.url)); - form.addButtonInput("", I18n.webhooks.copyURL(), () => { - navigator.clipboard.writeText(hook.url); - }); - - form.addText(I18n.webhooks.createdBy()); - - try { - const div = document.createElement("div"); - div.classList.add("flexltr", "createdWebhook"); - //TODO make sure this is something I can actually do here - const user = new User(hook.user, this.localuser); - const name = document.createElement("b"); - name.textContent = user.name; - const nameBox = document.createElement("div"); - nameBox.classList.add("flexttb"); - nameBox.append(name); - const pfp = user.buildpfp(); - div.append(pfp, nameBox); - form.addHTMLArea(div); - - Member.resolveMember(user, this.guild).then((_) => { - if (_) { - name.textContent = _.name; - pfp.src = _.getpfpsrc(); - } else { - const notFound = document.createElement("span"); - notFound.textContent = I18n.webhooks.notFound(); - nameBox.append(notFound); - } - }); - user.bind(div, this.guild); - } catch {} - }; - - console.log(hook); - - webhooks.addHTMLArea(div); - }; - for (const hook of hooks) { - makeHook(hook); - } - })(); + webhookMenu(this.guild, this.info.api + `/channels/${this.id}/webhooks`, webhooks, this.id); settings.show(); } diff --git a/src/webpage/guild.ts b/src/webpage/guild.ts index 64a7de7..67eb539 100644 --- a/src/webpage/guild.ts +++ b/src/webpage/guild.ts @@ -14,11 +14,11 @@ import { rolesjson, emojipjson, extendedProperties, - webhookType, } from "./jsontypes.js"; import {User} from "./user.js"; import {I18n} from "./i18n.js"; import {Emoji} from "./emoji.js"; +import {webhookMenu} from "./webhooks.js"; class Guild extends SnowFlake { owner!: Localuser; @@ -318,136 +318,7 @@ class Guild extends SnowFlake { emoji.addHTMLArea(containdiv); } const webhooks = settings.addButton(I18n.webhooks.base()); - (async () => { - const moveChannels = this.channels.filter( - (_) => _.hasPermission("MANAGE_WEBHOOKS") && _.type !== 4, - ); - const hooks = (await ( - await fetch(this.info.api + `/guilds/${this.id}/webhooks`, {headers: this.headers}) - ).json()) as webhookType[]; - webhooks.addButtonInput("", I18n.webhooks.newWebHook(), () => { - const nameBox = new Dialog(I18n.webhooks.EnterWebhookName()); - const options = nameBox.float.options; - options.addTextInput(I18n.webhooks.name(), async (name) => { - const json = await ( - await fetch(`${this.info.api}/channels/${moveChannels[select.index].id}/webhooks/`, { - method: "POST", - headers: this.headers, - body: JSON.stringify({name}), - }) - ).json(); - makeHook(json); - }); - const select = options.addSelect( - I18n.webhooks.channel(), - () => {}, - moveChannels.map((_) => _.name), - { - defaultIndex: 0, - }, - ); - options.addButtonInput("", I18n.submit(), () => { - options.submit(); - nameBox.hide(); - }); - nameBox.show(); - }); - - const makeHook = (hook: webhookType) => { - const div = document.createElement("div"); - div.classList.add("flexltr", "webhookArea"); - const pfp = document.createElement("img"); - if (hook.avatar) { - pfp.src = `${this.info.cdn}/avatars/${hook.id}/${hook.avatar}`; - } else { - const int = Number((BigInt(hook.id) >> 22n) % 6n); - pfp.src = `${this.info.cdn}/embed/avatars/${int}.png`; - } - pfp.classList.add("webhookpfppreview"); - - const namePlate = document.createElement("div"); - namePlate.classList.add("flexttb"); - - const name = document.createElement("b"); - name.textContent = hook.name; - - const createdAt = document.createElement("span"); - createdAt.textContent = I18n.webhooks.createdAt( - new Intl.DateTimeFormat(I18n.lang).format(SnowFlake.stringToUnixTime(hook.id)), - ); - - namePlate.append(name, createdAt); - - const icon = document.createElement("span"); - icon.classList.add("svg-intoMenu", "svgicon"); - - div.append(pfp, namePlate, icon); - - div.onclick = () => { - const form = webhooks.addSubForm( - hook.name, - (e) => { - console.log(e); - }, - {traditionalSubmit: true}, - ); - form.addTextInput(I18n.webhooks.name(), "name", {initText: hook.name}); - form.addFileInput(I18n.webhooks.avatar(), "avatar", {clear: true}); - - form.addSelect( - I18n.webhooks.channel(), - "channel_id", - moveChannels.map((_) => _.name), - { - defaultIndex: moveChannels.findIndex((_) => _.id === hook.channel_id), - }, - moveChannels.map((_) => _.id), - ); - - form.addMDText(I18n.webhooks.token(hook.token)); - form.addMDText(I18n.webhooks.url(hook.url)); - form.addButtonInput("", I18n.webhooks.copyURL(), () => { - navigator.clipboard.writeText(hook.url); - }); - - form.addText(I18n.webhooks.createdBy()); - - try { - const div = document.createElement("div"); - div.classList.add("flexltr", "createdWebhook"); - //TODO make sure this is something I can actually do here - const user = new User(hook.user, this.localuser); - const name = document.createElement("b"); - name.textContent = user.name; - const nameBox = document.createElement("div"); - nameBox.classList.add("flexttb"); - nameBox.append(name); - const pfp = user.buildpfp(); - div.append(pfp, nameBox); - form.addHTMLArea(div); - - Member.resolveMember(user, this).then((_) => { - if (_) { - name.textContent = _.name; - pfp.src = _.getpfpsrc(); - } else { - const notFound = document.createElement("span"); - notFound.textContent = I18n.webhooks.notFound(); - nameBox.append(notFound); - } - }); - user.bind(div, this); - } catch {} - }; - - console.log(hook); - - webhooks.addHTMLArea(div); - }; - for (const hook of hooks) { - makeHook(hook); - } - })(); + webhookMenu(this, this.info.api + `/guilds/${this.id}/webhooks`, webhooks); settings.show(); } makeInviteMenu(options: Options, valid: void | Channel[]) { diff --git a/src/webpage/jsontypes.ts b/src/webpage/jsontypes.ts index b9d6dc7..7e1ffc3 100644 --- a/src/webpage/jsontypes.ts +++ b/src/webpage/jsontypes.ts @@ -309,7 +309,7 @@ type webhookType = { guild_id: string; id: string; name: string; - type: 1; + type: 1 | 2 | 3; user: userjson; token: string; url: string; diff --git a/src/webpage/settings.ts b/src/webpage/settings.ts index d991c37..9e52657 100644 --- a/src/webpage/settings.ts +++ b/src/webpage/settings.ts @@ -664,6 +664,7 @@ class Options implements OptionsElement { this.noSubmit = noSubmit; } removeAll() { + this.returnFromSub(); while (this.options.length) { this.options.pop(); } diff --git a/src/webpage/webhooks.ts b/src/webpage/webhooks.ts new file mode 100644 index 0000000..76c557f --- /dev/null +++ b/src/webpage/webhooks.ts @@ -0,0 +1,194 @@ +import {Guild} from "./guild.js"; +import {I18n} from "./i18n.js"; +import {webhookType} from "./jsontypes.js"; +import {Member} from "./member.js"; +import {Dialog, Options} from "./settings.js"; +import {SnowFlake} from "./snowflake.js"; +import {User} from "./user.js"; + +async function webhookMenu( + guild: Guild, + hookURL: string, + webhooks: Options, + channelId: false | string = false, +) { + const moveChannels = guild.channels.filter( + (_) => _.hasPermission("MANAGE_WEBHOOKS") && _.type !== 4, + ); + async function regenArea() { + webhooks.removeAll(); + + webhooks.addButtonInput("", I18n.webhooks.newWebHook(), () => { + const nameBox = new Dialog(I18n.webhooks.EnterWebhookName()); + const options = nameBox.float.options; + let channel = channelId || moveChannels[0].id; + options.addTextInput(I18n.webhooks.name(), async (name) => { + const json = await ( + await fetch(`${guild.info.api}/channels/${channel}/webhooks/`, { + method: "POST", + headers: guild.headers, + body: JSON.stringify({name}), + }) + ).json(); + makeHook(json); + }); + if (!channelId) { + const select = options.addSelect( + I18n.webhooks.channel(), + () => {}, + moveChannels.map((_) => _.name), + { + defaultIndex: 0, + }, + ); + select.watchForChange((i: number) => { + channel = moveChannels[i].id; + }); + } + options.addButtonInput("", I18n.submit(), () => { + options.submit(); + nameBox.hide(); + }); + nameBox.show(); + }); + const hooks = (await (await fetch(hookURL, {headers: guild.headers})).json()) as webhookType[]; + for (const hook of hooks) { + makeHook(hook); + } + } + + const makeHook = (hook: webhookType) => { + const div = document.createElement("div"); + div.classList.add("flexltr", "webhookArea"); + const pfp = document.createElement("img"); + if (hook.avatar) { + pfp.src = `${guild.info.cdn}/avatars/${hook.id}/${hook.avatar}`; + } else { + const int = Number((BigInt(hook.id) >> 22n) % 6n); + pfp.src = `${guild.info.cdn}/embed/avatars/${int}.png`; + } + pfp.classList.add("webhookpfppreview"); + + const namePlate = document.createElement("div"); + namePlate.classList.add("flexttb"); + + const name = document.createElement("b"); + name.textContent = hook.name; + + const createdAt = document.createElement("span"); + createdAt.textContent = I18n.webhooks.createdAt( + new Intl.DateTimeFormat(I18n.lang).format(SnowFlake.stringToUnixTime(hook.id)), + ); + + const wtype = document.createElement("span"); + let typeText: string; + switch (hook.type) { + case 1: + typeText = I18n.webhooks.type1(); + break; + case 2: + typeText = I18n.webhooks.type2(); + break; + case 3: + typeText = I18n.webhooks.type3(); + break; + } + wtype.textContent = I18n.webhooks.type(typeText); + + namePlate.append(name, createdAt, wtype); + + const icon = document.createElement("span"); + icon.classList.add("svg-intoMenu", "svgicon"); + + div.append(pfp, namePlate, icon); + + div.onclick = () => { + const form = webhooks.addSubForm( + hook.name, + (e) => { + regenArea(); + console.log(e); + }, + { + traditionalSubmit: true, + method: "PATCH", + fetchURL: guild.info.api + "/webhooks/" + hook.id, + headers: guild.headers, + }, + ); + form.addTextInput(I18n.webhooks.name(), "name", {initText: hook.name}); + form.addFileInput(I18n.webhooks.avatar(), "avatar", {clear: true}); + + form.addSelect( + I18n.webhooks.channel(), + "channel_id", + moveChannels.map((_) => _.name), + { + defaultIndex: moveChannels.findIndex((_) => _.id === hook.channel_id), + }, + moveChannels.map((_) => _.id), + ); + + form.addMDText(I18n.webhooks.token(hook.token)); + form.addMDText(I18n.webhooks.url(hook.url)); + form.addText(I18n.webhooks.type(typeText)); + form.addButtonInput("", I18n.webhooks.copyURL(), () => { + navigator.clipboard.writeText(hook.url); + }); + + form.addText(I18n.webhooks.createdBy()); + + try { + const div = document.createElement("div"); + div.classList.add("flexltr", "createdWebhook"); + //TODO make sure this is something I can actually do here + const user = new User(hook.user, guild.localuser); + const name = document.createElement("b"); + name.textContent = user.name; + const nameBox = document.createElement("div"); + nameBox.classList.add("flexttb"); + nameBox.append(name); + const pfp = user.buildpfp(); + div.append(pfp, nameBox); + form.addHTMLArea(div); + + Member.resolveMember(user, guild).then((_) => { + if (_) { + name.textContent = _.name; + pfp.src = _.getpfpsrc(); + } else { + const notFound = document.createElement("span"); + notFound.textContent = I18n.webhooks.notFound(); + nameBox.append(notFound); + } + }); + user.bind(div, guild); + } catch {} + form.addButtonInput("", I18n.webhooks.deleteWebhook(), () => { + const d = new Dialog("areYouSureDelete"); + const opt = d.options; + opt.addTitle(I18n.webhooks.areYouSureDelete(hook.name)); + const opt2 = opt.addOptions("", {ltr: true}); + opt2.addButtonInput("", I18n.yes(), () => { + fetch(guild.info.api + "/webhooks/" + hook.id, { + method: "DELETE", + headers: guild.headers, + }).then(() => { + d.hide(); + regenArea(); + }); + }); + opt2.addButtonInput("", I18n.no(), () => { + d.hide(); + }); + d.show(); + }); + }; + + console.log(hook); + + webhooks.addHTMLArea(div); + }; + regenArea(); +} +export {webhookMenu}; diff --git a/translations/en.json b/translations/en.json index 6f82144..6f91bdd 100644 --- a/translations/en.json +++ b/translations/en.json @@ -167,7 +167,14 @@ "copyURL": "Copy Webhook URL", "newWebHook": "New Webhook", "EnterWebhookName": "Enter Webhook name", - "base": "Webhooks" + "base": "Webhooks", + "sillyDefaults": "", + "type1": "Incoming", + "type2": "Channel Follower", + "type3": "Application", + "type": "Type: $1", + "deleteWebhook": "Delete Webhook", + "areYouSureDelete": "Are you sure you want to delete $1?" }, "switchAccounts": "Switch accounts ⇌", "accountNotStart": "Account unable to start", diff --git a/translations/qqq.json b/translations/qqq.json index e6d374a..f1ee32c 100644 --- a/translations/qqq.json +++ b/translations/qqq.json @@ -3,11 +3,7 @@ "last-updated": "2024/11/4", "locale": "en", "comment": "Don't know how often I'll update this top part lol", - "authors": [ - "MathMan05", - "McDutchie", - "Vl1" - ] + "authors": ["MathMan05", "McDutchie", "Vl1"] }, "readableName": "{{doc-important|This should be the name of the language you are translating into, in that language. Please DO NOT translate this into your language’s word for “English”!}}", "typing": "$1 is the number of people typing and $2 is the names of the people typing separated by commas", @@ -25,5 +21,8 @@ "description": "This object contains strings related to the logged in user, which is mostly the settings for the user", "sillyDeleteConfirmPhrase": "This is a silly phrase, do not translate this directly, make a joke that is easy to type in your language" }, + "webhooks": { + "sillyDefaults": "{{doc-important|This is just a list of silly default names for webhooks, do not feel the need to translate dirrectly, and no need for the same count}}" + }, "errorReconnect": "Uses MarkDown" }