Greasy Fork is available in English.
Sends a revive request to United Flying Doctors
// ==UserScript==
// @name UFD Revive Request Button
// @namespace http://tampermonkey.net/
// @version 2.5
// @description Sends a revive request to United Flying Doctors
// @author Exiled[3527247] + NoNitro[3562297]
// @match https://www.torn.com/hospitalview.php*
// @match https://www.torn.com/hospital.php*
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function() {
'use strict';
const BUTTON_CLASS = 'request-revive-link';
const STYLE_ID = 'ufd-request-revive-style';
const ICON_URL = 'https://exiled.top/images/ufd-revive-icon.webp';
const USER_INFO_SELECTOR = '#sidebar > div:nth-child(1) > div > div.user-information___VBSOk > div > div.toggle-content___BJ9Q9 > div > p > a';
function isMobilePDA() {
const ua = navigator.userAgent || '';
return /Android|iPhone|iPad|iPod|Mobile/i.test(ua);
}
function addStyle() {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = `
.${BUTTON_CLASS}:hover .request-revive-img {
filter: brightness(1.3);
}
.${BUTTON_CLASS} .request-revive-img {
display: inline-block;
vertical-align: middle;
max-width: none;
}
#top-page-links-list .${BUTTON_CLASS}[data-ufd-mode="mobile"] {
display: inline-block;
vertical-align: middle;
margin-left: 1px;
margin-right: 5px;
line-height: 24px;
position: relative;
top: 5px;
}
#top-page-links-list .${BUTTON_CLASS}[data-ufd-mode="mobile"] .request-revive-img {
width: 28px;
height: 22px;
}
#top-page-links-list .${BUTTON_CLASS}[data-ufd-mode="desktop"] {
display: inline-block;
vertical-align: middle;
margin-left: -3px;
margin-right: 17px;
line-height: 24px;
position: relative;
top: -0px;
}
#top-page-links-list .${BUTTON_CLASS}[data-ufd-mode="desktop"] .request-revive-img {
width: 29px;
height: 21.75px;
}
`;
document.head.appendChild(style);
}
function getButtonInnerHTML() {
return `<img src="${ICON_URL}" class="request-revive-img" alt="Request Revive">`;
}
function buildOrUpdateButton(isMobile) {
let btn = document.querySelector('.' + BUTTON_CLASS);
const desiredMode = isMobile ? 'mobile' : 'desktop';
const desiredHTML = getButtonInnerHTML();
const desiredClassName = `${BUTTON_CLASS} request-revive t-clear h c-pointer m-icon line-h24 right`;
if (!btn) {
btn = document.createElement('a');
btn.href = '#';
btn.addEventListener('click', function(e) {
e.preventDefault();
RequestRevive({});
});
}
if (btn.className !== desiredClassName) {
btn.className = desiredClassName;
}
if (btn.dataset.ufdMode !== desiredMode) {
btn.dataset.ufdMode = desiredMode;
}
if (btn.innerHTML !== desiredHTML) {
btn.innerHTML = desiredHTML;
}
btn.title = 'Request Revive';
return btn;
}
function insertButton() {
const linksList = document.querySelector('#top-page-links-list');
if (!linksList) return false;
const btn = buildOrUpdateButton(isMobilePDA());
const reviveAvailability = linksList.querySelector('.revive-availability-btn');
if (!btn.parentNode) {
if (reviveAvailability) {
linksList.insertBefore(btn, reviveAvailability);
} else {
linksList.appendChild(btn);
}
} else if (reviveAvailability && btn.nextSibling !== reviveAvailability) {
linksList.insertBefore(btn, reviveAvailability);
}
return true;
}
function getHospitalTimer() {
try {
const header = JSON.parse(sessionStorage.getItem('headerData'));
const userID = header?.user?.data?.userID;
const sidebar = JSON.parse(sessionStorage.getItem(`sidebarData${userID}`));
const hospital = sidebar?.statusIcons?.icons?.hospital || null;
if (!hospital || !hospital.timerExpiresAt) {
return null;
}
return hospital.timerExpiresAt;
} catch (e) {
console.error('Failed to get timerExpiresAt from sidebarData:', e);
return null;
}
}
function getUserIdentity() {
const userInfo = document.querySelector(USER_INFO_SELECTOR);
if (userInfo) {
const match = userInfo.href.match(/XID=(\d+)/);
const userId = match ? match[1] : null;
const userName = userInfo.textContent.trim();
if (userId && userName) {
return { userId, userName };
}
}
try {
const header = JSON.parse(sessionStorage.getItem('headerData'));
const userId = header?.user?.data?.userID;
const userName =
window.topBannerInitData?.user?.playername ||
window.topBannerInitData?.playername ||
null;
if (userId && userName) {
return { userId: String(userId), userName: String(userName).trim() };
}
} catch (e) {
console.error('Failed to get identity from headerData/topBannerInitData:', e);
}
try {
const tornUserRaw = document.querySelector('#torn-user')?.value;
if (tornUserRaw) {
const tornUser = JSON.parse(tornUserRaw);
const userId = tornUser?.id;
const userName = tornUser?.playername;
if (userId && userName) {
return { userId: String(userId), userName: String(userName).trim() };
}
}
} catch (e) {
console.error('Failed to get identity from #torn-user:', e);
}
return { userId: null, userName: null };
}
function RequestRevive(payload) {
const { userId, userName } = getUserIdentity();
const timerExpiresAt = getHospitalTimer();
if (!timerExpiresAt) {
alert('You can only request a revive while hospitalized!');
return;
}
payload.userId = userId;
payload.userName = userName;
payload.userCountry = document.querySelector('#tcLogo')?.getAttribute('title') || 'Torn';
payload.timerExpiresAt = timerExpiresAt;
GM_xmlhttpRequest({
method: 'POST',
url: 'http://revive.exiled.top:5000/api/userdata',
data: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
onload: function(response) {
try {
const res = JSON.parse(response.responseText);
if (res?.message) alert(res.message);
} catch {}
}
});
}
function init() {
addStyle();
insertButton();
const observer = new MutationObserver(insertButton);
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();