您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Easily extend the rental property based on the previous rental agreement from your activity log
当前为
// ==UserScript== // @name Easy Property Rent Extension // @namespace easy.property.rent.extension // @version v2.0.0 // @description Easily extend the rental property based on the previous rental agreement from your activity log // @author IceBlueFire [776] // @license MIT // @match https://www.torn.com/properties.php* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @grant GM_xmlhttpRequest // @grant GM_addStyle // @require https://code.jquery.com/jquery-1.8.2.min.js // ==/UserScript== /******************** CONFIG SETTINGS ********************/ const apikey = "#######"; // Full access API key required to pull historical activity log data. const properties = [13]; // Array of property types allowed for renting. Ex: [12, 13] for Castles and Private Islands. Reference property IDs below as necessary. const days_remaining = 3; // Number of days remaining or less to be included in reminders. const hex_color = "#8ABEEF"; // Hexcode to apply to the box. const debug = 0; // Leave alone unless you want console logs. /****************** END CONFIG SETTINGS *******************/ /* Property IDs * 1 = Trailer; * 2 = Apartment; * 3 = Semi-Detached House; * 4 = Detached House; * 5 = Beach House; * 6 = Chalet; * 7 = Villa; * 8 = Penthouse; * 9 = Mansion; * 10 = Ranch; * 11 = Palace; * 12 = Castle; * 13 = Private Island; */ /******************** DO NOT TOUCH ANYTHING BELOW THIS ********************/ $(document).ready(function() { log("Ready"); let debounceTimeout; let rentalHistoryCache = null; let propertyListCache = null; let current_page = null; let hex_darker = "#53728f"; let player_id = $('#sidebarroot a[href*="profiles.php?XID="]').attr('href')?.match(/XID=(\d+)/)?.[1]; function drawNavigation() { let valid_properties = []; let total_properties = 0; const navigation_div = ` <div id="icey-container" style="display:none;"> <div id="icey-property-rentals" class="m-top10"> <div class="icey-head"> <span class="icey-title">Easy Rental Extensions</span> </div> <div class="icey-content"> <a id="previous-button" class="torn-btn">Previous</a> <div id="info-div" class="center-info"><p><strong>Rental Property Information</strong></p> <p><span class="rentals-count">0</span> / <span class="total-count">0</span> properties requiring attention.</p> <p class="viewing-rental"><span class="current-index">0</span> / <span class="rentals-count"></p> </div> <a id="next-button" class="torn-btn">Next</a> </div> <hr class="page-head-delimiter m-top10 m-bottom10"> </div> </div> `; if ($('#icey-property-rentals').length === 0) { log('Insert navigation div'); $('#properties-page-wrap .content-title.m-bottom10').after(navigation_div); const propertyList = propertyListCache ? Promise.resolve(propertyListCache) : getPropertyList(); propertyList.then(function(result) { if (!propertyListCache) { propertyListCache = result; } $.each(result.properties, function(key, value) { if (value.owner_id == player_id && properties.includes(value.property_type)) { total_properties++; if(value.rented && value.rented.days_left <= days_remaining) { valid_properties.push(key); } } }); // Get the current property ID from the URL const current_property_id = getParam('ID'); let current_index = valid_properties.indexOf(current_property_id); if (current_index > 0) { const previous_property_id = valid_properties[current_index - 1]; $('#previous-button').attr('href', `#/p=options&ID=${previous_property_id}&tab=offerExtension`); } else { $('#previous-button').attr('href', '#').addClass('disabled'); // Disable if no previous } // Handle the "Next" button if (current_index < valid_properties.length - 1) { const next_property_id = valid_properties[current_index === -1 ? 0 : current_index + 1]; $('#next-button').attr('href', `#/p=options&ID=${next_property_id}&tab=offerExtension`); } else { $('#next-button').attr('href', '#').addClass('disabled'); // Disable if no next } $('#icey-container .total-count').html(total_properties); $('#icey-container .rentals-count').html(valid_properties.length); if(current_index === -1 || current_page == 'properties') { $('#icey-container .viewing-rental').hide(); } else { $('#icey-container .viewing-rental').show(); $('#icey-container .current-index').html(current_index + 1); } setDynamicGradient(); $('.icey-head').css('background', `linear-gradient(180deg, ${hex_color}, ${hex_darker})`); $('#icey-container').show(); }); } } function checkTabAndRunScript() { // Do the stuff! drawNavigation(); if (getParam('tab') === 'offerExtension') { let current_renter = null; let link = $('.offerExtension-form').find('a.h.t-blue'); if (link.length > 0) { current_renter = link.attr('href')?.match(/XID=(\d+)/)?.[1] || null; log("Renter: " + current_renter); getPreviousValues(current_renter); } else { log("No renter found."); } } else { log("Wrong properties tab."); } } function getPreviousValues(current_renter) { // Look for previous rental agreements and auto-fill input boxes var duration = 0; var cost = 0; const property_id = getParam('ID'); const activity = rentalHistoryCache ? Promise.resolve(rentalHistoryCache) : getRentalHistory(); activity.then(function(result) { // Cache the result if not already cached if (!rentalHistoryCache) { rentalHistoryCache = result; } $.each(result.log, function(key, value) { if(value.data.property_id == property_id && value.data.renter == current_renter) { duration = value.data.days; cost = value.data.rent; return false; } }); if(duration != 0) { // We found a previous rental for this property that matched the current renter // Target the input fields let costInput = $('.offerExtension.input-money[data-name="offercost"]'); let daysInput = $('.offerExtension.input-money[data-name="days"]'); // Set the values costInput.val(cost); daysInput.val(duration); // costInput.tornInputMoney({buttonElement: null, skipBlurCheck: true}); // Trigger events to mimic manual input // costInput.trigger('input').trigger('change').trigger('keyup'); // Attempt to simulate input event for Torn // daysInput.trigger('input').trigger('change').trigger('keyup'); // Attempt to simulate input event for Torn $('.offerExtension-form input[type="submit"]').prop('disabled', false); } }); } async function getRentalHistory() { // Get the activity log for both rental extensions and new rental agreements return new Promise(resolve => { const request_url = `https://api.torn.com/user/?selections=log&key=`+apikey+`&log=5943,5937&comment=EasyRentalExtensions`; GM_xmlhttpRequest ({ method: "GET", url: request_url, headers: { "Content-Type": "application/json" }, onload: response => { try { const data = JSON.parse(response.responseText); if(!data) { log('No response from Torn API'); } else { log('Log data fetched.'); return resolve(data); } } catch (e) { console.error(e); } }, onerror: (e) => { console.error(e); } }) }); } async function getPropertyList() { // Get the activity log for both rental extensions and new rental agreements return new Promise(resolve => { const request_url = `https://api.torn.com/user/?selections=properties&key=`+apikey+`&comment=EasyRentalExtensions`; GM_xmlhttpRequest ({ method: "GET", url: request_url, headers: { "Content-Type": "application/json" }, onload: response => { try { const data = JSON.parse(response.responseText); if(!data) { log('No response from Torn API'); } else { log('Property data fetched.'); return resolve(data); } } catch (e) { console.error(e); } }, onerror: (e) => { console.error(e); } }) }); } function getParam(name) { // Extract the part of the URL after the # const fragment = window.location.href.split('#')[1]; if (!fragment) return null; // No fragment present // Treat the fragment like a query string const results = new RegExp(name + '=([^&#]*)').exec(fragment); return results ? decodeURIComponent(results[1]) : null; // Decode and return the value, or null if not found } function log(message) { if(debug){ console.log("[RentExtension] "+message); } } function setDynamicGradient() { // Convert base hex to RGB const baseRgb = hexToRgb(hex_color); // Create a darker shade by reducing brightness const darkerRgb = darkenRgb(baseRgb, 0.7); // 0.7 is the factor for darkening // Convert the darker RGB back to hex hex_darker = rgbToHex(darkerRgb.r, darkerRgb.g, darkerRgb.b); } // Helper: Convert hex to RGB function hexToRgb(hex) { const bigint = parseInt(hex.replace('#', ''), 16); return { r: (bigint >> 16) & 255, g: (bigint >> 8) & 255, b: bigint & 255, }; } // Helper: Darken an RGB color function darkenRgb(rgb, factor) { return { r: Math.max(0, Math.min(255, Math.floor(rgb.r * factor))), g: Math.max(0, Math.min(255, Math.floor(rgb.g * factor))), b: Math.max(0, Math.min(255, Math.floor(rgb.b * factor))), }; } // Helper: Convert RGB to hex function rgbToHex(r, g, b) { return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`; } // Create an observer for the properties page to watch for page changes // Select the target node const targetNode = document.getElementById('properties-page-wrap'); if (targetNode) { // Create a MutationObserver to watch for changes const observer = new MutationObserver((mutationsList) => { clearTimeout(debounceTimeout); // Reset the debounce timeout on every change debounceTimeout = setTimeout(() => { log("Content changed in #properties-page-wrap"); current_page = getParam('p'); checkTabAndRunScript(); // Run your script when content settles }, 500); // Debounce for 500ms }); // Start observing the target node for configured mutations observer.observe(targetNode, { childList: true, // Watch for added/removed child nodes subtree: true, // Watch the entire subtree of the target node }); //console.log("MutationObserver is set up with debouncing."); } else { console.error("Target node #properties-page-wrap not found."); } // Run the script initially in case the page is already on the correct tab checkTabAndRunScript(); }); GM_addStyle(` #icey-container { width: 100%; } .icey-head { border-radius: 5px 5px 0 0; height: 30px; line-height: 30px; width: 100%; background: linear-gradient(180deg, #8ABEEF, #53728f); color: white; } .icey-title { width: 100%; font-size: 13px; letter-spacing: 1px; font-weight: 700; line-height: 16px; flex-grow: 2; padding: 5px; margin: 5px; text-transform: capitalize; text-shadow: rgba(0, 0, 0, 0.65) 1px 1px 2px; } body.dark-mode .icey-content { background: #333333; } body.dark-mode .icey-content td { color: #c0c0c0; } .icey-content { display: flex; justify-content: space-between; align-items: center; padding: 5px; background: #f2f2f2; border-radius: 0 0 5px 5px; box-shadow: 0 1px 3px #06060680; } .icey-content .torn-btn { flex: 0 0 auto; } .center-info { flex: 1 1 auto; text-align: center; } `);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址