n8n-nodes-pronote 0.2.1 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,3 +29,5 @@ See `agent-tools/pronoteTools.ts` for a thin wrapper you can drop into LLM agent
29
29
  1) Run **PRONOTE Auth Helper** with operation “Prepare Mobile Validation” and your instance URL. It returns a `deviceUUID` and the `InfoMobileApp.json` URL.
30
30
  2) Open that URL in a browser, approve the device in your CAS portal (e.g., Lycée Connecté). Capture the returned `login` and `mdp` (token).
31
31
  3) Run **PRONOTE Auth Helper** with operation “Validate Mobile Token”, providing the `instanceUrl`, `login`, `mdp`, and the same `deviceUUID`. The node outputs a verified token plus user details—use these in the PRONOTE credentials.
32
+
33
+ > Use `mobileToken` (mdp) + `deviceUUID` in the PRONOTE credentials. The `sessionToken` returned is short‑lived; nodes will re-login with the mobile token on each run.
@@ -178,7 +178,8 @@ class PronoteAuthHelper {
178
178
  instanceUrl: auth.url,
179
179
  deviceUUID: auth.deviceUUID,
180
180
  username: auth.username,
181
- token: auth.token,
181
+ mobileToken: token,
182
+ sessionToken: auth.token,
182
183
  kind: auth.kind,
183
184
  userDisplayName: auth.session.user.name,
184
185
  establishment: auth.session.user.resources[0]?.establishmentName,
@@ -209,7 +210,7 @@ class PronoteAuthHelper {
209
210
  instanceUrl: auth.url,
210
211
  deviceUUID: auth.deviceUUID,
211
212
  username: auth.username,
212
- token: auth.token,
213
+ sessionToken: auth.token,
213
214
  kind: auth.kind,
214
215
  userDisplayName: auth.session.user.name,
215
216
  establishment: auth.session.user.resources[0]?.establishmentName,
@@ -1,5 +1,5 @@
1
- import { type INodeType, type INodeTypeDescription, type ISupplyDataFunctions, type SupplyData } from "n8n-workflow";
1
+ import { type INodeType, type INodeTypeDescription, type IExecuteFunctions, type INodeExecutionData } from "n8n-workflow";
2
2
  export declare class PronoteTool implements INodeType {
3
3
  description: INodeTypeDescription;
4
- supplyData(this: ISupplyDataFunctions): Promise<SupplyData>;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
5
  }
@@ -1,8 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PronoteTool = void 0;
4
- const tools_1 = require("@langchain/core/tools");
5
- const zod_1 = require("zod");
6
4
  const n8n_workflow_1 = require("n8n-workflow");
7
5
  const pawnoteClient_1 = require("../../shared/pawnoteClient");
8
6
  const GenericFunctions_1 = require("./GenericFunctions");
@@ -15,13 +13,14 @@ class PronoteTool {
15
13
  group: ["transform"],
16
14
  version: 1,
17
15
  description: "Expose PRONOTE timetable, tasks, and grades to AI Agents",
18
- subtitle: "={{ $parameter[\"toolDescription\"] || \"PRONOTE tool\" }}",
16
+ subtitle: "={{ $parameter[\"action\"] || \"PRONOTE\" }}",
19
17
  defaults: {
20
18
  name: "PRONOTE Tool",
21
19
  },
22
- inputs: [],
23
- outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool],
24
- outputNames: ["Tool"],
20
+ usableAsTool: true,
21
+ inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
22
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
23
+ outputNames: ["Result"],
25
24
  credentials: [
26
25
  {
27
26
  name: "pronoteApi",
@@ -30,104 +29,138 @@ class PronoteTool {
30
29
  ],
31
30
  properties: [
32
31
  {
33
- displayName: "Tool Description",
34
- name: "toolDescription",
32
+ displayName: "Action",
33
+ name: "action",
34
+ type: "options",
35
+ default: "timetable",
36
+ options: [
37
+ { name: "Timetable", value: "timetable" },
38
+ { name: "Tasks", value: "tasks" },
39
+ { name: "Grades", value: "grades" },
40
+ { name: "Grade Periods", value: "gradePeriods" },
41
+ ],
42
+ description: "What to retrieve from PRONOTE",
43
+ },
44
+ {
45
+ displayName: "Reference Date",
46
+ name: "referenceDate",
47
+ type: "string",
48
+ default: "",
49
+ placeholder: "2024-09-01",
50
+ description: "ISO date used to pick the PRONOTE week for timetable/tasks. Defaults to today.",
51
+ displayOptions: {
52
+ show: {
53
+ action: ["timetable", "tasks"],
54
+ },
55
+ },
56
+ },
57
+ {
58
+ displayName: "Only Pending Tasks",
59
+ name: "onlyPendingTasks",
60
+ type: "boolean",
61
+ default: false,
62
+ description: "Filter out tasks already marked done",
63
+ displayOptions: {
64
+ show: {
65
+ action: ["tasks"],
66
+ },
67
+ },
68
+ },
69
+ {
70
+ displayName: "Period Name or ID",
71
+ name: "periodName",
35
72
  type: "string",
36
- default: "Use this tool to read timetable, homework/tasks, or grade information from PRONOTE. " +
37
- "Provide action: timetable, tasks, grades, or gradePeriods. " +
38
- "Use referenceDate (ISO) for timetable/tasks; periodName for grades.",
39
- description: "Optional guidance shown to the model about how to use this tool.",
40
- typeOptions: {
41
- rows: 3,
73
+ default: "",
74
+ description: "Grade period to fetch (name or ID). Leave empty to use the first available period.",
75
+ displayOptions: {
76
+ show: {
77
+ action: ["grades"],
78
+ },
79
+ },
80
+ },
81
+ {
82
+ displayName: "Include Individual Grades",
83
+ name: "includeGrades",
84
+ type: "boolean",
85
+ default: true,
86
+ description: "If false, only averages and subjects are returned",
87
+ displayOptions: {
88
+ show: {
89
+ action: ["grades"],
90
+ },
42
91
  },
43
92
  },
44
93
  ],
45
94
  };
46
95
  }
47
- async supplyData() {
48
- try {
49
- const { session } = await (0, GenericFunctions_1.getPronoteAuth)(this);
50
- const tool = new tools_1.DynamicStructuredTool({
51
- name: "pronote",
52
- description: this.getNodeParameter("toolDescription", 0) ||
53
- "Fetch timetable, homework/tasks, or grade information from PRONOTE. " +
54
- "Actions: timetable, tasks, grades, gradePeriods. " +
55
- "Use referenceDate (ISO date) to target a week for timetable/tasks. " +
56
- "When action=grades, provide periodName (ID or label).",
57
- schema: zod_1.z.object({
58
- action: zod_1.z
59
- .enum(["timetable", "tasks", "grades", "gradePeriods"])
60
- .describe("What to retrieve from PRONOTE."),
61
- referenceDate: zod_1.z
62
- .string()
63
- .optional()
64
- .describe("ISO date (e.g. 2024-09-01). Used for timetable and tasks to select the week; defaults to today."),
65
- periodName: zod_1.z
66
- .string()
67
- .optional()
68
- .describe("Grade period name or ID. Used when action=grades."),
69
- onlyPendingTasks: zod_1.z
70
- .boolean()
71
- .optional()
72
- .describe("When action=tasks, if true only return undone tasks."),
73
- includeGrades: zod_1.z
74
- .boolean()
75
- .optional()
76
- .describe("When action=grades, if false only averages are returned."),
77
- }),
78
- func: async (input) => {
79
- const safeStringify = (data) => JSON.stringify(data, null, 2);
80
- switch (input.action) {
81
- case "timetable": {
82
- const referenceDate = (0, GenericFunctions_1.resolveReferenceDate)(input.referenceDate);
83
- const timetable = await (0, pawnoteClient_1.fetchTimetable)(session, referenceDate);
84
- return safeStringify(timetable);
85
- }
86
- case "tasks": {
87
- const referenceDate = (0, GenericFunctions_1.resolveReferenceDate)(input.referenceDate);
88
- const assignments = await (0, pawnoteClient_1.fetchAssignments)(session, referenceDate);
89
- const filtered = input.onlyPendingTasks
90
- ? assignments.filter((a) => !a.isDone)
91
- : assignments;
92
- return safeStringify(filtered);
93
- }
94
- case "gradePeriods": {
95
- const periods = await (0, pawnoteClient_1.fetchGradePeriods)(session);
96
- return safeStringify(periods);
97
- }
98
- case "grades": {
99
- const includeGrades = input.includeGrades !== false;
100
- const overview = await (0, pawnoteClient_1.fetchGrades)(session, input.periodName);
101
- if (!includeGrades) {
102
- return safeStringify({
103
- period: overview.period,
104
- studentOverall: overview.studentOverall,
105
- classAverage: overview.classAverage,
106
- subjects: overview.subjects.map((subject) => ({
107
- id: subject.id,
108
- name: subject.name,
109
- studentAverage: subject.studentAverage,
110
- classAverage: subject.classAverage,
111
- maximum: subject.maximum,
112
- minimum: subject.minimum,
113
- outOf: subject.outOf,
114
- })),
115
- });
116
- }
117
- return safeStringify(overview);
118
- }
119
- default:
120
- throw new Error(`Unsupported action: ${input["action"]}`);
96
+ async execute() {
97
+ const items = this.getInputData();
98
+ const returnData = [];
99
+ const auth = await (0, GenericFunctions_1.getPronoteAuth)(this);
100
+ const action = this.getNodeParameter("action", 0);
101
+ for (let i = 0; i < items.length; i++) {
102
+ try {
103
+ if (action === "timetable") {
104
+ const referenceDate = (0, GenericFunctions_1.resolveReferenceDate)(this.getNodeParameter("referenceDate", i));
105
+ const timetable = await (0, pawnoteClient_1.fetchTimetable)(auth.session, referenceDate);
106
+ for (const day of timetable) {
107
+ returnData.push({ json: day, pairedItem: { item: i } });
121
108
  }
122
- },
123
- });
124
- return {
125
- response: tool,
126
- };
127
- }
128
- catch (error) {
129
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), error.message);
109
+ continue;
110
+ }
111
+ if (action === "tasks") {
112
+ const referenceDate = (0, GenericFunctions_1.resolveReferenceDate)(this.getNodeParameter("referenceDate", i));
113
+ const onlyPending = this.getNodeParameter("onlyPendingTasks", i);
114
+ const assignments = await (0, pawnoteClient_1.fetchAssignments)(auth.session, referenceDate);
115
+ for (const assignment of assignments) {
116
+ if (onlyPending && assignment.isDone)
117
+ continue;
118
+ returnData.push({ json: assignment, pairedItem: { item: i } });
119
+ }
120
+ continue;
121
+ }
122
+ if (action === "gradePeriods") {
123
+ const periods = await (0, pawnoteClient_1.fetchGradePeriods)(auth.session);
124
+ for (const period of periods) {
125
+ returnData.push({ json: period, pairedItem: { item: i } });
126
+ }
127
+ continue;
128
+ }
129
+ if (action === "grades") {
130
+ const periodName = this.getNodeParameter("periodName", i).trim() || undefined;
131
+ const includeGrades = this.getNodeParameter("includeGrades", i);
132
+ const overview = await (0, pawnoteClient_1.fetchGrades)(auth.session, periodName);
133
+ if (!includeGrades) {
134
+ returnData.push({
135
+ json: {
136
+ period: overview.period,
137
+ studentOverall: overview.studentOverall,
138
+ classAverage: overview.classAverage,
139
+ subjects: overview.subjects.map((subject) => ({
140
+ id: subject.id,
141
+ name: subject.name,
142
+ studentAverage: subject.studentAverage,
143
+ classAverage: subject.classAverage,
144
+ maximum: subject.maximum,
145
+ minimum: subject.minimum,
146
+ outOf: subject.outOf,
147
+ })),
148
+ },
149
+ pairedItem: { item: i },
150
+ });
151
+ }
152
+ else {
153
+ returnData.push({ json: overview, pairedItem: { item: i } });
154
+ }
155
+ continue;
156
+ }
157
+ throw new Error(`Unsupported action ${action}`);
158
+ }
159
+ catch (error) {
160
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error.message, { itemIndex: i });
161
+ }
130
162
  }
163
+ return [returnData];
131
164
  }
132
165
  }
133
166
  exports.PronoteTool = PronoteTool;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-pronote",
3
- "version": "0.2.1",
3
+ "version": "0.2.5",
4
4
  "description": "n8n custom nodes and agent utilities to talk to PRONOTE via the Pawnote library.",
5
5
  "license": "MIT",
6
6
  "main": "dist/nodes/Pronote/Pronote.node.js",
@@ -14,9 +14,7 @@
14
14
  "dist"
15
15
  ],
16
16
  "dependencies": {
17
- "@langchain/core": "^0.2.29",
18
- "pawnote": "^1.6.2",
19
- "zod": "^3.23.8"
17
+ "pawnote": "^1.6.2"
20
18
  },
21
19
  "peerDependencies": {
22
20
  "n8n-core": "^1.0.0",