Greasy Fork is available in English.

OC 2.0 MemberWatch

Shows members which aren't currently participating in an OC in the OC2.0 system

  1. // ==UserScript==
  2. // @name OC 2.0 MemberWatch
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.6
  5. // @description Shows members which aren't currently participating in an OC in the OC2.0 system
  6. // @author Allenone [2033011]
  7. // @match https://www.torn.com/factions.php?step=your*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. const API_KEY = "";
  16. const INACTIVE_TIME_LIMIT = 3; // Days of inactivity to filter
  17. let isProcessing = false;
  18. let inactiveFilter = false;
  19.  
  20. // Faction CrimeWatch Panel Creation
  21. const factionTabs = document.querySelector('.faction-tabs.ui-tabs-nav.ui-helper-reset.ui-helper-clearfix.ui-widget-header.ui-corner-all');
  22.  
  23. function createCrimeWatchPanel() {
  24. if (document.querySelector('#crimewatch')) return;
  25.  
  26. const panelWrapper = document.createElement('div');
  27. panelWrapper.id = 'crimewatch';
  28. panelWrapper.className = 'border-round';
  29. panelWrapper.setAttribute('role', 'heading');
  30. panelWrapper.setAttribute('aria-level', '6');
  31.  
  32. // Add custom styling for panel
  33. const style = document.createElement('style');
  34. style.innerHTML = `
  35. .active.title-toggle .arrow {
  36. background-position: 0 0;
  37. border-left: 5px solid transparent;
  38. border-right: 5px solid transparent;
  39. border-top: 5px solid #999;
  40. margin-top: 13px;
  41. margin-right: 12px;
  42. }
  43.  
  44. .active.title-toggle:hover .arrow {
  45. background-position: 0 -14px;
  46. border-left: 5px solid transparent;
  47. border-right: 5px solid transparent;
  48. border-top: 5px solid #ddd;
  49. border-top-color: var(--panel-divider-outer-side-color);
  50. margin-top: 13px;
  51. margin-right: 12px;
  52. }
  53.  
  54. .title-toggle .arrow {
  55. background-position: -14px 0;
  56. border-left: 5px solid #999;
  57. border-bottom: 5px solid transparent;
  58. border-top: 5px solid transparent;
  59. margin-top: 10px;
  60. margin-right: 14px;
  61. }
  62.  
  63. .title-toggle:hover .arrow {
  64. background-position: -14px -14px;
  65. border-left: 5px solid #ddd;
  66. border-left-color: var(--panel-divider-outer-side-color);
  67. margin-top: 10px;
  68. margin-right: 14px;
  69. }
  70.  
  71. .arrow {
  72. height: 14px;
  73. width: 14px;
  74. float: right;
  75. margin-top: 8px;
  76. margin-right: 10px;
  77. background: url(/images/v2/forums/dropdown/arrows.png) 0 0 no-repeat;
  78. cursor: pointer;
  79. width: 0px;
  80. height: 0px;
  81. border-left: 5px solid transparent;
  82. border-right: 5px solid transparent;
  83. border-top: 5px solid #999;
  84. margin-top: 13px;
  85. }
  86. `;
  87. document.head.appendChild(style);
  88.  
  89. const panelTitle = createPanelTitle();
  90. const membersList = createMembersList();
  91.  
  92. panelWrapper.appendChild(panelTitle);
  93. panelWrapper.appendChild(membersList);
  94.  
  95. if (factionTabs && factionTabs.parentNode) {
  96. factionTabs.parentNode.insertBefore(panelWrapper, factionTabs.nextSibling);
  97. }
  98.  
  99. addPanelToggleFunctionality(panelWrapper);
  100. }
  101.  
  102. // Create title for the panel
  103. function createPanelTitle() {
  104. const titleDiv = document.createElement('div');
  105. titleDiv.className = 'border-round m-top10 title-black title-toggle active';
  106. titleDiv.setAttribute('role', 'button');
  107. titleDiv.setAttribute('aria-expanded', 'false');
  108. titleDiv.innerHTML = `
  109. <i class="arrow"></i>
  110. CrimeWatch
  111. `;
  112. return titleDiv;
  113. }
  114.  
  115. // Create the members list
  116. function createMembersList() {
  117. const panelUl = document.createElement('ul');
  118. panelUl.className = 'panel fm-list cont-gray bottom-round';
  119. panelUl.style.display = 'block';
  120.  
  121. const listItem = document.createElement('li');
  122. listItem.className = 'rating w2';
  123. listItem.style.height = 'auto';
  124. listItem.style.padding = '10px';
  125.  
  126. const membersUl = document.createElement('ul');
  127. membersUl.style.display = 'grid';
  128. membersUl.style.gridTemplateColumns = 'repeat(auto-fill, minmax(150px, 1fr))';
  129. membersUl.style.gap = '10px';
  130. membersUl.style.listStyleType = 'none';
  131. membersUl.style.padding = '0';
  132. membersUl.style.margin = '0';
  133.  
  134. listItem.appendChild(membersUl);
  135. panelUl.appendChild(listItem);
  136.  
  137. return panelUl;
  138. }
  139.  
  140. // Add the toggle functionality for the panel
  141. function addPanelToggleFunctionality(panelWrapper) {
  142. panelWrapper.addEventListener('click', function (event) {
  143. if (event.target.classList.contains('title-toggle')) {
  144. event.target.classList.toggle('active');
  145. const nextElement = event.target.nextElementSibling;
  146. if (nextElement) {
  147. nextElement.style.display = nextElement.style.display === 'none' ? '' : 'none';
  148. }
  149. }
  150. });
  151. }
  152.  
  153. // Fetch and process the member data
  154. async function loadMemberData() {
  155. if (isProcessing || document.querySelector('.wrapper')) return;
  156. isProcessing = true;
  157.  
  158. const members = await getFilteredMembers();
  159. const membersUl = document.querySelector('#crimewatch .rating ul');
  160.  
  161. // Clear and append new members
  162. membersUl.innerHTML = '';
  163. Object.entries(members).forEach(([id, name]) => {
  164. const memberLi = createMemberListItem(id, name);
  165. membersUl.appendChild(memberLi);
  166. });
  167.  
  168. isProcessing = false;
  169. }
  170.  
  171. // Create list item for each member
  172. function createMemberListItem(id, name) {
  173. const li = document.createElement('li');
  174. li.style.padding = '8px';
  175. li.style.border = '1px solid #ccc';
  176. li.style.borderRadius = '6px';
  177. li.style.textAlign = 'center';
  178. li.style.color = 'inherit';
  179. li.style.boxShadow = '0 1px 3px rgba(0, 0, 0, 0.1)';
  180. li.style.transition = 'background-color 0.2s, box-shadow 0.2s'; // Hover effects
  181.  
  182. const link = document.createElement('a');
  183. link.href = `https://www.torn.com/profiles.php?XID=${id}`;
  184. link.textContent = `[${id}] ${name}`;
  185. link.target = '_blank';
  186. link.style.textDecoration = 'none';
  187. link.style.color = 'inherit';
  188.  
  189. li.appendChild(link);
  190. return li;
  191. }
  192.  
  193. async function getFilteredMembers() {
  194. try {
  195. const unixTimestamp = Math.floor(Date.now() / 1000);
  196. const excludedUserIDs = new Set();
  197.  
  198. const response = await fetch(`https://api.torn.com/v2/faction/members,crimes?key=${API_KEY}&cat=available&offset=0`);
  199. const data = await response.json();
  200.  
  201. const members = data.members.reduce((acc, member) => {
  202. if (member.position !== 'Recruit') {
  203. if (inactiveFilter) {
  204. if (unixTimestamp - member.last_action.timestamp <= (INACTIVE_TIME_LIMIT * 86400)) {
  205. acc[member.id] = member.name;
  206. }
  207. } else {
  208. acc[member.id] = member.name;
  209. }
  210. }
  211. return acc;
  212. }, {});
  213.  
  214. data.crimes.forEach(crime => {
  215. crime.slots.forEach(slot => {
  216. if (slot.user_id) excludedUserIDs.add(slot.user_id);
  217. });
  218. });
  219.  
  220. return Object.fromEntries(
  221. Object.entries(members).filter(([userID]) => !excludedUserIDs.has(Number(userID)))
  222. );
  223.  
  224. console.log(members);
  225.  
  226. } catch {
  227. return [];
  228. }
  229. }
  230.  
  231. // Check if Crime tab is active, then initialize panel
  232. function checkFactionTab() {
  233. if (location.hash.includes('tab=crimes')) {
  234. createCrimeWatchPanel();
  235. loadMemberData();
  236. }
  237. }
  238.  
  239. window.addEventListener('hashchange', checkFactionTab);
  240. checkFactionTab();
  241.  
  242. })();