7

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?

Return to FAQ index

3
  • 3
    You are one of the most peculiar posters I've seen in a long time in the sense that you write in Apple Script :) It's so rare to see (especially when it comes to network-wide issues). The idea for the proposed FAQ looks good, with the size of the network, I wish automation hooks could be better documented. Drop by Stack Apps too to share the app Sep 10, 2021 at 20:57
  • 1
    @OlegValter For me, AppleScript was the only option. It had to be a language I knew which could interact with many different browser tabs. I will definitely visit Stack Apps and drop the script there too. Thanks for reminding me that Stack Apps isn't only about API.
    – gurkensaas
    Sep 10, 2021 at 21:10
  • 1
    I understand, it's just it's like seeing a unicorn live :) Yeah, it's basically an SE marketplace nobody knows about :) But it saved a lot of people a lot of frustration, so I try to promote it whenever I get a chance. Sep 10, 2021 at 22:56

1 Answer 1

10

What are Badge IDs?

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.

Where can I find these IDs?

The IDs are currently hosted on Github as tables in an array of different file formats and the way to reproduce said table.

How can I reproduce this table in case of changes in the Badge system?

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.

Who is going to benefit from this table?

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.

5
  • 3
    This is very nice. Did you know that the meta sites have separate id's for the badges? Example: Fanatic on Stack Overflow has id 83, while Fanatic on Stack Overflow Meta has id 55. Sep 10, 2021 at 17:34
  • 2
    @Unconsidered Thank you for your comment, I didn't consider that. I will fix my code as soon as possible.
    – gurkensaas
    Sep 10, 2021 at 19:17
  • 3
    @Unconsidered It should now work, thankfully it only took me a few lines of code.
    – gurkensaas
    Sep 10, 2021 at 20:32
  • 1
    Is it intentional that on the stackoverflow.com site, reviewer and down are all 0's? (Please do not take this with any sense of urgency :-) Sep 10, 2021 at 20:41
  • 2
    @Unconsidered Thank you for making me aware of that, the site probably didn't finish loading. I am going to add a few checks so that doesn't happen any time soon.
    – gurkensaas
    Sep 10, 2021 at 20:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .