From b21275398fd01d78c19ef132639e783d7e68d1e5 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Date: Tue, 14 Jul 2026 09:29:08 -0300 Subject: [PATCH] feat: count organization repositories and stars on the profile widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #4. Adds an opt-in &includeOrgs=true query param to the profile widget. When set, the 'repositories' and 'stars' data options also include public repositories (and their stargazer counts) from organizations the user is a member of, not just repos owned by their personal account. Off by default so existing embeds keep their current numbers. Implementation notes: - The GraphQL query now uses variables ($login, $includeOrgs) instead of interpolating the username directly into the query string, and only fetches the organizations field when requested, via @include(if: $includeOrgs), to avoid the extra API cost when the flag isn't set. - Limited to the first 25 organizations and first 100 repositories per organization (no cursor pagination yet — same limit the existing personal repositories query already had). Only sees organizations and repos that are publicly visible to the server's token. --- README.md | 10 ++++- api/routes/profile-route.ts | 4 +- public/api/routes/profile-route.js | 12 +++--- public/api/routes/profile-route.js.map | 2 +- public/src/fetchers/user-stats-fetcher.js | 25 +++++++++-- public/src/fetchers/user-stats-fetcher.js.map | 2 +- public/src/interfaces/Organizations.js | 3 ++ public/src/interfaces/Organizations.js.map | 1 + public/src/widgets/profile.js | 42 +++++++++++++------ public/src/widgets/profile.js.map | 2 +- src/fetchers/user-stats-fetcher.ts | 24 +++++++++-- src/interfaces/GithubUser.ts | 2 + src/interfaces/Organizations.ts | 10 +++++ src/widgets/profile.ts | 30 ++++++++++--- 14 files changed, 132 insertions(+), 37 deletions(-) create mode 100644 public/src/interfaces/Organizations.js create mode 100644 public/src/interfaces/Organizations.js.map create mode 100644 src/interfaces/Organizations.ts diff --git a/README.md b/README.md index 26604ae..5f70a7c 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,14 @@ Show off your profile with some interesting statistics. Perfect for profile READ ```md [![GitHub WidgetBox](https://github-widgetbox.vercel.app/api/profile?username=Jurredr&data=followers,repositories,stars,commits)](https://github.com/Jurredr/github-widgetbox) ``` + +By default, `repositories` and `stars` only count repos owned directly by your personal account. Add `&includeOrgs=true` to also count public repositories (and their stars) from organizations you're a member of: + +```md +[![GitHub WidgetBox](https://github-widgetbox.vercel.app/api/profile?username=Jurredr&data=followers,repositories,stars,commits&includeOrgs=true)](https://github.com/Jurredr/github-widgetbox) +``` + +This is opt-in so existing embeds keep their current numbers. It only sees organizations and repositories that are publicly visible, capped at the first 25 organizations and first 100 repositories per organization.

### Themes @@ -110,7 +118,7 @@ nautilus | serika * [x] Add Skills: Tools & Frameworks widget * [x] Add Skills: Software & IDEs widget * [ ] Make autobuilder instead of manual build and push -* [ ] Count organization repositories (+ their stars) +* [x] Count organization repositories (+ their stars) * [ ] Truncate name if too long on profile widget * [ ] Make all widgets a modular size * [ ] Add Profile Tag widget diff --git a/api/routes/profile-route.ts b/api/routes/profile-route.ts index 1d4a0c9..b3ad145 100644 --- a/api/routes/profile-route.ts +++ b/api/routes/profile-route.ts @@ -9,7 +9,7 @@ const githubUsernameRegex = require("github-username-regex") // Primary profile route router.get('/', function (req: Request, res: Response) { - const { username, data, theme } = req.query + const { username, data, theme, includeOrgs } = req.query // Set the header's type to svg/xml res.setHeader('Content-Type', 'image/svg+xml') @@ -39,7 +39,7 @@ router.get('/', function (req: Request, res: Response) { } // Grab the Profile widget - profileWidget(String(username), String(data), String(theme)).then((response) => { + profileWidget(String(username), String(data), String(theme), String(includeOrgs)).then((response) => { if (response === undefined || response === null) { res.send( errorWidget('Profile', '-25%', 'GitHub API-call error!', '-24%') diff --git a/public/api/routes/profile-route.js b/public/api/routes/profile-route.js index 8a81075..7c47dd9 100644 --- a/public/api/routes/profile-route.js +++ b/public/api/routes/profile-route.js @@ -10,28 +10,28 @@ const error_1 = __importDefault(require("../../src/widgets/error")); const githubUsernameRegex = require("github-username-regex"); // Primary profile route router.get('/', function (req, res) { - const { username, data, theme } = req.query; + const { username, data, theme, includeOrgs } = req.query; // Set the header's type to svg/xml res.setHeader('Content-Type', 'image/svg+xml'); // Check if username argument is not present if (username === undefined || username === null) { - res.send(error_1.default('Profile', '-25%', 'Username is undefined!', '-26%')); + res.send((0, error_1.default)('Profile', '-25%', 'Username is undefined!', '-26%')); return; } // Check validity based on GitHub's username rules if (!githubUsernameRegex.test(username)) { - res.send(error_1.default('Profile', '-25%', 'Username is invalid!', '-22%')); + res.send((0, error_1.default)('Profile', '-25%', 'Username is invalid!', '-22%')); return; } // Check if data argument is not present if (data === undefined || data === null) { - res.send(error_1.default('Profile', '-25%', 'Data option is missing!', '-25%')); + res.send((0, error_1.default)('Profile', '-25%', 'Data option is missing!', '-25%')); return; } // Grab the Profile widget - profile_1.default(String(username), String(data), String(theme)).then((response) => { + (0, profile_1.default)(String(username), String(data), String(theme), String(includeOrgs)).then((response) => { if (response === undefined || response === null) { - res.send(error_1.default('Profile', '-25%', 'GitHub API-call error!', '-24%')); + res.send((0, error_1.default)('Profile', '-25%', 'GitHub API-call error!', '-24%')); } else { res.send(response); diff --git a/public/api/routes/profile-route.js.map b/public/api/routes/profile-route.js.map index ae8ab57..6a42f65 100644 --- a/public/api/routes/profile-route.js.map +++ b/public/api/routes/profile-route.js.map @@ -1 +1 @@ -{"version":3,"file":"profile-route.js","sourceRoot":"","sources":["../../../api/routes/profile-route.ts"],"names":[],"mappings":";;;;;AAEA,sDAA6B;AAC7B,MAAM,MAAM,GAAG,iBAAO,CAAC,MAAM,EAAE,CAAA;AAE/B,wEAAqD;AACrD,oEAAiD;AACjD,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;AAE5D,wBAAwB;AACxB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAY,EAAE,GAAa;IACjD,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,KAAK,CAAA;IAE3C,mCAAmC;IACnC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,eAAe,CAAC,CAAA;IAE9C,4CAA4C;IAC5C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE;QAC7C,GAAG,CAAC,IAAI,CACJ,eAAW,CAAC,SAAS,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,CAAC,CACnE,CAAA;QACD,OAAM;KACT;IAED,kDAAkD;IAClD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACrC,GAAG,CAAC,IAAI,CACJ,eAAW,CAAC,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,CAAC,CACjE,CAAA;QACD,OAAM;KACT;IAED,wCAAwC;IACxC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,EAAE;QACrC,GAAG,CAAC,IAAI,CACJ,eAAW,CAAC,SAAS,EAAE,MAAM,EAAE,yBAAyB,EAAE,MAAM,CAAC,CACpE,CAAA;QACD,OAAM;KACT;IAED,0BAA0B;IAC1B,iBAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;QAC3E,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE;YAC7C,GAAG,CAAC,IAAI,CACJ,eAAW,CAAC,SAAS,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,CAAC,CACnE,CAAA;SACJ;aAAM;YACH,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;SACrB;IACL,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA;AAEF,kBAAe,MAAM,CAAA"} \ No newline at end of file +{"version":3,"file":"profile-route.js","sourceRoot":"","sources":["../../../api/routes/profile-route.ts"],"names":[],"mappings":";;;;;AAEA,sDAA6B;AAC7B,MAAM,MAAM,GAAG,iBAAO,CAAC,MAAM,EAAE,CAAA;AAE/B,wEAAqD;AACrD,oEAAiD;AACjD,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;AAE5D,wBAAwB;AACxB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAY,EAAE,GAAa;IACjD,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAA;IAExD,mCAAmC;IACnC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,eAAe,CAAC,CAAA;IAE9C,4CAA4C;IAC5C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE;QAC7C,GAAG,CAAC,IAAI,CACJ,IAAA,eAAW,EAAC,SAAS,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,CAAC,CACnE,CAAA;QACD,OAAM;KACT;IAED,kDAAkD;IAClD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;QACrC,GAAG,CAAC,IAAI,CACJ,IAAA,eAAW,EAAC,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,CAAC,CACjE,CAAA;QACD,OAAM;KACT;IAED,wCAAwC;IACxC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,EAAE;QACrC,GAAG,CAAC,IAAI,CACJ,IAAA,eAAW,EAAC,SAAS,EAAE,MAAM,EAAE,yBAAyB,EAAE,MAAM,CAAC,CACpE,CAAA;QACD,OAAM;KACT;IAED,0BAA0B;IAC1B,IAAA,iBAAa,EAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;QAChG,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE;YAC7C,GAAG,CAAC,IAAI,CACJ,IAAA,eAAW,EAAC,SAAS,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,CAAC,CACnE,CAAA;SACJ;aAAM;YACH,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;SACrB;IACL,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA;AAEF,kBAAe,MAAM,CAAA"} \ No newline at end of file diff --git a/public/src/fetchers/user-stats-fetcher.js b/public/src/fetchers/user-stats-fetcher.js index 1b074a1..0be8bc1 100644 --- a/public/src/fetchers/user-stats-fetcher.js +++ b/public/src/fetchers/user-stats-fetcher.js @@ -4,14 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); const node_fetch_1 = __importDefault(require("node-fetch")); -async function getGithubUserStats(token, username) { +async function getGithubUserStats(token, username, includeOrgs = false) { const headers = { Authorization: `bearer ${token}`, }; const body = { query: ` - query { - user(login: "${username}") { + query($login: String!, $includeOrgs: Boolean!) { + user(login: $login) { name login contributionsCollection { @@ -35,11 +35,28 @@ async function getGithubUserStats(token, username) { } } } + organizations(first: 25) @include(if: $includeOrgs) { + nodes { + login + repositories(first: 100, privacy: PUBLIC, orderBy: {direction: DESC, field: STARGAZERS}) { + totalCount + nodes { + stargazers { + totalCount + } + } + } + } + } } } `, + variables: { + login: username, + includeOrgs: includeOrgs, + }, }; - const response = await node_fetch_1.default('https://api.github.com/graphql', { + const response = await (0, node_fetch_1.default)('https://api.github.com/graphql', { method: 'POST', body: JSON.stringify(body), headers: headers, diff --git a/public/src/fetchers/user-stats-fetcher.js.map b/public/src/fetchers/user-stats-fetcher.js.map index cc5ffb4..11f95c8 100644 --- a/public/src/fetchers/user-stats-fetcher.js.map +++ b/public/src/fetchers/user-stats-fetcher.js.map @@ -1 +1 @@ -{"version":3,"file":"user-stats-fetcher.js","sourceRoot":"","sources":["../../../src/fetchers/user-stats-fetcher.ts"],"names":[],"mappings":";;;;;AAAA,4DAA8B;AAGf,KAAK,UAAU,kBAAkB,CAC5C,KAAyB,EACzB,QAAgB;IAGhB,MAAM,OAAO,GAAG;QACZ,aAAa,EAAE,UAAU,KAAK,EAAE;KACnC,CAAA;IAED,MAAM,IAAI,GAAG;QACT,KAAK,EAAE;;2BAEY,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;WA0BxB;KACN,CAAA;IAED,MAAM,QAAQ,GAAG,MAAM,oBAAK,CAAC,gCAAgC,EAAE;QAC3D,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;QAC1B,OAAO,EAAE,OAAO;KACnB,CAAC,CAAA;IAEF,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;AAChC,CAAC;AAhDD,qCAgDC"} \ No newline at end of file +{"version":3,"file":"user-stats-fetcher.js","sourceRoot":"","sources":["../../../src/fetchers/user-stats-fetcher.ts"],"names":[],"mappings":";;;;;AAAA,4DAA8B;AAGf,KAAK,UAAU,kBAAkB,CAC5C,KAAyB,EACzB,QAAgB,EAChB,WAAW,GAAG,KAAK;IAGnB,MAAM,OAAO,GAAG;QACZ,aAAa,EAAE,UAAU,KAAK,EAAE;KACnC,CAAA;IAED,MAAM,IAAI,GAAG;QACT,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAyCJ;QACH,SAAS,EAAE;YACP,KAAK,EAAE,QAAQ;YACf,WAAW,EAAE,WAAW;SAC3B;KACJ,CAAA;IAED,MAAM,QAAQ,GAAG,MAAM,IAAA,oBAAK,EAAC,gCAAgC,EAAE;QAC3D,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;QAC1B,OAAO,EAAE,OAAO;KACnB,CAAC,CAAA;IAEF,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;AAChC,CAAC;AAlED,qCAkEC"} \ No newline at end of file diff --git a/public/src/interfaces/Organizations.js b/public/src/interfaces/Organizations.js new file mode 100644 index 0000000..48774b9 --- /dev/null +++ b/public/src/interfaces/Organizations.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=Organizations.js.map \ No newline at end of file diff --git a/public/src/interfaces/Organizations.js.map b/public/src/interfaces/Organizations.js.map new file mode 100644 index 0000000..a13190d --- /dev/null +++ b/public/src/interfaces/Organizations.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Organizations.js","sourceRoot":"","sources":["../../../src/interfaces/Organizations.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/src/widgets/profile.js b/public/src/widgets/profile.js index f49752b..f4fd111 100644 --- a/public/src/widgets/profile.js +++ b/public/src/widgets/profile.js @@ -10,47 +10,63 @@ const error_1 = __importDefault(require("./error")); const card_1 = __importDefault(require("../components/card")); const user_stats_fetcher_1 = __importDefault(require("../fetchers/user-stats-fetcher")); const themes_1 = __importDefault(require("../data/themes")); -async function profileWidget(username, data, themeString) { +async function profileWidget(username, data, themeString, includeOrgsString) { + const includeOrgs = (0, utils_1.getBoolean)(includeOrgsString || ''); // Set the theme - let theme = utils_1.getTheme(themes_1.default, 'default'); + let theme = (0, utils_1.getTheme)(themes_1.default, 'default'); if (themeString) { - theme = utils_1.getTheme(themes_1.default, themeString); + theme = (0, utils_1.getTheme)(themes_1.default, themeString); } if (!theme) { - theme = utils_1.getTheme(themes_1.default, 'default'); + theme = (0, utils_1.getTheme)(themes_1.default, 'default'); } const dataOptions = data.split(','); // Return error if dataOptions argument is undefined if (dataOptions === undefined) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', 'Data option is missing!', '-25%')); + res((0, error_1.default)('Profile', '-25%', 'Data option is missing!', '-25%')); }); } // Return error if more than 4 dataOptions were supplied if (dataOptions.length > 4) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', `Can't have more than 4 data-options!`, '-40%')); + res((0, error_1.default)('Profile', '-25%', `Can't have more than 4 data-options!`, '-40%')); }); } const width = 842; const height = 165; async function getDataOptions() { let dataBoxes = ''; - const profile = await user_stats_fetcher_1.default(process.env.GITHUB_TOKEN, username); + const profile = await (0, user_stats_fetcher_1.default)(process.env.GITHUB_TOKEN, username, includeOrgs); const stargazers = []; profile.data.user.repositories.nodes.forEach((repo, index) => { stargazers[index] = repo.stargazers.totalCount; }); + // Organization repositories and stars are opt-in via includeOrgs, since + // adding them changes the totals for anyone already embedding this badge. + // Only organizations/repositories the server's token can see are counted + // (private org repos are excluded), and results are capped at the first + // 25 organizations and first 100 repositories per organization. + let orgRepositoryCount = 0; + let orgStars = 0; + if (includeOrgs && profile.data.user.organizations) { + profile.data.user.organizations.nodes.forEach((org) => { + orgRepositoryCount += org.repositories.totalCount; + org.repositories.nodes.forEach((repo) => { + orgStars += repo.stargazers.totalCount; + }); + }); + } for (let i = 0; i < dataOptions.length; i++) { switch (dataOptions[i].toLowerCase()) { case 'followers': addDataBox('followers', i, profile.data.user.followers.totalCount, '#CAF0FF', '#00C6FF', 'M3.625,9.5A2.417,2.417,0,1,0,1.208,7.084,2.419,2.419,0,0,0,3.625,9.5Zm16.919,0a2.417,2.417,0,1,0-2.417-2.417A2.419,2.419,0,0,0,20.544,9.5Zm1.208,1.208H19.336a2.41,2.41,0,0,0-1.7.7,5.524,5.524,0,0,1,2.836,4.132h2.493a1.207,1.207,0,0,0,1.208-1.208V13.126A2.419,2.419,0,0,0,21.753,10.709Zm-9.668,0a4.23,4.23,0,1,0-4.23-4.23A4.228,4.228,0,0,0,12.085,10.709Zm2.9,1.208h-.313a5.84,5.84,0,0,1-5.174,0H9.185a4.352,4.352,0,0,0-4.351,4.351v1.088a1.813,1.813,0,0,0,1.813,1.813H17.523a1.813,1.813,0,0,0,1.813-1.813V16.269A4.352,4.352,0,0,0,14.985,11.918Zm-8.448-.506a2.41,2.41,0,0,0-1.7-.7H2.417A2.419,2.419,0,0,0,0,13.126v1.208a1.207,1.207,0,0,0,1.208,1.208H3.7A5.538,5.538,0,0,1,6.537,11.412Z'); break; case 'repositories': - addDataBox('repositories', i, profile.data.user.repositories.totalCount, '#FFCEE4', '#FF0774', 'M7.106,3A2.106,2.106,0,0,0,5,5.106V17.74a.7.7,0,0,0,.207.5,2.026,2.026,0,0,0,1.9,1.608h.7v-1.4h-.7a.7.7,0,0,1,0-1.4H17.634a1.4,1.4,0,0,0,1.4-1.4V4.4a1.4,1.4,0,0,0-1.4-1.4Zm.7,2.106h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,5.106Zm0,3.51h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,8.615Zm0,3.51h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,12.125Zm1.4,6.317v3.51l2.106-1.4,2.106,1.4v-3.51Zm5.615,0v1.4h3.51a.7.7,0,0,0,0-1.4Z'); + addDataBox('repositories', i, profile.data.user.repositories.totalCount + orgRepositoryCount, '#FFCEE4', '#FF0774', 'M7.106,3A2.106,2.106,0,0,0,5,5.106V17.74a.7.7,0,0,0,.207.5,2.026,2.026,0,0,0,1.9,1.608h.7v-1.4h-.7a.7.7,0,0,1,0-1.4H17.634a1.4,1.4,0,0,0,1.4-1.4V4.4a1.4,1.4,0,0,0-1.4-1.4Zm.7,2.106h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,5.106Zm0,3.51h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,8.615Zm0,3.51h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,12.125Zm1.4,6.317v3.51l2.106-1.4,2.106,1.4v-3.51Zm5.615,0v1.4h3.51a.7.7,0,0,0,0-1.4Z'); break; case 'stars': - addDataBox('stars', i, stargazers.reduce((a, b) => a + b, 0), '#FFEFCD', '#FFA100', 'M9.6.608,7.369,5.131l-4.992.728a1.094,1.094,0,0,0-.6,1.865l3.611,3.519L4.53,16.215a1.093,1.093,0,0,0,1.585,1.151l4.465-2.347,4.465,2.347a1.094,1.094,0,0,0,1.585-1.151l-.854-4.971,3.611-3.519a1.094,1.094,0,0,0-.6-1.865l-4.992-.728L11.561.608A1.094,1.094,0,0,0,9.6.608Z'); + addDataBox('stars', i, stargazers.reduce((a, b) => a + b, 0) + orgStars, '#FFEFCD', '#FFA100', 'M9.6.608,7.369,5.131l-4.992.728a1.094,1.094,0,0,0-.6,1.865l3.611,3.519L4.53,16.215a1.093,1.093,0,0,0,1.585,1.151l4.465-2.347,4.465,2.347a1.094,1.094,0,0,0,1.585-1.151l-.854-4.971,3.611-3.519a1.094,1.094,0,0,0-.6-1.865l-4.992-.728L11.561.608A1.094,1.094,0,0,0,9.6.608Z'); break; case 'contributions': case 'commits': @@ -63,7 +79,7 @@ async function profileWidget(username, data, themeString) { // Incorrect data item found default: return new Promise((res) => { - res(error_1.default('Profile', '-25%', `Invalid data item found!`, '-26%')); + res((0, error_1.default)('Profile', '-25%', `Invalid data item found!`, '-26%')); }); } } @@ -90,14 +106,14 @@ async function profileWidget(username, data, themeString) { // Create the request const response = await axios_1.default.get(`https://api.github.com/users/${username}`); // Grab the avatar - const avatar = await utils_1.requestInBase64(response.data.avatar_url); + const avatar = await (0, utils_1.requestInBase64)(response.data.avatar_url); return ` - ${card_1.default(width, height, theme.background)} + ${(0, card_1.default)(width, height, theme.background)} ${response.data.name === null @@ -112,7 +128,7 @@ async function profileWidget(username, data, themeString) { } catch (error) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', 'GitHub API-call error!', '-24%')); + res((0, error_1.default)('Profile', '-25%', 'GitHub API-call error!', '-24%')); }); } } diff --git a/public/src/widgets/profile.js.map b/public/src/widgets/profile.js.map index f021809..638e766 100644 --- a/public/src/widgets/profile.js.map +++ b/public/src/widgets/profile.js.map @@ -1 +1 @@ -{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../src/widgets/profile.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../src/widgets/profile.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/fetchers/user-stats-fetcher.ts b/src/fetchers/user-stats-fetcher.ts index 5c47eeb..384a884 100644 --- a/src/fetchers/user-stats-fetcher.ts +++ b/src/fetchers/user-stats-fetcher.ts @@ -3,7 +3,8 @@ import GithubUserRequest from '../interfaces/GithubUser' export default async function getGithubUserStats( token: string | undefined, - username: string + username: string, + includeOrgs = false ): Promise { const headers = { @@ -12,8 +13,8 @@ export default async function getGithubUserStats( const body = { query: ` - query { - user(login: "${username}") { + query($login: String!, $includeOrgs: Boolean!) { + user(login: $login) { name login contributionsCollection { @@ -37,9 +38,26 @@ export default async function getGithubUserStats( } } } + organizations(first: 25) @include(if: $includeOrgs) { + nodes { + login + repositories(first: 100, privacy: PUBLIC, orderBy: {direction: DESC, field: STARGAZERS}) { + totalCount + nodes { + stargazers { + totalCount + } + } + } + } + } } } `, + variables: { + login: username, + includeOrgs: includeOrgs, + }, } const response = await fetch('https://api.github.com/graphql', { diff --git a/src/interfaces/GithubUser.ts b/src/interfaces/GithubUser.ts index 455a1db..dd6a071 100644 --- a/src/interfaces/GithubUser.ts +++ b/src/interfaces/GithubUser.ts @@ -1,5 +1,6 @@ import { ContributionsCollection, ContributedRepositories } from './Contributions' import { Repositories } from './Repositories' +import { Organizations } from './Organizations' export default interface GithubUserRequest { data: GithubUserData @@ -16,6 +17,7 @@ export interface GithubUser { repositoriesContributedTo: ContributedRepositories followers: Followers repositories: Repositories + organizations?: Organizations } export interface Followers { diff --git a/src/interfaces/Organizations.ts b/src/interfaces/Organizations.ts new file mode 100644 index 0000000..6936a09 --- /dev/null +++ b/src/interfaces/Organizations.ts @@ -0,0 +1,10 @@ +import { Repositories } from './Repositories' + +export interface Organizations { + nodes: Organization[] +} + +export interface Organization { + login: string + repositories: Repositories +} diff --git a/src/widgets/profile.ts b/src/widgets/profile.ts index 08a5c96..30875cd 100644 --- a/src/widgets/profile.ts +++ b/src/widgets/profile.ts @@ -1,7 +1,7 @@ require('dotenv').config() import axios from 'axios' -import { getTheme, requestInBase64 } from '../utils' +import { getBoolean, getTheme, requestInBase64 } from '../utils' import errorWidget from './error' import buildCard from '../components/card' import GithubUserRequest from '../interfaces/GithubUser' @@ -13,9 +13,12 @@ import themes from '../data/themes' export default async function profileWidget( username: string, data: string, - themeString?: string + themeString?: string, + includeOrgsString?: string ): Promise { + const includeOrgs = getBoolean(includeOrgsString || '') + // Set the theme let theme: Theme = getTheme(themes, 'default') @@ -64,7 +67,8 @@ export default async function profileWidget( const profile: GithubUserRequest = await getGithubUserStats( process.env.GITHUB_TOKEN, - username + username, + includeOrgs ) const stargazers: number[] = [] @@ -74,6 +78,22 @@ export default async function profileWidget( } ) + // Organization repositories and stars are opt-in via includeOrgs, since + // adding them changes the totals for anyone already embedding this badge. + // Only organizations/repositories the server's token can see are counted + // (private org repos are excluded), and results are capped at the first + // 25 organizations and first 100 repositories per organization. + let orgRepositoryCount = 0 + let orgStars = 0 + if (includeOrgs && profile.data.user.organizations) { + profile.data.user.organizations.nodes.forEach((org) => { + orgRepositoryCount += org.repositories.totalCount + org.repositories.nodes.forEach((repo: Repository) => { + orgStars += repo.stargazers.totalCount + }) + }) + } + for (let i = 0; i < dataOptions.length; i++) { switch (dataOptions[i].toLowerCase()) { case 'followers': @@ -90,7 +110,7 @@ export default async function profileWidget( addDataBox( 'repositories', i, - profile.data.user.repositories.totalCount, + profile.data.user.repositories.totalCount + orgRepositoryCount, '#FFCEE4', '#FF0774', 'M7.106,3A2.106,2.106,0,0,0,5,5.106V17.74a.7.7,0,0,0,.207.5,2.026,2.026,0,0,0,1.9,1.608h.7v-1.4h-.7a.7.7,0,0,1,0-1.4H17.634a1.4,1.4,0,0,0,1.4-1.4V4.4a1.4,1.4,0,0,0-1.4-1.4Zm.7,2.106h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,5.106Zm0,3.51h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,8.615Zm0,3.51h.7a.7.7,0,0,1,.7.7v.7a.7.7,0,0,1-.7.7h-.7a.7.7,0,0,1-.7-.7v-.7A.7.7,0,0,1,7.808,12.125Zm1.4,6.317v3.51l2.106-1.4,2.106,1.4v-3.51Zm5.615,0v1.4h3.51a.7.7,0,0,0,0-1.4Z' @@ -100,7 +120,7 @@ export default async function profileWidget( addDataBox( 'stars', i, - stargazers.reduce((a, b) => a + b, 0), + stargazers.reduce((a, b) => a + b, 0) + orgStars, '#FFEFCD', '#FFA100', 'M9.6.608,7.369,5.131l-4.992.728a1.094,1.094,0,0,0-.6,1.865l3.611,3.519L4.53,16.215a1.093,1.093,0,0,0,1.585,1.151l4.465-2.347,4.465,2.347a1.094,1.094,0,0,0,1.585-1.151l-.854-4.971,3.611-3.519a1.094,1.094,0,0,0-.6-1.865l-4.992-.728L11.561.608A1.094,1.094,0,0,0,9.6.608Z'