mirror of
https://codeberg.org/gothub/gothub
synced 2024-12-06 19:16:24 +01:00
213 lines
7.1 KiB
Go
213 lines
7.1 KiB
Go
package pages
|
|
|
|
import (
|
|
"codeberg.org/gothub/gothub/utils"
|
|
"context"
|
|
"github.com/carlmjohnson/requests"
|
|
"github.com/enescakir/emoji"
|
|
"github.com/gocolly/colly"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gomarkdown/markdown"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type User struct {
|
|
Login string
|
|
Name string
|
|
Bio string
|
|
Status string
|
|
StatusEmoji string
|
|
AvatarUrl string
|
|
Location string
|
|
Email string
|
|
Timezone string
|
|
Following int64
|
|
Followers int64
|
|
Link string
|
|
Social []string
|
|
Organizations []string
|
|
OrgMembers []string
|
|
Company string
|
|
Type string
|
|
Contributions string
|
|
Readme string
|
|
}
|
|
|
|
type Ratelimit struct {
|
|
Remaining int64
|
|
Limit int64
|
|
}
|
|
|
|
// HandleUser handles the user page.
|
|
func HandleUser(c *fiber.Ctx) error {
|
|
// Declare Array used for displaying data
|
|
var userArray []User
|
|
resp, err := http.Get("https://github.com/" + c.Params("user"))
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
if resp.StatusCode == 404 {
|
|
return c.Status(404).Render("error", fiber.Map{
|
|
"error": "User " + c.Params("user") + " not found",
|
|
})
|
|
}
|
|
// API
|
|
user := utils.GetRequest("https://api.github.com/users/" + c.Params("user"))
|
|
var link string
|
|
if user.Get("message").String() == "Not Found" {
|
|
return c.Status(404).Render("error", fiber.Map{
|
|
"error": "User " + c.Params("user") + " not found",
|
|
})
|
|
}
|
|
if strings.Contains(user.Get("message").String(), "rate limit") { // dont wanna get the status code so i'll just do this instead 👍
|
|
ratelimitJSON := utils.GetRequest("https://api.github.com/rate_limit")
|
|
log.Println(ratelimitJSON)
|
|
var ratelimitArray []Ratelimit
|
|
|
|
ratelimitArray = append(ratelimitArray, Ratelimit{
|
|
Remaining: ratelimitJSON.Get("resources.core.remaining").Int(),
|
|
Limit: ratelimitJSON.Get("resources.core.limit").Int(),
|
|
})
|
|
|
|
log.Println(ratelimitArray)
|
|
|
|
return c.Render("ratelimit", fiber.Map{
|
|
"Title": "GitHub API /users endpoint rate limit exceeded",
|
|
"ratelimit": ratelimitArray,
|
|
})
|
|
} else {
|
|
|
|
if user.Get("blog").String() == "" {
|
|
link = ""
|
|
} else {
|
|
link = user.Get("blog").String()
|
|
if strings.HasPrefix(link, "https://") {
|
|
link = strings.TrimPrefix(link, "https://")
|
|
} else if strings.HasPrefix(link, "http://") {
|
|
link = strings.TrimPrefix(link, "http://")
|
|
} else {
|
|
log.Println("Has no prefix")
|
|
}
|
|
}
|
|
}
|
|
// User README
|
|
var readmee string
|
|
|
|
err0 := requests.
|
|
URL("https://raw.githubusercontent.com/" + c.Params("user") + "/" + c.Params("user") + "/master/README.md").
|
|
ToString(&readmee).
|
|
Fetch(context.Background())
|
|
if err0 != nil {
|
|
err2 := requests.
|
|
URL("https://raw.githubusercontent.com/" + c.Params("user") + "/.github/master/profile/README.md").
|
|
ToString(&readmee).
|
|
Fetch(context.Background())
|
|
if err2 != nil {
|
|
readmee = ""
|
|
log.Println(err0)
|
|
}
|
|
}
|
|
mightBeUnsafe := markdown.ToHTML([]byte(readmee), nil, nil)
|
|
// Sanitize the user README incase there is any weird shit in it
|
|
readmeOutput := utils.UGCPolicy().SanitizeBytes(mightBeUnsafe)
|
|
|
|
// scraping
|
|
Scrape := User{
|
|
Readme: string(readmeOutput),
|
|
}
|
|
|
|
UserAgent, ok := os.LookupEnv("GOTHUB_USER_AGENT")
|
|
if !ok {
|
|
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
|
|
}
|
|
sc1 := colly.NewCollector(colly.AllowedDomains("github.com"), colly.UserAgent(UserAgent))
|
|
|
|
sc1.OnHTML("div[itemtype]", func(e *colly.HTMLElement) {
|
|
Scrape.Type = e.Attr("itemtype")
|
|
})
|
|
|
|
sc1.Visit("https://github.com/" + c.Params("user") + "/")
|
|
sc := colly.NewCollector(colly.AllowedDomains("github.com"), colly.UserAgent(UserAgent))
|
|
if Scrape.Type == "http://schema.org/Person" {
|
|
sc.OnHTML("div.js-profile-editable-replace", func(e *colly.HTMLElement) {
|
|
// Main info
|
|
Scrape.Login = e.ChildText("span[itemprop*='additionalName']")
|
|
Scrape.Name = e.ChildText("span[itemprop*='name']")
|
|
Scrape.Bio = e.ChildText("div[data-bio-text] div")
|
|
Scrape.AvatarUrl = e.ChildAttr("img[alt*='Avatar']", "src")
|
|
// Metadata
|
|
Scrape.Location = e.ChildText("li[itemprop*='homeLocation'] span")
|
|
Scrape.Timezone = e.ChildText("li[itemprop*='localTime'] span")
|
|
Scrape.Company = e.ChildText("li[itemprop*='worksFor'] span")
|
|
Scrape.Link = e.ChildText("a[itemprop*='url']")
|
|
e.ForEach("li[itemprop*='social']", func(i int, el *colly.HTMLElement) {
|
|
Scrape.Social = append(Scrape.Social, el.ChildText("a.Link--primary"))
|
|
})
|
|
// Followers/Following
|
|
Scrape.Followers, err = strconv.ParseInt(e.ChildText("a[href*='https://github.com/"+c.Params("user")+"?tab=followers' i] span"), 10, 64)
|
|
Scrape.Following, err = strconv.ParseInt(e.ChildText("a[href*='https://github.com/"+c.Params("user")+"?tab=following' i] span"), 10, 64)
|
|
// Organizations
|
|
e.ForEach("a[data-hovercard-type*='organization']", func(i int, el *colly.HTMLElement) {
|
|
Scrape.Organizations = append(Scrape.Organizations, el.Attr("aria-label"))
|
|
})
|
|
// User Status
|
|
Scrape.Status = e.ChildText("div.user-status-message-wrapper div")
|
|
Scrape.StatusEmoji = e.ChildAttr("div.user-status-circle-badge div.user-status-emoji-container g-emoji" ,"alias")
|
|
})
|
|
// Contributions
|
|
sc.OnHTML("div.js-yearly-contributions", func(e *colly.HTMLElement) {
|
|
Scrape.Contributions = e.ChildText("h2")
|
|
})
|
|
} else {
|
|
sc.OnHTML("div.container-xl div.flex-md-items-center", func(e *colly.HTMLElement) {
|
|
// Main info
|
|
Scrape.Login = e.ChildAttr("img[alt*='@"+c.Params("user")+"' i]", "alt") // Hacky
|
|
Scrape.Name = e.ChildText("h1.h2")
|
|
Scrape.Bio = e.ChildText("div.color-fg-muted div")
|
|
Scrape.AvatarUrl = e.ChildAttr("img[alt*='@"+c.Params("user")+"' i]", "src")
|
|
// Metadata
|
|
Scrape.Location = e.ChildText("span[itemprop*='location']")
|
|
Scrape.Email = e.ChildText("a[itemprop*='email']")
|
|
Scrape.Link = e.ChildText("a[itemprop*='url']")
|
|
e.ForEach("a.Link--primary", func(i int, el *colly.HTMLElement) {
|
|
Scrape.Social = append(Scrape.Social, el.Attr("href"))
|
|
})
|
|
// Followers
|
|
Scrape.Followers, err = strconv.ParseInt(e.ChildText("a[href*='/orgs/"+c.Params("user")+"/followers' i] span"), 10, 64)
|
|
})
|
|
// Org Members
|
|
sc.OnHTML("div.clearfix", func(e *colly.HTMLElement) {
|
|
e.ForEach("a[data-hovercard-type*='user'] img", func(i int, el *colly.HTMLElement) {
|
|
Scrape.OrgMembers = append(Scrape.OrgMembers, strings.TrimPrefix(el.Attr("alt"), "@"))
|
|
})
|
|
})
|
|
}
|
|
sc.Visit("https://github.com/" + c.Params("user") + "/")
|
|
// Fixing the output a bit
|
|
Scrape.AvatarUrl = strings.TrimPrefix(Scrape.AvatarUrl, "https://avatars.githubusercontent.com/u/")
|
|
Scrape.AvatarUrl = "/avatar/" + Scrape.AvatarUrl
|
|
if Scrape.StatusEmoji != "" {
|
|
Scrape.StatusEmoji = emoji.Parse(":" + Scrape.StatusEmoji + ":")
|
|
}
|
|
Scrape.Login = strings.TrimPrefix(Scrape.Login, "@") // Only for orgs
|
|
if strings.HasPrefix(Scrape.Link, "https://") {
|
|
Scrape.Link = strings.TrimPrefix(Scrape.Link, "https://")
|
|
} else if strings.HasPrefix(Scrape.Link, "http://") {
|
|
Scrape.Link = strings.TrimPrefix(Scrape.Link, "http://")
|
|
} else {
|
|
log.Println("Has no prefix")
|
|
}
|
|
|
|
// Add scrape-based info to userArray
|
|
userArray = append(userArray, Scrape)
|
|
|
|
return c.Render("user", fiber.Map{
|
|
"Title": "User " + c.Params("user"),
|
|
"user": userArray,
|
|
})
|
|
}
|