/** This file has been automatically generated at 2024-12-17T08:56:23.523Z Name: Jutge API Version: 2.0.0 Description: Jutge API */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-inner-declarations */ /* In order to use this module we recommend you to use bun. See https://bun.sh/ You should also install these dependencies: bun add yaml chalk @inquirer/prompts */ import { confirm, input, password as ipassword } from '@inquirer/prompts' import chalk from 'chalk' import * as fs from 'fs' import YAML from 'yaml' interface Meta { token: string exam: string | null } interface Download { content: Uint8Array name: string type: string } // default value for the Jutge API URL const JUTGE_API_URL = process.env.JUTGE_API_URL || 'https://api.jutge.org/api' // global variable to store the meta information let meta: Meta | undefined = undefined // exceptions export class UnauthorizedError extends Error { name: string = 'UnauthorizedError' constructor(public message: string = 'Unauthorized') { super(message) } } export class InfoError extends Error { name: string = 'InfoError' constructor(public message: string) { super(message) } } export class NotFoundError extends Error { name: string = 'NotFoundError' constructor(public message: string) { super(message) } } export class InputError extends Error { name: string = 'InputError' constructor(public message: string) { super(message) } } export class ProtocolError extends Error { name: string = 'ProtocolError' constructor(public message: string) { super(message) } } /** Function that sends a request to the API and returns the response **/ async function execute(func: string, input: any, ifiles: File[] = []): Promise<[any, Download[]]> { // prepare form const iform = new FormData() const idata = { func, input, meta: meta } iform.append('data', JSON.stringify(idata)) for (const index in ifiles) iform.append(`file_${index}`, ifiles[index]) // send request const response = await fetch(JUTGE_API_URL, { method: 'POST', body: iform, }) // process response const contentType = response.headers.get('content-type')?.split(';')[0].toLowerCase() if (contentType !== 'multipart/form-data') { throw new ProtocolError('The content type is not multipart/form-data') } const oform = await response.formData() const odata = oform.get('data') const { output, error, duration, operation_id, time } = JSON.parse(odata as string) if (error) { throwError(error, operation_id) } // extract ofiles const ofiles = [] for (const [key, value] of oform.entries()) { if (value instanceof File) { ofiles.push({ content: new Uint8Array(await value.arrayBuffer()), name: value.name, type: value.type, }) } } return [output, ofiles] } function throwError(error: Record, operation_id: string | undefined) { const message = error.message || 'Unknown error' if (error.name === 'UnauthorizedError') { throw new UnauthorizedError(message) } else if (error.name === 'InfoError') { throw new InfoError(message) } else if (error.name === 'NotFoundError') { throw new NotFoundError(message) } else if (error.name === 'InputError') { throw new InputError(message) } else { throw new Error(message) } } export async function login(silent: boolean = false): Promise { let email let password let credentials const filePath = process.env.HOME + '/.jutge-client.yml' if (await fs.promises.exists(filePath)) { if (!silent) { console.log(chalk.green(`Credentials file found at ${filePath}`)) } const content = await fs.promises.readFile(filePath, 'utf-8') const data = YAML.parse(content) email = data.email password = data.password } else { if (silent) { throw new UnauthorizedError(`Cannot log in. No credentials file found at ${filePath} and silent login requested.`) } console.log(chalk.yellow('Please enter your credentias for Jutge.org')) email = await input({ message: 'Email:' }) password = await ipassword({ message: 'Password:' }) } try { const answer = await execute('auth.login', { email, password }) credentials = answer[0] } catch (error) { if (silent) { throw new Error(`Cannot log in. Please check your credentials.`) } console.log(chalk.red('Cannot log in. Please check your credentials.')) process.exit(1) } if (!silent) { console.log(chalk.green(`Logged in as ${email}`)) } if (!(await fs.promises.exists(filePath))) { const answer = await confirm({ message: 'Do you want to save your credentials?' }) if (answer) { const data = { email, password } await fs.promises.writeFile(filePath, YAML.stringify(data)) } } meta = { token: credentials.token, exam: null, } } export async function logout(): Promise { try { execute('auth.logout', null) } catch (error) { console.error('Error logging out') } meta = { token: '', exam: null } } export type CredentialsIn = { email: string, password: string } export type CredentialsOut = { token: string, expiration: string | string | string, user_uid: string } export type Time = { full_time: string, int_timestamp: number, float_timestamp: number, time: string, date: string } export type HomepageStats = { users: number, problems: number, submissions: number } export type Language = { language_id: string, eng_name: string, own_name: string } export type Languages = Record export type Country = { country_id: string, eng_name: string } export type Countries = Record export type Compiler = { compiler_id: string, name: string, language: string, extension: string, description: string | null, version: string | null, flags1: string | null, flags2: string | null, type: string | null, warning: string | null, status: string | null, notes: string | null } export type Compilers = Record export type Driver = { driver_id: string } export type Drivers = Record export type Verdict = { verdict_id: string, name: string, description: string } export type Verdicts = Record export type Proglang = { proglang_id: string } export type Proglangs = Record export type Tables = { languages: Languages, countries: Countries, compilers: Compilers, drivers: Drivers, verdicts: Verdicts, proglangs: Proglangs } export type BriefAbstractProblem = { problem_nm: string, author: string | null, author_email: string | null, public: number | null, official: number | null, compilers: string | null, driver_id: string | null, type: string | null, deprecation: string | null } export type BriefProblem = { problem_id: string, problem_nm: string, language_id: string, title: string, original_language_id: string, translator: string | null, translator_email: string | null, checked: number | null } export type BriefProblemDict = Record export type AbstractProblem = { problem_nm: string, author: string | null, author_email: string | null, public: number | null, official: number | null, compilers: string | null, driver_id: string | null, type: string | null, deprecation: string | null, problems: BriefProblemDict } export type Problem = { problem_id: string, problem_nm: string, language_id: string, title: string, original_language_id: string, translator: string | null, translator_email: string | null, checked: number | null, abstract_problem: BriefAbstractProblem } export type AbstractProblems = Record export type AbstractProblemExtras = { compilers_with_ac: string[], proglangs_with_ac: string[] } export type ProblemExtras = { compilers_with_ac: string[], proglangs_with_ac: string[], official_solution_checks: Record, handler: any } export type Testcase = { name: string, input_b64: string, correct_b64: string } export type Testcases = Testcase[] export type AllKeys = { problems: string[], enrolled_courses: string[], available_courses: string[], lists: string[] } export type Profile = { user_uid: string, email: string, name: string, username: string | null, nickname: string | null, webpage: string | null, description: string | null, affiliation: string | null, birth_year: number, max_subsxhour: number, max_subsxday: number, administrator: number, instructor: number, parent_email: string | null, country_id: string | null, timezone_id: string, compiler_id: string | null, language_id: string | null } export type NewProfile = { name: string, birth_year: number, nickname: string, webpage: string, affiliation: string, description: string, country_id: string, timezone_id: string } export type PasswordUpdate = { oldPassword: string, newPassword: string } export type TypeA = { a: string } export type TypeB = { a: string } export type DateValue = { date: number, value: number } export type HeatmapCalendar = DateValue[] export type Distribution = Record export type Distributions = { verdicts: Distribution, compilers: Distribution, proglangs: Distribution, submissions_by_hour: Distribution, submissions_by_weekday: Distribution } export type DashboardStats = Record export type Dashboard = { stats: DashboardStats, heatmap: HeatmapCalendar, distributions: Distributions } export type Submission = { problem_id: string, submission_id: string, compiler_id: string, annotation: string | null, state: string, time_in: string | string | string, veredict: string | null, veredict_info: string | null, veredict_publics: string | null, ok_publics_but_wrong: number } export type Submissions = Submission[] export type DictSubmissions = Record export type DictDictSubmissions = Record export type SubmissionPostOut = { submission_id: string } export type PublicProfile = { email: string, name: string, username: string | null } export type BriefCourse = { course_nm: string, title: string | null, description: string | null, annotation: string | null, public: number, official: number } export type BriefCourses = Record export type Course = { course_nm: string, title: string | null, description: string | null, annotation: string | null, public: number, official: number, owner: PublicProfile, lists: string[] } export type ListItem = { problem_nm: string | null, description: string | null } export type ListItems = ListItem[] export type BriefList = { list_nm: string, title: string | null, description: string | null, annotation: string | null, public: number, official: number } export type BriefLists = Record export type List = { list_nm: string, title: string | null, description: string | null, annotation: string | null, public: number, official: number, items: ListItems, owner: PublicProfile } export type AbstractStatus = { problem_nm: string, nb_submissions: number, nb_pending_submissions: number, nb_accepted_submissions: number, nb_rejected_submissions: number, nb_scored_submissions: number, status: string } export type AbstractStatuses = Record export type Status = { problem_id: string, problem_nm: string, nb_submissions: number, nb_pending_submissions: number, nb_accepted_submissions: number, nb_rejected_submissions: number, nb_scored_submissions: number, status: string } export type Award = { award_id: string, time: string | string | string, type: string, icon: string, title: string, info: string, youtube: string | null, submission: Submission | null } export type BriefAward = { award_id: string, time: string | string | string, type: string, icon: string, title: string, info: string, youtube: string | null } export type BriefAwards = Record export type TagsDict = Record export type Document = { document_nm: string, title: string, description: string } export type Documents = Document[] export type InstructorList = { list_nm: string, title: string, description: string, annotation: string, official: number, public: number } export type InstructorLists = InstructorList[] export type InstructorListItem = { problem_nm: string | null, description: string | null } export type InstructorListItems = InstructorListItem[] export type InstructorListWithItems = { list_nm: string, title: string, description: string, annotation: string, official: number, public: number, items: InstructorListItems } export type InstructorCourse = { course_nm: string, title: string, description: string, annotation: string, official: number, public: number } export type InstructorCourses = InstructorCourse[] export type CourseMembers = { invited: string[], enrolled: string[], pending: string[] } export type InstructorCourseWithItems = { course_nm: string, title: string, description: string, annotation: string, official: number, public: number, lists: string[], students: CourseMembers, tutors: CourseMembers } export type InstructorExam = { exam_nm: string, title: string, place: string | null, description: string | null, code: string | null, time_start: string | string | string | null, exp_time_start: string | string | string, running_time: number, visible_submissions: number, started_by: string | null, contest: number, instructions: string | null, avatars: string | null, anonymous: number } export type InstructorExams = InstructorExam[] export type InstructorExamCourse = { title: string, description: string, course_nm: string, annotation: string } export type InstructorExamDocument = { document_nm: string, title: string, description: string } export type InstructorExamDocuments = InstructorExamDocument[] export type InstructorExamProblem = { problem_nm: string, weight: number | null, icon: string | null, caption: string | null } export type InstructorExamProblems = InstructorExamProblem[] export type InstructorExamStudent = { email: string, name: string, code: string | null, restricted: number, annotation: string | null, result: string | null, finished: number, banned: number, reason_ban: string | null, inc: number | null, reason_inc: string | null, taken_exam: number, emergency_password: string | null, invited: number } export type InstructorExamStudents = InstructorExamStudent[] export type InstructorExamWithItems = { exam_nm: string, title: string, place: string | null, description: string | null, code: string | null, time_start: string | string | string | null, exp_time_start: string | string | string, running_time: number, visible_submissions: number, started_by: string | null, contest: number, instructions: string | null, avatars: string | null, anonymous: number, course: InstructorExamCourse, documents: InstructorExamDocuments, problems: InstructorExamProblems, students: InstructorExamStudents } export type InstructorExamCreation = { exam_nm: string, course_nm: string, title: string, place: string, description: string, instructions: string, exp_time_start: string, running_time: number, contest: number } export type InstructorExamStudentPost = { email: string, invited: number, restricted: number, code: string, emergency_password: string, annotation: string } export type InstructorExamStudentsPost = InstructorExamStudentPost[] export type InstructorExamSubmissionsOptions = { problems: string, include_source: boolean, include_pdf: boolean, include_metadata: boolean, only_last: boolean } export type Pack = { message: string, href: string } export type SubmissionQuery = { email: string, problem_nm: string, problem_id: string, time: string | string | string, ip: string, verdict: string } export type SubmissionsQuery = SubmissionQuery[] export type UserCreation = { email: string, name: string, username: string, password: string, administrator: number, instructor: number } export type UpcomingExam = { exam_nm: string, title: string, username: string, exp_start_time: string | string | string, duration: number, name: string } export type UpcomingExams = UpcomingExam[] export type UserRankingEntry = { user_id: string, nickname: string | null, email: string, name: string, problems: number } export type UserRanking = UserRankingEntry[] export type TwoFloats = { a: number, b: number } export type TwoInts = { a: number, b: number } export type Name = { name: string } export namespace auth { export async function login(data: CredentialsIn) : Promise { /** Login: Get an access token **/ const [output, ofiles] = await execute('auth.login', data) return output } export async function logout() : Promise { /** Logout: Discard access token 🔐 Authenticated **/ const [output, ofiles] = await execute('auth.logout', null) return output } } export namespace misc { export async function getFortune() : Promise { /** Get a fortune message **/ const [output, ofiles] = await execute('misc.getFortune', null) return output } export async function getTime() : Promise