Mydealz User Profil Enhancer

Erweitert die Profilbuttons um zusätzliche Funktionen

目前为 2025-03-01 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Mydealz User Profil Enhancer
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Erweitert die Profilbuttons um zusätzliche Funktionen
  6. // @author MD928835
  7. // @license MIT
  8. // @match https://www.mydealz.de/*
  9. // @require https://update.gf.qytechs.cn/scripts/528391/1544983/MyDealz%20Reactions%20Viewer.js
  10. // @grant GM_xmlhttpRequest
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const observer = new MutationObserver((mutations) => {
  17. mutations.forEach((mutation) => {
  18. const popover = document.querySelector('.popover--visible');
  19. if (popover) {
  20. setTimeout(() => modifyPopup(popover), 100);
  21. }
  22. });
  23. });
  24.  
  25. async function modifyPopup(popover) {
  26. const profileBtn = popover.querySelector('a.width--all-12.space--mt-2.button');
  27. if (!profileBtn || popover.querySelector('.custom-buttons')) return;
  28.  
  29. const username = profileBtn.href.split('/profile/')[1];
  30. const container = profileBtn.parentElement;
  31. container.classList.add('custom-buttons');
  32.  
  33. // GraphQL Query für Online-Status und Beitrittsdatum
  34. const query = `query userProfile($username: String) {
  35. user(username: $username) {
  36. joinedAgo isOnline
  37. }
  38. }`;
  39.  
  40. try {
  41. const response = await fetch('/graphql', {
  42. method: 'POST',
  43. headers: {
  44. 'Content-Type': 'application/json'
  45. },
  46. body: JSON.stringify({
  47. query,
  48. variables: {
  49. username
  50. }
  51. })
  52. });
  53. const data = await response.json();
  54. const {
  55. isOnline,
  56. joinedAgo
  57. } = data.data.user;
  58.  
  59. // Mitgliedschaftsdauer aktualisieren
  60. const membershipElement = popover.querySelector('.overflow--wrap-off.size--all-s');
  61. if (membershipElement) {
  62. membershipElement.textContent = `Dabei seit ${joinedAgo}`;
  63. }
  64.  
  65. // Zeitangabe von der Profilseite holen
  66. let lastActivityTime = 'unbekannt';
  67. try {
  68. const profileHtml = await fetch(`https://www.mydealz.de/profile/${username}`);
  69. const tempDiv = document.createElement('div');
  70. tempDiv.innerHTML = await profileHtml.text();
  71. const timeElement = tempDiv.querySelector('.userProfile-action-item .overflow--wrap-break .mute--text');
  72. if (timeElement) {
  73. lastActivityTime = timeElement.textContent.trim();
  74. }
  75. } catch (error) {
  76. console.error('Fehler beim Abrufen der Profilseite:', error);
  77. }
  78.  
  79. // Badge-Container durch Online-Status ersetzen
  80. const badgeContainer = popover.querySelector('.flex.gap-1');
  81. if (badgeContainer) {
  82. const statusContainer = document.createElement('div');
  83. // statusContainer.className = 'size--all-s color--text-TranslucentSecondary space--mt-2 space--mb-4';
  84. statusContainer.className = 'size--all-s space--mt-2 space--mb-4';
  85. const status = isOnline ? 'ONline' : 'OFFline';
  86. statusContainer.textContent = `${status}, zuletzt aktiv ${lastActivityTime}`;
  87. badgeContainer.replaceWith(statusContainer);
  88. }
  89.  
  90. // Buttons Container erstellen
  91. const btnContainer = document.createElement('div');
  92. btnContainer.className = 'flex flex--grow-1 gap--all-2';
  93. btnContainer.style.gap = '5px';
  94. btnContainer.style.width = '100%';
  95.  
  96. // Profil Button
  97. const profileButton = document.createElement('a');
  98. profileButton.href = `/profile/${username}`;
  99. profileButton.className = 'flex button button--shape-circle button--type-secondary button--mode-default';
  100. profileButton.style.flex = '1';
  101. profileButton.innerHTML = `<svg width="17" height="14" class="icon icon--mail"><use xlink:href="/assets/img/ico_632f5.svg#person"></use></svg><span class="space--ml-2"> Profil </span>`;
  102.  
  103. // Nachricht Button (ehemals PN)
  104. const messageButton = document.createElement('button');
  105. messageButton.type = 'button';
  106. messageButton.className = 'flex button button--shape-circle button--type-secondary button--mode-default';
  107. messageButton.style.flex = '1';
  108. messageButton.innerHTML = `<svg width="17" height="14" class="icon icon--mail"><use xlink:href="/assets/img/ico_632f5.svg#mail"></use></svg><span class="space--ml-2"> Nachricht </span>`;
  109.  
  110. messageButton.onclick = async () => {
  111. const username = document.querySelector('.popover--visible a[href^="/profile/"]')?.href.split('/profile/')[1];
  112. if (!username) return;
  113.  
  114. try {
  115. // GET-Request zur Prüfung des Inhalts
  116. const response = await fetch(`/profile/messages/${username}`);
  117. const html = await response.text();
  118.  
  119. // Prüfen, ob der Username im HTML vorkommt
  120. const isSpecificMessagePage = html.includes(`<span class="size--all-l text--b space--mr-1">${username}</span>`);
  121.  
  122. if (isSpecificMessagePage) {
  123. // Bei existierendem User direkt zur Nachrichtenseite
  124. const win = window.open(`/profile/messages/${username}`, '_blank');
  125.  
  126. if (win) {
  127. win.addEventListener('load', () => {
  128. const observer = new MutationObserver((mutations, obs) => {
  129. const sendButton = win.document.querySelector('button[data-t="sendButton"]');
  130. if (sendButton) {
  131. sendButton.click();
  132. obs.disconnect();
  133. }
  134. });
  135.  
  136. observer.observe(win.document.body, {
  137. childList: true,
  138. subtree: true
  139. });
  140.  
  141. setTimeout(() => observer.disconnect(), 3000);
  142. });
  143. }
  144. } else {
  145. // Bei nicht-existierendem User zur Profilseite
  146. const win = window.open(`/profile/${username}`, '_blank');
  147.  
  148. if (win) {
  149. win.addEventListener('load', () => {
  150. const observer = new MutationObserver((mutations, obs) => {
  151. const sendButton = win.document.querySelector('button[data-t="sendButton"]');
  152. if (sendButton) {
  153. sendButton.click();
  154. obs.disconnect();
  155. }
  156. });
  157.  
  158. observer.observe(win.document.body, {
  159. childList: true,
  160. subtree: true
  161. });
  162.  
  163. setTimeout(() => observer.disconnect(), 3000);
  164. });
  165. }
  166. }
  167. } catch (error) {
  168. console.error('Fehler beim Prüfen der Nachrichtenseite:', error);
  169. window.open(`/profile/${username}`, '_blank');
  170. }
  171. };
  172.  
  173. // Buttons hinzufügen
  174. btnContainer.appendChild(profileButton);
  175. btnContainer.appendChild(messageButton);
  176.  
  177. // Alten Button ersetzen
  178. profileBtn.replaceWith(btnContainer);
  179.  
  180. // Statistikbereich finden und "letzte anzeigen" Link hinzufügen
  181. setTimeout(() => {
  182. const kommentareElement = Array.from(popover.querySelectorAll('li.lbox--f.lbox--v-3 .size--all-s'))
  183. .find(el => el.textContent.includes('Kommentare'));
  184.  
  185. if (kommentareElement) {
  186. // "letzte anzeigen" Link erstellen
  187. const linkElement = document.createElement('span');
  188. linkElement.className = 'showCommentsBtn';
  189. linkElement.textContent = 'anzeigen';
  190. linkElement.style.backgroundColor = '#e6f7e6';
  191. linkElement.style.padding = '0 4px';
  192. linkElement.style.borderRadius = '3px';
  193. linkElement.style.cursor = 'pointer';
  194. linkElement.style.marginLeft = '5px';
  195. linkElement.style.fontSize = '14px';
  196.  
  197. // Funktionalität des ehemaligen Vote-Buttons übernehmen
  198. linkElement.onclick = async () => {
  199. const CONFIG = {
  200. BATCH_SIZE: 5,
  201. MAX_COMMENTS_PER_PAGE: 20
  202. };
  203.  
  204. // Username aus dem PopUp holen
  205. const p = document.querySelector('.popover--visible');
  206. const username = p?.querySelector('a[href^="/profile/"]')?.getAttribute('href')?.split('/')[2];
  207. if (!username) return;
  208.  
  209. // Progress Bar
  210. const progress = document.createElement('div');
  211. progress.style.cssText = 'position:fixed;top:0;left:0;height:3px;background:#4CAF50;z-index:9999;';
  212. document.body.appendChild(progress);
  213.  
  214. try {
  215. const response = await fetch(`https://www.mydealz.de/profile/${username}?page=1`);
  216. if (!response.ok) throw new Error(`HTTP error! status:${response.status}`);
  217. const html = await response.text();
  218. const pattern = /href=https:\/\/www\.mydealz\.de\/.*?-(\d+)#(?:comment|reply)-(\d+)/g;
  219. const matches_raw = [...html.matchAll(pattern)];
  220.  
  221. const tempDiv = document.createElement('div');
  222. tempDiv.innerHTML = html;
  223. const titles = [];
  224. tempDiv.querySelectorAll('.userHtml').forEach(item => {
  225. if (item.textContent.includes('kommentiert')) {
  226. const strongTag = item.querySelector('strong');
  227. if (strongTag) titles.push(strongTag.textContent);
  228. }
  229. });
  230.  
  231. const pageResults = [];
  232. const commentPromises = matches_raw.map((match, index) =>
  233. fetch("https://www.mydealz.de/graphql", {
  234. method: 'POST',
  235. headers: {
  236. 'Content-Type': 'application/json'
  237. },
  238. body: JSON.stringify({
  239. query: 'query comment($id: ID!) { comment(id: $id) { preparedHtmlContent createdAt createdAtTs } }',
  240. variables: {
  241. id: match[2]
  242. }
  243. })
  244. })
  245. .then(res => res.json())
  246. .then(data => {
  247. progress.style.width = `${(index / matches_raw.length) * 100}%`;
  248. if (!data?.data?.comment) return null;
  249. const comment = data.data.comment.preparedHtmlContent.replace(/<img[^>]*>/g, '');
  250. const date = new Date(data.data.comment.createdAtTs * 1000)
  251. .toLocaleString('de-DE', {
  252. day: '2-digit',
  253. month: '2-digit',
  254. year: '2-digit',
  255. hour: '2-digit',
  256. minute: '2-digit'
  257. })
  258. .replace(',', '');
  259. const result = {
  260. html: `<div class="comment-card" style="background-color:white;padding:1rem;margin:0.75rem 0;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,0.1);"><span title="${date}">${data.data.comment.createdAt}</span> <b>${titles[index]}</b><br>${comment}<br><svg width="15px" height="16px" class="icon icon--comment" style="vertical-align: middle"><use xlink:href="/assets/img/ico_632f5.svg#comment"></use></svg> <a href='${match[0].replace('href=','')}'target='_blank'>Zum Kommentar</a></div>`,
  261. title: titles[index],
  262. comment: comment.replace(/<blockquote>.*?<\/blockquote>/g, ''),
  263. dealId: match[1],
  264. commentId: match[2]
  265. };
  266. pageResults.push(result);
  267. return result;
  268. })
  269. .catch(() => null)
  270. );
  271.  
  272. await Promise.all(commentPromises);
  273.  
  274. const flatResults = pageResults.filter(r => r);
  275. flatResults.sort((a, b) => b.commentId - a.commentId);
  276.  
  277. sessionStorage.setItem('mydealz_comments', JSON.stringify(flatResults));
  278. let clipboardString = '$' + username + '$' + flatResults.map(r => `${r.title}|${r.comment}`).join('||').replace(/<[^>]*>/g, '').replace(/(\r\n|\n|\r)/gm, ' ').replace(/"/g, "'").trim();
  279.  
  280. const resultWindow = window.open("", "Results", "width=1000,height=700,location=no,menubar=no,toolbar=no,status=no,titlebar=no");
  281. if (resultWindow) {
  282. resultWindow.document.write(`<html><head><title>Schlechter Tag oder schlechter User?</title><script>function copyToClip(){navigator.clipboard.writeText(${JSON.stringify(clipboardString)}).then(()=>document.querySelector("button").textContent="Kopiert!").catch(e=>alert(e))}function sortComments(type){let comments=JSON.parse(sessionStorage.getItem('mydealz_comments'));if(type==='all'){comments.sort((a,b)=>b.commentId-a.commentId);}else{comments.sort((a,b)=>b.dealId===a.dealId?b.commentId-a.commentId:b.dealId-a.dealId);}document.getElementById('comments-container').innerHTML=comments.map(r=>r.html).join('');}</script></head><body style="margin:0;padding:0;background:#f5f5f5"><div style="background:#005293;height:56px;display:flex;align-items:center;color:white;font-size:24px;text-align:center"><img src="https://www.mydealz.de/assets/img/logo/default-light_d4b86.svg" style="height:40px;margin-left:20px"><div style="flex-grow:1;text-align:center"><a href="https://www.mydealz.de/profile/${username}" style="color:white;text-decoration:none" target="_blank">${username}s letzte ${flatResults.length} Kommentare</a></div></div><div style="text-align:center;padding:10px"><button style="padding:10px" onclick="copyToClip()">In Zwischenablage kopieren</button></div><div style="text-align:center;padding:10px">Kommentare sortieren nach <label><input type="radio" name="sort" checked onclick="sortComments('all')"> alle chronologisch</label> <label><input type="radio" name="sort" onclick="sortComments('deal')"> beitragschronologisch</label></div><div id="comments-container" style="margin:20px">${flatResults.map(r => r.html).join('')}</div></body></html>`);
  283. resultWindow.document.close();
  284. resultWindow.focus();
  285. } else {
  286. alert("Popup blockiert!");
  287. }
  288.  
  289. progress.remove();
  290. } catch (err) {
  291. alert(`Fehler: ${err.message}`);
  292. progress.remove();
  293. }
  294. };
  295.  
  296. // Link zum Kommentare-Element hinzufügen
  297. kommentareElement.appendChild(document.createTextNode(' '));
  298. kommentareElement.appendChild(linkElement);
  299. }
  300. const reactionsElement = Array.from(popover.querySelectorAll('li.lbox--f.lbox--v-3 .size--all-s'))
  301. .find(el => el.textContent.includes('Reaktionen'));
  302.  
  303. if (reactionsElement) {
  304. const linkElement = document.createElement('span');
  305. linkElement.className = 'showReactionsBtn';
  306. linkElement.textContent = 'anzeigen';
  307. linkElement.style.backgroundColor = '#e6f7e6';
  308. linkElement.style.padding = '0 4px';
  309. linkElement.style.borderRadius = '3px';
  310. linkElement.style.cursor = 'pointer';
  311. linkElement.style.marginLeft = '5px';
  312. linkElement.style.fontSize = '14px';
  313.  
  314. linkElement.onclick = () => {
  315. const p = document.querySelector('.popover--visible');
  316. const username = p?.querySelector('a[href^="/profile/"]')?.getAttribute('href')?.split('/')[2];
  317. if (!username) return;
  318. viewReactions(username);
  319. };
  320.  
  321. reactionsElement.appendChild(document.createTextNode(' '));
  322. reactionsElement.appendChild(linkElement);
  323. }
  324. }, 500);
  325. } catch (error) {
  326. console.error('Fehler:', error);
  327. }
  328. }
  329.  
  330. observer.observe(document.body, {
  331. childList: true,
  332. subtree: true,
  333. attributes: true,
  334. attributeFilter: ['class']
  335. });
  336. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址