yorgei22 ревизій цього gist . До ревизії
1 file changed, 40 insertions, 40 deletions
discord_msg_webhook.user.js
@@ -117,7 +117,7 @@ | |||
117 | 117 | function hslaToHex(hsla) { | |
118 | 118 | // Handle the specific calc variable case by replacing it with 100% | |
119 | 119 | const processedHsla = hsla.replace(/calc\(var\(--saturation-factor,\s*1\)\s*\*\s*100%\)/g, '100%'); | |
120 | - | ||
120 | + | ||
121 | 121 | // Parse the HSLA values | |
122 | 122 | const parts = processedHsla.match(/hsla?\((\d+),\s*(\d+(\.\d+)?%),\s*(\d+(\.\d+)?%),\s*(\d?(\.\d+)?)\)/); | |
123 | 123 | if (!parts) { | |
@@ -125,15 +125,15 @@ | |||
125 | 125 | console.error("Failed to parse HSLA string:", hsla); | |
126 | 126 | return null; // Or return a default hex color like 0 | |
127 | 127 | } | |
128 | - | ||
128 | + | ||
129 | 129 | const h = parseInt(parts[1], 10); | |
130 | 130 | const s = parseFloat(parts[2]) / 100; | |
131 | 131 | const l = parseFloat(parts[4]) / 100; | |
132 | 132 | const a = parseFloat(parts[7] !== undefined ? parts[7] : 1); // Default alpha to 1 if not present | |
133 | - | ||
133 | + | ||
134 | 134 | // Convert HSL to RGB | |
135 | 135 | let r, g, b; | |
136 | - | ||
136 | + | ||
137 | 137 | if (s === 0) { | |
138 | 138 | r = g = b = l; // Achromatic | |
139 | 139 | } else { | |
@@ -145,23 +145,23 @@ | |||
145 | 145 | if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; | |
146 | 146 | return p; | |
147 | 147 | }; | |
148 | - | ||
148 | + | ||
149 | 149 | const q = l < 0.5 ? l * (1 + s) : l + s - l * s; | |
150 | 150 | const p = 2 * l - q; | |
151 | - | ||
151 | + | ||
152 | 152 | r = hue2rgb(p, q, h / 360 + 1 / 3); | |
153 | 153 | g = hue2rgb(p, q, h / 360); | |
154 | 154 | b = hue2rgb(p, q, h / 360 - 1 / 3); | |
155 | 155 | } | |
156 | - | ||
156 | + | ||
157 | 157 | // Convert RGB to Hex | |
158 | 158 | const toHex = x => { | |
159 | 159 | const hex = Math.round(x * 255).toString(16); | |
160 | 160 | return hex.length === 1 ? '0' + hex : hex; | |
161 | 161 | }; | |
162 | - | ||
162 | + | ||
163 | 163 | const hex = `${toHex(r)}${toHex(g)}${toHex(b)}`; | |
164 | - | ||
164 | + | ||
165 | 165 | // Handle Alpha (optional, as webhook colors are typically RGB hex) | |
166 | 166 | // If you need to include alpha in the hex (RRGGBBAA), uncomment the following: | |
167 | 167 | /* | |
@@ -171,7 +171,7 @@ | |||
171 | 171 | }; | |
172 | 172 | return parseInt(hex + toHexAlpha(a), 16); | |
173 | 173 | */ | |
174 | - | ||
174 | + | ||
175 | 175 | return parseInt(hex, 16); | |
176 | 176 | } | |
177 | 177 | ||
@@ -248,7 +248,7 @@ | |||
248 | 248 | ||
249 | 249 | // Embeds | |
250 | 250 | if (message.embeds) { | |
251 | - | ||
251 | + | ||
252 | 252 | webhookJson.embeds = message.embeds.map(embed => { | |
253 | 253 | const webhookEmbed = {}; | |
254 | 254 | // console.log(embed); | |
@@ -315,7 +315,7 @@ | |||
315 | 315 | if (Object.keys(webhookAuthor).length > 0) webhookEmbed.author = webhookAuthor; | |
316 | 316 | } | |
317 | 317 | ||
318 | - | if (embed.fields) { | |
318 | + | if (embed.fields && embed.fields.length > 0) { | |
319 | 319 | webhookEmbed.fields = embed.fields.map(field => { | |
320 | 320 | console.log(field); | |
321 | 321 | const webhookField = {}; | |
@@ -338,7 +338,7 @@ | |||
338 | 338 | jsonDisplayBox = document.createElement('div'); | |
339 | 339 | jsonDisplayBox.id = 'discord-message-json-display'; | |
340 | 340 | jsonDisplayBox.style.display = 'none'; // Initially hidden | |
341 | - | ||
341 | + | ||
342 | 342 | const header = document.createElement('div'); | |
343 | 343 | header.className = 'header'; | |
344 | 344 | // Create a text node as the first child | |
@@ -363,7 +363,7 @@ | |||
363 | 363 | const jsonString = jsonToDisplay ? JSON.stringify(jsonToDisplay, null, 2) : 'Could not retrieve message data.'; | |
364 | 364 | const contentElement = document.getElementById('discord-message-json-content'); | |
365 | 365 | contentElement.textContent = jsonString; | |
366 | - | ||
366 | + | ||
367 | 367 | // Apply our own highlighting | |
368 | 368 | highlightJsonContent(); | |
369 | 369 | } | |
@@ -381,10 +381,10 @@ | |||
381 | 381 | copyButton.style.cssText = 'font-weight: normal; font-size: 10px; margin-right: 10px; cursor: pointer; display: flex; align-items: center;'; | |
382 | 382 | copyButton.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z" fill="currentColor"/></svg>'; | |
383 | 383 | copyButton.innerHTML += '<span style="margin-left: 4px;">Copy</span>'; | |
384 | - | ||
384 | + | ||
385 | 385 | copyButton.onclick = () => { | |
386 | 386 | const jsonContent = document.getElementById('discord-message-json-content'); | |
387 | - | ||
387 | + | ||
388 | 388 | // Get the plain text content, not the highlighted HTML | |
389 | 389 | let textToCopy = ''; | |
390 | 390 | if (currentMessage) { | |
@@ -393,7 +393,7 @@ | |||
393 | 393 | } else { | |
394 | 394 | textToCopy = jsonContent.textContent; | |
395 | 395 | } | |
396 | - | ||
396 | + | ||
397 | 397 | // Copy to clipboard | |
398 | 398 | navigator.clipboard.writeText(textToCopy) | |
399 | 399 | .then(() => { | |
@@ -408,7 +408,7 @@ | |||
408 | 408 | console.error('Failed to copy JSON: ', err); | |
409 | 409 | }); | |
410 | 410 | }; | |
411 | - | ||
411 | + | ||
412 | 412 | controls.appendChild(copyButton); | |
413 | 413 | ||
414 | 414 | const closeButton = document.createElement('span'); | |
@@ -454,13 +454,13 @@ | |||
454 | 454 | function highlightJsonContent() { | |
455 | 455 | const codeElement = document.getElementById('discord-message-json-content'); | |
456 | 456 | if (!codeElement) return; | |
457 | - | ||
457 | + | ||
458 | 458 | // Get the text content | |
459 | 459 | const text = codeElement.textContent; | |
460 | - | ||
460 | + | ||
461 | 461 | // JSON syntax highlighting with colons preserved | |
462 | 462 | let highlighted = text.replace( | |
463 | - | /"(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, | |
463 | + | /"(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, | |
464 | 464 | function (match) { | |
465 | 465 | let cls = 'json-number'; | |
466 | 466 | if (/^"/.test(match)) { | |
@@ -475,16 +475,16 @@ | |||
475 | 475 | } else if (/null/.test(match)) { | |
476 | 476 | cls = 'json-null'; | |
477 | 477 | } | |
478 | - | ||
478 | + | ||
479 | 479 | return `<span class="${cls}">${match}</span>`; | |
480 | 480 | } | |
481 | 481 | ); | |
482 | - | ||
482 | + | ||
483 | 483 | // Also highlight brackets and punctuation (but not colons as they're already handled) | |
484 | 484 | highlighted = highlighted.replace(/[{}\[\],]/g, function(match) { | |
485 | 485 | return `<span class="json-punctuation">${match}</span>`; | |
486 | 486 | }); | |
487 | - | ||
487 | + | ||
488 | 488 | codeElement.innerHTML = highlighted; | |
489 | 489 | } | |
490 | 490 | ||
@@ -507,10 +507,10 @@ | |||
507 | 507 | const jsonString = jsonToDisplay ? JSON.stringify(jsonToDisplay, null, 2) : 'Could not retrieve message data.'; | |
508 | 508 | const contentElement = document.getElementById('discord-message-json-content'); | |
509 | 509 | contentElement.textContent = jsonString; | |
510 | - | ||
510 | + | ||
511 | 511 | // Apply our custom syntax highlighting | |
512 | 512 | highlightJsonContent(); | |
513 | - | ||
513 | + | ||
514 | 514 | jsonDisplayBox.style.display = 'flex'; | |
515 | 515 | } | |
516 | 516 | ||
@@ -536,7 +536,7 @@ | |||
536 | 536 | return; | |
537 | 537 | } | |
538 | 538 | } | |
539 | - | ||
539 | + | ||
540 | 540 | // log.warn("Could not find React instance for message element."); | |
541 | 541 | if (jsonDisplayBox && document.getElementById('discord-message-json-content')) { | |
542 | 542 | document.getElementById('discord-message-json-content').textContent = 'Could not retrieve message data.'; | |
@@ -569,7 +569,7 @@ | |||
569 | 569 | justify-content: center; | |
570 | 570 | `; | |
571 | 571 | jsonViewerBtn.innerHTML = jsonSvgIcon; | |
572 | - | ||
572 | + | ||
573 | 573 | // Add click handler | |
574 | 574 | jsonViewerBtn.onclick = () => { | |
575 | 575 | if (jsonDisplayBox) { | |
@@ -579,19 +579,19 @@ | |||
579 | 579 | jsonDisplayBox.style.display = 'flex'; | |
580 | 580 | } | |
581 | 581 | }; | |
582 | - | ||
582 | + | ||
583 | 583 | // Add hover effects | |
584 | 584 | jsonViewerBtn.onmouseover = () => { | |
585 | 585 | jsonViewerBtn.style.color = 'var(--interactive-hover)'; | |
586 | 586 | }; | |
587 | - | ||
587 | + | ||
588 | 588 | jsonViewerBtn.onmouseout = () => { | |
589 | 589 | jsonViewerBtn.style.color = 'var(--interactive-normal)'; | |
590 | 590 | }; | |
591 | - | ||
591 | + | ||
592 | 592 | return jsonViewerBtn; | |
593 | 593 | } | |
594 | - | ||
594 | + | ||
595 | 595 | function mountJsonViewerButton() { | |
596 | 596 | const toolbar = document.querySelector('#app-mount [class^=toolbar]'); | |
597 | 597 | if (toolbar) { | |
@@ -605,23 +605,23 @@ | |||
605 | 605 | } | |
606 | 606 | } | |
607 | 607 | } | |
608 | - | ||
608 | + | ||
609 | 609 | function initJsonViewer() { | |
610 | 610 | // Insert CSS | |
611 | 611 | insertCss(themeCss); | |
612 | - | ||
612 | + | ||
613 | 613 | // Create button | |
614 | 614 | createJsonViewerButton(); | |
615 | - | ||
615 | + | ||
616 | 616 | // Create display box (initially hidden) | |
617 | 617 | createJsonDisplayBox(); | |
618 | - | ||
618 | + | ||
619 | 619 | // Mount button | |
620 | 620 | mountJsonViewerButton(); | |
621 | - | ||
621 | + | ||
622 | 622 | // Add message click listener | |
623 | 623 | document.addEventListener('click', handleMessageClick, true); | |
624 | - | ||
624 | + | ||
625 | 625 | // Setup observer to re-mount button if needed | |
626 | 626 | const discordElm = document.querySelector('#app-mount'); | |
627 | 627 | if (discordElm) { | |
@@ -634,10 +634,10 @@ | |||
634 | 634 | } | |
635 | 635 | }, 3000); | |
636 | 636 | }); | |
637 | - | ||
637 | + | ||
638 | 638 | observer.observe(discordElm, { childList: true, subtree: true }); | |
639 | 639 | } | |
640 | - | ||
640 | + | ||
641 | 641 | console.log('JSON Viewer initialized successfully'); | |
642 | 642 | } | |
643 | 643 |
yorgei22 ревизій цього gist . До ревизії
1 file changed, 5 insertions, 4 deletions
discord_msg_webhook.user.js
@@ -1,7 +1,7 @@ | |||
1 | 1 | // ==UserScript== | |
2 | 2 | // @name Discord Message to Webhook | |
3 | 3 | // @description Combined tool for viewing JSON data of Discord messages, based off undiscord | |
4 | - | // @version 1.0.4 | |
4 | + | // @version 1.0.5 | |
5 | 5 | // @author Original scripts by various authors, edited by shayne | |
6 | 6 | // @match https://*.discord.com/app | |
7 | 7 | // @match https://*.discord.com/channels/* | |
@@ -315,11 +315,12 @@ | |||
315 | 315 | if (Object.keys(webhookAuthor).length > 0) webhookEmbed.author = webhookAuthor; | |
316 | 316 | } | |
317 | 317 | ||
318 | - | if (embed.fields && embed.fields.length > 0) { | |
318 | + | if (embed.fields) { | |
319 | 319 | webhookEmbed.fields = embed.fields.map(field => { | |
320 | + | console.log(field); | |
320 | 321 | const webhookField = {}; | |
321 | - | if (field.rawName) webhookField.name = field.name; | |
322 | - | if (field.rawValue) webhookField.value = field.value; | |
322 | + | if (field.rawName) webhookField.name = field.rawName; | |
323 | + | if (field.rawValue) webhookField.value = field.rawValue; | |
323 | 324 | if (field.inline !== undefined) webhookField.inline = field.inline; | |
324 | 325 | return webhookField; | |
325 | 326 | }); |
yorgei22 ревизій цього gist . До ревизії
1 file changed, 6 insertions, 5 deletions
discord_msg_webhook.user.js
@@ -1,13 +1,14 @@ | |||
1 | 1 | // ==UserScript== | |
2 | 2 | // @name Discord Message to Webhook | |
3 | - | // @description Combined tool for viewing JSON data of Discord messages and bulk deletion | |
4 | - | // @version 1.0.2 | |
5 | - | // @author Original scripts by various authors, edited by jirachi | |
3 | + | // @description Combined tool for viewing JSON data of Discord messages, based off undiscord | |
4 | + | // @version 1.0.4 | |
5 | + | // @author Original scripts by various authors, edited by shayne | |
6 | 6 | // @match https://*.discord.com/app | |
7 | 7 | // @match https://*.discord.com/channels/* | |
8 | 8 | // @match https://*.discord.com/login | |
9 | 9 | // @license none | |
10 | 10 | // @grant none | |
11 | + | // @downloadURL https://gist.yorgei.dev/yorgei22/1a8dc2789b484f879d271c90885b1026/raw/HEAD/discord_msg_webhook.user.js | |
11 | 12 | // ==/UserScript== | |
12 | 13 | ||
13 | 14 | (function () { | |
@@ -317,8 +318,8 @@ | |||
317 | 318 | if (embed.fields && embed.fields.length > 0) { | |
318 | 319 | webhookEmbed.fields = embed.fields.map(field => { | |
319 | 320 | const webhookField = {}; | |
320 | - | if (field.name) webhookField.name = field.name; | |
321 | - | if (field.value) webhookField.value = field.value; | |
321 | + | if (field.rawName) webhookField.name = field.name; | |
322 | + | if (field.rawValue) webhookField.value = field.value; | |
322 | 323 | if (field.inline !== undefined) webhookField.inline = field.inline; | |
323 | 324 | return webhookField; | |
324 | 325 | }); |
yorgei22 ревизій цього gist . До ревизії
1 file changed, 3 insertions, 6 deletions
discord_msg_webhook.user.js
@@ -1,12 +1,12 @@ | |||
1 | 1 | // ==UserScript== | |
2 | 2 | // @name Discord Message to Webhook | |
3 | 3 | // @description Combined tool for viewing JSON data of Discord messages and bulk deletion | |
4 | - | // @version 1.0.0 | |
5 | - | // @author Original scripts by various authors, combined version | |
4 | + | // @version 1.0.2 | |
5 | + | // @author Original scripts by various authors, edited by jirachi | |
6 | 6 | // @match https://*.discord.com/app | |
7 | 7 | // @match https://*.discord.com/channels/* | |
8 | 8 | // @match https://*.discord.com/login | |
9 | - | // @license MIT | |
9 | + | // @license none | |
10 | 10 | // @grant none | |
11 | 11 | // ==/UserScript== | |
12 | 12 | ||
@@ -14,10 +14,7 @@ | |||
14 | 14 | 'use strict'; | |
15 | 15 | ||
16 | 16 | // --- Configuration --- | |
17 | - | const VERSION = "1.0.1"; | |
18 | - | const PREFIX = ''; | |
19 | 17 | let logFn = null; | |
20 | - | const ui = {}; | |
21 | 18 | let observerThrottle = null; | |
22 | 19 | ||
23 | 20 | // --- CSS --- |
yorgei22 ревизій цього gist . До ревизії
1 file changed, 84 insertions, 5 deletions
discord_msg_webhook.user.js
@@ -1,12 +1,12 @@ | |||
1 | 1 | // ==UserScript== | |
2 | 2 | // @name Discord Message to Webhook | |
3 | 3 | // @description Combined tool for viewing JSON data of Discord messages and bulk deletion | |
4 | - | // @version 1.0.1 | |
5 | - | // @author Original scripts by various authors, edited by jirachi | |
4 | + | // @version 1.0.0 | |
5 | + | // @author Original scripts by various authors, combined version | |
6 | 6 | // @match https://*.discord.com/app | |
7 | 7 | // @match https://*.discord.com/channels/* | |
8 | 8 | // @match https://*.discord.com/login | |
9 | - | // @license none | |
9 | + | // @license MIT | |
10 | 10 | // @grant none | |
11 | 11 | // ==/UserScript== | |
12 | 12 | ||
@@ -14,7 +14,10 @@ | |||
14 | 14 | 'use strict'; | |
15 | 15 | ||
16 | 16 | // --- Configuration --- | |
17 | + | const VERSION = "1.0.1"; | |
18 | + | const PREFIX = ''; | |
17 | 19 | let logFn = null; | |
20 | + | const ui = {}; | |
18 | 21 | let observerThrottle = null; | |
19 | 22 | ||
20 | 23 | // --- CSS --- | |
@@ -113,6 +116,67 @@ | |||
113 | 116 | success() { return logFn ? logFn('success', arguments) : console.info.apply(console, arguments); } | |
114 | 117 | }; | |
115 | 118 | ||
119 | + | function hslaToHex(hsla) { | |
120 | + | // Handle the specific calc variable case by replacing it with 100% | |
121 | + | const processedHsla = hsla.replace(/calc\(var\(--saturation-factor,\s*1\)\s*\*\s*100%\)/g, '100%'); | |
122 | + | ||
123 | + | // Parse the HSLA values | |
124 | + | const parts = processedHsla.match(/hsla?\((\d+),\s*(\d+(\.\d+)?%),\s*(\d+(\.\d+)?%),\s*(\d?(\.\d+)?)\)/); | |
125 | + | if (!parts) { | |
126 | + | // If parsing fails, return a default color or handle the error | |
127 | + | console.error("Failed to parse HSLA string:", hsla); | |
128 | + | return null; // Or return a default hex color like 0 | |
129 | + | } | |
130 | + | ||
131 | + | const h = parseInt(parts[1], 10); | |
132 | + | const s = parseFloat(parts[2]) / 100; | |
133 | + | const l = parseFloat(parts[4]) / 100; | |
134 | + | const a = parseFloat(parts[7] !== undefined ? parts[7] : 1); // Default alpha to 1 if not present | |
135 | + | ||
136 | + | // Convert HSL to RGB | |
137 | + | let r, g, b; | |
138 | + | ||
139 | + | if (s === 0) { | |
140 | + | r = g = b = l; // Achromatic | |
141 | + | } else { | |
142 | + | const hue2rgb = (p, q, t) => { | |
143 | + | if (t < 0) t += 1; | |
144 | + | if (t > 1) t -= 1; | |
145 | + | if (t < 1 / 6) return p + (q - p) * 6 * t; | |
146 | + | if (t < 1 / 2) return q; | |
147 | + | if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; | |
148 | + | return p; | |
149 | + | }; | |
150 | + | ||
151 | + | const q = l < 0.5 ? l * (1 + s) : l + s - l * s; | |
152 | + | const p = 2 * l - q; | |
153 | + | ||
154 | + | r = hue2rgb(p, q, h / 360 + 1 / 3); | |
155 | + | g = hue2rgb(p, q, h / 360); | |
156 | + | b = hue2rgb(p, q, h / 360 - 1 / 3); | |
157 | + | } | |
158 | + | ||
159 | + | // Convert RGB to Hex | |
160 | + | const toHex = x => { | |
161 | + | const hex = Math.round(x * 255).toString(16); | |
162 | + | return hex.length === 1 ? '0' + hex : hex; | |
163 | + | }; | |
164 | + | ||
165 | + | const hex = `${toHex(r)}${toHex(g)}${toHex(b)}`; | |
166 | + | ||
167 | + | // Handle Alpha (optional, as webhook colors are typically RGB hex) | |
168 | + | // If you need to include alpha in the hex (RRGGBBAA), uncomment the following: | |
169 | + | /* | |
170 | + | const toHexAlpha = x => { | |
171 | + | const hexA = Math.round(x * 255).toString(16); | |
172 | + | return hexA.length === 1 ? '0' + hexA : hexA; | |
173 | + | }; | |
174 | + | return parseInt(hex + toHexAlpha(a), 16); | |
175 | + | */ | |
176 | + | ||
177 | + | return parseInt(hex, 16); | |
178 | + | } | |
179 | + | ||
116 | 180 | const setLogFn = (fn) => logFn = fn; | |
117 | 181 | ||
118 | 182 | function createElm(html) { | |
@@ -197,12 +261,27 @@ | |||
197 | 261 | if (embed.url) webhookEmbed.url = embed.url; | |
198 | 262 | ||
199 | 263 | // Color | |
264 | + | // Original snippet with the added HSLA handling | |
200 | 265 | if (embed.color !== undefined) { | |
201 | 266 | if (typeof embed.color === 'string') { | |
202 | 267 | try { | |
203 | - | webhookEmbed.color = parseInt(embed.color.replace('#', ''), 16); | |
268 | + | // Check if the color string is an HSLA value | |
269 | + | if (embed.color.startsWith('hsl')) { | |
270 | + | const hexColor = hslaToHex(embed.color); | |
271 | + | if (hexColor !== null) { | |
272 | + | webhookEmbed.color = hexColor; | |
273 | + | } else { | |
274 | + | // Handle the case where HSLA parsing failed | |
275 | + | console.error("Could not convert HSLA color to hex:", embed.color); | |
276 | + | // Optionally set a default color or leave it undefined | |
277 | + | webhookEmbed.color = 0; // Example: set to black | |
278 | + | } | |
279 | + | } else { | |
280 | + | // Assume it's a hex string if not HSLA | |
281 | + | webhookEmbed.color = parseInt(embed.color.replace('#', ''), 16); | |
282 | + | } | |
204 | 283 | } catch (e) { | |
205 | - | console.error("Failed to parse embed color string:", embed.color, e); | |
284 | + | console.error("Failed to process embed color string:", embed.color, e); | |
206 | 285 | } | |
207 | 286 | } else if (typeof embed.color === 'number') { | |
208 | 287 | webhookEmbed.color = embed.color; |
yorgei22 ревизій цього gist . До ревизії
1 file changed, 572 insertions
discord_msg_webhook.user.js(файл створено)
@@ -0,0 +1,572 @@ | |||
1 | + | // ==UserScript== | |
2 | + | // @name Discord Message to Webhook | |
3 | + | // @description Combined tool for viewing JSON data of Discord messages and bulk deletion | |
4 | + | // @version 1.0.1 | |
5 | + | // @author Original scripts by various authors, edited by jirachi | |
6 | + | // @match https://*.discord.com/app | |
7 | + | // @match https://*.discord.com/channels/* | |
8 | + | // @match https://*.discord.com/login | |
9 | + | // @license none | |
10 | + | // @grant none | |
11 | + | // ==/UserScript== | |
12 | + | ||
13 | + | (function () { | |
14 | + | 'use strict'; | |
15 | + | ||
16 | + | // --- Configuration --- | |
17 | + | let logFn = null; | |
18 | + | let observerThrottle = null; | |
19 | + | ||
20 | + | // --- CSS --- | |
21 | + | const themeCss = ` | |
22 | + | /* JSON Viewer Box */ | |
23 | + | #discord-message-json-display { | |
24 | + | position: fixed; | |
25 | + | top: 50px; | |
26 | + | right: 50px; | |
27 | + | width: 450px; | |
28 | + | height: 300px; | |
29 | + | background-color: var(--background-secondary); | |
30 | + | border: 1px solid var(--background-tertiary); | |
31 | + | border-radius: 5px; | |
32 | + | z-index: 1000; | |
33 | + | overflow: hidden; | |
34 | + | display: flex; | |
35 | + | flex-direction: column; | |
36 | + | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; | |
37 | + | font-size: 12px; | |
38 | + | color: var(--text-normal); | |
39 | + | box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2); | |
40 | + | resize: both; | |
41 | + | overflow: auto; | |
42 | + | min-width: 250px; | |
43 | + | min-height: 150px; | |
44 | + | } | |
45 | + | #discord-message-json-display .header { | |
46 | + | padding: 10px; | |
47 | + | background-color: var(--background-tertiary); | |
48 | + | cursor: grab; | |
49 | + | font-weight: bold; | |
50 | + | border-bottom: 1px solid var(--background-tertiary); | |
51 | + | display: flex; | |
52 | + | justify-content: space-between; | |
53 | + | align-items: center; | |
54 | + | user-select: none; | |
55 | + | } | |
56 | + | #discord-message-json-content { | |
57 | + | flex-grow: 1; | |
58 | + | padding: 10px; | |
59 | + | margin: 0; | |
60 | + | overflow: auto; | |
61 | + | white-space: pre-wrap; | |
62 | + | word-wrap: break-word; | |
63 | + | background-color: var(--background-primary); | |
64 | + | } | |
65 | + | #discord-message-json-display::-webkit-resizer { | |
66 | + | background-color: var(--brand-experiment); | |
67 | + | } | |
68 | + | .format-toggle-container { | |
69 | + | display: flex; | |
70 | + | align-items: center; | |
71 | + | font-weight: normal; | |
72 | + | font-size: 10px; | |
73 | + | margin-right: 10px; | |
74 | + | cursor: pointer; | |
75 | + | } | |
76 | + | .format-toggle-container input { | |
77 | + | margin-right: 5px; | |
78 | + | cursor: pointer; | |
79 | + | } | |
80 | + | ||
81 | + | /* Custom JSON syntax highlighting */ | |
82 | + | .json-string { color: #a8ff60; } | |
83 | + | .json-number { color: #d8a0df; } | |
84 | + | .json-boolean { color: #b285fe; } | |
85 | + | .json-null { color: #70a0d8; } | |
86 | + | .json-key { color: #f08d49; } | |
87 | + | .json-punctuation { color: #7a82da; } | |
88 | + | `; | |
89 | + | ||
90 | + | // --- HTML Elements --- | |
91 | + | // SVG Data for the JSON icon | |
92 | + | const jsonSvgIcon = `<svg width="18px" height="18px" viewBox="-10 -5 1034 1034" xmlns="http://www.w3.org/2000/svg"> | |
93 | + | <path fill="currentColor" d="M482 226h-1l-10 2q-33 4 -64.5 18.5t-55.5 38.5q-41 37 -57 91q-9 30 -8 63t12 63q17 45 52 78l13 12l-83 135q-26 -1 -45 7q-30 13 -45 40q-7 15 -9 31t2 32q8 30 33 48q15 10 33 14.5t36 2t34.5 -12.5t27.5 -25q12 -17 14.5 -39t-5.5 -41q-1 -5 -7 -14l-3 -6l118 -192 q6 -9 8 -14l-10 -3q-9 -2 -13 -4q-23 -10 -41.5 -27.5t-28.5 -39.5q-17 -36 -9 -75q4 -23 17 -43t31 -34q37 -27 82 -27q27 -1 52.5 9.5t44.5 30.5q17 16 26.5 38.5t10.5 45.5q0 17 -6 42l70 19l8 1q14 -43 7 -86q-4 -33 -19.5 -63.5t-39.5 -53.5q-42 -42 -103 -56 q-6 -2 -18 -4l-14 -2h-37zM500 350q-17 0 -34 7t-30.5 20.5t-19.5 31.5q-8 20 -4 44q3 18 14 34t28 25q24 15 56 13q3 4 5 8l112 191q3 6 6 9q27 -26 58.5 -35.5t65 -3.5t58.5 26q32 25 43.5 61.5t0.5 73.5q-8 28 -28.5 50t-48.5 33q-31 13 -66.5 8.5t-63.5 -24.5 q-4 -3 -13 -10l-5 -6q-4 3 -11 10l-47 46q23 23 52 38.5t61 21.5l22 4h39l28 -5q64 -13 110 -60q22 -22 36.5 -50.5t19.5 -59.5q5 -36 -2 -71.5t-25 -64.5t-44 -51t-57 -35q-34 -14 -70.5 -16t-71.5 7l-17 5l-81 -137q13 -19 16 -37q5 -32 -13 -60q-16 -25 -44 -35 q-17 -6 -35 -6zM218 614q-58 13 -100 53q-47 44 -61 105l-4 24v37l2 11q2 13 4 20q7 31 24.5 59t42.5 49q50 41 115 49q38 4 76 -4.5t70 -28.5q53 -34 78 -91q7 -17 14 -45q6 -1 18 0l125 2q14 0 20 1q11 20 25 31t31.5 16t35.5 4q28 -3 50 -20q27 -21 32 -54 q2 -17 -1.5 -33t-13.5 -30q-16 -22 -41 -32q-17 -7 -35.5 -6.5t-35.5 7.5q-28 12 -43 37l-3 6q-14 0 -42 -1l-113 -1q-15 -1 -43 -1l-50 -1l3 17q8 43 -13 81q-14 27 -40 45t-57 22q-35 6 -70 -7.5t-57 -42.5q-28 -35 -27 -79q1 -37 23 -69q13 -19 32 -32t41 -19l9 -3z"/> | |
94 | + | </svg>`; | |
95 | + | ||
96 | + | // --- JSON Viewer Variables --- | |
97 | + | let jsonDisplayBox = null; | |
98 | + | let isDragging = false; | |
99 | + | let dragOffsetX, dragOffsetY; | |
100 | + | let isScriptActive = true; | |
101 | + | let currentMessage = null; | |
102 | + | let isWebhookFormat = false; | |
103 | + | ||
104 | + | // --- Utility Functions --- | |
105 | + | const $ = s => document.querySelector(s); | |
106 | + | ||
107 | + | const log = { | |
108 | + | debug() { return logFn ? logFn('debug', arguments) : console.debug.apply(console, arguments); }, | |
109 | + | info() { return logFn ? logFn('info', arguments) : console.info.apply(console, arguments); }, | |
110 | + | verb() { return logFn ? logFn('verb', arguments) : console.log.apply(console, arguments); }, | |
111 | + | warn() { return logFn ? logFn('warn', arguments) : console.warn.apply(console, arguments); }, | |
112 | + | error() { return logFn ? logFn('error', arguments) : console.error.apply(console, arguments); }, | |
113 | + | success() { return logFn ? logFn('success', arguments) : console.info.apply(console, arguments); } | |
114 | + | }; | |
115 | + | ||
116 | + | const setLogFn = (fn) => logFn = fn; | |
117 | + | ||
118 | + | function createElm(html) { | |
119 | + | const temp = document.createElement('div'); | |
120 | + | temp.innerHTML = html; | |
121 | + | return temp.removeChild(temp.firstElementChild); | |
122 | + | } | |
123 | + | ||
124 | + | function insertCss(css) { | |
125 | + | const style = document.createElement('style'); | |
126 | + | style.innerHTML = css; | |
127 | + | document.head.appendChild(style); | |
128 | + | return style; | |
129 | + | } | |
130 | + | ||
131 | + | // --- React Instance Finders --- | |
132 | + | function findReactInstance(element) { | |
133 | + | for (const key in element) { | |
134 | + | if (key.startsWith('__reactFiber$') || key.startsWith('__reactProps$')) { | |
135 | + | return element[key]; | |
136 | + | } | |
137 | + | } | |
138 | + | return null; | |
139 | + | } | |
140 | + | ||
141 | + | function getMessageFromReactInstance(reactInstance) { | |
142 | + | if (!reactInstance) return null; | |
143 | + | ||
144 | + | let current = reactInstance; | |
145 | + | while (current) { | |
146 | + | if (current.memoizedProps && current.memoizedProps.message) { | |
147 | + | return current.memoizedProps.message; | |
148 | + | } | |
149 | + | current = current.return; | |
150 | + | } | |
151 | + | return null; | |
152 | + | } | |
153 | + | ||
154 | + | // --- JSON Conversion Function --- | |
155 | + | function convertMessageToWebhookJson(message) { | |
156 | + | if (!message) return null; | |
157 | + | ||
158 | + | const webhookJson = {}; | |
159 | + | ||
160 | + | // Basic message properties | |
161 | + | if (message.content) { | |
162 | + | webhookJson.content = message.content; | |
163 | + | } | |
164 | + | // console.log(message); | |
165 | + | // Username and Avatar | |
166 | + | if (message.author) { | |
167 | + | webhookJson.username = message.author.username; | |
168 | + | if (message.author.avatar) { | |
169 | + | const avatarHash = message.author.avatar; | |
170 | + | const userId = message.author.id; | |
171 | + | const isGif = avatarHash.startsWith('a_'); | |
172 | + | webhookJson.avatar_url = `https://cdn.discordapp.com/avatars/${userId}/${avatarHash}.${isGif ? 'gif' : 'png'}`; | |
173 | + | } else { | |
174 | + | // Default avatar if none is set | |
175 | + | const discriminator = message.author.discriminator; | |
176 | + | let defaultAvatarId; | |
177 | + | if (discriminator === '0') { | |
178 | + | defaultAvatarId = (BigInt(message.author.id) >> 22n) % 6n; | |
179 | + | webhookJson.avatar_url = `https://cdn.discordapp.com/assets/${defaultAvatarId}.png`; | |
180 | + | } else { | |
181 | + | defaultAvatarId = parseInt(discriminator) % 5; | |
182 | + | webhookJson.avatar_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarId}.png`; | |
183 | + | } | |
184 | + | } | |
185 | + | } | |
186 | + | ||
187 | + | // Embeds | |
188 | + | if (message.embeds) { | |
189 | + | ||
190 | + | webhookJson.embeds = message.embeds.map(embed => { | |
191 | + | const webhookEmbed = {}; | |
192 | + | // console.log(embed); | |
193 | + | if (embed.rawTitle) webhookEmbed.title = embed.rawTitle; | |
194 | + | if (embed.title) webhookEmbed.title = embed.title; | |
195 | + | if (embed.rawDescription) webhookEmbed.description = embed.rawDescription; | |
196 | + | if (embed.description) webhookEmbed.description = embed.description; | |
197 | + | if (embed.url) webhookEmbed.url = embed.url; | |
198 | + | ||
199 | + | // Color | |
200 | + | if (embed.color !== undefined) { | |
201 | + | if (typeof embed.color === 'string') { | |
202 | + | try { | |
203 | + | webhookEmbed.color = parseInt(embed.color.replace('#', ''), 16); | |
204 | + | } catch (e) { | |
205 | + | console.error("Failed to parse embed color string:", embed.color, e); | |
206 | + | } | |
207 | + | } else if (typeof embed.color === 'number') { | |
208 | + | webhookEmbed.color = embed.color; | |
209 | + | } | |
210 | + | } | |
211 | + | ||
212 | + | if (embed.timestamp) webhookEmbed.timestamp = embed.timestamp; | |
213 | + | ||
214 | + | if (embed.footer) { | |
215 | + | const webhookFooter = {}; | |
216 | + | if (embed.footer.text) webhookFooter.text = embed.footer.text; | |
217 | + | if (embed.footer.icon_url) webhookFooter.icon_url = embed.footer.icon_url; | |
218 | + | if (Object.keys(webhookFooter).length > 0) webhookEmbed.footer = webhookFooter; | |
219 | + | } | |
220 | + | ||
221 | + | if (embed.image) { | |
222 | + | const webhookImage = {}; | |
223 | + | if (embed.image.url) webhookImage.url = embed.image.url; | |
224 | + | if (Object.keys(webhookImage).length > 0) webhookEmbed.image = webhookImage; | |
225 | + | } | |
226 | + | ||
227 | + | if (embed.thumbnail) { | |
228 | + | const webhookThumbnail = {}; | |
229 | + | if (embed.thumbnail.url) webhookThumbnail.url = embed.thumbnail.url; | |
230 | + | if (Object.keys(webhookThumbnail).length > 0) webhookEmbed.thumbnail = webhookThumbnail; | |
231 | + | } | |
232 | + | ||
233 | + | if (embed.author) { | |
234 | + | const webhookAuthor = {}; | |
235 | + | if (embed.author.name) webhookAuthor.name = embed.author.name; | |
236 | + | if (embed.author.url) webhookAuthor.url = embed.author.url; | |
237 | + | if (embed.author.icon_url) webhookAuthor.icon_url = embed.author.icon_url; | |
238 | + | if (Object.keys(webhookAuthor).length > 0) webhookEmbed.author = webhookAuthor; | |
239 | + | } | |
240 | + | ||
241 | + | if (embed.fields && embed.fields.length > 0) { | |
242 | + | webhookEmbed.fields = embed.fields.map(field => { | |
243 | + | const webhookField = {}; | |
244 | + | if (field.name) webhookField.name = field.name; | |
245 | + | if (field.value) webhookField.value = field.value; | |
246 | + | if (field.inline !== undefined) webhookField.inline = field.inline; | |
247 | + | return webhookField; | |
248 | + | }); | |
249 | + | } | |
250 | + | ||
251 | + | return webhookEmbed; | |
252 | + | }).filter(embed => Object.keys(embed).length > 0); | |
253 | + | } | |
254 | + | ||
255 | + | return webhookJson; | |
256 | + | } | |
257 | + | ||
258 | + | // --- JSON Viewer Functions --- | |
259 | + | function createJsonDisplayBox() { | |
260 | + | jsonDisplayBox = document.createElement('div'); | |
261 | + | jsonDisplayBox.id = 'discord-message-json-display'; | |
262 | + | jsonDisplayBox.style.display = 'none'; // Initially hidden | |
263 | + | ||
264 | + | const header = document.createElement('div'); | |
265 | + | header.className = 'header'; | |
266 | + | // Create a text node as the first child | |
267 | + | header.appendChild(document.createTextNode('Discord Message JSON Data')); | |
268 | + | ||
269 | + | const controls = document.createElement('div'); | |
270 | + | controls.style.cssText = 'display: flex; align-items: center;'; | |
271 | + | ||
272 | + | // Webhook Toggle | |
273 | + | const webhookToggleLabel = document.createElement('label'); | |
274 | + | webhookToggleLabel.style.cssText = 'font-weight: normal; font-size: 10px; margin-right: 10px; display: flex; align-items: center; cursor: pointer;'; | |
275 | + | webhookToggleLabel.textContent = 'Webhook Format'; | |
276 | + | ||
277 | + | const webhookToggle = document.createElement('input'); | |
278 | + | webhookToggle.type = 'checkbox'; | |
279 | + | webhookToggle.style.cssText = 'margin-right: 5px; cursor: pointer;'; | |
280 | + | webhookToggle.addEventListener('change', function() { | |
281 | + | isWebhookFormat = this.checked; | |
282 | + | if (currentMessage) { | |
283 | + | // Only update content, not the entire structure | |
284 | + | const jsonToDisplay = isWebhookFormat ? convertMessageToWebhookJson(currentMessage) : currentMessage; | |
285 | + | const jsonString = jsonToDisplay ? JSON.stringify(jsonToDisplay, null, 2) : 'Could not retrieve message data.'; | |
286 | + | const contentElement = document.getElementById('discord-message-json-content'); | |
287 | + | contentElement.textContent = jsonString; | |
288 | + | ||
289 | + | // Apply our own highlighting | |
290 | + | highlightJsonContent(); | |
291 | + | } | |
292 | + | // Update header text but preserve the DOM structure | |
293 | + | const headerText = document.querySelector('#discord-message-json-display .header'); | |
294 | + | const headerTitle = headerText.childNodes[0]; | |
295 | + | headerTitle.textContent = isWebhookFormat ? 'Discord Webhook JSON Data' : 'Discord Message JSON Data'; | |
296 | + | }); | |
297 | + | ||
298 | + | webhookToggleLabel.prepend(webhookToggle); | |
299 | + | controls.appendChild(webhookToggleLabel); | |
300 | + | ||
301 | + | // Copy Button | |
302 | + | const copyButton = document.createElement('div'); | |
303 | + | copyButton.style.cssText = 'font-weight: normal; font-size: 10px; margin-right: 10px; cursor: pointer; display: flex; align-items: center;'; | |
304 | + | copyButton.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z" fill="currentColor"/></svg>'; | |
305 | + | copyButton.innerHTML += '<span style="margin-left: 4px;">Copy</span>'; | |
306 | + | ||
307 | + | copyButton.onclick = () => { | |
308 | + | const jsonContent = document.getElementById('discord-message-json-content'); | |
309 | + | ||
310 | + | // Get the plain text content, not the highlighted HTML | |
311 | + | let textToCopy = ''; | |
312 | + | if (currentMessage) { | |
313 | + | const jsonToDisplay = isWebhookFormat ? convertMessageToWebhookJson(currentMessage) : currentMessage; | |
314 | + | textToCopy = JSON.stringify(jsonToDisplay, null, 2); | |
315 | + | } else { | |
316 | + | textToCopy = jsonContent.textContent; | |
317 | + | } | |
318 | + | ||
319 | + | // Copy to clipboard | |
320 | + | navigator.clipboard.writeText(textToCopy) | |
321 | + | .then(() => { | |
322 | + | // Visual feedback | |
323 | + | const originalText = copyButton.querySelector('span').textContent; | |
324 | + | copyButton.querySelector('span').textContent = 'Copied!'; | |
325 | + | setTimeout(() => { | |
326 | + | copyButton.querySelector('span').textContent = originalText; | |
327 | + | }, 1000); | |
328 | + | }) | |
329 | + | .catch(err => { | |
330 | + | console.error('Failed to copy JSON: ', err); | |
331 | + | }); | |
332 | + | }; | |
333 | + | ||
334 | + | controls.appendChild(copyButton); | |
335 | + | ||
336 | + | const closeButton = document.createElement('span'); | |
337 | + | closeButton.style.cssText = 'margin-left: 10px; cursor: pointer; font-size: 16px; color: var(--interactive-normal);'; | |
338 | + | closeButton.textContent = '✕'; | |
339 | + | closeButton.onclick = () => { | |
340 | + | jsonDisplayBox.style.display = 'none'; | |
341 | + | }; | |
342 | + | ||
343 | + | controls.appendChild(closeButton); | |
344 | + | header.appendChild(controls); | |
345 | + | jsonDisplayBox.appendChild(header); | |
346 | + | ||
347 | + | const content = document.createElement('pre'); | |
348 | + | content.id = 'discord-message-json-content'; | |
349 | + | jsonDisplayBox.appendChild(content); | |
350 | + | ||
351 | + | document.body.appendChild(jsonDisplayBox); | |
352 | + | ||
353 | + | // Make the box draggable | |
354 | + | header.addEventListener('mousedown', (e) => { | |
355 | + | if (e.button !== 0) return; | |
356 | + | isDragging = true; | |
357 | + | dragOffsetX = e.clientX - jsonDisplayBox.getBoundingClientRect().left; | |
358 | + | dragOffsetY = e.clientY - jsonDisplayBox.getBoundingClientRect().top; | |
359 | + | jsonDisplayBox.style.cursor = 'grabbing'; | |
360 | + | document.body.style.userSelect = 'none'; | |
361 | + | }); | |
362 | + | ||
363 | + | document.addEventListener('mousemove', (e) => { | |
364 | + | if (!isDragging) return; | |
365 | + | jsonDisplayBox.style.left = (e.clientX - dragOffsetX) + 'px'; | |
366 | + | jsonDisplayBox.style.top = (e.clientY - dragOffsetY) + 'px'; | |
367 | + | }); | |
368 | + | ||
369 | + | document.addEventListener('mouseup', () => { | |
370 | + | isDragging = false; | |
371 | + | if (jsonDisplayBox) jsonDisplayBox.style.cursor = ''; | |
372 | + | document.body.style.userSelect = ''; | |
373 | + | }); | |
374 | + | } | |
375 | + | ||
376 | + | function highlightJsonContent() { | |
377 | + | const codeElement = document.getElementById('discord-message-json-content'); | |
378 | + | if (!codeElement) return; | |
379 | + | ||
380 | + | // Get the text content | |
381 | + | const text = codeElement.textContent; | |
382 | + | ||
383 | + | // JSON syntax highlighting with colons preserved | |
384 | + | let highlighted = text.replace( | |
385 | + | /"(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, | |
386 | + | function (match) { | |
387 | + | let cls = 'json-number'; | |
388 | + | if (/^"/.test(match)) { | |
389 | + | if (/:$/.test(match)) { | |
390 | + | cls = 'json-key'; | |
391 | + | // Don't remove the colon | |
392 | + | } else { | |
393 | + | cls = 'json-string'; | |
394 | + | } | |
395 | + | } else if (/true|false/.test(match)) { | |
396 | + | cls = 'json-boolean'; | |
397 | + | } else if (/null/.test(match)) { | |
398 | + | cls = 'json-null'; | |
399 | + | } | |
400 | + | ||
401 | + | return `<span class="${cls}">${match}</span>`; | |
402 | + | } | |
403 | + | ); | |
404 | + | ||
405 | + | // Also highlight brackets and punctuation (but not colons as they're already handled) | |
406 | + | highlighted = highlighted.replace(/[{}\[\],]/g, function(match) { | |
407 | + | return `<span class="json-punctuation">${match}</span>`; | |
408 | + | }); | |
409 | + | ||
410 | + | codeElement.innerHTML = highlighted; | |
411 | + | } | |
412 | + | ||
413 | + | function displayMessageJson(message) { | |
414 | + | if (!jsonDisplayBox) { | |
415 | + | createJsonDisplayBox(); | |
416 | + | } | |
417 | + | ||
418 | + | currentMessage = message; | |
419 | + | ||
420 | + | let jsonToDisplay = null; | |
421 | + | if (currentMessage) { | |
422 | + | if (isWebhookFormat) { | |
423 | + | jsonToDisplay = convertMessageToWebhookJson(currentMessage); | |
424 | + | } else { | |
425 | + | jsonToDisplay = currentMessage; | |
426 | + | } | |
427 | + | } | |
428 | + | ||
429 | + | const jsonString = jsonToDisplay ? JSON.stringify(jsonToDisplay, null, 2) : 'Could not retrieve message data.'; | |
430 | + | const contentElement = document.getElementById('discord-message-json-content'); | |
431 | + | contentElement.textContent = jsonString; | |
432 | + | ||
433 | + | // Apply our custom syntax highlighting | |
434 | + | highlightJsonContent(); | |
435 | + | ||
436 | + | jsonDisplayBox.style.display = 'flex'; | |
437 | + | } | |
438 | + | ||
439 | + | // Handle message clicks for JSON viewing | |
440 | + | function handleMessageClick(event) { | |
441 | + | if (!isScriptActive) return; | |
442 | + | ||
443 | + | const target = event.target; | |
444 | + | const messageElement = target.closest('[id^="message-"]'); | |
445 | + | ||
446 | + | if (messageElement) { | |
447 | + | // Skip if clicking interactive elements | |
448 | + | const interactiveElements = target.closest('a, button, .markup-2BOw-j'); | |
449 | + | if (interactiveElements && !interactiveElements.classList.contains('embedWrapper-lXp9gn')) { | |
450 | + | return; | |
451 | + | } | |
452 | + | ||
453 | + | const reactInstance = findReactInstance(messageElement); | |
454 | + | if (reactInstance) { | |
455 | + | const message = getMessageFromReactInstance(reactInstance); | |
456 | + | if (message) { | |
457 | + | displayMessageJson(message); | |
458 | + | return; | |
459 | + | } | |
460 | + | } | |
461 | + | ||
462 | + | // log.warn("Could not find React instance for message element."); | |
463 | + | if (jsonDisplayBox && document.getElementById('discord-message-json-content')) { | |
464 | + | document.getElementById('discord-message-json-content').textContent = 'Could not retrieve message data.'; | |
465 | + | currentMessage = null; | |
466 | + | } | |
467 | + | } | |
468 | + | } | |
469 | + | ||
470 | + | // --- Button Creation and Mounting --- | |
471 | + | let jsonViewerBtn = null; | |
472 | + | ||
473 | + | function createJsonViewerButton() { | |
474 | + | // Create button container | |
475 | + | jsonViewerBtn = document.createElement('div'); | |
476 | + | jsonViewerBtn.id = 'json-viewer-btn'; | |
477 | + | jsonViewerBtn.setAttribute('role', 'button'); | |
478 | + | jsonViewerBtn.setAttribute('aria-label', 'View Message JSON'); | |
479 | + | jsonViewerBtn.setAttribute('tabindex', '0'); | |
480 | + | jsonViewerBtn.title = 'View Message JSON'; | |
481 | + | jsonViewerBtn.style.cssText = ` | |
482 | + | position: relative; | |
483 | + | width: auto; | |
484 | + | height: 24px; | |
485 | + | margin: 0 8px; | |
486 | + | cursor: pointer; | |
487 | + | color: var(--interactive-normal); | |
488 | + | flex: 0 0 auto; | |
489 | + | display: flex; | |
490 | + | align-items: center; | |
491 | + | justify-content: center; | |
492 | + | `; | |
493 | + | jsonViewerBtn.innerHTML = jsonSvgIcon; | |
494 | + | ||
495 | + | // Add click handler | |
496 | + | jsonViewerBtn.onclick = () => { | |
497 | + | if (jsonDisplayBox) { | |
498 | + | jsonDisplayBox.style.display = jsonDisplayBox.style.display === 'none' ? 'flex' : 'none'; | |
499 | + | } else { | |
500 | + | createJsonDisplayBox(); | |
501 | + | jsonDisplayBox.style.display = 'flex'; | |
502 | + | } | |
503 | + | }; | |
504 | + | ||
505 | + | // Add hover effects | |
506 | + | jsonViewerBtn.onmouseover = () => { | |
507 | + | jsonViewerBtn.style.color = 'var(--interactive-hover)'; | |
508 | + | }; | |
509 | + | ||
510 | + | jsonViewerBtn.onmouseout = () => { | |
511 | + | jsonViewerBtn.style.color = 'var(--interactive-normal)'; | |
512 | + | }; | |
513 | + | ||
514 | + | return jsonViewerBtn; | |
515 | + | } | |
516 | + | ||
517 | + | function mountJsonViewerButton() { | |
518 | + | const toolbar = document.querySelector('#app-mount [class^=toolbar]'); | |
519 | + | if (toolbar) { | |
520 | + | // Check if button already exists | |
521 | + | if (!document.getElementById('json-viewer-btn')) { | |
522 | + | if (!jsonViewerBtn) { | |
523 | + | jsonViewerBtn = createJsonViewerButton(); | |
524 | + | } | |
525 | + | toolbar.appendChild(jsonViewerBtn); | |
526 | + | console.log('Mounted JSON Viewer button'); | |
527 | + | } | |
528 | + | } | |
529 | + | } | |
530 | + | ||
531 | + | function initJsonViewer() { | |
532 | + | // Insert CSS | |
533 | + | insertCss(themeCss); | |
534 | + | ||
535 | + | // Create button | |
536 | + | createJsonViewerButton(); | |
537 | + | ||
538 | + | // Create display box (initially hidden) | |
539 | + | createJsonDisplayBox(); | |
540 | + | ||
541 | + | // Mount button | |
542 | + | mountJsonViewerButton(); | |
543 | + | ||
544 | + | // Add message click listener | |
545 | + | document.addEventListener('click', handleMessageClick, true); | |
546 | + | ||
547 | + | // Setup observer to re-mount button if needed | |
548 | + | const discordElm = document.querySelector('#app-mount'); | |
549 | + | if (discordElm) { | |
550 | + | const observer = new MutationObserver(() => { | |
551 | + | if (observerThrottle) return; | |
552 | + | observerThrottle = setTimeout(() => { | |
553 | + | observerThrottle = null; | |
554 | + | if (!document.body.contains(jsonViewerBtn)) { | |
555 | + | mountJsonViewerButton(); | |
556 | + | } | |
557 | + | }, 3000); | |
558 | + | }); | |
559 | + | ||
560 | + | observer.observe(discordElm, { childList: true, subtree: true }); | |
561 | + | } | |
562 | + | ||
563 | + | console.log('JSON Viewer initialized successfully'); | |
564 | + | } | |
565 | + | ||
566 | + | // Initialize when the page loads | |
567 | + | if (document.readyState === 'loading') { | |
568 | + | window.addEventListener('DOMContentLoaded', initJsonViewer); | |
569 | + | } else { | |
570 | + | initJsonViewer(); | |
571 | + | } | |
572 | + | })(); |