Last active
September 21, 2022 11:29
-
-
Save hearimm/f1c21aee3beb2c2149d067b55a85d322 to your computer and use it in GitHub Desktop.
fifa23 fut web app tampermonkey helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Fifa 23 FUT Show Futbin player price | |
// @version 0.1 | |
// @description Show the Futbin prices for players in the Search Results, Club Search and Trade Pile | |
// @license MIT | |
// @author Hyuk Choi | |
// @match https://www.ea.com/fifa/ultimate-team/web-app/ | |
// @match https://www.ea.com/*/fifa/ultimate-team/web-app/* | |
// @namespace https://github.com/hearimm | |
// @grant GM_xmlhttpRequest | |
// @grant GM.xmlHttpRequest | |
// @grant GM_setClipboard | |
// @grant GM_addStyle | |
// @grant GM_getResourceText | |
// @connect www.futbin.com | |
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js | |
// @require https://cdn.jsdelivr.net/npm/axios@^0.22.0/dist/axios.min.js | |
// @require https://cdn.jsdelivr.net/npm/axios-userscript-adapter@~0.1.7/dist/axiosGmxhrAdapter.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery-toast-plugin/1.3.2/jquery.toast.min.js | |
// @resource jqToast_CSS https://cdnjs.cloudflare.com/ajax/libs/jquery-toast-plugin/1.3.2/jquery.toast.min.css | |
// | |
// Thanks to braceta for your work. i'm work just old codes. he's stuff https://github.com/braceta | |
// Thanks to Mardaneus86 for the inspiration | |
// Check he's stuff at https://github.com/Mardaneus86/futwebapp-tampermonkey/ | |
// ==/UserScript== | |
var cssSrc = GM_getResourceText ("jqToast_CSS"); | |
GM_addStyle(cssSrc); | |
console.log(axios.defaults.adapter); | |
axios.defaults.adapter = axiosGmxhrAdapter; | |
function toast(option) { | |
const defaultOption = { | |
text: 'hello world! <button>click me!</button>', | |
heading: 'Hello!', // Optional heading to be shown on the toast | |
loader: true, | |
loaderBg: '#9EC600', | |
showHideTransition: 'slide', // fade, slide or plain | |
allowToastClose: true, // Boolean value true or false | |
hideAfter: 10000, // false to make it sticky or number representing the miliseconds as time after which toast needs to be hidden | |
stack: 5, // false if there should be only one toast at a time or a number representing the maximum number of toasts to be shown at a time | |
position: 'top-right', | |
bgColor: '#444444', | |
textColor: '#eeeeee', | |
...option | |
} | |
$.toast(defaultOption); | |
} | |
var main = function() { | |
"use strict"; | |
$("head").append(`<style id="addedCSS" type="text/css"> | |
.listFUTItem .auction>.auction-state, .listFUTItem .auction>.auctionStartPrice, .listFUTItem .auction>.auctionValue { | |
flex: 1 1 30%; | |
overflow: hidden; | |
} | |
.ut-transfer-list-view .sectioned-item-list:nth-child(3) .listFUTItem .auction >div:not(.futbin) { | |
display: none; | |
} | |
.ut-club-search-results-view .listFUTItem .auction >div:not(.futbin) { | |
display: none; | |
} | |
.listFUTItem .auction .auction-state { | |
width: 25%; | |
float: right; | |
} | |
.listFUTItem .auction .auctionValue { | |
width: 24%; | |
float: left; | |
padding-right: 1%; | |
} | |
.listFUTItem .auction .futbin { | |
// background-color: lightgoldenrodyellow; | |
} | |
.listFUTItem .auction .futbin.oktobuy { | |
background-color: lightgreen; | |
} | |
.listFUTItem .auction .delta-green { | |
font-size: small; | |
color: green; | |
vertical-align: super; | |
} | |
.listFUTItem .auction .delta-red { | |
font-size: 14px; | |
color: red; | |
vertical-align: super; | |
} | |
.futbinupdate { | |
font-size: 14px; | |
clear: both; | |
display: block; | |
} | |
,currency-coins .value.futbin { | |
-webkit-filter: hue-rotate(165deg); | |
filter: hue-rotate(165deg); | |
} | |
.popup { | |
position: fixed; | |
width: 440px; | |
height: 40px; | |
right: 130px; | |
top: 4px; | |
z-index: 999999; | |
font-family: Helvetica; | |
background: lightgreen; | |
border-radius: 3px; | |
line-height: 40px; | |
padding: 0 10px; | |
} | |
.futbin-reload { | |
position: fixed; | |
width: 440px; | |
right: 300px; | |
top: 4px; | |
z-index: 999999; | |
} | |
.futbin-reload .btn { | |
border-radius: 4px; | |
--button-color: #212529; | |
--button-bg-color: #ffc107; | |
--button-hover-bg-color: #e0a800; | |
} | |
.popup .info { | |
font-weight:bold; | |
} | |
</style>`); | |
$("body").append( | |
'<div class="popup" style="display:none"><span "info">Best Page Deal:<span> <span class="name"></span> <span class="price"></span> <span class="gain"></span></div>' | |
); | |
$("body").append( | |
`<div class="futbin-reload"> | |
<div class="button-container" style="padding:0"> | |
<button id="fut-fetch" class="btn-standard mini call-to-action" type="button">Futbin Fetch</button> | |
<button id="fut-setData" class="btn-standard mini call-to-action" type="button">Set Data</button> | |
<div id="fut-autoFetch" class="ut-toggle-control-group-view"> | |
<div class="ut-toggle-cell-view"> | |
<span class="ut-toggle-cell-view--label">Auto Futbin Fetch</span> | |
<div class="ut-toggle-control"> | |
<div class="ut-toggle-control--track"></div> | |
<div class="ut-toggle-control--grip"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div>` | |
); | |
var intervalTime = 1000; | |
var lastListRows = []; | |
var lastFutbinData = {}; | |
var globalInterval; | |
var isAutoFetch = false; | |
$("#fut-fetch").click(function() { | |
fetchData(); | |
}); | |
$("#fut-setData").click(function() { | |
setData(); | |
}); | |
$("#fut-autoFetch").click(function() { | |
autoFetchToggle(); | |
}); | |
function run() { | |
globalInterval = setInterval(() => { | |
var listRows = getPagePlayerData(); | |
if(_.isEmpty(listRows)) { | |
return; | |
} | |
if(isAutoFetch){ | |
fetchData(); | |
} | |
setData(); | |
lastListRows = listRows | |
}, intervalTime); | |
} | |
run(); | |
function hasFetchTarget(listRows) { | |
var playersIds = _.chain(listRows) | |
.filter(o => o.data.isPlayer) | |
.map(o => o.data.definitionId) | |
.uniq() | |
.filter(id => !_.has(lastFutbinData, id)) | |
.value(); // 버튼 누를때 10개씩 가져오도록 | |
return playersIds.length > 0 | |
} | |
function fetchData() { | |
var listRows = getPagePlayerData(); | |
if (_.isEmpty(listRows)) { | |
return; | |
} | |
if(!hasFetchTarget(listRows)){ | |
return; | |
} | |
fetchFutbinPlayerPrices(listRows); | |
} | |
function getPagePlayerData() { | |
var playerElements = document.querySelectorAll(".listFUTItem .player"); | |
if (playerElements.length <= 0) { | |
return []; | |
} | |
// Club search or Player search | |
if (isClubSearchController() || isTransferMarketSearchController()) { | |
return getCurrentController()._listController._view._list.listRows; | |
} | |
// Transfer list | |
if (isTransferListController() || isWatchListController()) { | |
return getCurrentController()._listController._view.sections.flatMap(a => a.listRows); | |
} | |
} | |
function isTransferMarketSearchController() { | |
return getCurrentControllerClassName() === "UTMarketSearchResultsSplitViewController"; | |
} | |
function isClubSearchController() { | |
return getCurrentControllerClassName() === "ClubSearchResultsSplitViewController"; | |
} | |
function isTransferListController() { | |
return getCurrentControllerClassName() === "UTTransferListSplitViewController"; | |
} | |
function isWatchListController() { | |
return getCurrentControllerClassName() === "UTWatchListSplitViewController"; | |
} | |
function getCurrentController() { | |
return getAppMain() | |
.getRootViewController() | |
.getPresentedViewController() | |
.getCurrentViewController() | |
.getCurrentController(); | |
} | |
function getCurrentControllerClassName() { | |
return getCurrentController().className; | |
} | |
function fetchFutbinPlayerPrices(listRows) { | |
console.log('fetch FutbinPlayerPrices listRows',listRows) | |
var playersIds = _.chain(listRows) | |
.filter(o => o.data.isPlayer) | |
.map(o => o.data.definitionId) | |
.uniq() | |
.filter(id => !_.has(lastFutbinData, id)) | |
.slice(0,10) | |
.value(); // 버튼 누를때 10개씩 가져오도록 | |
console.log('playersIds',playersIds) | |
var futbinUrl = "https://www.futbin.com/23/playerPrices?player="; | |
const axiosList = _.map(playersIds, playerId => axios.get(futbinUrl+playerId)) | |
Promise.all(axiosList).then(function(values) { | |
const dataList = _.map(values, o => o.data) | |
const futbinData = Object.assign(...dataList) | |
lastFutbinData = Object.assign(lastFutbinData, futbinData); | |
console.log(lastFutbinData); | |
showFutbinPriceInPage(listRows, lastFutbinData); | |
// showBestDealMessage(listRows, lastFutbinData); | |
}); | |
} | |
function showFutbinPriceInPage(listRows, futbinData) { | |
listRows.map((item, idx) => { | |
if(!isAuctionShowTarget(futbinData, item, idx)){ | |
return; | |
} | |
setListFutItemAddClass(futbinData, item, idx); | |
setListFutItemClickEvent(futbinData, item, idx); | |
setListFutItemAuctionDom(futbinData, item, idx); | |
}); | |
} | |
function isAuctionShowTarget(futbinData, item, idx) { | |
if (!futbinData) { | |
return false; | |
} | |
if (!item.data.isPlayer()) { | |
return false; | |
} | |
var playerId = item.data.definitionId; | |
if (!futbinData[playerId]) { | |
//console.error('not found playerId from futbinData:', futbinData, playerId); | |
return false; // futbin data might not be available for this player | |
} | |
var targetDomElements = jQuery(".listFUTItem").get(); | |
var target = jQuery(targetDomElements[idx]); | |
if (target.find(".futbin").length > 0) { | |
return false; // futbin price already added to the row | |
} | |
return true; | |
} | |
function setListFutItemAddClass(futbinData, item, idx) { | |
var targetDomElements = jQuery(".listFUTItem").get(); | |
var target = jQuery(targetDomElements[idx]); | |
if(!target.hasClass('has-auction-data')){ | |
target.addClass('has-auction-data'); | |
} | |
} | |
function setListFutItemClickEvent(futbinData, item, idx) { | |
var targetDomElements = jQuery(".listFUTItem").get(); | |
var target = jQuery(targetDomElements[idx]); | |
var futbinPriceRaw = getFutbinPriceRaw(futbinData, item); | |
target.click( | |
function() { | |
toast({heading: "Copied Futbin Price ", text: futbinPriceRaw }); | |
GM_setClipboard(futbinPriceRaw, { type: 'text', mimetype: 'text/plain'}) | |
} | |
); | |
} | |
function setListFutItemAuctionDom(futbinData, item, idx) { | |
var targetDomElements = jQuery(".listFUTItem").get(); | |
var target = jQuery(targetDomElements[idx]); | |
var targetAuctionDom = target.find(".auction"); | |
targetAuctionDom.show(); | |
targetAuctionDom.append(getAppendAuctionHtmlStr(futbinData, item)); | |
} | |
function getAppendAuctionHtmlStr(futbinData, item) { | |
var okToBuyClass = getOkToBuyClass(futbinData, item); | |
var futBinDataUpated = getFutBinDataUpated(futbinData, item); | |
var futbinPriceRawText = getFutbinPriceRawText(futbinData, item); | |
return ` | |
<div class="auctionValue futbin ${okToBuyClass}"> | |
<span class="label">Futbin Updated:</span> | |
<span class="value">${futBinDataUpated}</span> | |
</div> | |
<div class="auctionValue futbin ${okToBuyClass}"> | |
<span class="label">Futbin Price:</span> | |
<span class="currency-coins value">${futbinPriceRawText}</span> | |
</div> | |
` | |
} | |
function getOkToBuyClass(futbinData, item) { | |
var futbinPriceRaw = getFutbinPriceRaw(futbinData, item); | |
if (!isTransferMarketSearchController()) { | |
return; | |
} | |
// Stuff for transfermarket | |
var futbinPrice = parseInt(futbinPriceRaw.replace(/,/g, "")); | |
var isBuyNowLowerThanFutbinPrice = item.data.getAuctionData().buyNowPrice < futbinPrice; | |
return isBuyNowLowerThanFutbinPrice ? "oktobuy" : ""; | |
} | |
function getFutbinPriceRaw(futbinData, item) { | |
var playerId = item.data.definitionId; | |
var platform = getPersonaPlatform(); | |
return futbinData[playerId].prices[platform].LCPrice; | |
} | |
function getPersonaPlatform() { | |
var platform = ""; | |
if (services.User.getUser().getSelectedPersona().isPlaystation) platform = "ps"; | |
if (services.User.getUser().getSelectedPersona().isPC) platform = "pc"; | |
if (services.User.getUser().getSelectedPersona().isXbox) platform = "xbox"; | |
return platform; | |
} | |
function getFutBinDataUpated(futbinData, item){ | |
var playerId = item.data.definitionId; | |
var platform = getPersonaPlatform(); | |
return futbinData[playerId].prices[platform].updated | |
} | |
function getFutbinPriceRawText(futbinData, item) { | |
var futbinPriceRaw = getFutbinPriceRaw(futbinData, item); | |
if (!isTransferMarketSearchController()) { | |
return futbinPriceRaw; | |
} | |
// Stuff for transfermarket | |
var futbinPrice = parseInt(futbinPriceRaw.replace(/,/g, "")); | |
var delta = item.data.getAuctionData().buyNowPrice - futbinPrice; | |
var deltaClass = delta < 0 ? "delta-green" : "delta-red"; | |
var deltaString = delta > 0 ? "+" + delta : delta; | |
return futbinPriceRaw + '<span class="' + deltaClass + '">(' + deltaString + ")</span>"; | |
} | |
// function showBestDealMessage(listRows, futbinData) { | |
// if (!isTransferMarketSearchController()) { | |
// return; | |
// } | |
// var bestDeal = { | |
// item: "", | |
// price: 0, | |
// futbinPrice: 0, | |
// gain: 0 | |
// }; | |
// listRows.map((item, idx) => { | |
// if(!isAuctionShowTarget(futbinData, item, idx)){ | |
// return; | |
// } | |
// bestDeal = getBestDeal(futbinData, item, bestDeal); | |
// }); | |
// toastBestDeal(bestDeal); | |
// } | |
// function getBestDeal(futbinData, item, bestDeal) { | |
// // Stuff for transfermarket | |
// var futbinPriceRaw = getFutbinPriceRaw(futbinData, item); | |
// var futbinPrice = parseInt(futbinPriceRaw.replace(/,/g, "")); | |
// var delta = item.data.getAuctionData().buyNowPrice - futbinPrice; | |
// if (delta < bestDeal.gain) { | |
// bestDeal.gain = delta; | |
// bestDeal.name = item.data.getStaticData().name; | |
// bestDeal.futbinPrice = futbinPrice; | |
// bestDeal.price = item.data.getAuctionData().buyNowPrice; | |
// } | |
// return { | |
// ...bestDeal, | |
// gain: delta, | |
// name: item.data.getStaticData().name, | |
// futbinPrice: futbinPrice, | |
// price: item.data.getAuctionData().buyNowPrice | |
// } | |
// } | |
// function toastBestDeal(bestDeal) { | |
// if (bestDeal.gain === 0) { | |
// toast({heading: "Best Deal", text: getBestDealText('Nothing found', 'None', 'None')}); | |
// } else { | |
// toast({heading: "Best Deal", text: getBestDealText(bestDeal.name, bestDeal.price, `${bestDeal.gain} (coins cheaper)`)}); | |
// } | |
// } | |
function getBestDealText(name, price, gain) { | |
return ` | |
Best Page Deal: ${name} | |
<br>Best Page Price: ${price}</br> | |
<br>Best Page Gain: ${gain} (coins cheaper)</br> | |
`; | |
} | |
function setData() { | |
var listRows = getPagePlayerData(); | |
if (_.isEmpty(listRows)) { | |
return; | |
} | |
showFutbinPriceInPage(listRows, lastFutbinData); | |
// showBestDealMessage(listRows, lastFutbinData); | |
} | |
function autoFetchToggle() { | |
isAutoFetch = !isAutoFetch; | |
if(isAutoFetch){ | |
$('#fut-autoFetch .ut-toggle-control').addClass('toggled'); | |
}else { | |
$('#fut-autoFetch .ut-toggle-control').removeClass('toggled'); | |
} | |
} | |
}; | |
$(document).ready(function () { | |
main() | |
}); | |
(async function() { | |
function isIncludeText(url, txt){ | |
return url.includes(txt) | |
} | |
// function getCopyData(json){ //복사할 데이터 | |
// const maxBid = getMaxBid(json); | |
// const minBuyNowPrice = getMinBuyNowPrice(json); | |
// if(maxBid == 0) { | |
// return minBuyNowPrice -50; // 즉구가일땐 -50원 | |
// } | |
// const resultArr = [maxBid + 100 , minBuyNowPrice - 50]; // 비딩가에선 +100원, 즉구가일땐 -50원 | |
// const sorted = resultArr.sort((a,b) => { return a - b }) | |
// return sorted[0]; | |
// } | |
// function getMaxBid(json) { // 30초 미만 비딩중 제일 비싼가격 | |
// const mapped = json.auctionInfo.map(x => { | |
// if(x.currentBid > 0 && x.expires < 30){ | |
// return x.currentBid; | |
// } | |
// }); | |
// const sorted = mapped.sort((a,b) => { return b - a }) | |
// const maxBid = sorted[0]; | |
// if(maxBid == null){ | |
// return 0; | |
// } | |
// console.log('maxBid',maxBid); | |
// return maxBid; | |
// } | |
function getMinBuyNowPrice(json) { // 즉구가 중 가잔 싼 가격 | |
if(_.isEmpty(json.auctionInfo)) { return 0 } | |
const mapped = json.auctionInfo.map(x => { | |
return x.buyNowPrice; | |
}); | |
const sorted = mapped.sort((a,b) => { return a - b }) | |
const minBuyNowPrice = sorted[0]; | |
console.log('minBuyNowPrice',minBuyNowPrice); | |
return minBuyNowPrice | |
} | |
function auctionSellReq(id, startingBid, buyNowPrice) { | |
console.log('bidBuyNowReq tradeId', tradeId); | |
console.log('bidBuyNowReq buyNowPrice', buyNowPrice); | |
var xhr = new XMLHttpRequest(); | |
xhr.open("PUT", getAuctionSellUrl(), true); | |
xhr.setRequestHeader('Content-type','application/json'); | |
xhr.setRequestHeader('X-UT-SID',sid); | |
xhr.onload = function () { | |
if (isSuccessOnLoad(xhr)) { | |
toastSuccess({heading: "auction success 200! ", text: xhr.responseText }); | |
} else { | |
toast({heading: "auction fail error ", text: "status : "+xhr.status + " : " + xhr.responseText , bgColor: "#d9534f" }); | |
console.error(xhr.responseText); | |
} | |
} | |
xhr.send(JSON.stringify({ "bid": buyNowPrice })); | |
} | |
function getAuctionSellUrl() { | |
return 'https://utas.external.s2.fut.ea.com/ut/game/fifa23/auctionhouse' | |
} | |
let oldXHROpen = window.XMLHttpRequest.prototype.open; | |
window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) { | |
// do something with the method, url and etc. | |
this.addEventListener('load', function() { | |
// do something with the response text | |
if( isIncludeText(this.responseURL,'transfermarket')){ | |
var data = this.responseText; | |
var json = JSON.parse(data); | |
const copyData = getMinBuyNowPrice(json); | |
if(copyData <= 0) { return } | |
toast({heading: "Copied Min BuyNow Price ", text: copyData }); | |
GM_setClipboard(copyData, { type: 'text', mimetype: 'text/plain'}) | |
} | |
}); | |
return oldXHROpen.apply(this, arguments); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment