@@ -15,7 +15,7 @@ import {getMemberIcon, getMemberName} from '../lib/slackUtils';
1515import State from '../lib/state' ;
1616// eslint-disable-next-line no-unused-vars
1717import type { Results , Standings } from './types' ;
18- import { fetchUserACsInContest } from './utils' ;
18+ import { fetchUserACsInContests } from './utils' ;
1919
2020const log = logger . child ( { bot : 'atcoder' } ) ;
2121const mutex = new Mutex ( ) ;
@@ -114,6 +114,15 @@ interface ContestEntry {
114114const SILENT_CONTEST_PREFIXES = [ 'awc' , 'adt_' ] ;
115115const SILENT_START_CONTEST_PREFIXES = [ 'adt_' ] ;
116116
117+ // AtCoder problem sets whose solve counts unlock completion achievements.
118+ // contestId is the AtCoder Problems contest id; counter matches an achievement in achievements.ts.
119+ const PROBLEM_SET_COUNTERS : { contestId : string , counter : string } [ ] = [
120+ { contestId : 'fps-24' , counter : 'atcoder-fps24-solves' } ,
121+ { contestId : 'tdpc' , counter : 'atcoder-tdpc-solves' } ,
122+ { contestId : 'dp' , counter : 'atcoder-edpc-solves' } ,
123+ { contestId : 'ndpc' , counter : 'atcoder-ndpc-solves' } ,
124+ ] ;
125+
117126export default async ( { eventClient, webClient : slack } : SlackInterface ) => {
118127 const state = await State . init < StateObj > ( 'atcoder' , {
119128 users : [ ] ,
@@ -493,15 +502,26 @@ export default async ({eventClient, webClient: slack}: SlackInterface) => {
493502 await new Promise < void > ( ( resolve ) => {
494503 setTimeout ( resolve , 1000 ) ;
495504 } ) ;
496- const absACs = await fetchUserACsInContest ( user . atcoder , 'abs' ) ;
505+ // abs / typical90 are fetched alongside the completion sets but kept out of
506+ // PROBLEM_SET_COUNTERS because they don't map 1:1 to a counter
507+ // (atcoder-typical-solves counts abs + typical90 combined).
508+ const acsByContest = await fetchUserACsInContests ( user . atcoder , [
509+ 'abs' ,
510+ 'typical90' ,
511+ ...PROBLEM_SET_COUNTERS . map ( ( { contestId} ) => contestId ) ,
512+ ] ) . catch ( ( error : unknown ) : null => {
513+ log . error ( `[atcoder-daily] Failed to fetch submissions for ${ user . atcoder } : ${ String ( error ) } ` ) ;
514+ return null ;
515+ } ) ;
516+ // Skip this user for today's run if their submissions could not be fetched
517+ if ( acsByContest === null ) {
518+ continue ;
519+ }
520+
521+ const absACs = acsByContest . get ( 'abs' ) ?? new Set < string > ( ) ;
497522 absACs . delete ( 'practice_1' ) ;
498523 const absSolve = absACs . size ;
499-
500- await new Promise < void > ( ( resolve ) => {
501- setTimeout ( resolve , 1000 ) ;
502- } ) ;
503- const typicalACs = await fetchUserACsInContest ( user . atcoder , 'typical90' ) ;
504- const typicalSolve = typicalACs . size ;
524+ const typicalSolve = acsByContest . get ( 'typical90' ) ?. size ?? 0 ;
505525
506526 const previousAbsSolve = ( await get ( user . slack , 'atcoder-abs-solves' ) ) || 0 ;
507527 if ( typeof previousAbsSolve !== 'number' ) {
@@ -513,12 +533,17 @@ export default async ({eventClient, webClient: slack}: SlackInterface) => {
513533 }
514534 const previousTypicalSolve = previousTypicalSolves - previousAbsSolve ;
515535
516- set ( user . slack , 'atcoder-abs-solves' , absSolve ) ;
517- set ( user . slack , 'atcoder-typical-solves' , absSolve + typicalSolve ) ;
536+ await set ( user . slack , 'atcoder-abs-solves' , absSolve ) ;
537+ await set ( user . slack , 'atcoder-typical-solves' , absSolve + typicalSolve ) ;
518538
519539 increase += Math . max ( 0 , absSolve - previousAbsSolve ) ;
520540 increase += Math . max ( 0 , typicalSolve - previousTypicalSolve ) ;
521541
542+ // Unlock completion achievements for well-known problem sets (EDPC, TDPC, etc.)
543+ for ( const { contestId, counter} of PROBLEM_SET_COUNTERS ) {
544+ await set ( user . slack , counter , acsByContest . get ( contestId ) ?. size ?? 0 ) ;
545+ }
546+
522547 let username = await getMemberName ( user . slack ) ;
523548 if ( username . length > 12 ) {
524549 username = `${ username . slice ( 0 , 10 ) } ...` ;
0 commit comments