Torn.com Enhanced Chat Buttons V2

Add customizable buttons to Torn.com chat with enhanced UI and features

当前为 2024-12-16 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Torn.com Enhanced Chat Buttons V2
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.40
  5. // @description Add customizable buttons to Torn.com chat with enhanced UI and features
  6. // @author Created by Callz [2188704], updated by Weav3r [1853324]
  7. // @match https://www.torn.com/*
  8. // @grant GM_setClipboard
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. const CACHE_TTL = 24 * 60 * 60 * 1000;
  15.  
  16. const buttonCSS = `
  17. .custom-chat-button {
  18. background-color: #007BFF;
  19. color: white;
  20. padding: 2px 7px;
  21. text-align: center;
  22. text-decoration: none;
  23. display: inline-block;
  24. font-size: 14px;
  25. margin: 4px 6px;
  26. cursor: pointer;
  27. border-radius: 5px;
  28. border: none;
  29. transition: transform 0.1s ease, box-shadow 0.1s ease;
  30. min-width: 80px;
  31. overflow: hidden;
  32. white-space: nowrap;
  33. }
  34.  
  35. .custom-chat-button:active {
  36. transform: scale(0.95);
  37. box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  38. }
  39.  
  40. .custom-chat-button.recent {
  41. border: 2px solid #FFD700;
  42. box-shadow: 0 0 5px rgba(255, 215, 0, 0.8);
  43. }
  44.  
  45. .custom-ui-panel {
  46. position: fixed;
  47. top: 50%;
  48. left: 50%;
  49. transform: translate(-50%, -50%);
  50. background-color: #f5f5f5;
  51. padding: 10px;
  52. color: black;
  53. border-radius: 10px;
  54. box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  55. z-index: 9999999999;
  56. width: 90%;
  57. max-width: 500px;
  58. box-sizing: border-box;
  59. max-height: 90vh;
  60. overflow: auto;
  61. }
  62.  
  63. .custom-ui-panel h3 {
  64. font-size: 20px;
  65. margin-bottom: 10px;
  66. text-align: center;
  67. }
  68.  
  69. .custom-ui-panel label {
  70. font-size: 14px;
  71. margin-bottom: 5px;
  72. display: block;
  73. }
  74.  
  75. .custom-ui-panel input[type="text"],
  76. .custom-ui-panel select,
  77. .custom-ui-panel textarea {
  78. width: calc(100% - 12px);
  79. padding: 5px;
  80. margin-bottom: 10px;
  81. border: 1px solid #ccc;
  82. border-radius: 5px;
  83. font-size: 14px;
  84. }
  85.  
  86. .custom-ui-panel input[type="color"] {
  87. padding: 0;
  88. margin-top: 5px;
  89. border: none;
  90. }
  91.  
  92. .custom-ui-panel button {
  93. background-color: #007BFF;
  94. color: white;
  95. border: none;
  96. padding: 8px 12px;
  97. border-radius: 5px;
  98. cursor: pointer;
  99. margin: 5px;
  100. font-size: 14px;
  101. transition: background-color 0.3s ease;
  102. }
  103.  
  104. .custom-ui-panel button#close-ui {
  105. background-color: #ccc;
  106. }
  107.  
  108. .custom-ui-panel button#close-ui:hover {
  109. background-color: #999;
  110. }
  111.  
  112. .custom-ui-panel textarea {
  113. height: 60px;
  114. resize: vertical;
  115. position: relative;
  116. }
  117.  
  118. .custom-ui-panel hr {
  119. margin: 10px 0;
  120. border: 0;
  121. border-top: 1px solid #ccc;
  122. }
  123.  
  124. .char-counter {
  125. position: absolute;
  126. bottom: 10px;
  127. right: 10px;
  128. font-size: 12px;
  129. color: #999;
  130. }
  131.  
  132. #chat-config-button {
  133. color: green;
  134. }
  135.  
  136. #button-configs {
  137. max-height: 400px;
  138. overflow-y: auto;
  139. margin-bottom: 10px;
  140. }
  141.  
  142. @media (max-width: 600px) {
  143. .custom-ui-panel {
  144. width: 95%;
  145. max-width: none;
  146. padding: 8px;
  147. }
  148.  
  149. .custom-ui-panel h3 {
  150. font-size: 18px;
  151. }
  152.  
  153. .custom-ui-panel label,
  154. .custom-ui-panel button {
  155. font-size: 12px;
  156. }
  157.  
  158. .custom-ui-panel input[type="text"],
  159. .custom-ui-panel select,
  160. .custom-ui-panel textarea {
  161. font-size: 12px;
  162. }
  163.  
  164. .custom-ui-panel button {
  165. padding: 6px 10px;
  166. }
  167.  
  168. .char-counter {
  169. font-size: 10px;
  170. }
  171. }
  172.  
  173. .tabs {
  174. display: flex;
  175. margin-bottom: 10px;
  176. }
  177.  
  178. .tab {
  179. flex: 1;
  180. padding: 10px;
  181. cursor: pointer;
  182. text-align: center;
  183. background-color: #e9e9e9;
  184. border: 1px solid #ccc;
  185. border-bottom: none;
  186. border-radius: 10px 10px 0 0;
  187. }
  188.  
  189. .tab.settings-tab {
  190. flex: 0.2;
  191. padding: 10px;
  192. cursor: pointer;
  193. text-align: center;
  194. background-color: #e9e9e9;
  195. border: 1px solid #ccc;
  196. border-bottom: none;
  197. border-radius: 10px 10px 0 0;
  198. }
  199.  
  200. .tab.active {
  201. background-color: #fff;
  202. border-bottom: 1px solid #fff;
  203. }
  204.  
  205. .tab-content {
  206. display: none;
  207. }
  208.  
  209. .tab-content.active {
  210. display: block;
  211. }
  212.  
  213. .custom-ui-panel.config-list-tab-active {
  214. max-height: 80vh;
  215. }
  216.  
  217. .search-container {
  218. display: flex;
  219. margin-bottom: 10px;
  220. }
  221.  
  222. .search-container input[type="text"] {
  223. flex: 3;
  224. padding: 5px;
  225. margin-right: 5px;
  226. }
  227.  
  228. .search-container select {
  229. flex: 1;
  230. padding: 5px;
  231. }
  232.  
  233. .highlight {
  234. background-color: yellow;
  235. }
  236.  
  237. .tt-chat-filter {
  238. display: flex;
  239. padding: 4px;
  240. align-items: center;
  241. background-color: var(--chat-box-bg);
  242. color: var(--chat-box-label-info);
  243. border-bottom: 1px solid var(--chat-box-input-border);
  244. margin-bottom: 8px;
  245. }
  246.  
  247. .tt-chat-filter input {
  248. margin-left: 4px;
  249. margin-right: 4px;
  250. border-radius: 5px;
  251. width: -webkit-fill-available;
  252. width: -moz-available;
  253. border: 1px solid var(--chat-box-input-border);
  254. background-color: var(--chat-box-bg);
  255. color: var(--chat-box-label-info);
  256. }
  257.  
  258. .notification {
  259. position: fixed;
  260. bottom: 20px;
  261. right: 20px;
  262. background-color: rgba(0, 0, 0, 0.8);
  263. color: white;
  264. padding: 10px;
  265. border-radius: 5px;
  266. z-index: 9999999999;
  267. opacity: 0;
  268. transition: opacity 0.5s ease;
  269. }
  270.  
  271. .notification.show {
  272. opacity: 1;
  273. }
  274.  
  275. .button-config-card {
  276. border: 1px solid #ccc;
  277. background-color: #fff;
  278. padding: 10px;
  279. margin: 10px 0;
  280. border-radius: 5px;
  281. }
  282.  
  283. .button-config-message {
  284. white-space: pre-wrap;
  285. background: #f9f9f9;
  286. padding: 5px;
  287. border-radius: 5px;
  288. border: 1px solid #ddd;
  289. margin: 5px 0;
  290. }
  291. `;
  292.  
  293. const conditions = {
  294. TradeChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Trade',
  295. HospitalChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Hospital',
  296. FactionChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Faction',
  297. CompanyChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Company',
  298. GlobalChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM') && chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Global',
  299. UserChat: chatBox => chatBox.querySelector('.chat-box-header__options___nTsMU')
  300. };
  301.  
  302. const companyTypes = {
  303. 1: "Hair Salon",
  304. 2: "Law Firm",
  305. 3: "Flower Shop",
  306. 4: "Car Dealership",
  307. 5: "Clothing Store",
  308. 6: "Gun Shop",
  309. 7: "Game Shop",
  310. 8: "Candle Shop",
  311. 9: "Toy Shop",
  312. 10: "Adult Novelties",
  313. 11: "Cyber Cafe",
  314. 12: "Grocery Store",
  315. 13: "Theater",
  316. 14: "Sweet Shop",
  317. 15: "Cruise Line",
  318. 16: "Television Network",
  319. 18: "Zoo",
  320. 19: "Firework Stand",
  321. 20: "Property Broker",
  322. 21: "Furniture Store",
  323. 22: "Gas Station",
  324. 23: "Music Store",
  325. 24: "Nightclub",
  326. 25: "Pub",
  327. 26: "Gents Strip Club",
  328. 27: "Restaurant",
  329. 28: "Oil Rig",
  330. 29: "Fitness Center",
  331. 30: "Mechanic Shop",
  332. 31: "Amusement Park",
  333. 32: "Lingerie Store",
  334. 33: "Meat Warehouse",
  335. 34: "Farm",
  336. 35: "Software Corporation",
  337. 36: "Ladies Strip Club",
  338. 37: "Private Security Firm",
  339. 38: "Mining Corporation",
  340. 39: "Detective Agency",
  341. 40: "Logistics Management",
  342. };
  343.  
  344. function addCSS(cssString) {
  345. const style = document.createElement('style');
  346. style.textContent = cssString;
  347. document.head.append(style);
  348. }
  349.  
  350. function showNotification(message) {
  351. const notification = document.createElement('div');
  352. notification.className = 'notification';
  353. notification.innerText = message;
  354. document.body.appendChild(notification);
  355. requestAnimationFrame(() => {
  356. notification.classList.add('show');
  357. });
  358. setTimeout(() => {
  359. notification.classList.remove('show');
  360. setTimeout(() => {
  361. notification.remove();
  362. }, 500);
  363. }, 2000);
  364. }
  365.  
  366. function saveRecentButtonInfo(buttonText, chatBoxName) {
  367. localStorage.setItem('recentButtonInfo', JSON.stringify({ buttonText, chatBoxName }));
  368. }
  369.  
  370. function clearRecentButtonInfo() {
  371. localStorage.removeItem('recentButtonInfo');
  372. }
  373.  
  374. function getButtonConfigurations() {
  375. return JSON.parse(localStorage.getItem('chatButtonConfig')) || { buttons: [] };
  376. }
  377.  
  378. function saveButtonConfigurations(config) {
  379. localStorage.setItem('chatButtonConfig', JSON.stringify(config));
  380. }
  381.  
  382. function getAPIKey() {
  383. return localStorage.getItem('apiKey') || '';
  384. }
  385.  
  386. function saveAPIKey(key) {
  387. localStorage.setItem('apiKey', key);
  388. showNotification('API key saved.');
  389. }
  390.  
  391. function saveCache(key, data) {
  392. const cacheData = {
  393. timestamp: Date.now(),
  394. data
  395. };
  396. localStorage.setItem(key, JSON.stringify(cacheData));
  397. }
  398.  
  399. function loadCache(key) {
  400. const cacheData = JSON.parse(localStorage.getItem(key));
  401. if (cacheData && (Date.now() - cacheData.timestamp < CACHE_TTL)) {
  402. return cacheData.data;
  403. }
  404. return null;
  405. }
  406.  
  407. function clearCache() {
  408. localStorage.removeItem('companyCache');
  409. localStorage.removeItem('factionCache');
  410. showNotification('API cache cleared.');
  411. }
  412.  
  413. function createUIPanel() {
  414. if (document.querySelector('.custom-ui-panel')) {
  415. return;
  416. }
  417.  
  418. const panel = document.createElement('div');
  419. panel.className = 'custom-ui-panel';
  420. panel.innerHTML = `
  421. <div class="tabs">
  422. <div class="tab active" data-tab="config-list-tab">Configured Buttons</div>
  423. <div class="tab" data-tab="config-edit-tab">Create/Edit Button</div>
  424. <div class="tab settings-tab" data-tab="config-settings-tab">⚙️</div>
  425. </div>
  426. <div id="config-list-tab" class="tab-content active">
  427. <div class="search-container">
  428. <input type="text" id="search-input" placeholder="Search...">
  429. <select id="search-select">
  430. <option value="buttonText">Text</option>
  431. <option value="condition">Condition</option>
  432. <option value="text">Message</option>
  433. </select>
  434. </div>
  435. <div id="button-configs"></div>
  436. </div>
  437. <div id="config-edit-tab" class="tab-content">
  438. <div>
  439. <label for="button-text">Button Text</label>
  440. <input type="text" id="button-text" placeholder="Button Text">
  441.  
  442. <label for="button-color">Background Color</label>
  443. <input type="color" id="button-color">
  444.  
  445. <label for="button-condition">Condition</label>
  446. <select id="button-condition">
  447. <option value="TradeChat">Trade Chat</option>
  448. <option value="HospitalChat">Hospital Chat</option>
  449. <option value="FactionChat">Faction Chat</option>
  450. <option value="CompanyChat">Company Chat</option>
  451. <option value="GlobalChat">Global Chat</option>
  452. <option value="UserChat">User Chat</option>
  453. </select>
  454.  
  455. <label for="button-text-content">Message</label>
  456. <textarea id="button-text-content" placeholder="Enter your message here. Use {name} for chatter's name, {company} for company info, {faction} for faction info."></textarea>
  457. <div class="char-counter" id="char-counter">0</div>
  458.  
  459. <button id="add-button">Add Button</button>
  460. <button id="edit-button" style="display: none;">Save Button</button>
  461. </div>
  462. </div>
  463. <div id="config-settings-tab" class="tab-content">
  464. <label for="api-key">API Key</label>
  465. <input type="text" id="api-key" placeholder="Enter your API key" value="${getAPIKey()}">
  466. <button id="save-api-key-button">Save API Key</button>
  467. <button id="import-button">Import Config (File)</button>
  468. <button id="export-button">Export Config (File)</button>
  469. <button id="clear-cache-button">Clear API Cache</button>
  470. </div>
  471. <button id="close-ui">Close</button>
  472. `;
  473. document.body.appendChild(panel);
  474.  
  475. document.querySelectorAll('.tab').forEach(tab => {
  476. tab.addEventListener('click', () => {
  477. switchTab(tab.dataset.tab);
  478. });
  479. });
  480.  
  481. document.getElementById('add-button').addEventListener('click', addNewButtonConfig);
  482. document.getElementById('edit-button').addEventListener('click', editButtonConfig);
  483. document.getElementById('close-ui').addEventListener('click', closeUI);
  484. document.getElementById('import-button').addEventListener('click', importConfig);
  485. document.getElementById('export-button').addEventListener('click', exportConfig);
  486. document.getElementById('clear-cache-button').addEventListener('click', clearCache);
  487. document.getElementById('button-text-content').addEventListener('input', updateCharCounter);
  488. document.getElementById('search-input').addEventListener('input', filterButtonConfigs);
  489. document.getElementById('save-api-key-button').addEventListener('click', () => {
  490. const key = document.getElementById('api-key').value;
  491. saveAPIKey(key);
  492. });
  493.  
  494. populateButtonConfigs();
  495. }
  496.  
  497. function switchTab(tabId) {
  498. document.querySelectorAll('.tab, .tab-content').forEach(el => {
  499. el.classList.remove('active');
  500. });
  501. document.querySelector(`[data-tab="${tabId}"]`).classList.add('active');
  502. document.getElementById(tabId).classList.add('active');
  503.  
  504. const panel = document.querySelector('.custom-ui-panel');
  505. if (tabId === 'config-list-tab') {
  506. panel.classList.add('config-list-tab-active');
  507. } else {
  508. panel.classList.remove('config-list-tab-active');
  509. }
  510. }
  511.  
  512. function populateButtonConfigs() {
  513. const configsContainer = document.getElementById('button-configs');
  514. configsContainer.innerHTML = '';
  515. const configs = getButtonConfigurations();
  516.  
  517. configs.buttons.forEach((buttonConfig, index) => {
  518. const configDiv = document.createElement('div');
  519. configDiv.className = 'button-config-card draggable';
  520. configDiv.dataset.index = index;
  521.  
  522. const textDiv = document.createElement('div');
  523. textDiv.innerHTML = `<strong>Text:</strong> ${buttonConfig.buttonText}`;
  524. configDiv.appendChild(textDiv);
  525.  
  526. const colorDiv = document.createElement('div');
  527. colorDiv.innerHTML = `<strong>Color:</strong> ${buttonConfig.backgroundColor}`;
  528. configDiv.appendChild(colorDiv);
  529.  
  530. const conditionDiv = document.createElement('div');
  531. conditionDiv.innerHTML = `<strong>Condition:</strong> ${buttonConfig.condition}`;
  532. configDiv.appendChild(conditionDiv);
  533.  
  534. const messageDiv = document.createElement('div');
  535. messageDiv.className = 'button-config-message';
  536. messageDiv.innerText = buttonConfig.text;
  537. configDiv.appendChild(messageDiv);
  538.  
  539. const editButton = document.createElement('button');
  540. editButton.textContent = 'Edit';
  541. editButton.addEventListener('click', () => {
  542. selectForEdit(index);
  543. switchTab('config-edit-tab');
  544. });
  545. configDiv.appendChild(editButton);
  546.  
  547. const deleteButton = document.createElement('button');
  548. deleteButton.textContent = 'Delete';
  549. deleteButton.addEventListener('click', () => deleteButtonConfig(index));
  550. configDiv.appendChild(deleteButton);
  551.  
  552. const moveUpButton = document.createElement('button');
  553. moveUpButton.textContent = 'Up';
  554. moveUpButton.addEventListener('click', () => moveButtonConfig(index, -1));
  555. configDiv.appendChild(moveUpButton);
  556.  
  557. const moveDownButton = document.createElement('button');
  558. moveDownButton.textContent = 'Down';
  559. moveDownButton.addEventListener('click', () => moveButtonConfig(index, 1));
  560. configDiv.appendChild(moveDownButton);
  561.  
  562. configsContainer.appendChild(configDiv);
  563. });
  564. }
  565.  
  566. function filterButtonConfigs() {
  567. const searchInput = document.getElementById('search-input').value.toLowerCase();
  568. const searchBy = document.getElementById('search-select').value;
  569. const configs = getButtonConfigurations();
  570.  
  571. const filteredConfigs = configs.buttons.filter(buttonConfig => {
  572. const fieldValue = buttonConfig[searchBy].toLowerCase();
  573. return fieldValue.includes(searchInput);
  574. });
  575.  
  576. const configsContainer = document.getElementById('button-configs');
  577. configsContainer.innerHTML = '';
  578.  
  579. filteredConfigs.forEach((buttonConfig, index) => {
  580. const configDiv = document.createElement('div');
  581. configDiv.className = 'button-config-card draggable';
  582. configDiv.dataset.index = index;
  583.  
  584. const textDiv = document.createElement('div');
  585. textDiv.innerHTML = `<strong>Text:</strong> ${buttonConfig.buttonText}`;
  586. configDiv.appendChild(textDiv);
  587.  
  588. const colorDiv = document.createElement('div');
  589. colorDiv.innerHTML = `<strong>Color:</strong> ${buttonConfig.backgroundColor}`;
  590. configDiv.appendChild(colorDiv);
  591.  
  592. const conditionDiv = document.createElement('div');
  593. conditionDiv.innerHTML = `<strong>Condition:</strong> ${buttonConfig.condition}`;
  594. configDiv.appendChild(conditionDiv);
  595.  
  596. const messageDiv = document.createElement('div');
  597. messageDiv.className = 'button-config-message';
  598. messageDiv.innerText = buttonConfig.text;
  599. configDiv.appendChild(messageDiv);
  600.  
  601. const editButton = document.createElement('button');
  602. editButton.textContent = 'Edit';
  603. editButton.addEventListener('click', () => {
  604. selectForEdit(index);
  605. switchTab('config-edit-tab');
  606. });
  607. configDiv.appendChild(editButton);
  608.  
  609. const deleteButton = document.createElement('button');
  610. deleteButton.textContent = 'Delete';
  611. deleteButton.addEventListener('click', () => deleteButtonConfig(index));
  612. configDiv.appendChild(deleteButton);
  613.  
  614. const moveUpButton = document.createElement('button');
  615. moveUpButton.textContent = 'Up';
  616. moveUpButton.addEventListener('click', () => moveButtonConfig(index, -1));
  617. configDiv.appendChild(moveUpButton);
  618.  
  619. const moveDownButton = document.createElement('button');
  620. moveDownButton.textContent = 'Down';
  621. moveDownButton.addEventListener('click', () => moveButtonConfig(index, 1));
  622. configDiv.appendChild(moveDownButton);
  623.  
  624. configsContainer.appendChild(configDiv);
  625. });
  626. }
  627.  
  628. function selectForEdit(index) {
  629. const config = getButtonConfigurations().buttons[index];
  630. document.getElementById('button-text').value = config.buttonText;
  631. document.getElementById('button-color').value = config.backgroundColor;
  632. document.getElementById('button-condition').value = config.condition;
  633. document.getElementById('button-text-content').value = config.text;
  634.  
  635. document.getElementById('add-button').style.display = 'block';
  636. document.getElementById('edit-button').style.display = 'block';
  637. document.getElementById('edit-button').setAttribute('data-edit-index', index);
  638. updateCharCounter();
  639. }
  640.  
  641. function deleteButtonConfig(index) {
  642. const config = getButtonConfigurations();
  643. config.buttons.splice(index, 1);
  644. saveButtonConfigurations(config);
  645. populateButtonConfigs();
  646. showNotification('Button deleted.');
  647. }
  648.  
  649. function moveButtonConfig(index, direction) {
  650. const config = getButtonConfigurations();
  651. const newIndex = index + direction;
  652.  
  653. if (newIndex >= 0 && newIndex < config.buttons.length) {
  654. const buttonConfig = config.buttons.splice(index, 1)[0];
  655. config.buttons.splice(newIndex, 0, buttonConfig);
  656. saveButtonConfigurations(config);
  657. populateButtonConfigs();
  658. showNotification('Button moved.');
  659. }
  660. }
  661.  
  662. function addNewButtonConfig() {
  663. const buttonText = document.getElementById('button-text').value;
  664. const backgroundColor = document.getElementById('button-color').value;
  665. const condition = document.getElementById('button-condition').value;
  666. const text = document.getElementById('button-text-content').value;
  667.  
  668. const config = getButtonConfigurations();
  669. config.buttons.push({ buttonText, backgroundColor, condition, text });
  670. saveButtonConfigurations(config);
  671. populateButtonConfigs();
  672. highlightButton(config.buttons.length - 1);
  673. switchTab('config-list-tab');
  674.  
  675. clearInputFields();
  676. showNotification('Button added.');
  677. }
  678.  
  679. function editButtonConfig() {
  680. const index = parseInt(document.getElementById('edit-button').getAttribute('data-edit-index'), 10);
  681. const buttonText = document.getElementById('button-text').value;
  682. const backgroundColor = document.getElementById('button-color').value;
  683. const condition = document.getElementById('button-condition').value;
  684. const text = document.getElementById('button-text-content').value;
  685.  
  686. const config = getButtonConfigurations();
  687. config.buttons[index] = { buttonText, backgroundColor, condition, text };
  688. saveButtonConfigurations(config);
  689. populateButtonConfigs();
  690. highlightButton(index);
  691. switchTab('config-list-tab');
  692.  
  693. document.getElementById('add-button').style.display = 'block';
  694. document.getElementById('edit-button').style.display = 'none';
  695.  
  696. clearInputFields();
  697. showNotification('Button edited.');
  698. }
  699.  
  700. function clearInputFields() {
  701. document.getElementById('button-text').value = '';
  702. document.getElementById('button-text-content').value = '';
  703. document.getElementById('button-color').value = '';
  704. updateCharCounter();
  705. }
  706.  
  707. function closeUI() {
  708. const panel = document.querySelector('.custom-ui-panel');
  709. if (panel) {
  710. panel.remove();
  711. }
  712. }
  713.  
  714. function createConfigButton() {
  715. const noteButton = document.querySelector('.chat-note-button___oaIVx');
  716. if (!noteButton || document.querySelector('#chat-config-button')) return;
  717.  
  718. const button = document.createElement('button');
  719. button.id = 'chat-config-button';
  720. button.type = 'button';
  721. button.title = 'Edit Chat Buttons';
  722. button.className = 'chat-list-button___d1Olw';
  723.  
  724. const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  725. svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
  726. svg.setAttribute('viewBox', '0 0 512 512');
  727. svg.setAttribute('height', '14');
  728. svg.setAttribute('width', '14');
  729. svg.innerHTML = `
  730. <path d="M312 201.8c0-17.4 9.2-33.2 19.9-47C344.5 138.5 352 118.1 352 96c0-53-43-96-96-96s-96 43-96 96c0 22.1 7.5 42.5 20.1 58.8c10.7 13.8 19.9 29.6 19.9 47c0 29.9-24.3 54.2-54.2 54.2L112 256C50.1 256 0 306.1 0 368c0 20.9 13.4 38.7 32 45.3L32 464c0 26.5 21.5 48 48 48l352 0c26.5 0 48-21.5 48-48l0-50.7c18.6-6.6 32-24.4 32-45.3c0-61.9-50.1-112-112-112l-33.8 0c-29.9 0-54.2-24.3-54.2-54.2zM416 416l0 32L96 448l0-32 320 0z" fill="url(#config-default-blue)"/>
  731. <defs>
  732. <linearGradient id="config-default-blue" x1="0.5" x2="0.5" y2="1">
  733. <stop offset="0" stop-color="#8faeb4"/>
  734. <stop offset="1" stop-color="#638c94"/>
  735. </linearGradient>
  736. <linearGradient id="config-hover-blue" x1="0.5" x2="0.5" y2="1">
  737. <stop offset="0" stop-color="#eaf0f1"/>
  738. <stop offset="1" stop-color="#7b9fa6"/>
  739. </linearGradient>
  740. </defs>
  741. `;
  742.  
  743. button.appendChild(svg);
  744. button.addEventListener('click', createUIPanel);
  745.  
  746. const path = svg.querySelector('path');
  747. button.addEventListener('mouseenter', () => path.setAttribute('fill', 'url(#config-hover-blue)'));
  748. button.addEventListener('mouseleave', () => path.setAttribute('fill', 'url(#config-default-blue)'));
  749.  
  750. noteButton.insertAdjacentElement('afterend', button);
  751. }
  752.  
  753.  
  754. function applyButtonConfigurations() {
  755. const configs = getButtonConfigurations();
  756. document.querySelectorAll('.chat-box___mHm01').forEach(chatBox => {
  757. configs.buttons.forEach(buttonConfig => {
  758. const conditionFunc = conditions[buttonConfig.condition];
  759. if (conditionFunc && conditionFunc(chatBox) && !chatBox.querySelector(`[data-button-text="${buttonConfig.buttonText}"]`)) {
  760. const button = document.createElement('button');
  761. button.className = 'custom-chat-button';
  762. button.innerText = buttonConfig.buttonText;
  763. button.style.backgroundColor = buttonConfig.backgroundColor;
  764. button.setAttribute('data-button-text', buttonConfig.buttonText);
  765. button.addEventListener('click', (event) => addCustomText(chatBox, buttonConfig.text, event));
  766. button.addEventListener('mousedown', (event) => {
  767. if (event.button === 0) {
  768. let timer;
  769. const delay = 1000;
  770. timer = setTimeout(() => {
  771. button.classList.remove('recent');
  772. clearRecentButtonInfo();
  773. }, delay);
  774. button.addEventListener('mouseup', () => {
  775. clearTimeout(timer);
  776. }, { once: true });
  777. button.addEventListener('mouseleave', () => {
  778. clearTimeout(timer);
  779. }, { once: true });
  780. }
  781. });
  782.  
  783. const footerContainer = chatBox.querySelector('.chat-box-footer___YK914');
  784. if (footerContainer) {
  785. let buttonContainer = footerContainer.parentElement.querySelector('.button-container');
  786. if (!buttonContainer) {
  787. buttonContainer = document.createElement('div');
  788. buttonContainer.className = 'button-container';
  789. buttonContainer.style.display = 'flex';
  790. buttonContainer.style.flexWrap = 'wrap';
  791. footerContainer.insertAdjacentElement('beforebegin', buttonContainer);
  792. }
  793. buttonContainer.appendChild(button);
  794. }
  795. }
  796. });
  797. });
  798. }
  799.  
  800. async function addCustomText(chatBox, messageTemplate, event) {
  801. const nameElement = chatBox.querySelector('.typography___Dc5WV');
  802. const name = nameElement ? nameElement.textContent.trim() : 'Trader';
  803. let message = messageTemplate.replace('{name}', name);
  804.  
  805. if (message.includes('{company}')) {
  806. const apiKey = getAPIKey();
  807. if (!apiKey) {
  808. alert('API key not set. Please set the API key in the settings tab.');
  809. return;
  810. }
  811.  
  812. let companyInfo = loadCache('companyCache');
  813. if (!companyInfo) {
  814. const apiUrl = `https://api.torn.com/company/?selections=profile&key=${apiKey}`;
  815. try {
  816. const response = await fetch(apiUrl);
  817. const data = await response.json();
  818. if (!data.error && data.company) {
  819. companyInfo = data.company;
  820. saveCache('companyCache', companyInfo);
  821. } else {
  822. alert('Failed to retrieve company information. Check your API key.');
  823. return;
  824. }
  825. } catch (error) {
  826. alert('Error fetching company information:', error);
  827. return;
  828. }
  829. }
  830.  
  831. const companyType = companyTypes[companyInfo.company_type] || 'Unknown';
  832. const companyDetails = `${companyInfo.rating}* ${companyType}`;
  833. message = message.replace('{company}', companyDetails);
  834. }
  835.  
  836. if (message.includes('{faction}')) {
  837. const apiKey = getAPIKey();
  838. if (!apiKey) {
  839. alert('API key not set. Please set the API key in the settings tab.');
  840. return;
  841. }
  842.  
  843. let factionInfo = loadCache('factionCache');
  844. if (!factionInfo) {
  845. const apiUrl = `https://api.torn.com/faction/?selections=basic&key=${apiKey}`;
  846. try {
  847. const response = await fetch(apiUrl);
  848. const data = await response.json();
  849. if (!data.error && data.respect && data.name && data.rank) {
  850. factionInfo = data;
  851. saveCache('factionCache', factionInfo);
  852. } else {
  853. alert('Failed to retrieve faction information. Check your API key.');
  854. return;
  855. }
  856. } catch (error) {
  857. alert('Error fetching faction information:', error);
  858. return;
  859. }
  860. }
  861.  
  862. const respectFormatted = factionInfo.respect >= 1000000 ? (factionInfo.respect / 1000000).toFixed(1) + 'm' : (factionInfo.respect / 1000).toFixed(1) + 'k';
  863. const factionDetails = `${factionInfo.name}, ${factionInfo.rank.name} Ranked ${respectFormatted} Respect`;
  864. message = message.replace('{faction}', factionDetails);
  865. }
  866.  
  867. insertMessage(chatBox, message, event.target);
  868. }
  869.  
  870. function insertMessage(chatBox, message, targetButton) {
  871. navigator.clipboard.writeText(message).then(() => {
  872. const textArea = chatBox.querySelector('textarea');
  873. if (!textArea) return;
  874. textArea.focus();
  875. textArea.value = '';
  876. const startPos = textArea.selectionStart;
  877. const endPos = textArea.selectionEnd;
  878. textArea.setRangeText(message, startPos, endPos, 'end');
  879. textArea.dispatchEvent(new Event('input', { bubbles: true }));
  880. textArea.focus();
  881. textArea.selectionStart = textArea.selectionEnd = startPos + message.length;
  882.  
  883. document.querySelectorAll('.custom-chat-button').forEach(btn => {
  884. btn.classList.remove('recent');
  885. });
  886. targetButton.classList.add('recent');
  887.  
  888. const chatBoxNameElem = chatBox.querySelector('.chat-box-header__name___jIjjM');
  889. const chatBoxName = chatBoxNameElem ? chatBoxNameElem.textContent : '';
  890. saveRecentButtonInfo(targetButton.getAttribute('data-button-text'), chatBoxName);
  891. });
  892. }
  893.  
  894. function applyRecentButtonClass() {
  895. const recentButtonInfo = JSON.parse(localStorage.getItem('recentButtonInfo'));
  896. if (recentButtonInfo) {
  897. document.querySelectorAll('.custom-chat-button').forEach(btn => {
  898. btn.classList.remove('recent');
  899. });
  900.  
  901. document.querySelectorAll('.chat-box___mHm01').forEach(chatBox => {
  902. const chatBoxNameElem = chatBox.querySelector('.chat-box-header__name___jIjjM');
  903. const chatBoxName = chatBoxNameElem ? chatBoxNameElem.textContent : '';
  904. if (chatBoxName === recentButtonInfo.chatBoxName) {
  905. const button = chatBox.querySelector(`[data-button-text="${recentButtonInfo.buttonText}"]`);
  906. if (button) {
  907. button.classList.add('recent');
  908. }
  909. }
  910. });
  911. }
  912. }
  913.  
  914. function importConfig() {
  915. const input = document.createElement('input');
  916. input.type = 'file';
  917. input.accept = '.json';
  918. input.onchange = async (event) => {
  919. const file = event.target.files[0];
  920. if (!file) {
  921. showNotification('No file selected.');
  922. return;
  923. }
  924. const reader = new FileReader();
  925. reader.onload = (e) => {
  926. try {
  927. const config = JSON.parse(e.target.result);
  928. if (config && config.buttons) {
  929. saveButtonConfigurations(config);
  930. populateButtonConfigs();
  931. applyButtonConfigurations();
  932. showNotification('Configuration imported from file.');
  933. } else {
  934. showNotification('Invalid configuration file.');
  935. }
  936. } catch (err) {
  937. showNotification('Error: Invalid JSON.');
  938. }
  939. };
  940. reader.readAsText(file);
  941. };
  942. input.click();
  943. }
  944.  
  945. function exportConfig() {
  946. const config = getButtonConfigurations();
  947. const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
  948. const url = URL.createObjectURL(blob);
  949. const a = document.createElement('a');
  950. a.href = url;
  951. a.download = 'chatButtonConfig.json';
  952. document.body.appendChild(a);
  953. a.click();
  954. document.body.removeChild(a);
  955. URL.revokeObjectURL(url);
  956. showNotification('Configuration exported to file.');
  957. }
  958.  
  959. function updateCharCounter() {
  960. const textArea = document.getElementById('button-text-content');
  961. if (!textArea) return;
  962. const counter = document.getElementById('char-counter');
  963. if (!counter) return;
  964. counter.textContent = textArea.value.length;
  965. }
  966.  
  967. function highlightButton(index) {
  968. const configsContainer = document.getElementById('button-configs');
  969. const buttonDiv = configsContainer.querySelector(`.draggable[data-index="${index}"]`);
  970. if (buttonDiv) {
  971. buttonDiv.classList.add('highlight');
  972. buttonDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
  973. setTimeout(() => {
  974. buttonDiv.classList.remove('highlight');
  975. }, 2000);
  976. }
  977. }
  978.  
  979. addCSS(buttonCSS);
  980.  
  981. const chatContainerObserver = new MutationObserver(function() {
  982. createConfigButton();
  983. applyButtonConfigurations();
  984. applyRecentButtonClass();
  985. });
  986.  
  987. const chatContainer = document.querySelector('#chatRoot');
  988. if (chatContainer) {
  989. chatContainerObserver.observe(chatContainer, { childList: true, subtree: true });
  990. }
  991.  
  992. applyButtonConfigurations();
  993. applyRecentButtonClass();
  994. })();

QingJ © 2025

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