import fetch from "node-fetch" ; import cheerio from "cheerio" ; import { formatParams , Options } from "./formatParams" ; param userName param options example export const getGithubUserContribution = async ( userName , options = { } ) => { const url = "year" in options || "from" in options || "to" in options ? `https://github.com/users/${ userName }/contributions?` + formatParams ( options ) : `https://github.com/${ userName }` ; const res = await fetch ( url ) ; if ( ! res . ok ) throw new Error ( res . statusText ) ; const resText = await res . text ( ) ; return parseUserPage ( resText ) ; } ; const defaultColorScheme = [ "#ebedf0" , "#9be9a8" , "#40c463" , "#30a14e" , "#216e39" , ] ; const parseUserPage = ( content ) => { const $ = cheerio . load ( content ) ; const colorScheme = [ ... defaultColorScheme ] ; const rawCells = $ ( ".js-calendar-graph rect[data-count]" ) . toArray ( ) . map ( ( x ) => { const level = + x . attribs [ "data-level" ] ; const count = + x . attribs [ "data-count" ] ; const date = x . attribs [ "data-date" ] ; const color = colorScheme [ level ] ; if ( ! color ) throw new Error ( "could not determine the color of the cell" ) ; return { svgPosition : getSvgPosition ( x ) , color , count , date } ; } ) ; const xMap = { } ; const yMap = { } ; rawCells . forEach ( ( { svgPosition : { x , y } } ) => { xMap [ x ] = true ; yMap [ y ] = true ; } ) ; const xRange = Object . keys ( xMap ) . map ( ( x ) => + x ) . sort ( ( a , b ) => + a - + b ) ; const yRange = Object . keys ( yMap ) . map ( ( x ) => + x ) . sort ( ( a , b ) => + a - + b ) ; const cells = rawCells . map ( ( { svgPosition , ... c } ) => ( { ... c , x : xRange . indexOf ( svgPosition . x ) , y : yRange . indexOf ( svgPosition . y ) } ) ) ; return { cells , colorScheme } ; } ; const getSvgPosition = ( e ) : { x : number ; y : number ; } => { if ( ! e || e . tagName === "svg" ) return { x : 0 , y : 0 } ; const p = getSvgPosition ( e . parent ) ; if ( e . attribs . x ) p . x += + e . attribs . x ; if ( e . attribs . y ) p . y += + e . attribs . y ; if ( e . attribs . transform ) { const m = e . attribs . transform . match ( /translate\( *([\.\d]+) *, *([\.\d]+) *\)/ ) ; if ( m ) { p . x += + m [ 1 ] ; p . y += + m [ 2 ] ; } } return p ; } ; type ThenArg < T > = T extends PromiseLike < infer U > ? U : T ; export type Res = ThenArg < ReturnType < typeof getGithubUserContribution > > ; export type Cell = Res [ "cells" ] [ number ] ;