diff --git a/src/api/user/data/types.ts b/src/api/user/data/types.ts index 4f00918..bc55fdb 100644 --- a/src/api/user/data/types.ts +++ b/src/api/user/data/types.ts @@ -212,10 +212,10 @@ export interface PronoteApiUserData { /** Authorization for the current student. */ autorisations: { - /** Is allowed to create discussions ? */ + /** Whether the user is allowed to read discussions or messages. */ AvecDiscussion?: boolean - /** Whether the user is disallowed to read/create discussions. */ + /** Whether the user is disallowed to create discussions or messages. */ discussionInterdit?: boolean /** diff --git a/src/client/Pronote.ts b/src/client/Pronote.ts index a880e8e..0f0be59 100644 --- a/src/client/Pronote.ts +++ b/src/client/Pronote.ts @@ -479,8 +479,18 @@ export default class Pronote { }); } + #throwIfNotAllowedReadMessages (): void { + if (!this.authorizations.canReadDiscussions) throw new Error("You can't read messages in this instance."); + } + + #throwIfNotAllowedCreateMessages (): void { + if (!this.authorizations.canDiscuss) throw new Error("You can't create messages in this instance."); + } + public async getDiscussionsOverview (): Promise { return this.queue.push(async () => { + this.#throwIfNotAllowedReadMessages(); + const { data } = await callApiUserDiscussions(this.fetcher, { session: this.session }); return new StudentDiscussionsOverview(this, this.queue, this.session, data.donnees); }); @@ -488,6 +498,8 @@ export default class Pronote { public async getMessagesOverviewFromDiscussion (discussion: StudentDiscussion, markAsRead = false, limit = 0): Promise { return this.queue.push(async () => { + this.#throwIfNotAllowedReadMessages(); + const { data } = await callApiUserMessages(this.fetcher, { possessions: discussion.possessions, session: this.session, markAsRead, limit }); return new MessagesOverview( this, this.queue, this.session, @@ -499,6 +511,8 @@ export default class Pronote { public async postDiscussionCommand (payload: ApiUserDiscussionAvailableCommands): Promise { await this.queue.push(async () => { + this.#throwIfNotAllowedCreateMessages(); + await callApiUserDiscussionCommand(this.fetcher, { session: this.session, ...payload @@ -516,6 +530,8 @@ export default class Pronote { public async getRecipientsForMessage (messageID: string): Promise { return this.queue.push(async () => { + this.#throwIfNotAllowedReadMessages(); + const { data } = await callApiUserMessageRecipients(this.fetcher, { session: this.session, messageID @@ -612,8 +628,6 @@ export default class Pronote { } #throwIfNotAllowedRecipientType (type: PronoteApiUserResourceType): void { - if (!this.authorizations.canDiscuss) throw new Error("You don't have access to discussion."); - switch (type) { case PronoteApiResourceType.Teacher: if (!this.authorizations.canDiscussWithTeachers) @@ -637,9 +651,9 @@ export default class Pronote { * It allows to know who can be the recipient of the discussion. */ public async getRecipientsForDiscussionCreation (type: PronoteApiUserResourceType): Promise { - this.#throwIfNotAllowedRecipientType(type); - return this.queue.push(async () => { + this.#throwIfNotAllowedRecipientType(type); + const response = await callApiUserCreateDiscussionRecipients(this.fetcher, { recipientType: type, session: this.session, @@ -664,9 +678,10 @@ export default class Pronote { * discussions list once again using `getDiscussionsOverview()`. */ public async createDiscussion (subject: string, content: string, recipients: DiscussionCreationRecipient[]): Promise { - if (recipients.length <= 0) throw new Error("You need to select at least one recipient to create a discussion."); - return this.queue.push(async () => { + this.#throwIfNotAllowedCreateMessages(); + if (recipients.length <= 0) throw new Error("You need to select at least one recipient to create a discussion."); + await callApiUserCreateDiscussion(this.fetcher, { session: this.session, recipients, @@ -680,9 +695,10 @@ export default class Pronote { } public async replyToDiscussionMessage (replyMessageID: string, content: string, button: PronoteApiMessagesButtonType, includeParentsAndStudents = false): Promise { - const buttonType = getPronoteMessageButtonType(button, includeParentsAndStudents); - return this.queue.push(async () => { + this.#throwIfNotAllowedCreateMessages(); + const buttonType = getPronoteMessageButtonType(button, includeParentsAndStudents); + await callApiUserCreateDiscussionMessage(this.fetcher, { session: this.session, replyMessageID, diff --git a/src/parser/authorizations.ts b/src/parser/authorizations.ts index 409e1e3..de7685c 100644 --- a/src/parser/authorizations.ts +++ b/src/parser/authorizations.ts @@ -1,6 +1,7 @@ import type { ApiUserData } from "~/api"; class Authorizations { + readonly #canReadDiscussions: boolean; readonly #canDiscuss: boolean; readonly #canDiscussWithStaff: boolean; @@ -13,7 +14,8 @@ class Authorizations { readonly #maxHomeworkFileUploadSize: number; constructor (data: ApiUserData["output"]["data"]["donnees"]["autorisations"]) { - this.#canDiscuss = (data.AvecDiscussion ?? false) && !(data.discussionInterdit ?? false); + this.#canReadDiscussions = data.AvecDiscussion ?? false; + this.#canDiscuss = this.#canReadDiscussions && !(data.discussionInterdit ?? false); this.#canDiscussWithStaff = this.#canDiscuss && (data.AvecDiscussionPersonnels ?? false); this.#canDiscussWithParents = this.#canDiscuss && (data.AvecDiscussionParents ?? false); @@ -26,7 +28,14 @@ class Authorizations { } /** - * Whether the user is allowed to discuss. + * Whether the user is allowed to read discussions. + */ + public get canReadDiscussions (): boolean { + return this.#canReadDiscussions; + } + + /** + * Whether the user is allowed to create messages in discussions. */ public get canDiscuss (): boolean { return this.#canDiscuss; diff --git a/src/parser/messages.ts b/src/parser/messages.ts index a770f2a..8282135 100644 --- a/src/parser/messages.ts +++ b/src/parser/messages.ts @@ -25,7 +25,7 @@ export class MessagesOverview { #messages: SentMessage[] = []; #savedDrafts: DraftMessage[] = []; // Needed to create a new message... - #sendButtonGenre: PronoteApiMessagesButtonType; + #sendButtonGenre?: PronoteApiMessagesButtonType; #fetchLimit: number; public async refetch (limit = this.#fetchLimit) { @@ -53,8 +53,9 @@ export class MessagesOverview { this.#fetchLimit = limit; } - #readSendButton (listeBoutons: PronoteApiUserMessages["response"]["donnees"]["listeBoutons"]["V"]): PronoteApiMessagesButtonType { - return listeBoutons.find((button) => button.L.startsWith("Envoyer"))!.G; + #readSendButton (listeBoutons: PronoteApiUserMessages["response"]["donnees"]["listeBoutons"]["V"]): PronoteApiMessagesButtonType | undefined { + const button = listeBoutons.find((button) => button.L.startsWith("Envoyer")); + return button?.G; } #parseMessages (listeMessages: PronoteApiUserMessages["response"]["donnees"]["listeMessages"]["V"]): void { @@ -146,6 +147,8 @@ export class MessagesOverview { * internally so properties are automatically updated. */ public async sendMessage (content: string, includeParentsAndStudents = false, replyTo = this.#defaultReplyMessageID): Promise { + if (typeof this.#sendButtonGenre === "undefined") throw new Error("You can't create messages in this discussion."); + await this.#client.replyToDiscussionMessage(replyTo, content, this.#sendButtonGenre, includeParentsAndStudents); await this.refetch(); } @@ -182,6 +185,7 @@ export class MessagesOverview { } public async sendDraft (draft: DraftMessage, includeParentsAndStudents = false): Promise { + if (typeof this.#sendButtonGenre === "undefined") throw new Error("You can't create drafts in this discussion."); const buttonType = getPronoteMessageButtonType(this.#sendButtonGenre, includeParentsAndStudents); await this.#client.postDiscussionCommand({