mcp-memory-libsql
by spences10
Verified
import { google } from 'googleapis';
import {
GmailAttachment,
IncomingGmailAttachment,
OutgoingGmailAttachment,
GmailError
} from '../types.js';
import { AttachmentIndexService } from '../../attachments/index-service.js';
export class GmailAttachmentService {
private static instance: GmailAttachmentService;
private indexService: AttachmentIndexService;
private gmailClient?: ReturnType<typeof google.gmail>;
private constructor() {
this.indexService = AttachmentIndexService.getInstance();
}
/**
* Add attachment metadata to the index
*/
addAttachment(messageId: string, attachment: {
id: string;
name: string;
mimeType: string;
size: number;
}): void {
this.indexService.addAttachment(messageId, attachment);
}
public static getInstance(): GmailAttachmentService {
if (!GmailAttachmentService.instance) {
GmailAttachmentService.instance = new GmailAttachmentService();
}
return GmailAttachmentService.instance;
}
/**
* Updates the Gmail client instance
*/
updateClient(client: ReturnType<typeof google.gmail>) {
this.gmailClient = client;
}
private ensureClient(): ReturnType<typeof google.gmail> {
if (!this.gmailClient) {
throw new GmailError(
'Gmail client not initialized',
'CLIENT_ERROR',
'Please ensure the service is initialized'
);
}
return this.gmailClient;
}
/**
* Get attachment content from Gmail
*/
async getAttachment(
email: string,
messageId: string,
filename: string
): Promise<IncomingGmailAttachment> {
try {
// Get original metadata from index
const metadata = this.indexService.getMetadata(messageId, filename);
if (!metadata) {
throw new GmailError(
'Attachment not found',
'ATTACHMENT_ERROR',
'Attachment metadata not found - message may need to be refreshed'
);
}
const client = this.ensureClient();
const { data } = await client.users.messages.attachments.get({
userId: 'me',
messageId,
id: metadata.originalId,
});
if (!data.data) {
throw new Error('No attachment data received');
}
return {
id: metadata.originalId,
content: data.data,
size: metadata.size,
name: metadata.filename,
mimeType: metadata.mimeType,
};
} catch (error) {
throw new GmailError(
'Failed to get attachment',
'ATTACHMENT_ERROR',
`Error: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Validate attachment content and size
*/
validateAttachment(attachment: OutgoingGmailAttachment): void {
if (!attachment.content) {
throw new GmailError(
'Invalid attachment',
'VALIDATION_ERROR',
'Attachment content is required'
);
}
// Gmail's attachment size limit is 25MB
const MAX_SIZE = 25 * 1024 * 1024;
if (attachment.size > MAX_SIZE) {
throw new GmailError(
'Invalid attachment',
'VALIDATION_ERROR',
`Attachment size ${attachment.size} exceeds maximum allowed size ${MAX_SIZE}`
);
}
}
/**
* Prepare attachment for sending
*/
prepareAttachment(attachment: OutgoingGmailAttachment): {
filename: string;
mimeType: string;
content: string;
} {
this.validateAttachment(attachment);
return {
filename: attachment.name,
mimeType: attachment.mimeType,
content: attachment.content,
};
}
}