master
  1import { Commit, define, latest, User } from '#src'
  2
  3export default define({
  4  url: import.meta.url,
  5  badges: [
  6    'midnight-commits',
  7    'morning-commits',
  8    'evening-commits',
  9    'sleepy-coder',
 10  ] as const,
 11  present(data, grant) {
 12    const morningCommits: Commit[] = []
 13    const eveningCommits: Commit[] = []
 14    const midnightCommits: Commit[] = []
 15    const sleepycoderCommits: Commit[] = []
 16
 17    const timezoneOffset = guessTimezone(data.user)
 18
 19    for (const repo of data.repos) {
 20      for (const commit of repo.commits) {
 21        const date = new Date(commit.committedDate)
 22
 23        date.setUTCHours(date.getUTCHours() + timezoneOffset)
 24
 25        if (date.getUTCHours() >= 4 && date.getUTCHours() < 6) {
 26          sleepycoderCommits.push(commit)
 27        }
 28        if (date.getUTCHours() >= 6 && date.getUTCHours() < 8) {
 29          morningCommits.push(commit)
 30        }
 31        if (date.getUTCHours() >= 21) {
 32          eveningCommits.push(commit)
 33        }
 34        if (date.getUTCHours() == 0 && date.getUTCMinutes() < 5) {
 35          midnightCommits.push(commit)
 36        }
 37      }
 38    }
 39
 40    if (sleepycoderCommits.length > 0) {
 41      grant('sleepy-coder', 'I am a sleepy coder.').evidenceCommits(
 42        ...sleepycoderCommits.sort(latest).slice(0, 6),
 43      )
 44    }
 45    if (morningCommits.length > 0) {
 46      grant('morning-commits', 'I commit in the morning.').evidenceCommits(
 47        ...morningCommits.sort(latest).slice(0, 6),
 48      )
 49    }
 50    if (eveningCommits.length > 0) {
 51      grant('evening-commits', 'I commit in the evening.').evidenceCommits(
 52        ...eveningCommits.sort(latest).slice(0, 6),
 53      )
 54    }
 55    if (midnightCommits.length > 0) {
 56      grant('midnight-commits', 'I commit at midnight.').evidenceCommits(
 57        ...midnightCommits.sort(latest).slice(0, 6),
 58      )
 59    }
 60  },
 61})
 62
 63function guessTimezone(user: User) {
 64  const location = user.location ? user.location.toLowerCase() : ''
 65  const regexMapping = [
 66    { pattern: /\bamsterdam\b|\bnetherlands\b/, offset: 2 },
 67    { pattern: /\bantwerp\b|\bghent\b/, offset: 2 },
 68    { pattern: /\bargentina\b|\bbuenos aires\b/, offset: -3 },
 69    { pattern: /\bathens\b|\bgreece\b/, offset: 3 },
 70    { pattern: /\bbangkok\b|\bthailand\b/, offset: 7 },
 71    { pattern: /\bbarcelona\b|\bvalencia\b|\bseville\b/, offset: 2 },
 72    { pattern: /\bbeijing\b|\bchina\b/, offset: 8 },
 73    { pattern: /\bbelgrade\b|\bserbia\b/, offset: 2 },
 74    { pattern: /\bberlin\b|\bgermany\b/, offset: 2 },
 75    { pattern: /\bbilbao\b|\bzaragoza\b/, offset: 2 },
 76    { pattern: /\bbordeaux\b|\bmarseille\b|\blyon\b/, offset: 2 },
 77    { pattern: /\bboston\b|\bmassachusetts\b|\bma\b/, offset: -4 },
 78    { pattern: /\bbratislava\b|\bslovakia\b/, offset: 2 },
 79    { pattern: /\bbrazil\b|\bsao paulo\b|\brio de janeiro\b/, offset: -3 },
 80    { pattern: /\bbrussels\b|\bbelgium\b/, offset: 2 },
 81    { pattern: /\bbucharest\b|\bromania\b/, offset: 3 },
 82    { pattern: /\bbudapest\b|\bhungary\b/, offset: 2 },
 83    { pattern: /\bcairo\b|\begypt\b/, offset: 2 },
 84    { pattern: /\bcasablanca\b|\bmorocco\b/, offset: 1 },
 85    { pattern: /\bchicago\b|\billinois\b|\bil\b/, offset: -5 },
 86    { pattern: /\bchile\b|\bsantiago\b/, offset: -3 },
 87    { pattern: /\bchisinau\b|\bmoldova\b/, offset: 3 },
 88    { pattern: /\bcolombia\b|\bbogota\b/, offset: -5 },
 89    { pattern: /\bcopenhagen\b|\bdenmark\b/, offset: 2 },
 90    { pattern: /\bcosta rica\b|\bsan jose\b/, offset: -6 },
 91    { pattern: /\bcrete\b|\bthessaloniki\b/, offset: 3 },
 92    { pattern: /\bdhaka\b|\bbangladesh\b/, offset: 6 },
 93    { pattern: /\bdubai\b|\buae\b|\babu dhabi\b/, offset: 4 },
 94    { pattern: /\bdublin\b|\bireland\b/, offset: 1 },
 95    { pattern: /\becuador\b|\bquito\b/, offset: -5 },
 96    { pattern: /\bfaro\b|\bevora\b/, offset: 1 },
 97    { pattern: /\bfiji\b/, offset: 12 },
 98    { pattern: /\bflorence\b|\bpalermo\b/, offset: 2 },
 99    { pattern: /\bflorida\b|\bmiami\b/, offset: -4 },
100    { pattern: /\bgdansk\b|\bwroclaw\b/, offset: 2 },
101    { pattern: /\bgeneva\b|\bbern\b/, offset: 2 },
102    { pattern: /\bgenoa\b|\bturin\b/, offset: 2 },
103    { pattern: /\bglasgow\b|\bliverpool\b|\bmanchester\b/, offset: 0 },
104    { pattern: /\bgothenburg\b|\bmalmo\b/, offset: 2 },
105    { pattern: /\bhamburg\b|\bmunich\b|\bfrankfurt\b/, offset: 2 },
106    { pattern: /\bhawaii\b/, offset: -10 },
107    { pattern: /\bhelsinki\b|\bfinland\b/, offset: 3 },
108    { pattern: /\biraq\b|\bbaghdad\b/, offset: 3 },
109    { pattern: /\bisrael\b|\btel aviv\b/, offset: 3 },
110    { pattern: /\bjakarta\b|\bindonesia\b/, offset: 7 },
111    { pattern: /\bjohannesburg\b|\bsouth africa\b/, offset: 2 },
112    { pattern: /\bjordan\b|\bamman\b/, offset: 3 },
113    { pattern: /\bkathmandu\b|\bnepal\b/, offset: 5.75 },
114    { pattern: /\bkiev\b|\bukraine\b/, offset: 3 },
115    { pattern: /\bkuala lumpur\b|\bmalaysia\b/, offset: 8 },
116    { pattern: /\blagos\b|\bnigeria\b/, offset: 1 },
117    { pattern: /\bled\b|\bslovenia\b/, offset: 2 },
118    { pattern: /\bledz\b|\bkrakow\b/, offset: 2 },
119    { pattern: /\blisbon\b|\bportugal\b/, offset: 1 },
120    { pattern: /\bljubljana\b|\bslovenia\b/, offset: 2 },
121    { pattern: /\blondon\b|\buk\b/, offset: 0 },
122    { pattern: /\blos angeles\b|\bcalifornia\b|\bca\b/, offset: -7 },
123    { pattern: /\bluxembourg\b/, offset: 2 },
124    { pattern: /\blyon\b|\bnantes\b/, offset: 2 },
125    { pattern: /\bmadrid\b|\bspain\b/, offset: 2 },
126    { pattern: /\bmalta\b|\bvalletta\b/, offset: 2 },
127    { pattern: /\bmelbourne\b/, offset: 11 },
128    { pattern: /\bmexico\b|\bmexico city\b/, offset: -5 },
129    { pattern: /\bminsk\b|\bbelarus\b/, offset: 3 },
130    { pattern: /\bmonaco\b/, offset: 2 },
131    { pattern: /\bmontreal\b|\bquebec\b/, offset: -4 },
132    { pattern: /\bmoscow\b|\brussia\b/, offset: 3 },
133    { pattern: /\bmumbai\b|\bindia\b/, offset: 5.5 },
134    { pattern: /\bnairobi\b|\bkenya\b/, offset: 3 },
135    { pattern: /\bnaples\b|\bmilan\b|\bvenice\b/, offset: 2 },
136    { pattern: /\bnew delhi\b/, offset: 5.5 },
137    { pattern: /\bnew york\b|\bny\b/, offset: -4 },
138    { pattern: /\bnew zealand\b|\bauckland\b/, offset: 13 },
139    { pattern: /\bnice\b|\btoulouse\b/, offset: 2 },
140    { pattern: /\bnicosia\b|\bcyprus\b/, offset: 3 },
141    { pattern: /\bodessa\b|\blviv\b/, offset: 3 },
142    { pattern: /\boslo\b|\bnorway\b/, offset: 2 },
143    { pattern: /\bpakistan\b|\blahore\b|\bkarachi\b/, offset: 5 },
144    { pattern: /\bpanama\b|\bpanama city\b/, offset: -5 },
145    { pattern: /\bpapua new guinea\b|\bport moresby\b/, offset: 10 },
146    { pattern: /\bparis\b|\bfrance\b/, offset: 2 },
147    { pattern: /\bperu\b|\blima\b/, offset: -5 },
148    { pattern: /\bpodgorica\b|\bmontenegro\b/, offset: 2 },
149    { pattern: /\bporto\b|\bbraga\b/, offset: 1 },
150    { pattern: /\bprague\b|\bczech republic\b|\bczechia\b/, offset: 2 },
151    { pattern: /\breykjavik\b|\biceland\b/, offset: 0 },
152    { pattern: /\briga\b|\blatvia\b/, offset: 3 },
153    { pattern: /\brome\b|\bitaly\b/, offset: 2 },
154    { pattern: /\brotterdam\b|\butrecht\b/, offset: 2 },
155    { pattern: /\bsalzburg\b|\bgraz\b/, offset: 2 },
156    { pattern: /\bsarajevo\b|\bbosnia and herzegovina\b/, offset: 2 },
157    { pattern: /\bsaudi arabia\b|\briyadh\b/, offset: 3 },
158    { pattern: /\bseoul\b|\bsouth korea\b/, offset: 9 },
159    { pattern: /\bshanghai\b/, offset: 8 },
160    { pattern: /\bsheffield\b|\bleeds\b/, offset: 0 },
161    { pattern: /\bsingapore\b/, offset: 8 },
162    { pattern: /\bskopje\b|\bnorth macedonia\b/, offset: 2 },
163    { pattern: /\bsofia\b|\bbulgaria\b/, offset: 3 },
164    { pattern: /\bstockholm\b|\bsweden\b/, offset: 2 },
165    { pattern: /\bstuttgart\b|\bdusseldorf\b/, offset: 2 },
166    { pattern: /\bsydney\b|\baustralia\b/, offset: 11 },
167    { pattern: /\btallinn\b|\bestonia\b/, offset: 3 },
168    { pattern: /\btehran\b|\biran\b/, offset: 3.5 },
169    { pattern: /\btexas\b|\bhouston\b|\bdallas\b/, offset: -5 },
170    { pattern: /\btirana\b|\balbania\b/, offset: 2 },
171    { pattern: /\btokyo\b|\bjapan\b/, offset: 9 },
172    { pattern: /\btoronto\b|\bontario\b/, offset: -4 },
173    { pattern: /\btunisia\b|\btunis\b/, offset: 1 },
174    { pattern: /\bvalencia\b|\bmalaga\b/, offset: 2 },
175    { pattern: /\bvancouver\b|\bbritish columbia\b/, offset: -7 },
176    { pattern: /\bvenezuela\b|\bcaracas\b/, offset: -4 },
177    { pattern: /\bvienna\b|\baustria\b/, offset: 2 },
178    { pattern: /\bvilnius\b|\blithuania\b/, offset: 3 },
179    { pattern: /\bwarsaw\b|\bpoland\b/, offset: 2 },
180    { pattern: /\bzagreb\b|\bcroatia\b/, offset: 2 },
181    { pattern: /\bzurich\b|\bswitzerland\b/, offset: 2 },
182    { pattern: /\bturkey\b|\bistanbul\b|\bankara\b/, offset: 3 },
183  ]
184
185  for (const mapping of regexMapping) {
186    if (mapping.pattern.test(location)) {
187      return mapping.offset
188    }
189  }
190
191  return 0
192}