Aether/server/src/Account.ts

149 lines
3.6 KiB
TypeScript

import Message from './Message';
import Conversation from './Conversation';
import Imap, { ConnectionProperties, Message as RawMessage, MailboxType } from './Imap';
interface Contact {
name: string;
addresses: Set<string>;
}
export default class Account {
private imap: Imap;
private contacts: Contact[] = [];
private conversations: Conversation[] = [];
private name: string;
private image: string;
private address: string;
private unread: boolean;
constructor(name: string, image: string, connection: ConnectionProperties) {
this.name = name;
this.image = image;
this.address = connection.username;
this.unread = true;
this.imap = new Imap(connection);
}
async connect() {
await this.imap.connect();
const messages = await this.fetchAllMessages();
this.conversations = this.createConversations(messages).filter(c => c.active);
this.contacts = this.createContacts(messages);
}
getName() {
return this.name;
}
getAddress() {
return this.address;
}
getImage() {
return this.image;
}
hasUnreads() {
return this.unread;
}
getConversations() {
return this.conversations;
}
getContacts() {
return this.contacts;
}
getMessages(_messages: string[]): Message[] {
return [{
id: 'AOUEOAEu',
date: new Date(),
from: 'me@auri.xyz',
to: [ 'nicole@aurailus.design' ],
content: '<p>Lorem ipsum dolor sit amet.</p>'
}];
}
async fetchAllMessages(): Promise<RawMessage[]> {
const boxes = (await this.imap.listBoxes()).filter(box =>
!box.treeTypes.has(MailboxType.Spam) && !box.treeTypes.has(MailboxType.Trash));
let allMeta: RawMessage[] = [];
for (let box of boxes) {
await this.imap.openBox(box.path);
const meta = await this.imap.fetchMessages('1:*');
Object.keys(meta).forEach(id => allMeta.push(meta[id]));
}
allMeta = allMeta.sort((a, b) => +a.date - +b.date);
return allMeta;
}
private createConversations(messages: RawMessage[]): Conversation[] {
const conversations: Conversation[] = [];
messages.forEach(message => {
if (message.replyTo) {
for (let conversation of conversations) {
for (let reference of [ ...message.references, message.replyTo ]) {
if (conversation.messages.has(reference)) {
conversation.date = message.date;
conversation.messages.add(message.messageId);
conversation.title = this.cleanSubjectLine(message.subject);
conversation.active = conversation.active || message.active;
message.to.forEach(p => conversation.participants.add(p.address));
conversation.participants.add(message.from.address);
return;
}
}
}
}
conversations.push({
title: this.cleanSubjectLine(message.subject),
messages: new Set([ message.messageId ]),
date: message.date,
active: message.active,
participants: new Set([ message.from.address, ...message.to.map(p => p.address) ])
});
});
conversations.forEach(conversation => {
conversation.participants.delete(this.address);
});
return conversations.sort((a, b) => +a.date - +b.date);
}
private createContacts(messages: RawMessage[]): Contact[] {
const contacts: Contact[] = [];
for (let message of messages) {
[ message.from, ...message.to ].forEach(participant => {
for (let contact of contacts) {
if (contact.addresses.has(participant.address)) {
if (participant.name) contact.name = participant.name;
return;
}
}
contacts.push({
name: participant.name ?? participant.address,
addresses: new Set([ participant.address ])
});
});
}
return contacts;
}
private cleanSubjectLine(subject: string = '') {
return subject.replace(/^((re|fwd?|b?cc)(:| ) *)*/gi, '').trim();
}
};