Badge IDs are, especially on older sites, very random.
- What are Badge IDs?
- Where can I find these IDs?
- How can I reproduce this table in case of changes in the Badge system?
- Who is going to profit from this table?
Meta Stack Exchange is a question and answer site for meta-discussion of the Stack Exchange family of Q&A websites. It only takes a minute to sign up.
Sign up to join this communityBadge IDs are, especially on older sites, very random.
When this post mentions Badge IDs, it refers to the part of the URL that is actually important to SE to redirect you to the right badge. For example, consider this URL:
https://stackoverflow.com/help/badges/1/teacher
Here, the Badge ID is 1. The page you would land on would be the same if the /teacher
was removed from the URL.
The IDs are currently hosted on Github as tables in an array of different file formats and the way to reproduce said table.
Since the table is hosted on Github, you can execute the following code and then you can make a pull request on Github. Here is the code, written in AppleScript with as much documentation as possible:
(*
Basic rules:
IDs are stored in a nested list.
Output is in a CSV-format (Comma separated)
IDs of Tags that don't exist on certain networks are 0
Networks (in the first row of the table) are in the URL format
*)
set AppleScript's text item delimiters to "/" --This allows us to read the links more easily later on.
try --the try statement in Applescript is the same as in Javascript where errors don't halt the script.
tell application "Google Chrome" to tell front window --The tell statement targets the front window of your browser. Currently, only Chromium-based browsers are supported.
if URL of active tab does not contain "https://stackexchange.com/sites?view=list" then
set theTab to make new tab with properties {URL:"https://stackexchange.com/sites?view=list"}
else
set theTab to active tab
end if
set theTabs to tabs --we store the tabs of the front window here, because in the tell statement we targeted the front window.
tell theTab --This further specifies the tab where the sites section of stackexchange.com was opened.
set siteLinks to execute javascript "[...document.getElementsByClassName('lv-info')].map(el => el.children[0].children[0].getAttribute('href'));" as list
-- This returns the URL's to all stackexchange sites.
repeat until ((count of siteLinks) > 150) --repeat until is the same as while not and count is the same as len(array) or array.length. It sometimes happens that it will only retrieve a few sites.
delay 0.6
set siteLinks to execute javascript "[...document.getElementsByClassName('lv-info')].map(el => el.children[0].children[0].getAttribute('href'));" as list
--here we assign the same thing as above to check if the site has finished loading.
end repeat
if (count of theTabs) is not equal to 1 then close --Here is the reason why we assigned the tabs to a variable.
end tell
end tell
on error --Here is why we used a try statement at the very top. If you don't have a browser window open, it's going to produce an error.
tell application "Google Chrome"
set theWindow to make new window
set URL of tab 1 of theWindow to "https://stackexchange.com/sites?view=list"
tell tab 1 of theWindow --This targets the first tab of a new window we just made. In this tab, it is going to get the URL's to all sites like above.
set siteLinks to execute javascript "[...document.getElementsByClassName('lv-info')].map(el => el.children[0].children[0].getAttribute('href').toString());" as list
repeat until ((count of siteLinks) > 150)
set siteLinks to execute javascript "[...document.getElementsByClassName('lv-info')].map(el => el.children[0].children[0].getAttribute('href').toString());" as list
delay 0.6
end repeat
end tell
end tell
end try
set siteLinks to siteLinks & getMetaSites(siteLinks) --This adds the meta sites to the rest of the sites (see function below for more documentation.)
tell application "Google Chrome" to tell its front window
set theTab to make new tab --Here we assign a variable to a tab that we are going to use for every single site.
set theBadges to {}
set theBadgeIDs to {} --Here I declare two empty lists/arrays where the data is going to be stored.
repeat with siteLink in siteLinks --Applescript repeat with loops are like for loops in other languages. Here it is looping over every stackexchange URL.
set URL of theTab to siteLink & "/help/badges" --Here we change the URL of theTab to the badge section of the new network.
tell theTab
set theBadgeLinks to (execute javascript "[...= document.getElementsByClassName('badge m0')].map(el => el.getAttribute('href'));") as list
--Here it is getting all URLs to badges.
repeat until ((count of theBadgeLinks) > 80) --In this loop it is using the same trick as above to check if the site has finished loading
delay 0.5
set theBadgeLinks to (execute javascript "[...document.getElementsByClassName('badge m0')].map(el => el.getAttribute('href'));") as list
end repeat
end tell
set n to theBadges is not equal to {} and theBadgeIDs is not equal to {} --This is going to be true except for the first run.
repeat with i from 1 to count of theBadgeLinks --Here it is initiating a more traditional for loop. List indexes in Applescript start at 1.
try --The reason why this is in a try-block is because of site-exclusive badges, mostly on stackapps.
if n then --This checks for the variable we assigned above. We can't have it in here or else it will only fail for the first badge on the first sit
set end of item (my indexOf(text item 5 of item i of theBadgeLinks, theBadges)) of theBadgeIDs to text item 4 of item i of theBadgeLinks
(*
indexOf() finds the index of a certain item in a list/array and is a function on the bottom of this script.
text items are delimited by the text item delimiters we declared in the first line.
text item 5 is the name of the badge, spaces are replaced hyphens.
text item 4 is the id of the badge which is the goal of this entire script.
*)
else
set end of theBadges to text item 5 of theBadgeLink --This populates the list containing all the badge names with the badges on the first network, here Stack Overflow
set end of theBadgeIDs to {}
set end of last item of theBadgeIDs to text item 4 of theBadgeLink --This populates list containing all the IDs with nested lists containing the badge-ids of the first network.
end if
on error --If indexOf() can't find text item 5, that is because it isn't in the list yet and it is going to throw an error.
set end of theBadges to text item 5 of item i of theBadgeLinks --Here we insert the new badge into the pool of other badges.
set end of theBadgeIDs to {} --Here we set the end to a new list. This is all a bit out of order, you will see why in a minute.
set end of last item of theBadgeIDs to text item 4 of item i of theBadgeLinks --Here we set the end of the new list to the badge ID of the site specific badge.
repeat until (count of last item of theBadgeIDs) is equal to (count of first item of theBadgeIDs)
set beginning of last item of theBadgeIDs to 0 --This just fills the rest of the list up to this point with 0.
end repeat
end try
end repeat
repeat with k from 1 to count of theBadgeIDs
set standard to count of item 1 of theBadgeIDs
if (count of item k of theBadgeIDs) < standard then set end of item k of theBadgeIDs to 0 --Here we check if all list are as long as the first list and if not, we populate the end with 0's
end repeat
end repeat
end tell
set MasterOutput to "" --We declare an empty string which is going to be output later.
set AppleScript's text item delimiters to "," --Here we change the delimiters so later when the lists get compressed to one single string, they are comma separated.
set j to 1
repeat while j < (count of theBadges) + 1
if MasterOutput is not equal to "" then --This check only fails the first time so in the else block we can define the header.
set MasterOutput to (MasterOutput & replaceText("-", " ", item j of theBadges) & "," & item j of theBadgeIDs as string) & return
(*
If we return a list as string, it gets compressed to a string and items get seperated by the text item delimiters
return in a string context is the same as "\n"
The first item of the row is the badge name, all lower case and thanks to the replaceText() function hyphen-free.
*)
set j to j + 1
else
set MasterOutput to "badge," & (siteLinks as string) & return --This is for the first time and sets the first row/column headers.
end if
end repeat
set the clipboard to MasterOutput --This cuts the need to copy the huge amounts of data output by this program.
beep --This notifies you when the program finishes.
return MasterOutput --Just to be safe, this line also displays the MasterOutput in the result tab of Script Editor
on indexOf(theItem, theList)
repeat with a from 1 to count of theList
if item a of theList is theItem then return a
end repeat
end indexOf
on replaceText(find, replace, subject)
set prevTIDs to text item delimiters of AppleScript
set text item delimiters of AppleScript to find
set subject to text items of subject
set text item delimiters of AppleScript to replace
set subject to subject as text
set text item delimiters of AppleScript to prevTIDs
return subject
end replaceText
on getMetaSites(theLinks)
set prevTIDs to text item delimiters of AppleScript --This stores the previous text item delimters so we can reset them after the function is called.
set metaLinks to {} --This is the list that is getting returned in the end.
set sitesWithoutMeta to {"https://stackapps.com", "https://meta.stackexchange.com"} --These sites don't have a meta site.
repeat with i from 1 to count of theLinks --Here we iterate over all links that we took as input.
if sitesWithoutMeta does not contain item i of theLinks then --Here we check if the site has a meta.
set AppleScript's text item delimiters to "." --This step is essential for splitting sub-domain, domain and top-level domain of the links.
set theItems to text items of item i of theLinks --Here we split by periods.
if (count of theItems) is equal to 2 then --Here we check if the site has a sub-domain.
set AppleScript's text item delimiters to ""
set end of metaLinks to characters 1 thru 8 of item 1 of theItems & "meta." & characters 9 thru (count of characters of (item 1 of theItems)) of item 1 of theItems & "." & item 2 of theItems as string
--This mess adds the meta into the URL.
else if (count of theItems) is equal to 3 then
set end of metaLinks to item 1 of theItems & ".meta." & items 2 thru 3 of theItems as string --It's quite a lot simpler if the URL has a sub-domain.
end if
end if
end repeat
set text item delimiters of AppleScript to prevTIDs --Here we reset the text item delimiters.
return metaLinks
end getMetaSites
Feel free to rewrite this code in other languages. Currently, this method only supports macOS-Systems with Chrome or Chromium-based browsers.
Some people have made meta-posts wanting to access badge IDs. The Stack Exchange API also provides a way to interact with badges, but there is no complete table of badge IDs and as stated above and in the docs, there is no real order to badge IDs. It is potentially possible to do the same thing using the Stack Exchange Data Explorer.