@fajarkim/github-readme-profile
Version:
🙀 Generate your Stats GitHub Profile in SVG
369 lines (339 loc) • 11 kB
text/typescript
import axios from "axios";
import { getToken, getToken2, getToken3 } from "../getToken";
/**
* Represents a user's information and statistics.
*
* @interface User
* @property {string} name - The name of the user.
* @property {string} login - The login username of the user.
* @property {string} avatarUrl - The URL of the user's avatar.
* @property {Repositories} repositories - Information about user repositories.
* @property {Followers} followers - Information about user followers.
* @property {Following} following - Information about users being followed by the user.
* @property {OpenedIssues} openedIssues - Information about opened issues by the user.
* @property {ClosedIssues} closedIssues - Information about closed issues by the user.
* @property {PullRequests} pullRequests - Information about user pull requests.
* @property {MergedPullRequests} mergedPullRequests - Information about merged pull requests by the user.
* @property {DiscussionStarted} discussionStarted - Information about discussions started by the user.
* @property {DiscussionAnswered} discussionAnswered - Information about discussions answered by the user.
* @property {RepositoriesContributedTo} repositoriesContributedTo - Information about repositories contributed to by the user.
* @property {number} totalCommitContributions - The total count of commit contributions by the user.
* @property {number} restrictedContributionsCount - The count of restricted contributions by the user.
* @property {number} totalPullRequestReviewContributions - The total count of pull request review contributions by the user.
*/
interface User {
name: string;
login: string;
avatarUrl: string;
repositories: Repositories;
followers: Followers;
following: Following;
openedIssues: OpenedIssues;
closedIssues: ClosedIssues;
pullRequests: PullRequests;
mergedPullRequests: MergedPullRequests;
discussionStarted: DiscussionStarted;
discussionAnswered: DiscussionAnswered;
repositoriesContributedTo: RepositoriesContributedTo;
totalCommitContributions: number;
restrictedContributionsCount: number;
totalPullRequestReviewContributions: number;
}
/**
* Represents information about user repositories.
*
* @interface Repositories
* @property {number} totalCount - The total count of repositories.
*/
interface Repositories {
totalCount: number;
}
/**
* Represents information about user followers.
*
* @interface Followers
* @property {number} totalCount - The total count of followers.
*/
interface Followers {
totalCount: number;
}
/**
* Represents information about user following.
*
* @interface Following
* @property {number} totalCount - The total count of following.
*/
interface Following {
totalCount: number;
}
/**
* Represents contributions data collected over a specific time period.
*
* @interface ContributionsCollection
* @property {number} totalCommitContributions - The total count of commit contributions.
* @property {number} restrictedContributionsCount - The count of restricted contributions.
* @property {number} totalPullRequestReviewContributions - The total count of pull request review contributions.
*/
interface ContributionsCollection {
totalCommitContributions: number;
restrictedContributionsCount: number;
totalPullRequestReviewContributions: number;
}
/**
* Represents information about user opened issues.
*
* @interface OpenedIssues
* @property {number} totalCount - The total count of opened issues.
*/
interface OpenedIssues {
totalCount: number;
}
/**
* Represents information about user closed issues.
*
* @interface ClosedIssues
* @property {number} totalCount - The total count of closed issues.
*/
interface ClosedIssues {
totalCount: number;
}
/**
* Represents information about user pull requests.
*
* @interface PullRequests
* @property {number} totalCount - The total count of pull requests.
*/
interface PullRequests {
totalCount: number;
}
/**
* Represents information about user merged pull requests.
*
* @interface MergedPullRequests
* @property {number} totalCount - The total count of merged pull requests.
*/
interface MergedPullRequests {
totalCount: number;
}
/**
* Represents information about user discussion started.
*
* @interface DiscussionStarted
* @property {number} totalCount - The total count of discussion started.
*/
interface DiscussionStarted {
totalCount: number;
}
/**
* Represents information about user discussion answered.
*
* @interface DiscussionAnswered
* @property {number} totalCount - The total count of discussion answered.
*/
interface DiscussionAnswered {
totalCount: number;
}
/**
* Represents information about user repositories contributed to.
*
* @interface RepositoriesContributedTo
* @property {number} totalCount - The total count of repositories contributed to.
*/
interface RepositoriesContributedTo {
totalCount: number;
}
/**
* Fetches the user's join year based on the provided username.
*
* @param {string} username - The GitHub username of the user.
* @returns {Promise<number>} - A promise that resolves with the user's join year.
*/
async function getUserJoinYear(username: string): Promise<number> {
const data = await axios({
method: "post",
url: "https://api.github.com/graphql",
headers: {
"User-Agent": "FajarKim/github-readme-profile",
Authorization: getToken3(true),
},
data: {
query: `query userInfo($username: String!) {
user(login: $username) {
createdAt
}
}`,
variables: {
username,
},
},
});
if (data.data.errors?.length > 0) {
throw new Error(data.data.errors[0].message);
}
const user = data.data.data.user;
if (!user || !user.createdAt) {
throw new Error("User data is missing.");
}
const joinDate = new Date(user.createdAt);
return joinDate.getFullYear();
}
/**
* Fetches the contributions data for the specified year.
*
* @param {string} username - The GitHub username of the user.
* @param {number} year - The year for which contributions data is fetched.
* @returns {Promise<ContributionsCollection>} - A promise that resolves with contributions data.
*/
async function fetchContributions(username: string, year: number): Promise<ContributionsCollection> {
const from = `${year}-01-01T00:00:00Z`;
const to = `${year}-12-31T23:59:59Z`;
const data = await axios({
method: "post",
url: "https://api.github.com/graphql",
headers: {
"User-Agent": "FajarKim/github-readme-profile",
Authorization: getToken2(true),
},
data: {
query: `query userInfo($username: String!, $from: DateTime!, $to: DateTime!) {
user(login: $username) {
contributionsCollection(from: $from, to: $to) {
totalCommitContributions
restrictedContributionsCount
totalPullRequestReviewContributions
}
}
}`,
variables: {
username,
from,
to,
},
},
});
if (data.data.errors?.length > 0) {
throw new Error(data.data.errors[0].message);
}
const contributions = data.data.data.user.contributionsCollection;
return {
totalCommitContributions: contributions.totalCommitContributions,
restrictedContributionsCount: contributions.restrictedContributionsCount,
totalPullRequestReviewContributions: contributions.totalPullRequestReviewContributions,
};
}
/**
* Fetches various statistics and information about the user.
*
* @param {string} username - The GitHub username of the user.
* @returns {Promise<User>} - A promise that resolves with the user's information and statistics.
*/
async function stats(username: string): Promise<User> {
const startYear = await getUserJoinYear(username);
const endYear = new Date().getFullYear();
const contributionPromises = [];
for (let year = startYear; year <= endYear; year++) {
contributionPromises.push(fetchContributions(username, year));
}
const contributions = await Promise.all(contributionPromises);
const TotalCommitContributions = contributions.reduce(
(total, contribution) => total + contribution.totalCommitContributions,
0
);
const RestrictedContributionsCount = contributions.reduce(
(total, contribution) => total + contribution.restrictedContributionsCount,
0
);
const TotalPullRequestReviewContributions = contributions.reduce(
(total, contribution) => total + contribution.totalPullRequestReviewContributions,
0
);
const data = await axios({
method: "post",
url: "https://api.github.com/graphql",
headers: {
"User-Agent": "FajarKim/github-readme-profile",
Authorization: getToken(true),
},
data: {
query: `query userInfo($username: String!) {
user(login: $username) {
name
login
avatarUrl
repositories(ownerAffiliations: OWNER, privacy: PUBLIC) {
totalCount
}
followers {
totalCount
}
following {
totalCount
}
openedIssues: issues(states: OPEN) {
totalCount
}
closedIssues: issues(states: CLOSED) {
totalCount
}
pullRequests(first: 1) {
totalCount
}
mergedPullRequests: pullRequests(states: MERGED) {
totalCount
}
discussionStarted: repositoryDiscussions {
totalCount
}
discussionAnswered: repositoryDiscussionComments(onlyAnswers: true) {
totalCount
}
repositoriesContributedTo(first: 1, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
totalCount
}
}
}`,
variables: {
username,
},
},
});
if (data.data.errors?.length > 0)
throw new Error(data.data.errors[0].message);
const user = data.data.data.user;
return {
name: user.name,
login: user.login,
avatarUrl: user.avatarUrl,
repositories: user.repositories,
followers: user.followers,
following: user.following,
openedIssues: user.openedIssues,
closedIssues: user.closedIssues,
pullRequests: user.pullRequests,
mergedPullRequests: user.mergedPullRequests,
discussionStarted: user.discussionStarted,
discussionAnswered: user.discussionAnswered,
repositoriesContributedTo: user.repositoriesContributedTo,
totalCommitContributions: TotalCommitContributions,
restrictedContributionsCount: RestrictedContributionsCount,
totalPullRequestReviewContributions: TotalPullRequestReviewContributions,
};
}
export {
User,
Repositories,
Followers,
Following,
ContributionsCollection,
OpenedIssues,
ClosedIssues,
PullRequests,
MergedPullRequests,
DiscussionStarted,
DiscussionAnswered,
RepositoriesContributedTo,
getUserJoinYear,
fetchContributions,
stats
};
export default stats;