Yeah, I just randomly wondered about this earlier today. Codeberg doesn’t have an option to sort users by anything, but it’s simple to do it manually anyway, so I decided to find the answer to this question right away.
You can search for users by using the Forgejo’s
API, the endpoint for the “users
search” is
/api/user
,
which fortunately doesn’t require
authentication
to be used, and
the data also contains the followers count, which simplifies the
process of collecting data a lot. There seems to be no rate limits either.
The only problem is that Codeberg has a relatively large amount of users (152091, at the time of writing), in which most of them could be (unfortunately) SEO bots or spam users (IDK but there are a lot of Vietnamese sports betting/gambling sites advertisements on Codeberg). I can’t think of a good way to filter them out, so there’s no other way but to include all of them, and accept that it’ll take a decent amount of time to fully iterate through all users.
The code
It’s just very simple. We iterate through all pages, parse the data of each user into a CSV file, and then do stuff with that file.
The following Go script parses all available users into a CSV file. I wrote this in a rush so it’s not very efficient, but it does its job.
package main
import (
"encoding/csv"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
)
type UserSearch struct {
Data []struct {
ID int `json:"id"`
Login string `json:"login"`
LoginName string `json:"login_name"`
SourceID int `json:"source_id"`
FullName string `json:"full_name"`
Email string `json:"email"`
AvatarURL string `json:"avatar_url"`
HTMLURL string `json:"html_url"`
Language string `json:"language"`
IsAdmin bool `json:"is_admin"`
LastLogin time.Time `json:"last_login"`
Created time.Time `json:"created"`
Restricted bool `json:"restricted"`
Active bool `json:"active"`
ProhibitLogin bool `json:"prohibit_login"`
Location string `json:"location"`
Pronouns string `json:"pronouns"`
Website string `json:"website"`
Description string `json:"description"`
Visibility string `json:"visibility"`
FollowersCount int `json:"followers_count"`
FollowingCount int `json:"following_count"`
StarredReposCount int `json:"starred_repos_count"`
Username string `json:"username"`
} `json:"data"`
Ok bool `json:"ok"`
}
func main() {
user_search_template := "https://codeberg.org/api/v1/users/search?limit=10&page=%d"
data_file, err := os.Create("data.csv")
if err != nil {
panic(err)
}
defer data_file.Close()
data_file_writer := csv.NewWriter(data_file)
defer data_file_writer.Flush()
for p := 1 ;; p++ {
user_search_uri := fmt.Sprintf(user_search_template, p)
req, err := http.NewRequest(http.MethodGet, user_search_uri, nil)
if err != nil {
log.Fatalln("Something failed.")
}
req.Header.Add("accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalln("Something failed.")
}
res_body, err := io.ReadAll(res.Body)
if err != nil {
log.Fatalln("Something failed.")
}
var users_data UserSearch
err = json.Unmarshal(res_body, &users_data)
if err != nil {
log.Fatalln("Something failed.")
}
if !users_data.Ok {
log.Fatalln("Not OK.")
}
if len(users_data.Data) < 1 {
break
}
for _, user := range users_data.Data {
log.Printf("Parsing user %s", user.Username)
source_template := "%d|%s|%s|%d|%d|%d|%d"
source := fmt.Sprintf(source_template, user.ID,
user.Login, user.LoginName, user.SourceID,
user.FollowersCount, user.FollowingCount, user.StarredReposCount)
source2 := strings.Split(source, "|") // Maybe this is more reliable...
err = data_file_writer.Write(source2)
if err != nil {
log.Fatalln("Something failed.")
}
}
}
}
The script took over 6 hours to finish for me. After finishing, we
should have a data.csv
in your working directory with the data of all users on
Codeberg.
33341,_,,0,0,0,6
71406,___henil,,0,0,0,0
92767,__BlackJack,,0,0,0,0
84906,__d,,0,0,0,0
57448,__rod,,0,0,0,0
57129,_-Caleb-_,,0,1,0,1
185,_1,,0,0,0,0
29308,_456789,,0,0,0,0
39689,_a,,0,0,0,0
90652,_arabica,,0,0,0,0
...
The CSV format for each user is
id,login,login_name,source_id,followers_count,following_count,starred_repos_count
The result
So we have the data now. The last thing to do is to now sort the file
by their followers count. We can easily do that by using the sort
command. There is a Stack Overflow
answer
for this.
And now we should have our results. The head -n10
most followed user
on Codeberg, at the time of writing, is:
blau_araujo
(252 followers)dnkl
(217 followers) (creator of foot, fuzzel,…)smart-airdrop
(114 followers) (spam!!)fnetX
(101 followers) (executive director of Codeberg)aral
(75 followers) (head of Small Technology Foundation)Galkurta
(63 followers) (spam!!)izzy
(62 followers) (head of IzzyOnDroid)circlebuilder
(57 followers) (member of Fediverse Futures)andrewrk
(56 followers) (benevolent dictator of Zig)daviwil
(55 followers) (creator of System Crafters)
And before you ask, I’m at the 54th place with 25 followers, sharing
the position with aryak (creator of
Mozhi and Gothub). I could have been on the head -n50
if there were
no bots :(
If you are a Codeberg user, check out your position on the list! The sorted CSV file can be viewed here.