1
0
Fork 0
mirror of https://github.com/classchartsapi/classcharts-api-js.git synced 2026-05-14 11:58:13 +00:00

Merge branch 'main' into feat/getCode

This commit is contained in:
James 2023-06-02 22:34:48 +01:00 committed by GitHub
commit 7e53992dbc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 427 additions and 196 deletions

View file

@ -0,0 +1,5 @@
---
"classcharts-api": patch
---
Export types first

View file

@ -1,16 +0,0 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/javascript-node/.devcontainer/base.Dockerfile
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 16, 14, 12, 16-bullseye, 14-bullseye, 12-bullseye, 16-buster, 14-buster, 12-buster
ARG VARIANT="16-bullseye"
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment if you want to install an additional version of node using nvm
# ARG EXTRA_NODE_VERSION=10
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
# [Optional] Uncomment if you want to install more global node modules
RUN su node -c "npm install -g pnpm np"

View file

@ -1,24 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/javascript-node
{
"name": "Node.js",
"build": {
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick a Node version: 16, 14, 12.
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local arm64/Apple Silicon.
"args": {
"VARIANT": "16-bullseye"
}
},
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": ["dbaeumer.vscode-eslint"],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "pnpm install",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node"
}

View file

@ -1,5 +1,5 @@
dist/ dist/
docs/ docs/
node_modules/ node_modules/
.eslintrc.js .eslintrc.cjs
tsconfig.json tsconfig.json

View file

@ -1,4 +1,4 @@
name: Typedoc name: Docs
on: on:
push: push:
@ -19,21 +19,21 @@ jobs:
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
with: with:
version: 8.2.0 version: 8.2.0
- name: Retrieve the cached "node_modules" directory (if present) - name: Retrieve the cache
uses: actions/cache@v3 uses: actions/cache@v3
id: node-cache id: node-cache
with: with:
path: node_modules path: node_modules
key: node-modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} key: node-modules-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install dependencies (if the cached directory was not found) - name: Install dependencies
if: steps.node-cache.outputs.cache-hit != 'true' if: steps.node-cache.outputs.cache-hit != 'true'
run: pnpm install run: pnpm install
- name: Test to see if the project compiles - name: Build
run: pnpm run build run: pnpm run build
- name: Create the docs directory locally in CI - name: Generate typedoc
run: pnpm generateDocs run: pnpm generateDocs
- name: Deploy 🚀 - name: Deploy 🚀

3
.gitignore vendored
View file

@ -118,6 +118,5 @@ dist
.pnp.* .pnp.*
tests/config.json tests/config.json
tests/sandbox.js
docs/ docs/typedoc

View file

@ -1,6 +1,6 @@
dist dist/
docs node_modules/
node_modules
pnpm-lock.yaml pnpm-lock.yaml
.prettierrc.json .prettierrc.json
.eslint.js .eslint.js
CHANGELOG.md

View file

@ -1,7 +1,3 @@
{ {
"recommendations": [ "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"EditorConfig.EditorConfig"
]
} }

View file

@ -1,5 +1,22 @@
# classcharts-api # classcharts-api
## 2.3.0
### Minor Changes
- 6552c28: Migrate to native fetch
## 2.2.0
### Minor Changes
- a3e078b: Throw more description JSON parsing errors
### Patch Changes
- 350878c: Fixed internal typing typo
- 451f800: Specify the required node version
## 2.1.3 ## 2.1.3
### Patch Changes ### Patch Changes

0
docs/.nojekyll Normal file
View file

297
docs/README.md Normal file
View file

@ -0,0 +1,297 @@
<p align="center">
<h1 align="center">ClassCharts API JS</h1>
<p align="center">
A typescript wrapper for getting information from the Classcharts API
</p>
</p>
<p align="center">
<a href="https://github.com/classchartsapi/classcharts-api-js">Source</a>
<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>
<a href="https://github.com/classchartsapi/classcharts-api-js/issues">Issues</a>
<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>
<a href="https://www.npmjs.com/package/classcharts-api">NPM</a>
<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>
<a href="https://discord.gg/DTcwugcgZ2">Discord</a>
<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>
<a href="https://classchartsapi.github.io/classcharts-api-js/typedoc/">TypeDoc</a>
<span>&nbsp;&nbsp;&nbsp;&nbsp;</span>
<a href="https://classchartsapi.github.io/api-docs/">API Docs</a>
</p>
# Introduction
The ClassCharts API is a typescript wrapper around the ClassCharts API. It allows you to easily make requests to the ClassCharts API without having to worry about the underlying implementation of making requests.
## Help
For any help with the library, please join the [discord](https://discord.gg/DTcwugcgZ2) where you can ask questions and get help from the community.
# Installation
## Requirements
- Node.js 18 or newer
## NPM
```bash
npm install classcharts-api # npm
```
```bash
yarn add classcharts-api # yarn
```
```bash
pnpm add classcharts-api # pnpm
```
# Logging In
Before making any requests, you must login to the client. This will get you a session ID which will be used for all requests.
## Student Client
```typescript
import { StudentClient } from "classcharts-api";
// Date of birth MUST in the format DD/MM/YYYY
const client = new StudentClient("classchartsCode", "01/1/2000");
await client.login();
```
## Parent Client
```typescript
import { ParentClient } from "classcharts-api";
const client = new ParentClient("username", "password");
await client.login();
```
# Universal Methods
All the following methods can be used on both the student and parent client. Each example expects the client to be already logged in.
## `.getStudentInfo`
```typescript
const studentInfo = await client.getStudentInfo();
console.log(studentInfo);
/**
{
success: 1,
data: {
user: {
id: 2339528,
...
}
},
meta: {
session_id: '5vf2v7n5uk9jftrxaarrik39vk6yjm48',
...
}
}
*/
```
## `.getActivity`
```typescript
// Dates must be in format YYYY-MM-DD
const activity = await client.getActivity({
from: "2023-04-01",
to: "2023-05-10",
last_id: "12",
});
console.log(activity);
```
## `.getFullActivity`
```typescript
// Dates must be in format YYYY-MM-DD
const activity = await client.getFullActivity({
from: "2023-04-01",
to: "2023-05-10",
});
console.log(activity);
```
## `.getBehaviour`
Gets behaviour for a given date range.
```typescript
// Dates must be in format YYYY-MM-DD
const behaviour = await client.getBehaviour({
from: "2023-04-01",
to: "2023-05-10",
});
console.log(behaviour);
/**
{
"success": 1,
"data": {
"timeline": [
{
"positive": 426,
...
},
],
...
"meta": {
"start_date": "2023-04-01T00:00:00+00:00",
...
}
}
*/
```
## `.getHomeworks`
Gets homeworks for a given date range.
```typescript
// Dates must be in format YYYY-MM-DD
const homeworks = await client.getHomeworks({
from: "2023-04-01",
to: "2023-05-10",
displayDate: 'issue_date' // Can be 'due_date' or 'issue_date'
});
console.log(homeworks);
/**
{
success: 1,
data: [
{
lesson: '7A/Pe1',
...
},
],
meta: {
start_date: '2023-04-01T00:00:00+00:00',
...
}
}
```
## `.getLessons`
Gets lessons for a specific date.
```typescript
// Dates must be in format YYYY-MM-DD
const lessons = await client.getLessons({
date: "2023-04-01",
});
console.log(lessons);
/**
{
"success": 1,
"data": [
{
"teacher_name": "Mr J Doe",
...
}
...
],
"meta": {
"dates": [
"2023-05-04"
],
...
}
}
*/
```
## `.getBadges`
Gets all earned badges.
```typescript
const badges = await client.getBadges();
console.log(badges);
/**
{
success: 1,
data: [
{
id: 123,
name: 'Big Badge',
...
},
...
],
meta: []
}
*/
```
## `.getAnnouncements`
?> This method does not include `meta` in the response, since I do not have access to this endpoint to test it. If you have access to this endpoint, please open a PR to add the `meta` response. Thanks!
Gets all announcements.
```typescript
const announcements = await client.getAnnouncements();
console.log(announcements);
```
## `.getDetentions`
?> This method does not include `meta` in the response, since I do not have access to this endpoint to test it. If you have access to this endpoint, please open a PR to add the `meta` response. Thanks!
Gets all detentions.
```typescript
const detentions = await client.getDetentions();
console.log(detentions);
```
## `.getAttendance`
?> This method does not include `meta` in the response, since I do not have access to this endpoint to test it. If you have access to this endpoint, please open a PR to add the `meta` response. Thanks!
Gets attendance.
```typescript
const attendance = await client.getAttendance();
console.log(attendance);
```
# Parent Specific Methods
## `.getPupils`
Gets a list of all pupils the parent has access to.
```typescript
const pupils = await client.getPupils();
console.log(pupils);
/**
[
{
id: 123,
name: 'John Doe',
...
},
...
]
*/
```
## `.selectPupil`
Selects a pupil to make subsequent requests for.
```typescript
await client.selectPupil(123);
```

40
docs/index.html Normal file
View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>ClassCharts-API</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<!-- <meta name="description" content="Description"> -->
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0"
/>
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css"
/>
<script
async
src="https://umami.jaminit.co.uk/script.js"
data-website-id="3f01674a-5c63-47a2-962b-43d0c62c7c6c"
data-domains="classchartsapi.github.io"
></script>
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: "ClassCharts API JS",
repo: "classchartsapi/classcharts-api-js",
auto2top: true,
autoHeader: true,
};
</script>
<!-- Docsify v4 -->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-typescript.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
</body>
</html>

View file

@ -1,13 +1,13 @@
{ {
"name": "classcharts-api", "name": "classcharts-api",
"type": "module", "type": "module",
"version": "2.1.3", "version": "2.3.0",
"license": "ISC", "license": "ISC",
"author": { "author": {
"name": "James Cook", "name": "James Cook",
"email": "james@jaminit.co.uk" "email": "james@jaminit.co.uk"
}, },
"description": "A Typescript wrapper for getting information from the Classcharts API", "description": "A Typescript wrapper for getting information from the ClassCharts API",
"keywords": [ "keywords": [
"node", "node",
"typescript", "typescript",
@ -28,9 +28,6 @@
"test": "jest", "test": "jest",
"release": "pnpm run build && changeset publish" "release": "pnpm run build && changeset publish"
}, },
"dependencies": {
"ky-universal": "^0.11.0"
},
"devDependencies": { "devDependencies": {
"@changesets/cli": "^2.26.1", "@changesets/cli": "^2.26.1",
"@types/jest": "^29.5.1", "@types/jest": "^29.5.1",
@ -51,14 +48,17 @@
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"exports": { "exports": {
".": { ".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js", "import": "./dist/index.js",
"require": "./dist/index.js", "require": "./dist/index.js"
"types": "./dist/index.d.ts"
}, },
"./types": "./dist/index.d.ts", "./types": "./dist/index.d.ts",
"./package.json": "./package.json" "./package.json": "./package.json"
}, },
"files": [ "files": [
"dist/**" "dist/**"
] ],
"engines": {
"node": ">=18"
}
} }

76
pnpm-lock.yaml generated
View file

@ -1,10 +1,5 @@
lockfileVersion: '6.0' lockfileVersion: '6.0'
dependencies:
ky-universal:
specifier: ^0.11.0
version: 0.11.0(ky@0.33.3)
devDependencies: devDependencies:
'@changesets/cli': '@changesets/cli':
specifier: ^2.26.1 specifier: ^2.26.1
@ -1200,13 +1195,6 @@ packages:
eslint-visitor-keys: 3.4.0 eslint-visitor-keys: 3.4.0
dev: true dev: true
/abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
dependencies:
event-target-shim: 5.0.1
dev: false
/acorn-jsx@5.3.2(acorn@8.8.2): /acorn-jsx@5.3.2(acorn@8.8.2):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies: peerDependencies:
@ -1630,11 +1618,6 @@ packages:
stream-transform: 2.1.3 stream-transform: 2.1.3
dev: true dev: true
/data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
dev: false
/debug@4.3.4: /debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@ -1949,11 +1932,6 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
dev: false
/execa@5.1.1: /execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -2033,14 +2011,6 @@ packages:
bser: 2.1.1 bser: 2.1.1
dev: true dev: true
/fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 3.2.1
dev: false
/file-entry-cache@6.0.1: /file-entry-cache@6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0} engines: {node: ^10.12.0 || >=12.0.0}
@ -2096,13 +2066,6 @@ packages:
is-callable: 1.2.7 is-callable: 1.2.7
dev: true dev: true
/formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
dependencies:
fetch-blob: 3.2.0
dev: false
/fs-extra@7.0.1: /fs-extra@7.0.1:
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
engines: {node: '>=6 <7 || >=8'} engines: {node: '>=6 <7 || >=8'}
@ -3089,26 +3052,6 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/ky-universal@0.11.0(ky@0.33.3):
resolution: {integrity: sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==}
engines: {node: '>=14.16'}
peerDependencies:
ky: '>=0.31.4'
web-streams-polyfill: '>=3.2.1'
peerDependenciesMeta:
web-streams-polyfill:
optional: true
dependencies:
abort-controller: 3.0.0
ky: 0.33.3
node-fetch: 3.3.1
dev: false
/ky@0.33.3:
resolution: {integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==}
engines: {node: '>=14.16'}
dev: false
/leven@3.1.0: /leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -3302,20 +3245,6 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true dev: true
/node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
dev: false
/node-fetch@3.3.1:
resolution: {integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
data-uri-to-buffer: 4.0.1
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
dev: false
/node-int64@0.4.0: /node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
dev: true dev: true
@ -4191,11 +4120,6 @@ packages:
defaults: 1.0.4 defaults: 1.0.4
dev: true dev: true
/web-streams-polyfill@3.2.1:
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
engines: {node: '>= 8'}
dev: false
/which-boxed-primitive@1.0.2: /which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies: dependencies:

View file

@ -1,31 +1,23 @@
# Classcharts API <h1 align="center">Classcharts API JS</h1>
<div align="center">
<img alt="Discord" src="https://img.shields.io/discord/918103752068726855">
<img alt="npm" src="https://img.shields.io/npm/dm/classcharts-api">
<img alt="NPM" src="https://img.shields.io/npm/l/classcharts-api">
<img alt="npm" src="https://img.shields.io/npm/v/classcharts-api">
<img alt="node-current" src="https://img.shields.io/node/v/classcharts-api">
</div>
A client for the classcharts API <p align="center">A typescript wrapper for getting information from the Classcharts API.</p>
# Links
- [Documentation](https://classchartsapi.github.io/classcharts-api-js/) - [Documentation](https://classchartsapi.github.io/classcharts-api-js/)
- [API Documentation (with library examples)](https://classchartsapi.github.io/api-docs/#introduction)
- [Discord](https://discord.gg/DTcwugcgZ2) - [Discord](https://discord.gg/DTcwugcgZ2)
# Examples # Contributing
Docs are very much a WIP, for any help with the library, please join the discord above Contributions are welcome! There are lots of API endpoints which we simply do not have access to to implement, so if you have access to them, please consider contributing! If you have any questions, feel free to ask in the [discord](https://discord.gg/DTcwugcgZ2).
```typescript # Help
import { StudentClient } from "classcharts-api";
async function main() {
const client = new StudentClient("classchartsCode", "01/1/2000");
await client.login();
console.log(
await client.getBehaviour({
fromDate: "20/01/2000",
toDate: "01/02/2000",
})
);
console.log(await client.getActivity());
console.log(await client.getStudentInfo());
console.log(await client.getActivity());
console.log(await client.getActivity());
}
main(); For any help with the library, please join the [discord](https://discord.gg/DTcwugcgZ2) where you can ask questions and get help from the community.
```

View file

@ -1,4 +1,3 @@
import ky, { type Options as KyOptions } from "ky-universal";
import type { import type {
ActivityResponse, ActivityResponse,
AnnouncementsResponse, AnnouncementsResponse,
@ -44,13 +43,13 @@ export class BaseClient {
*/ */
public lastPing = 0; public lastPing = 0;
/** /**
* @property API_BASE Base API URL, this is different depending if its called as a parent or student * @property API_BASE Base API URL, this is different depending on if its called as a parent or student
* @internal * @internal
*/ */
protected API_BASE = ""; protected API_BASE = "";
/** /**
* *
* @param API_BASE Base API URL, this is different depending if its called as a parent or student * @param API_BASE Base API URL, this is different depending on if its called as a parent or student
*/ */
constructor(API_BASE: string) { constructor(API_BASE: string) {
this.authCookies = []; this.authCookies = [];
@ -59,7 +58,7 @@ export class BaseClient {
/** /**
* Revalidates the session ID. * Revalidates the session ID.
* *
* This is called automatically when the session ID is older than 3 minutes or when intially using the .login() method * This is called automatically when the session ID is older than 3 minutes or when initially using the .login() method
* @internal * @internal
*/ */
public async getNewSessionId() { public async getNewSessionId() {
@ -77,7 +76,7 @@ export class BaseClient {
this.lastPing = Date.now(); this.lastPing = Date.now();
} }
/** /**
* Makes a request to the Classcharts API with the required authentication headers * Makes a request to the ClassCharts API with the required authentication headers
* *
* @param path Path to the API endpoint * @param path Path to the API endpoint
* @param kyOptions Ky (fetch library) request options * @param kyOptions Ky (fetch library) request options
@ -89,7 +88,7 @@ export class BaseClient {
*/ */
public async makeAuthedRequest( public async makeAuthedRequest(
path: string, path: string,
kyOptions: KyOptions, fetchOptions: RequestInit,
options?: { revalidateToken?: boolean } options?: { revalidateToken?: boolean }
) { ) {
if (!this.sessionId) throw new Error("No session ID"); if (!this.sessionId) throw new Error("No session ID");
@ -100,24 +99,27 @@ export class BaseClient {
options.revalidateToken = true; options.revalidateToken = true;
} }
const requestOptions = { const requestOptions = {
...kyOptions, ...fetchOptions,
headers: { headers: {
Cookie: this?.authCookies?.join(";") ?? [], Cookie: this?.authCookies?.join(";") ?? [],
Authorization: "Basic " + this.sessionId, Authorization: "Basic " + this.sessionId,
...kyOptions.headers, ...fetchOptions.headers,
}, },
credentials: undefined, } satisfies RequestInit;
} satisfies KyOptions;
if (options?.revalidateToken === true && this.lastPing) { if (options?.revalidateToken === true && this.lastPing) {
if (Date.now() - this.lastPing + 5000 > PING_INTERVAL) { if (Date.now() - this.lastPing + 5000 > PING_INTERVAL) {
await this.getNewSessionId(); await this.getNewSessionId();
} }
} }
const request = await ky(path, requestOptions); const request = await fetch(path, requestOptions);
const responseJSON = (await request.json()) as ClassChartsResponse< let responseJSON: ClassChartsResponse<unknown, unknown>;
unknown, try {
unknown responseJSON = await request.json();
>; } catch (err) {
throw new Error(
"Error parsing JSON. Returned response: " + (await request.text())
);
}
if (responseJSON.success == 0) { if (responseJSON.success == 0) {
throw new Error(responseJSON.error); throw new Error(responseJSON.error);
} }

View file

@ -1,4 +1,3 @@
import ky from "ky-universal";
import type { GetPupilsResponse } from "../types.js"; import type { GetPupilsResponse } from "../types.js";
import { BaseClient } from "./baseClient.js"; import { BaseClient } from "./baseClient.js";
@ -24,7 +23,7 @@ export class ParentClient extends BaseClient {
} }
/** /**
* Authenticates with classcharts * Authenticates with ClassCharts
*/ */
async login(): Promise<void> { async login(): Promise<void> {
if (!this.email) throw new Error("Email not inputted"); if (!this.email) throw new Error("Email not inputted");
@ -33,11 +32,11 @@ export class ParentClient extends BaseClient {
formData.append("email", this.email); formData.append("email", this.email);
formData.append("logintype", "existing"); formData.append("logintype", "existing");
formData.append("password", this.password); formData.append("password", this.password);
formData.append("recaptcha-token", "no-token-avaliable"); formData.append("recaptcha-token", "no-token-available");
const headers = new Headers({ const headers = new Headers({
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
}); });
const response = await ky(BASE_URL + "/parent/login", { const response = await fetch(BASE_URL + "/parent/login", {
method: "POST", method: "POST",
body: formData, body: formData,
headers: headers, headers: headers,
@ -45,7 +44,7 @@ export class ParentClient extends BaseClient {
}); });
if (response.status != 302 || !response.headers.get("set-cookie")) if (response.status != 302 || !response.headers.get("set-cookie"))
throw new Error( throw new Error(
"Unauthenticated: Classcharts returned an error: " + "Unauthenticated: ClassCharts returned an error: " +
response.status + response.status +
" " + " " +
response.statusText response.statusText

View file

@ -1,14 +1,13 @@
import { API_BASE_STUDENT, BASE_URL } from "../utils/consts.js"; import { API_BASE_STUDENT, BASE_URL } from "../utils/consts.js";
import { BaseClient } from "./baseClient.js"; import { BaseClient } from "./baseClient.js";
import { parseCookies } from "../utils/utils.js"; import { parseCookies } from "../utils/utils.js";
import ky from "ky-universal";
/** /**
* Student Client * Student Client
*/ */
export class StudentClient extends BaseClient { export class StudentClient extends BaseClient {
/** /**
* @property studentCode Classcharts student code * @property studentCode ClassCharts student code
* @internal * @internal
*/ */
private studentCode = ""; private studentCode = "";
@ -20,7 +19,7 @@ export class StudentClient extends BaseClient {
/** /**
* *
* @param studentCode Classcharts student code * @param studentCode ClassCharts student code
* @param dateOfBirth Student's date of birth * @param dateOfBirth Student's date of birth
*/ */
constructor(studentCode: string, dateOfBirth?: string) { constructor(studentCode: string, dateOfBirth?: string) {
@ -30,7 +29,7 @@ export class StudentClient extends BaseClient {
} }
/** /**
* Authenticates with classcharts * Authenticates with ClassCharts
*/ */
async login(): Promise<void> { async login(): Promise<void> {
if (!this.studentCode) throw new Error("Student Code not inputted"); if (!this.studentCode) throw new Error("Student Code not inputted");
@ -39,17 +38,16 @@ export class StudentClient extends BaseClient {
formData.append("code", this.studentCode.toUpperCase()); formData.append("code", this.studentCode.toUpperCase());
formData.append("dob", this.dateOfBirth); formData.append("dob", this.dateOfBirth);
formData.append("remember_me", "1"); formData.append("remember_me", "1");
formData.append("recaptcha-token", "no-token-avaliable"); formData.append("recaptcha-token", "no-token-available");
const request = await ky(BASE_URL + "/student/login", { const request = await fetch(BASE_URL + "/student/login", {
method: "POST", method: "POST",
body: formData, body: formData,
redirect: "manual", redirect: "manual",
throwHttpErrors: false,
credentials: undefined, credentials: undefined,
}); });
if (request.status != 302 || !request.headers.get("set-cookie")) { if (request.status != 302 || !request.headers.get("set-cookie")) {
throw new Error( throw new Error(
"Unauthenticated: Classcharts returned an error: " + "Unauthenticated: ClassCharts returned an error: " +
request.status + request.status +
" " + " " +
request.statusText request.statusText

View file

@ -4,9 +4,9 @@
* Helper type to define response from ClassCharts * Helper type to define response from ClassCharts
* @internal * @internal
*/ */
export type ClassChartsResponse<Data, Error> = { export type ClassChartsResponse<Data, Meta> = {
data: Data; data: Data;
meta: Error; meta: Meta;
error?: string; error?: string;
success: number; success: number;
}; };

View file

@ -1,4 +1,6 @@
import { StudentClient } from "../src"; import { StudentClient } from "../src";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { code, dob } from "./config.json"; import { code, dob } from "./config.json";
import "jest-extended"; import "jest-extended";
const client = new StudentClient(code, dob); const client = new StudentClient(code, dob);

View file

@ -16,7 +16,7 @@
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"skipLibCheck": true, "skipLibCheck": true,
"isolatedModules": true "isolatedModules": true,
}, },
"include": ["./src/**/*"] "include": ["./src/**/*"]
} }

View file

@ -13,7 +13,7 @@ module.exports = {
Github: "https://github.com/classchartsapi/classcharts-api-js", Github: "https://github.com/classchartsapi/classcharts-api-js",
}, },
includeVersion: true, includeVersion: true,
out: "docs", out: "docs/typedoc",
sort: "required-first", sort: "required-first",
umamiOptions: { umamiOptions: {
src: "https://umami.jaminit.co.uk/script.js", src: "https://umami.jaminit.co.uk/script.js",