The ULTIMATE wrapper for EcoleDirecte's private API.
Version 🌸 Flower 1.0 (🌱 Seedling)
npm install wrapdirecteimport { WrapDirecte } from 'wrapdirecte';
const client = new WrapDirecte();
const loginResult = await client.login('USERNAME', 'PASSWORD');
if (loginResult.status === '2FA_REQUIRED') {
console.log(loginResult.challenge?.question);
const answer = '...';
await client.submit2FA(answer);
}
const account = client.getAccount();
console.log(account?.firstName, account?.lastName);
const homework = await client.homework?.getHomework('2026-04-08');
console.log(homework);Important
The user-agent uses, by default, the name and version of your project from package.json.
You can also manually override this when doing new WrapDirecte like this : new WrapDirecte({ appName: 'MyApp', appVersion: '1.2.3' })
new WrapDirecte()- Create a new client instancelogin(username, password, uuid?, preferredAccountId?)- Login with credentialsrelogin(username, password, uuid)- Re-login with existing sessiondirectLogin(username, password, cn, cv, uuid?)- Direct login with 2FA tokenssubmit2FA(answer, uuid?)- Submit 2FA answerselectAccount(accountId)- Select a specific student accountlogout()- Logout and clear sessiongetAccount()/getRawAccount()- Get current account infoisAuthenticated- Check if authenticatedaccounts- Get all available student accounts
After successful authentication, the following modules become available:
client.homework- Homework managementclient.grades- Grades and periodsclient.messaging- Messaging systemclient.timetable- Timetable and calendarclient.absences- Absences and delaysclient.timeline- Timeline eventsclient.documents- Administrative documentsclient.cloud- Cloud file storageclient.settings- Account settings
All module methods accept an optional options parameter with {raw: true} to return raw API data instead of cleaned data.
const client = new WrapDirecte();
// Basic login
const result = await client.login('username', 'password');
// If 2FA is required
if (result.status === '2FA_REQUIRED') {
console.log(result.challenge.question); // Display question
console.log(result.challenge.proposals); // Display proposals
const answer = 'user_answer';
await client.submit2FA(answer);
}
// Direct login with 2FA tokens (if you have cn/cv)
const result = await client.directLogin('username', 'password', 'cn', 'cv');
// Re-login (for session refresh)
const result = await client.relogin('username', 'password', 'uuid');
// Select account (if multiple students)
await client.selectAccount(accountId);
// Check authentication
console.log(client.isAuthenticated); // true/false
console.log(client.accounts); // Array of available accountsconst homework = await client.homework.getHomework('2026-04-08');
// Returns: CleanHomework[]await client.homework.markAsDone(homeworkId, true); // Mark as done
await client.homework.markAsDone(homeworkId, false); // Mark as not doneconst commentId = await client.homework.addComment(homeworkId, 'My comment');const { grades, periods, settings } = await client.grades.getGrades('2025-2026');
// grades: CleanGrade[]
// periods: CleanPeriod[]
// settings: GradeSettingsconst messages = await client.messaging.getMessages('2025-2026');
// Returns: { received: CleanMessage[], sent: CleanMessage[], draft: CleanMessage[], archived: CleanMessage[] }const folderMessages = await client.messaging.getMessagesByFolder(folderId);
// Returns: CleanMessage[]const messageContent = await client.messaging.getMessageContent(messageId, '2025-2026');
// Returns: CleanMessage with content propertyconst messageId = await client.messaging.sendMessage({
subject: 'Subject',
content: 'Message content',
recipients: [{ id: 123, type: 'P' }], // Teacher recipients
year: '2025-2026'
});const teachers = await client.messaging.getContacts('professeurs');
const staff = await client.messaging.getContacts('personnels');
const companies = await client.messaging.getContacts('entreprises');
// Returns: Contact[]const timetable = await client.timetable.getTimetable('2026-04-01', '2026-04-07');
// Returns: CleanCourse[]const icalUrl = await client.timetable.getIcalUrl();
// Returns: string (full URL for calendar subscription)const absences = await client.absences.getAbsences();
// Returns: CleanAbsence[]const timeline = await client.timeline.getStudentTimeline();
// Returns: CleanTimelineEvent[]const { events, postits } = await client.timeline.getCommonTimeline();
// events: CleanTimelineEvent[]
// postits: CleanPostIt[]const documents = await client.documents.getDocuments();
// Returns: { factures: CleanDocument[], notes: CleanDocument[], ... }const files = await client.cloud.getCloudFiles(3); // depth 3
// Returns: CleanCloudNode[]await client.cloud.createFolder('parent/path', 'newFolder');await client.cloud.uploadFile('destination/path', fileBuffer, 'filename.txt');const blob = await client.cloud.downloadFile({
type: 'CLOUD',
fileId: 'fileId',
year: '2025-2026' // optional
});// Copy nodes
await client.cloud.copyNodes('destination/path', [node1, node2]);
// Move nodes
await client.cloud.moveNodes('destination/path', [node1, node2]);
// Delete nodes
await client.cloud.deleteNodes([node1, node2]);
// Restore nodes
await client.cloud.restoreNodes([node1, node2]);await client.cloud.exportToCloud(fileId, 'CAHIER_DE_TEXTES'); // or 'MESSAGERIE'await client.settings.updateIndividualParam('value');await client.settings.updateAccountSettings({
identifiant: 'newUsername',
nouveauMotDePasse: 'newPassword',
confirmationMotDePasse: 'newPassword',
email: 'new@email.com',
portable: '0612345678',
questionSecrete: 'Secret question',
reponse: 'Answer',
uuid: 'optional-uuid'
});const settings = await client.settings.getAccountSettings();
// or with specific login ID
const settings = await client.settings.getAccountSettings(loginId);interface CleanHomework {
id: number;
date: Date | null;
subject: string;
subjectCode: string;
teacherName: string;
isInterrogation: boolean;
isDone: boolean;
content: string;
files: CleanFile[];
comments: CleanComment[];
sessionContent: {
content: string;
files: CleanFile[];
comments: CleanComment[];
};
}interface CleanGrade {
id: number;
title: string;
subjectCode: string;
subjectLabel: string;
date: Date | null;
value: string;
outOf: string;
coefficient: number;
isLetter: boolean;
isSignificant: boolean;
comment: string;
periodCode: string;
}interface CleanMessage {
id: number;
subject: string;
date: Date | null;
from: CleanContact;
to: CleanContact[];
content?: string;
isRead: boolean;
hasAttachments: boolean;
attachments: CleanAttachment[];
type: 'received' | 'sent' | 'draft' | 'archived' | 'classeur';
}interface CleanCourse {
id: number;
subject: string;
subjectCode: string;
type: string;
startDate: Date | null;
endDate: Date | null;
color: string;
teacher: string;
room: string;
group: string;
groupCode: string;
isModified: boolean;
isCancelled: boolean;
hasSessionContent: boolean;
hasHomework: boolean;
}interface CleanAbsence {
id: number;
type: 'Absence' | 'Retard' | string;
date: Date | null;
displayDate: string;
label: string;
reason: string;
isJustified: boolean;
comment: string;
}interface CleanTimelineEvent {
date: Date | null;
type: string;
id: number;
title: string;
subtitle: string;
content: string;
}interface CleanDocument {
id: number;
name: string;
date: Date | null;
type: string;
studentId: number;
}interface CleanCloudNode {
type: 'file' | 'folder';
name: string;
date: Date | null;
size: number;
id: string;
children?: CleanCloudNode[];
}The project includes comprehensive integration tests for all functionalities.
- Copy
.env.exampleto.envand fill in your EcoleDirecte credentials. - Run individual tests:
npm run test-[functionality](e.g.,npm run test-login) - Run all tests: Uncomment desired tests in
tests/all.test.ts, thennpm run test
Available test scripts:
npm run test-loginnpm run test-reloginnpm run test-selectAccountnpm run test-homeworknpm run test-gradesnpm run test-messagingnpm run test-timetablenpm run test-absencesnpm run test-timelinenpm run test-documentsnpm run test-cloudnpm run test-settingsnpm run test(all, with uncommented tests)
See the project wiki.
wrapDirecte is licensed under the LGPL 3.0, allowing you to use, modify, and distribute it for both commercial and non-commercial purposes, provided that the license terms are respected. See the LICENSE file for more details.
YOU are responsible for everything you made wrapDirecte process. Don't use wrapDirecte with accounts you don't have the permission to use.