mirror of
https://github.com/anotherhadi/github-recon.git
synced 2026-10-05 19:08:24 +02:00
+109
-16
@@ -1,45 +1,138 @@
|
|||||||
package ghrecon
|
package ghrecon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
type CloseFriendsResult struct {
|
type CloseFriendsResult struct {
|
||||||
Login string
|
Login string
|
||||||
Score int
|
Score int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxFollowingForTarget = 50
|
||||||
|
maxFollowersForFollowing = 20
|
||||||
|
pointPerCriterion = 1
|
||||||
|
)
|
||||||
|
|
||||||
// CloseFriends returns a list of close friends of the user
|
// CloseFriends returns a list of close friends of the user
|
||||||
// To derive this, we check the following:
|
// To derive this, we check the following:
|
||||||
// 1. The target has less than 50 Following
|
// 1. The target has less than 50 Following
|
||||||
// 2. The target's following has less than 20 followers
|
// 2. The target's following has less than 20 followers (+1 point)
|
||||||
|
// 3. The target's following follows the target (+1 point)
|
||||||
func (r Recon) CloseFriends(username string) (response []CloseFriendsResult) {
|
func (r Recon) CloseFriends(username string) (response []CloseFriendsResult) {
|
||||||
r.PrintTitle("🧑🤝🧑 Close Friends")
|
r.PrintTitle("🧑🤝🧑 Close Friends")
|
||||||
|
|
||||||
following, resp, err := r.client.Users.ListFollowing(r.ctx, username, nil)
|
following, resp, err := r.client.Users.ListFollowing(r.ctx, username, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Fatal("Failed to fetch user's close friends", "err", err)
|
r.logger.Error("Failed to fetch user's following list", "user", username, "err", err)
|
||||||
}
|
|
||||||
if len(following) > 50 {
|
|
||||||
r.PrintInfo("INFO", "No commits found")
|
|
||||||
r.PrintNewline()
|
r.PrintNewline()
|
||||||
return []CloseFriendsResult{}
|
return
|
||||||
}
|
}
|
||||||
WaitForRateLimit(resp)
|
WaitForRateLimit(resp)
|
||||||
for _, user := range following {
|
|
||||||
followers, resp, err := r.client.Users.Get(r.ctx, user.GetLogin())
|
if len(following) >= maxFollowingForTarget {
|
||||||
WaitForRateLimit(resp)
|
r.PrintInfo(
|
||||||
if err != nil {
|
"INFO",
|
||||||
|
fmt.Sprintf(
|
||||||
|
"%s follows %d or more users (%d). Skipping close friends check.",
|
||||||
|
username,
|
||||||
|
maxFollowingForTarget,
|
||||||
|
len(following),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
r.PrintNewline()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(following) == 0 {
|
||||||
|
r.PrintInfo("INFO", fmt.Sprintf("%s is not following anyone.", username))
|
||||||
|
r.PrintNewline()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userBeingFollowedByTarget := range following {
|
||||||
|
loginName := userBeingFollowedByTarget.GetLogin()
|
||||||
|
if loginName == "" {
|
||||||
|
r.logger.Warn("User in following list has an empty login", "target_user", username)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if followers.GetFollowers() < 20 {
|
|
||||||
|
currentScore := 0
|
||||||
|
|
||||||
|
userDetails, userResp, userErr := r.client.Users.Get(r.ctx, loginName)
|
||||||
|
if userErr != nil {
|
||||||
|
r.logger.Warn(
|
||||||
|
"Failed to fetch details for followed user",
|
||||||
|
"followed_user",
|
||||||
|
loginName,
|
||||||
|
"err",
|
||||||
|
userErr,
|
||||||
|
)
|
||||||
|
if userResp != nil {
|
||||||
|
WaitForRateLimit(userResp)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
WaitForRateLimit(userResp)
|
||||||
|
|
||||||
|
if userDetails.GetFollowers() < maxFollowersForFollowing {
|
||||||
|
currentScore += pointPerCriterion
|
||||||
|
}
|
||||||
|
|
||||||
|
followsTargetBack, checkErr := r.checkIfUserFollows(loginName, username)
|
||||||
|
if checkErr != nil {
|
||||||
|
} else if followsTargetBack {
|
||||||
|
currentScore += pointPerCriterion
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentScore > 0 {
|
||||||
response = append(response, CloseFriendsResult{
|
response = append(response, CloseFriendsResult{
|
||||||
Login: user.GetLogin(),
|
Login: loginName,
|
||||||
Score: 1,
|
Score: currentScore,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, friend := range response {
|
if len(response) == 0 {
|
||||||
r.PrintInfo("Username", "@"+friend.Login)
|
r.PrintInfo(
|
||||||
|
"INFO",
|
||||||
|
fmt.Sprintf("No close friends found for %s based on the criteria.", username),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
sort.Slice(response, func(i, j int) bool {
|
||||||
|
return response[i].Score > response[j].Score
|
||||||
|
})
|
||||||
|
for i, friend := range response {
|
||||||
|
r.PrintInfo(
|
||||||
|
fmt.Sprintf("Friend n°%d", i+1),
|
||||||
|
"@"+friend.Login,
|
||||||
|
"Score: "+fmt.Sprintf("%d", friend.Score),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.PrintNewline()
|
r.PrintNewline()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkIfUserFollows checks if sourceUserLogin follows targetUserLogin.
|
||||||
|
func (r Recon) checkIfUserFollows(sourceUserLogin, targetUserLogin string) (bool, error) {
|
||||||
|
isFollowing, resp, err := r.client.Users.IsFollowing(r.ctx, sourceUserLogin, targetUserLogin)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Warn("Error checking if user follows target",
|
||||||
|
"source_user_checking", sourceUserLogin,
|
||||||
|
"target_user_to_check", targetUserLogin,
|
||||||
|
"err", err)
|
||||||
|
if resp != nil {
|
||||||
|
WaitForRateLimit(resp)
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != nil {
|
||||||
|
WaitForRateLimit(resp)
|
||||||
|
}
|
||||||
|
return isFollowing, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user