Basic implementation of Codeforces contest reminders
Signed-off-by: adithyagenie <adithyagenie@gmail.com>
This commit is contained in:
parent
deca1f0045
commit
4119d8a143
7 changed files with 175 additions and 2 deletions
|
@ -17,7 +17,8 @@
|
|||
"node-schedule": "^2.1.1",
|
||||
"@grammyjs/parse-mode": "^1.10.0",
|
||||
"fastify": "^4.27.0",
|
||||
"leetcode-query": "^1.2.3"
|
||||
"leetcode-query": "^1.2.3",
|
||||
"codeforces-api-ts": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@grammyjs/types": "^3.8.0",
|
||||
|
|
123
pnpm-lock.yaml
123
pnpm-lock.yaml
|
@ -8,6 +8,9 @@ dependencies:
|
|||
'@grammyjs/parse-mode':
|
||||
specifier: ^1.10.0
|
||||
version: 1.10.0(grammy@1.24.1)
|
||||
codeforces-api-ts:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
dotenv:
|
||||
specifier: ^16.4.5
|
||||
version: 16.4.5
|
||||
|
@ -493,6 +496,17 @@ packages:
|
|||
ieee754: 1.2.1
|
||||
dev: false
|
||||
|
||||
/call-bind@1.0.7:
|
||||
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
es-define-property: 1.0.0
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
get-intrinsic: 1.2.4
|
||||
set-function-length: 1.2.2
|
||||
dev: false
|
||||
|
||||
/callsites@3.1.0:
|
||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -506,6 +520,13 @@ packages:
|
|||
supports-color: 7.2.0
|
||||
dev: true
|
||||
|
||||
/codeforces-api-ts@3.0.1:
|
||||
resolution: {integrity: sha512-VkHDKFD8EA94OqJD9GGehJeNP5GtACNrhEzZBejroTl6k8GlTH/X/s7yQUnzkqfek8gWe2dvuhvvE07DkZ25CQ==}
|
||||
dependencies:
|
||||
lodash: 4.17.21
|
||||
qs: 6.12.1
|
||||
dev: false
|
||||
|
||||
/color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
|
@ -572,6 +593,15 @@ packages:
|
|||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
dev: true
|
||||
|
||||
/define-data-property@1.1.4:
|
||||
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
es-define-property: 1.0.0
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.0.1
|
||||
dev: false
|
||||
|
||||
/delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
@ -589,6 +619,18 @@ packages:
|
|||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/es-define-property@1.0.0:
|
||||
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.4
|
||||
dev: false
|
||||
|
||||
/es-errors@1.3.0:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: false
|
||||
|
||||
/escape-string-regexp@4.0.0:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
|
@ -842,6 +884,21 @@ packages:
|
|||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/function-bind@1.1.2:
|
||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||
dev: false
|
||||
|
||||
/get-intrinsic@1.2.4:
|
||||
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
has-proto: 1.0.3
|
||||
has-symbols: 1.0.3
|
||||
hasown: 2.0.2
|
||||
dev: false
|
||||
|
||||
/glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
|
@ -873,6 +930,12 @@ packages:
|
|||
slash: 3.0.0
|
||||
dev: true
|
||||
|
||||
/gopd@1.0.1:
|
||||
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
||||
dependencies:
|
||||
get-intrinsic: 1.2.4
|
||||
dev: false
|
||||
|
||||
/grammy@1.24.1:
|
||||
resolution: {integrity: sha512-0ijKmqL1wlxIk+Zml4qZDXTbacWFf9IEmTPAjvNpl5jpaarkE7SOxst1gDb0ZmySAPimzvP2vH4rc0IhwexSOA==}
|
||||
engines: {node: ^12.20.0 || >=14.13.1}
|
||||
|
@ -895,6 +958,29 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/has-property-descriptors@1.0.2:
|
||||
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
|
||||
dependencies:
|
||||
es-define-property: 1.0.0
|
||||
dev: false
|
||||
|
||||
/has-proto@1.0.3:
|
||||
resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: false
|
||||
|
||||
/has-symbols@1.0.3:
|
||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: false
|
||||
|
||||
/hasown@2.0.2:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
dev: false
|
||||
|
||||
/ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
dev: false
|
||||
|
@ -1021,6 +1107,10 @@ packages:
|
|||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
dev: true
|
||||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: false
|
||||
|
||||
/long-timeout@0.1.1:
|
||||
resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==}
|
||||
dev: false
|
||||
|
@ -1096,6 +1186,10 @@ packages:
|
|||
sorted-array-functions: 1.3.0
|
||||
dev: false
|
||||
|
||||
/object-inspect@1.13.1:
|
||||
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
|
||||
dev: false
|
||||
|
||||
/on-exit-leak-free@2.1.2:
|
||||
resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
@ -1208,6 +1302,13 @@ packages:
|
|||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
/qs@6.12.1:
|
||||
resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==}
|
||||
engines: {node: '>=0.6'}
|
||||
dependencies:
|
||||
side-channel: 1.0.6
|
||||
dev: false
|
||||
|
||||
/queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
dev: true
|
||||
|
@ -1289,6 +1390,18 @@ packages:
|
|||
resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==}
|
||||
dev: false
|
||||
|
||||
/set-function-length@1.2.2:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
define-data-property: 1.1.4
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
get-intrinsic: 1.2.4
|
||||
gopd: 1.0.1
|
||||
has-property-descriptors: 1.0.2
|
||||
dev: false
|
||||
|
||||
/shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -1301,6 +1414,16 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/side-channel@1.0.6:
|
||||
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
call-bind: 1.0.7
|
||||
es-errors: 1.3.0
|
||||
get-intrinsic: 1.2.4
|
||||
object-inspect: 1.13.1
|
||||
dev: false
|
||||
|
||||
/slash@3.0.0:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
17
src/api/cfFetch.ts
Normal file
17
src/api/cfFetch.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { CodeforcesAPI } from "codeforces-api-ts";
|
||||
|
||||
export async function cfFetchContests(): Promise<{ name: string; startTime: number; id: number }[]> {
|
||||
const res = await CodeforcesAPI.call("contest.list", {});
|
||||
if (res.status == "OK") {
|
||||
let futureContests = res.result.filter(o => o.phase == "BEFORE");
|
||||
futureContests.filter(o => o.name.includes("Codeforces") && o.name.includes("Div. ") && o.startTimeSeconds !== undefined);
|
||||
return futureContests.map((o) => ({
|
||||
name: o.name,
|
||||
id: o.id,
|
||||
startTime: o.startTimeSeconds as number
|
||||
}));
|
||||
}
|
||||
else {
|
||||
throw new Error("Unable to reach Codeforces API :(");
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { bot } from "../bot";
|
||||
import { CodeforcesAPI } from "codeforces-api-ts";
|
||||
|
||||
export async function reminderContestLC(contestName: string, contestKey: string): Promise<void> {
|
||||
await bot.api.sendMessage(parseInt(process.env.CHAT_ID as string),
|
||||
|
@ -10,4 +11,14 @@ export async function reminderContestCodeChef(): Promise<void> {
|
|||
await bot.api.sendMessage(parseInt(process.env.CHAT_ID as string),
|
||||
`CodeChef contest is in ~10 minutes.\n
|
||||
Attend the contest here: https://www.codechef.com/contests/`);
|
||||
}
|
||||
|
||||
export async function reminderContestCF(contestName: string, contestID: number, contestTime: Date) {
|
||||
let options: {timeZone: string, timeStyle: "short"} = {
|
||||
timeZone: 'Asia/Calcutta',
|
||||
timeStyle: "short"
|
||||
};
|
||||
await bot.api.sendMessage(parseInt(process.env.CHAT_ID as string),
|
||||
`${contestName} at ${new Date(contestTime).toLocaleTimeString('en-US', options)} IST starts in ~10 minutes.
|
||||
Attend the contest here: https://codeforces.com/contests/${contestID}`);
|
||||
}
|
18
src/helpers/cfSchedule.ts
Normal file
18
src/helpers/cfSchedule.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { cfFetchContests } from "../api/cfFetch";
|
||||
import { scheduleJob } from "node-schedule";
|
||||
import { reminderContestCF } from "../bot/commands/contestRem";
|
||||
|
||||
|
||||
export async function cfSchedule() {
|
||||
const contests = await cfFetchContests();
|
||||
if (contests.length === 0) {
|
||||
console.log("No codeforces contests found!");
|
||||
return;
|
||||
}
|
||||
for (let contest of contests) {
|
||||
const contestRemTime = new Date((contest.startTime - 600) * 1000);
|
||||
const contestTime = new Date(contest.startTime * 1000);
|
||||
scheduleJob(`${contest.name}`, contestRemTime, reminderContestCF.bind(null, contest.name, contest.id, contestTime));
|
||||
}
|
||||
console.log("Codeforces contests scheduled!");
|
||||
}
|
|
@ -20,7 +20,6 @@ function getNextBiweeklyDate(): Date {
|
|||
|
||||
|
||||
export async function lcSchedule() {
|
||||
await gracefulShutdown();
|
||||
const contestNames = await fetchLCContests();
|
||||
const biweekly = contestNames.filter(o =>
|
||||
o.key.match(/^biweekly-contest-(\d+)$/)
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { lcSchedule } from "./lcSchedule";
|
||||
import { ccSchedule } from "./ccSchedule";
|
||||
import { cfSchedule } from "./cfSchedule";
|
||||
import { gracefulShutdown } from "node-schedule";
|
||||
|
||||
export async function contestScheduler() {
|
||||
await gracefulShutdown();
|
||||
await lcSchedule();
|
||||
ccSchedule();
|
||||
await cfSchedule();
|
||||
}
|
Loading…
Reference in a new issue