GGn Mining Stats

Adds a button to the userlog page to calculate personal mining drops statistics.

  1. // ==UserScript==
  2. // @name GGn Mining Stats
  3. // @description Adds a button to the userlog page to calculate personal mining drops statistics.
  4. // @namespace https://gazellegames.net/
  5. // @version 1.0.4.1
  6. // @match https://gazellegames.net/user.php?action=userlog
  7. // @grant GM_setValue
  8. // @grant GM_getValue
  9. // @icon https://gazellegames.net/favicon.ico
  10. // @supportURL https://github.com/freshwater/userscripts
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15. const userLink = document.querySelector('h2 a.username');
  16. if (!userLink) return;
  17. const href = userLink.getAttribute('href');
  18. const userId = new URL(href, window.location.href).searchParams.get('id');
  19. if (!userId) return;
  20.  
  21. const header = document.querySelector('h2');
  22. if (!header) return;
  23.  
  24. const btn = document.createElement('button');
  25. btn.textContent = 'Mining Stats';
  26. Object.assign(btn.style, {
  27. marginLeft: '8px',
  28. border: '1px solid white',
  29. cursor: 'pointer'
  30. });
  31.  
  32. btn.addEventListener('click', async () => {
  33. let apiKey = GM_getValue('mining_stats_api_key');
  34. let needsRetry = false;
  35.  
  36. do {
  37. try {
  38. if (!apiKey) {
  39. apiKey = prompt('Enter your API key (requires "User" permissions):');
  40. if (!apiKey) return;
  41. GM_setValue('mining_stats_api_key', apiKey);
  42. }
  43.  
  44. console.log('[Mining Stats] Fetching data...');
  45. const [logData, userData] = await Promise.all([
  46. fetchData(`https://gazellegames.net/api.php?request=userlog&limit=-1&search=as an irc reward.`, apiKey),
  47. fetchData(`https://gazellegames.net/api.php?request=user&id=${userId}`, apiKey)
  48. ]);
  49.  
  50. const drops = logData?.response || [];
  51. const flameEntries = drops.filter(e => e.message.toLowerCase().includes('flame'));
  52. const flameCounts = flameEntries.reduce((acc, entry) => {
  53. const msg = entry.message.toLowerCase();
  54. ['nayru', 'din', 'farore'].forEach(flame => {
  55. if (msg.includes(`${flame}'s flame`)) acc[flame]++;
  56. });
  57. return acc;
  58. }, { nayru: 0, din: 0, farore: 0 });
  59.  
  60. const actualLines = userData?.response?.community?.ircActualLines ?? 0;
  61. const totalMines = drops.length;
  62. const totalFlames = flameEntries.length;
  63.  
  64. alert(`Mining Stats:
  65. Mines: ${totalMines} | Flames: ${totalFlames}
  66. Nayru: ${flameCounts.nayru}, Din: ${flameCounts.din}, Farore: ${flameCounts.farore}
  67. Lines/Mine: ${(actualLines / (totalMines || 1)).toFixed(2)}
  68. Lines/Flame: ${(actualLines / (totalFlames || 1)).toFixed(2)}`
  69. .replace(/ {2,}/g, ''));
  70.  
  71. needsRetry = false;
  72. } catch (error) {
  73. console.error('[Mining Stats] Error:', error);
  74. if ([401, 403].includes(error.status)) {
  75. GM_setValue('mining_stats_api_key', '');
  76. apiKey = null;
  77. needsRetry = confirm(`API Error: ${error.status === 401 ? 'Invalid key' : 'No permissions'}. Retry?`);
  78. } else {
  79. alert(`Error: ${error.message}`);
  80. needsRetry = false;
  81. }
  82. }
  83. } while (needsRetry);
  84. });
  85.  
  86. async function fetchData(url, apiKey) {
  87. const response = await fetch(url, { headers: { 'X-API-Key': apiKey } });
  88. if (!response.ok) throw Object.assign(new Error(`HTTP ${response.status}`), { status: response.status });
  89. const data = await response.json();
  90. if (data?.status !== 'success') throw new Error(data?.error || 'API request failed');
  91. return data;
  92. }
  93.  
  94. header.appendChild(btn);
  95. })();