Changing how messages are fetched and marked as read

This commit is contained in:
Collin Duncan 2023-09-05 13:09:10 +02:00
parent 29ec45e690
commit 931a8b7f6c
No known key found for this signature in database
4 changed files with 36 additions and 14 deletions

View file

@ -15,6 +15,7 @@ export enum EmailClientStatus {
@Injectable() @Injectable()
export class EmailClient { export class EmailClient {
public readonly imapClient: Imap public readonly imapClient: Imap
private readonly mailboxName: string
private mailbox?: Imap.Box private mailbox?: Imap.Box
private status: EmailClientStatus private status: EmailClientStatus
@ -25,6 +26,7 @@ export class EmailClient {
@Inject(ConfigService) @Inject(ConfigService)
private readonly configService: ConfigService, private readonly configService: ConfigService,
) { ) {
this.mailboxName = this.configService.getOrThrow<string>('EMAIL_MAILBOX')
this.imapClient = new Imap({ this.imapClient = new Imap({
host: this.configService.getOrThrow('EMAIL_HOST'), host: this.configService.getOrThrow('EMAIL_HOST'),
port: this.configService.get('EMAIL_PORT', 993), port: this.configService.get('EMAIL_PORT', 993),
@ -90,8 +92,10 @@ export class EmailClient {
) )
if (newMessages > 0) { if (newMessages > 0) {
const startingMessage = totalMessages - newMessages + 1 // starting message is inclusive const emails = await this.fetchMailsFrom(
const emails = await this.fetchMails(`${startingMessage}`, '*') totalMessages - (numMessages - 1),
)
this.loggerService.debug(`Fetched ${emails.length} emails`)
callback(emails) callback(emails)
} }
} }
@ -102,8 +106,7 @@ export class EmailClient {
throw new Error('Not ready to listen') throw new Error('Not ready to listen')
} }
const mailbox = this.configService.get<string>('EMAIL_MAILBOX', 'INBOX') this.imapClient.openBox(this.mailboxName, (error, mailbox) => {
this.imapClient.openBox(mailbox, (error, mailbox) => {
if (error) { if (error) {
this.loggerService.error('Error opening mailbox', { this.loggerService.error('Error opening mailbox', {
...error, ...error,
@ -171,17 +174,13 @@ export class EmailClient {
}) })
} }
public async fetchMails(startingMessageId: string, endingMessageId: string) { public async fetchMailsFrom(startingMessageSeqNo: number) {
this.loggerService.debug( this.loggerService.debug(
`Fetching mails ${startingMessageId}:${endingMessageId}`, `Fetching mails starting from ${startingMessageSeqNo}`,
) )
const fetcher = this.imapClient.fetch( const fetcher = this.imapClient.seq.fetch(`${startingMessageSeqNo}:*`, {
`${startingMessageId}:${endingMessageId}`,
{
bodies: '', bodies: '',
markSeen: true, })
},
)
return new Promise<Email[]>((res, rej) => { return new Promise<Email[]>((res, rej) => {
const generateEmailPromises: Promise<Email>[] = [] const generateEmailPromises: Promise<Email>[] = []
@ -198,6 +197,18 @@ export class EmailClient {
}) })
} }
public async markMailsSeen(messageIds: string[]) {
this.loggerService.debug('Marking mails as seen', { messageIds })
return new Promise<void>((res, rej) => {
this.imapClient.addFlags(messageIds.join(','), ['\\Seen'], (error) => {
if (error != null) {
rej(error)
}
res()
})
})
}
private setupDefaultListeners() { private setupDefaultListeners() {
this.imapClient.on('ready', () => { this.imapClient.on('ready', () => {
this.loggerService.debug('email client ready') this.loggerService.debug('email client ready')

View file

@ -19,7 +19,7 @@ export class EmailProvider {
} }
private async handleReceivedEmails(emails: Email[]) { private async handleReceivedEmails(emails: Email[]) {
this.emailsQueue.addBulk( await this.emailsQueue.addBulk(
emails.map((email) => ({ name: email.id, data: email })), emails.map((email) => ({ name: email.id, data: email })),
) )
} }
@ -27,4 +27,8 @@ export class EmailProvider {
public registerEmailListener() { public registerEmailListener() {
this.emailClient.listen((emails) => this.handleReceivedEmails(emails)) this.emailClient.listen((emails) => this.handleReceivedEmails(emails))
} }
public async readEmails(emails: Email[]) {
await this.emailClient.markMailsSeen(emails.map((e) => e.id))
}
} }

View file

@ -1,5 +1,6 @@
import { BullModule } from '@nestjs/bull' import { BullModule } from '@nestjs/bull'
import { Module } from '@nestjs/common' import { Module } from '@nestjs/common'
import { EmailModule } from 'src/email/module'
import { ReservationsModule } from 'src/reservations/module' import { ReservationsModule } from 'src/reservations/module'
import { EMAILS_QUEUE_NAME } from '../email/config' import { EMAILS_QUEUE_NAME } from '../email/config'
@ -13,6 +14,7 @@ import { WaitingListService } from './service'
ReservationsModule, ReservationsModule,
BullModule.registerQueue({ name: EMAILS_QUEUE_NAME }), BullModule.registerQueue({ name: EMAILS_QUEUE_NAME }),
BullModule.registerQueue({ name: RESERVATIONS_QUEUE_NAME }), BullModule.registerQueue({ name: RESERVATIONS_QUEUE_NAME }),
EmailModule,
], ],
providers: [WaitingListService], providers: [WaitingListService],
exports: [WaitingListService], exports: [WaitingListService],

View file

@ -1,6 +1,7 @@
import { InjectQueue, Process, Processor } from '@nestjs/bull' import { InjectQueue, Process, Processor } from '@nestjs/bull'
import { Inject } from '@nestjs/common' import { Inject } from '@nestjs/common'
import { Job, Queue } from 'bull' import { Job, Queue } from 'bull'
import { EmailProvider } from 'src/email/provider'
import { ReservationsService } from 'src/reservations/service' import { ReservationsService } from 'src/reservations/service'
import dayjs from '../common/dayjs' import dayjs from '../common/dayjs'
@ -31,6 +32,9 @@ export class WaitingListService {
@Inject(ReservationsService) @Inject(ReservationsService)
private readonly reservationsService: ReservationsService, private readonly reservationsService: ReservationsService,
@Inject(EmailProvider)
private readonly emailProvider: EmailProvider,
@Inject(LoggerService) @Inject(LoggerService)
private readonly loggerService: LoggerService, private readonly loggerService: LoggerService,
) {} ) {}
@ -46,6 +50,7 @@ export class WaitingListService {
if (!this.isRelevantEmail) return if (!this.isRelevantEmail) return
await this.emailProvider.readEmails([email])
await this.handleWaitingListEmail(email) await this.handleWaitingListEmail(email)
} }