Skip to content
TORNLIFE More

PSA: How to prevent your scripts from breaking after site updates

Started by neth [3564828] Reporter on in Tools & Userscripts.

17 replies · 743 views · thread synced · 5 days ago · View on torn.com
About this thread

Posts archived: 18 / 18 posts (100%) · the total is Torn's reply count + the opening post at the last fetch

Counted by TornLife from the archived posts.

Archived posts
18
Discussion span
→
People posting
7
Likes on archived posts
63
Posts by staff, officers and moderators
9
Authority score
58 / 100
Historical score
38 / 100
Story score
44 / 100
Engagement score
73 / 100

Most-liked replies

neth [3564828] Reporter

I noticed a ton of scripts just broke due to recent updates to the website. 

 

Per cloudjumper's reply to one of the bug threads:

 

This is basically on the script writers to fix, not for Torn devs. Torn uses React with CSS modules, which generates class names like status-icons___xYz123 where the suffix is a hash that changes between builds. Never hardcode the full class name - it will break when Torn deploys updates. Based on what the devs have told me, this is just down to bad scripting practices. You need to reach out to the script writers to get the issue fixed.

 

Closing and locking the report.

 

 

Examining a lot of the scripts I use which broke, I found that they all fall victim to hardcoding the react generated class names instead of an attribute selector with substring matching.

 

Using cloudjumper's example, here is how you can fix it to not have to fix this again in the future.

 

Change

 


document.querySelector('status-icons___xYz123');

 

To

 


document.querySelector('[class^="status-icons___"]');

 

 

This tells querySelector to search for any elements whose class name starts with status-icons___  and completely ignores the randomly generated react class name ending.

 

Here are my selectors for many different native torn elements in the code for my torn-stock-pocket script. Personally I omit most of the 3 underscores ___  just for the sake of readability.

 

 



export const STOCK_MARKET_SELECTOR = 'div[class^="stockMarket"]'
export const STOCK_DROPDOWN_ID = 'panel-ownedTab'
export const BUY_BUTTON_SELECTOR = '#panel-ownedTab button[class*="buy_"]'
export const SELL_BUTTON_SELECTOR = '#panel-ownedTab button[class*="sell_"]'
export const STOCK_LIST_SELECTOR = '#stockmarketroot > div[class^="stockMarket"] ul'
export const STOCK_NAME_SELECTOR = 'li[class^="stockName"]'
export const STOCK_ACRONYM_SELECTOR = '[data-acronym]'
export const STOCK_ACRONYM_ATTRIBUTE = 'data-acronym'
export const STOCK_AMOUNT_OWNED_SELECTOR = "p[class^='count_']"
export const STOCK_ICON_SVG_URL = 'https://www.torn.com/images/v2/stock-market/dark-mode/logos/'

Mentions: [Script] Stock Pocket: 1-click highly configurable buy/sell stock buttons, sell/buy by $ amount

DeKleineKobini [2114440] Committee Committee

I'm astonished that so many scripts would rely on hardcoding those. Any class with 2 or 3 underscores should never be hardcoded since they mean it's modular css (which can have a different suffic each build). Always use *= or ^= with attribute selectors (both have their up and downsides)! 

 

This also applies to those modifying TornTools as I'm starting to use modular css as well. 

ThtAstronautGuy [1977683] Moderator Official

How would this work for things like custom CSS on those elements? There is a variable containing custom style that gets applied to some of the native Torn elements. Just doing GM_addStyle. Thanks for the guidance so far!

 

.formTitle___TPKVR {
    flex-direction: column;
    text-align: left;
}

Xiphias [187717]

Something like this should work. *= means contains. You can also use ^= which means "starts with".

 

GM_addStyle(`
    [class*="formTitle___"] {
        flex-direction: column;
        text-align: left;
    }
`);

ThtAstronautGuy [1977683] Moderator Official

Thanks, I'll give that a shot! Can you do a similar thing with waitForKeyElements just switching to the square bracket notation instead of directly referencing the name?

Xiphias [187717]

According to the documentation that I found of waitForKeyElements (https://gist.github.com/BrockA/2625891) then yes, it should work in the same way.

The function takes a jQuery selector string as the first parameter, so you should be able to use any of the methods listed here: https://api.jquery.com/category/selectors/

ThtAstronautGuy [1977683] Moderator Official

Got it! Thanks for the help!

 

tl;dr replacing .class_xxx with [class*="class___"] seemed to work everywhere. For something type specific, like li.class_xxx, li[class*="listItem___"] did it for me!

neth [3564828] Reporter

Great utility, useful for beginner devs for sure :)

 

Granted if I were them I would take this opportunity to learn more about the DOM, React, CSS, etc and fix it without that just to gain experience, however for those who just want to fix their shit and move on this is perfect

DeKleineKobini [2114440] Committee Committee

Pretty sure that isn't such a great idea for general usage tbh, as with CSS modules class names can and will be reused by Torn. Here is some nice example (chat v3):

PedroXimenez [3391983] Committee Committee

In practice it works pretty well, but you do need to use the `within` part for generic-named classes (like 'title'). That root class set is wild.

DeKleineKobini [2114440] Committee Committee

Each 'root' is from a different React component. So let's say one is called ChatList and another one ChatHeader. Both have a modular CSS attached, and they just call the only class there 'root', as they are already in `ChatList.module.css` so they don't need to have the class be chat-list either as they already know.

 

p.s.: this is just speculation on my end, but this is how I justify them doing this

ThtAstronautGuy [1977683] Moderator Official

Okay I have an easy regex based find and replace that worked for at least one script using Sublime Text. Note: this only works if all your stuff is in single quotes. Some minor changes may be required for double quotes. It also will not support things like looking for elements in a list like I mentioned in one of my previous comments. But it could probably be easily modified to do that.

 

Find: ('| ).(.*?___).{5}

 

Replace: $1[class*="$2"]

 

Explanation: searches for either ' or a space to find both single, and multi class searches in a capture group. Then it looks for the period. Next there's a capture group to get the class name and the underscores. Finally, capture the hash. The replace uses the first capture group to put the ' or space back, and adds in the new search format with the second capture group of the class name with the underscores.