1- import { Contest } from '@entities/contest.entity' ;
1+ import { Contest , ContestList } from '@entities/contest.entity' ;
22import { HttpService } from '@nestjs/axios' ;
33import { Injectable } from '@nestjs/common' ;
44import { ConfigService } from '@nestjs/config' ;
55import * as cheerio from 'cheerio' ;
6- import { Cheerio } from 'cheerio' ;
7- import { AnyNode } from 'domhandler' ;
86import * as process from 'node:process' ;
97
108@Injectable ( )
@@ -56,62 +54,94 @@ export class BojRepository {
5654 return problems ;
5755 }
5856
59- async getEndedContests ( ) : Promise < Contest [ ] > {
60- const endedUrl = 'https://www.acmicpc.net/contest/official/list' ;
57+ async getContestsFromBoj ( ) : Promise < ContestList > {
58+ const url = 'https://www.acmicpc.net/contest/official/list' ;
6159
6260 const response = cheerio . load (
6361 await this . httpService . axiosRef
64- . get < string > ( endedUrl , {
62+ . get < string > ( url , {
6563 headers : {
6664 'User-Agent' : this . configService . get < string > ( 'BOJ_USER_AGENT' ) ,
6765 } ,
6866 } )
69-
7067 . then ( ( res ) => res . data ) ,
7168 ) ;
7269
73- const contests : Contest [ ] = [ ] ;
70+ const contests : ContestList = new ContestList ( ) ;
7471
7572 const rows = response (
7673 'body > div.wrapper > div.container.content > div.row > div:nth-child(2) > div > table > tbody > tr' ,
7774 ) ;
7875 for ( let i = 0 ; i < rows . length ; i ++ ) {
79- if ( rows . eq ( i ) . find ( 'td:nth-child(6)' ) . text ( ) !== '종료' ) {
80- continue ;
81- }
82-
8376 const venue = 'BOJ Open' ;
8477 const name = rows . eq ( i ) . find ( 'td:nth-child(1) > a' ) . text ( ) ;
8578 const url = 'https://www.acmicpc.net' + rows . eq ( i ) . find ( 'td:nth-child(1) > a' ) . attr ( 'href' ) ;
8679 const startDate = new Date (
8780 1000 * parseInt ( < string > rows . eq ( i ) . find ( 'td:nth-child(4) > span' ) . attr ( 'data-timestamp' ) ) ,
88- ) . toISOString ( ) ;
81+ ) ;
8982 const endDate = new Date (
9083 1000 * parseInt ( < string > rows . eq ( i ) . find ( 'td:nth-child(5) > span' ) . attr ( 'data-timestamp' ) ) ,
91- ) . toISOString ( ) ;
92-
93- contests . push ( new Contest ( venue , name , url , startDate , endDate ) ) ;
84+ ) ;
85+
86+ const contest = new Contest ( venue , name , url , startDate , endDate ) ;
87+ if ( endDate < new Date ( ) ) {
88+ contests . ended . push ( contest ) ;
89+ } else if ( new Date ( ) < startDate ) {
90+ contests . upcoming . push ( contest ) ;
91+ } else {
92+ contests . ongoing . push ( contest ) ;
93+ }
9494 }
9595
9696 return contests ;
9797 }
9898
99- async getOngoingContests ( ) : Promise < Contest [ ] > {
100- const response = await this . otherResponse ( ) ;
101-
102- if ( response ( '.col-md-12' ) . length < 6 ) {
103- return [ ] ;
104- }
105-
106- return this . contestsFromOther ( response , 3 ) ;
107- }
99+ async getContestsFromCList ( ) : Promise < ContestList > {
100+ const url = 'https://clist.by/api/v4/contest/' ;
101+ const headers = {
102+ Authorization : ` ${ process . env . CLIST_API_KEY } ` ,
103+ } ;
104+ const params = {
105+ resource_id__in : '1, 25, 86, 141, 93, 102' ,
106+ order_by : '-start' ,
107+ } ;
108108
109- async getUpcomingContests ( ) : Promise < Contest [ ] > {
110- const response = await this . otherResponse ( ) ;
109+ const response = await this . httpService . axiosRef . get < {
110+ objects : {
111+ event : string ;
112+ start : string ;
113+ end : string ;
114+ href : string ;
115+ resource_id : number ;
116+ } [ ] ;
117+ } > ( url , {
118+ headers : headers ,
119+ params : params ,
120+ } ) ;
111121
112- const rowIndex = response ( '.col-md-12' ) . length === 6 ? 5 : 3 ;
122+ const clist : {
123+ event : string ;
124+ start : string ;
125+ end : string ;
126+ href : string ;
127+ resource_id : number ;
128+ } [ ] = response . data . objects ;
129+
130+ const contests : ContestList = new ContestList ( ) ;
131+ for ( const contest of clist ) {
132+ const startDate = new Date ( contest . start ) ;
133+ const endDate = new Date ( contest . end ) ;
134+
135+ if ( endDate < new Date ( ) ) {
136+ contests . ended . push ( Contest . fromCList ( contest ) ) ;
137+ } else if ( new Date ( ) < startDate ) {
138+ contests . upcoming . push ( Contest . fromCList ( contest ) ) ;
139+ } else {
140+ contests . ongoing . push ( Contest . fromCList ( contest ) ) ;
141+ }
142+ }
113143
114- return this . contestsFromOther ( response , rowIndex ) ;
144+ return contests ;
115145 }
116146
117147 async getSSUInfo ( ) {
@@ -181,10 +211,10 @@ export class BojRepository {
181211 return ranking ;
182212 }
183213
184- async getBaechu ( ) {
214+ async getBaechu ( ) : Promise < Record < string , { badge : string ; background : string } > > {
185215 const url = 'https://raw.githubusercontent.com/kiwiyou/baechu/main/db.json' ;
186216
187- const data : Record < string , Record < string , string > > = { } ;
217+ const data : Record < string , { badge : string ; background : string } > = { } ;
188218 await this . httpService . axiosRef . get < Record < string , { badge : string ; background : string } > > ( url ) . then ( ( res ) => {
189219 for ( const contestId in res . data ) {
190220 const contest : Record < string , string > = res . data [ contestId ] ;
@@ -197,43 +227,4 @@ export class BojRepository {
197227
198228 return data ;
199229 }
200-
201- private async otherResponse ( ) {
202- const otherUrl = 'https://www.acmicpc.net/contest/other/list' ;
203-
204- return cheerio . load (
205- await this . httpService . axiosRef
206- . get < string > ( otherUrl , {
207- headers : {
208- 'User-Agent' : this . configService . get < string > ( 'BOJ_USER_AGENT' ) ,
209- Cookie : 'bojautologin=' + process . env . BOJ_AUTO_LOGIN + ';' ,
210- } ,
211- } )
212- . then ( ( res ) => res . data ) ,
213- ) ;
214- }
215-
216- private contestsFromOther ( response : any , rowIndex : number ) : Contest [ ] {
217- const contests : Contest [ ] = [ ] ;
218-
219- const rows = response (
220- `body > div.wrapper > div.container.content > div.row > div:nth-child(${ rowIndex } ) > div > table > tbody > tr` ,
221- ) as Cheerio < AnyNode > ;
222-
223- for ( let i = 0 ; i < rows . length ; i ++ ) {
224- const venue = rows . eq ( i ) . find ( 'td:nth-child(1)' ) . text ( ) . trim ( ) ;
225- const name = rows . eq ( i ) . find ( 'td:nth-child(2)' ) . text ( ) . trim ( ) ;
226- const url = rows . eq ( i ) . find ( 'td:nth-child(2) > a' ) . attr ( 'href' ) ?? '' ; // `null` 방지
227- const startTime = new Date (
228- 1000 * Number ( rows . eq ( i ) . find ( 'td:nth-child(3) > span' ) . attr ( 'data-timestamp' ) ?? 0 ) ,
229- ) . toISOString ( ) ;
230- const endTime = new Date (
231- 1000 * Number ( rows . eq ( i ) . find ( 'td:nth-child(4) > span' ) . attr ( 'data-timestamp' ) ?? 0 ) ,
232- ) . toISOString ( ) ;
233-
234- contests . push ( new Contest ( venue , name , url , startTime , endTime ) ) ;
235- }
236-
237- return contests ;
238- }
239230}
0 commit comments