Dernière activité 1745295964

yorgei22 a révisé ce gist 1745295964. Aller à la révision

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 a révisé ce gist 1745295315. Aller à la révision

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 a révisé ce gist 1745295166. Aller à la révision

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 a révisé ce gist 1745294866. Aller à la révision

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 a révisé ce gist 1745294832. Aller à la révision

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 a révisé ce gist 1745294411. Aller à la révision

1 file changed, 572 insertions

discord_msg_webhook.user.js(fichier créé)

@@ -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 + })();
Plus récent Plus ancien