@@ -118,7 +118,7 @@ export class GitHubService extends BaseService {
118118 private getDefaultStats ( username : string ) : GitHubStats {
119119 return {
120120 name : username ,
121- avatarUrl : ` https://avatars.githubusercontent.com/u/0?v=4?s=130` ,
121+ avatarUrl : ' https://avatars.githubusercontent.com/u/0?v=4' ,
122122 totalStars : 0 ,
123123 totalCommits : 0 ,
124124 totalPRs : 0 ,
@@ -131,6 +131,75 @@ export class GitHubService extends BaseService {
131131 } ;
132132 }
133133
134+ private buildContributionYearRanges ( createdAt : Date ) : Array < { from : string ; to : string } > {
135+ const now = new Date ( ) ;
136+ const years : Array < { from : string ; to : string } > = [ ] ;
137+ let yearStart = new Date ( createdAt . getFullYear ( ) , 0 , 1 ) ;
138+
139+ while ( yearStart <= now ) {
140+ const yearEnd = new Date ( yearStart . getFullYear ( ) , 11 , 31 , 23 , 59 , 59 ) ;
141+ years . push ( {
142+ from : ( yearStart > createdAt ? yearStart : createdAt ) . toISOString ( ) ,
143+ to : ( yearEnd > now ? now : yearEnd ) . toISOString ( ) ,
144+ } ) ;
145+ yearStart = new Date ( yearStart . getFullYear ( ) + 1 , 0 , 1 ) ;
146+ }
147+
148+ return years ;
149+ }
150+
151+ private async fetchTotalCommitContributions ( username : string , createdAt : string ) : Promise < number > {
152+ const ranges = this . buildContributionYearRanges ( new Date ( createdAt ) ) ;
153+
154+ if ( ranges . length === 0 ) {
155+ return 0 ;
156+ }
157+
158+ const variableDefinitions = ranges
159+ . map ( ( _ , index ) => `$from${ index } : DateTime!, $to${ index } : DateTime!` )
160+ . join ( ', ' ) ;
161+ const contributionSelections = ranges
162+ . map ( ( _ , index ) => `year${ index } : contributionsCollection(from: $from${ index } , to: $to${ index } ) { totalCommitContributions restrictedContributionsCount }` )
163+ . join ( '\n' ) ;
164+
165+ const query = `
166+ query($username: String!, ${ variableDefinitions } ) {
167+ user(login: $username) {
168+ ${ contributionSelections }
169+ }
170+ }
171+ ` ;
172+
173+ const variables : Record < string , string > = { username } ;
174+ ranges . forEach ( ( range , index ) => {
175+ variables [ `from${ index } ` ] = range . from ;
176+ variables [ `to${ index } ` ] = range . to ;
177+ } ) ;
178+
179+ const result : any = await this . octokit . graphql ( query , variables ) ;
180+ const user = result . user ;
181+
182+ if ( ! user ) {
183+ return 0 ;
184+ }
185+
186+ return ranges . reduce ( ( sum , _ , index ) => {
187+ const contributionYear = user [ `year${ index } ` ] ;
188+ return sum + ( contributionYear ?. totalCommitContributions || 0 ) + ( contributionYear ?. restrictedContributionsCount || 0 ) ;
189+ } , 0 ) ;
190+ }
191+
192+ private withAvatarMode ( stats : GitHubStats , avatarMode : 'none' | 'avatar' | 'radar' ) : GitHubStats {
193+ if ( avatarMode === 'none' ) {
194+ return stats ;
195+ }
196+
197+ return {
198+ ...stats ,
199+ avatarUrl : `${ stats . avatarUrl } ${ stats . avatarUrl . includes ( '?' ) ? '&' : '?' } s=130` ,
200+ } ;
201+ }
202+
134203 /**
135204 * Calculate user rank based on stats
136205 */
@@ -162,7 +231,7 @@ export class GitHubService extends BaseService {
162231 options : { avatarMode : 'none' | 'avatar' | 'radar' }
163232 ) : Promise < GitHubStats > {
164233 return this . executeWithLogging ( `fetchUserStats(${ username } )` , async ( ) => {
165- return this . cachedRequest ( `user-stats-${ username } - ${ options . avatarMode } ` , async ( ) => {
234+ const stats = await this . cachedRequest ( `user-stats-${ username } ` , async ( ) => {
166235 try {
167236 // Use GraphQL to get all-time stats in a single request
168237 const query = `
@@ -217,64 +286,14 @@ export class GitHubService extends BaseService {
217286 const totalPRs = userData . pullRequests . totalCount ;
218287 const totalIssues = userData . issues . totalCount ;
219288
220- // Get all-time commits by summing contributions from account creation to now
221- const createdAt = new Date ( userData . createdAt ) ;
222- const now = new Date ( ) ;
223- let totalCommits = 0 ;
224-
225- // Fetch commits year by year (GitHub only allows 1 year at a time)
226- const years : { from : Date ; to : Date } [ ] = [ ] ;
227- let yearStart = new Date ( createdAt . getFullYear ( ) , 0 , 1 ) ;
228-
229- while ( yearStart <= now ) {
230- const yearEnd = new Date ( yearStart . getFullYear ( ) , 11 , 31 , 23 , 59 , 59 ) ;
231- years . push ( {
232- from : yearStart > createdAt ? yearStart : createdAt ,
233- to : yearEnd > now ? now : yearEnd
234- } ) ;
235- yearStart = new Date ( yearStart . getFullYear ( ) + 1 , 0 , 1 ) ;
236- }
237-
238- // Fetch all years' contributions in parallel
239- const commitPromises = years . map ( async ( { from, to } ) => {
240- const commitQuery = `
241- query($username: String!, $from: DateTime!, $to: DateTime!) {
242- user(login: $username) {
243- contributionsCollection(from: $from, to: $to) {
244- totalCommitContributions
245- restrictedContributionsCount
246- }
247- }
248- }
249- ` ;
250- try {
251- const result : any = await this . octokit . graphql ( commitQuery , {
252- username,
253- from : from . toISOString ( ) ,
254- to : to . toISOString ( )
255- } ) ;
256- const collection = result . user ?. contributionsCollection ;
257- return ( collection ?. totalCommitContributions || 0 ) + ( collection ?. restrictedContributionsCount || 0 ) ;
258- } catch {
259- return 0 ;
260- }
261- } ) ;
262-
263- const yearlyCommits = await Promise . all ( commitPromises ) ;
264- totalCommits = yearlyCommits . reduce ( ( sum , count ) => sum + count , 0 ) ;
289+ const totalCommits = await this . fetchTotalCommitContributions ( username , userData . createdAt ) ;
265290
266291 // Calculate rank
267292 const rank = this . calculateRank ( totalStars , totalCommits , totalPRs , totalIssues ) ;
268293
269- // Format avatar URL
270- let avatarUrl = userData . avatarUrl ;
271- if ( options . avatarMode !== 'none' ) {
272- avatarUrl = `${ userData . avatarUrl } ${ userData . avatarUrl . includes ( '?' ) ? '&' : '?' } s=130` ;
273- }
274-
275294 return {
276295 name : userData . name || username ,
277- avatarUrl,
296+ avatarUrl : userData . avatarUrl ,
278297 totalStars,
279298 totalCommits,
280299 totalPRs,
@@ -286,6 +305,8 @@ export class GitHubService extends BaseService {
286305 return this . handleGitHubError ( error , username ) ;
287306 }
288307 } ) ;
308+
309+ return this . withAvatarMode ( stats , options . avatarMode ) ;
289310 } ) ;
290311 }
291312
0 commit comments