@cli4ai/gmail 1.0.6 → 1.0.8
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/cli4ai.json +11 -1
- package/package.json +1 -3
- package/lib/api.test.ts +0 -287
- package/lib/api.ts +0 -299
- package/lib/attachments.ts +0 -199
- package/lib/drafts.ts +0 -434
- package/lib/labels.ts +0 -198
- package/lib/messages.ts +0 -310
- package/lib/send.ts +0 -331
- package/lib/threads.ts +0 -164
package/lib/attachments.ts
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { getGmail, output, decodeBase64Url, type OutputOptions, type MessagePayload } from './api';
|
|
4
|
-
|
|
5
|
-
/** Attachment info */
|
|
6
|
-
export interface AttachmentInfo {
|
|
7
|
-
filename: string;
|
|
8
|
-
mimeType?: string;
|
|
9
|
-
size: number;
|
|
10
|
-
attachmentId?: string | null;
|
|
11
|
-
partId?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Download result */
|
|
15
|
-
export interface DownloadResult {
|
|
16
|
-
ok: boolean;
|
|
17
|
-
filename: string;
|
|
18
|
-
savedTo?: string;
|
|
19
|
-
size?: number;
|
|
20
|
-
error?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Download all result */
|
|
24
|
-
export interface DownloadAllResult {
|
|
25
|
-
ok: boolean;
|
|
26
|
-
message?: string;
|
|
27
|
-
downloaded: DownloadResult[];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Extract attachment info from message payload
|
|
32
|
-
*/
|
|
33
|
-
function extractAttachmentInfo(payload: MessagePayload | undefined, attachments: AttachmentInfo[] = []): AttachmentInfo[] {
|
|
34
|
-
if (!payload) return attachments;
|
|
35
|
-
|
|
36
|
-
// Check if this part is an attachment
|
|
37
|
-
if (payload.filename && payload.filename.length > 0) {
|
|
38
|
-
attachments.push({
|
|
39
|
-
filename: payload.filename,
|
|
40
|
-
mimeType: payload.mimeType,
|
|
41
|
-
size: payload.body?.size || 0,
|
|
42
|
-
attachmentId: payload.body?.attachmentId || null,
|
|
43
|
-
partId: payload.partId
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Recursively check parts
|
|
48
|
-
if (payload.parts) {
|
|
49
|
-
for (const part of payload.parts) {
|
|
50
|
-
extractAttachmentInfo(part, attachments);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return attachments;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* List attachments in a message
|
|
59
|
-
*/
|
|
60
|
-
export async function list(messageId: string, options: OutputOptions = {}): Promise<AttachmentInfo[]> {
|
|
61
|
-
const gmail = await getGmail();
|
|
62
|
-
|
|
63
|
-
const res = await gmail.users.messages.get({
|
|
64
|
-
userId: 'me',
|
|
65
|
-
id: messageId,
|
|
66
|
-
format: 'full'
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const attachments = extractAttachmentInfo(res.data.payload as MessagePayload);
|
|
70
|
-
|
|
71
|
-
output(attachments, options);
|
|
72
|
-
return attachments;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Download an attachment
|
|
77
|
-
*/
|
|
78
|
-
export async function download(messageId: string, filename?: string, outputPath?: string, options: OutputOptions = {}): Promise<DownloadResult> {
|
|
79
|
-
const gmail = await getGmail();
|
|
80
|
-
|
|
81
|
-
// Get message to find attachment
|
|
82
|
-
const res = await gmail.users.messages.get({
|
|
83
|
-
userId: 'me',
|
|
84
|
-
id: messageId,
|
|
85
|
-
format: 'full'
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const attachments = extractAttachmentInfo(res.data.payload as MessagePayload);
|
|
89
|
-
|
|
90
|
-
// Find the attachment by filename (or get first if not specified)
|
|
91
|
-
let attachment: AttachmentInfo | undefined;
|
|
92
|
-
if (filename) {
|
|
93
|
-
attachment = attachments.find(a =>
|
|
94
|
-
a.filename.toLowerCase() === filename.toLowerCase() ||
|
|
95
|
-
a.filename.toLowerCase().includes(filename.toLowerCase())
|
|
96
|
-
);
|
|
97
|
-
} else if (attachments.length === 1) {
|
|
98
|
-
attachment = attachments[0];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!attachment) {
|
|
102
|
-
throw new Error(`Attachment not found: ${filename || '(no attachments)'}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (!attachment.attachmentId) {
|
|
106
|
-
throw new Error(`Attachment ${attachment.filename} has no downloadable content`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Get the attachment data
|
|
110
|
-
const attachmentRes = await gmail.users.messages.attachments.get({
|
|
111
|
-
userId: 'me',
|
|
112
|
-
messageId: messageId,
|
|
113
|
-
id: attachment.attachmentId
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Decode and save
|
|
117
|
-
const data = decodeBase64Url(attachmentRes.data.data);
|
|
118
|
-
const savePath = outputPath || attachment.filename;
|
|
119
|
-
|
|
120
|
-
fs.writeFileSync(savePath, data, 'binary');
|
|
121
|
-
|
|
122
|
-
const result: DownloadResult = {
|
|
123
|
-
ok: true,
|
|
124
|
-
filename: attachment.filename,
|
|
125
|
-
savedTo: path.resolve(savePath),
|
|
126
|
-
size: attachment.size
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
output(result, options);
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Download all attachments from a message
|
|
135
|
-
*/
|
|
136
|
-
export async function downloadAll(messageId: string, outputDir?: string, options: OutputOptions = {}): Promise<DownloadAllResult> {
|
|
137
|
-
const gmail = await getGmail();
|
|
138
|
-
|
|
139
|
-
// Ensure output directory exists
|
|
140
|
-
const dir = outputDir || '.';
|
|
141
|
-
if (!fs.existsSync(dir)) {
|
|
142
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Get message
|
|
146
|
-
const res = await gmail.users.messages.get({
|
|
147
|
-
userId: 'me',
|
|
148
|
-
id: messageId,
|
|
149
|
-
format: 'full'
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
const attachments = extractAttachmentInfo(res.data.payload as MessagePayload);
|
|
153
|
-
|
|
154
|
-
if (attachments.length === 0) {
|
|
155
|
-
const result: DownloadAllResult = { ok: true, message: 'No attachments found', downloaded: [] };
|
|
156
|
-
output(result, options);
|
|
157
|
-
return result;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const downloaded: DownloadResult[] = [];
|
|
161
|
-
|
|
162
|
-
for (const attachment of attachments) {
|
|
163
|
-
if (!attachment.attachmentId) continue;
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const attachmentRes = await gmail.users.messages.attachments.get({
|
|
167
|
-
userId: 'me',
|
|
168
|
-
messageId: messageId,
|
|
169
|
-
id: attachment.attachmentId
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
const data = decodeBase64Url(attachmentRes.data.data);
|
|
173
|
-
const savePath = path.join(dir, attachment.filename);
|
|
174
|
-
|
|
175
|
-
fs.writeFileSync(savePath, data, 'binary');
|
|
176
|
-
|
|
177
|
-
downloaded.push({
|
|
178
|
-
ok: true,
|
|
179
|
-
filename: attachment.filename,
|
|
180
|
-
savedTo: path.resolve(savePath),
|
|
181
|
-
size: attachment.size
|
|
182
|
-
});
|
|
183
|
-
} catch (err) {
|
|
184
|
-
downloaded.push({
|
|
185
|
-
ok: false,
|
|
186
|
-
filename: attachment.filename,
|
|
187
|
-
error: (err as Error).message
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const result: DownloadAllResult = {
|
|
193
|
-
ok: true,
|
|
194
|
-
downloaded
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
output(result, options);
|
|
198
|
-
return result;
|
|
199
|
-
}
|
package/lib/drafts.ts
DELETED
|
@@ -1,434 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { getGmail, output, formatDate, encodeBase64Url, extractHeaders, extractBody, type OutputOptions, type MessagePayload } from './api';
|
|
4
|
-
|
|
5
|
-
/** Attachment path */
|
|
6
|
-
export interface AttachmentPath {
|
|
7
|
-
path: string;
|
|
8
|
-
mimeType?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Draft data */
|
|
12
|
-
export interface DraftData {
|
|
13
|
-
id: string;
|
|
14
|
-
messageId?: string;
|
|
15
|
-
threadId?: string;
|
|
16
|
-
to: string;
|
|
17
|
-
cc?: string | null;
|
|
18
|
-
subject: string;
|
|
19
|
-
snippet?: string;
|
|
20
|
-
body?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Draft result */
|
|
24
|
-
export interface DraftResult {
|
|
25
|
-
ok: boolean;
|
|
26
|
-
id?: string;
|
|
27
|
-
messageId?: string;
|
|
28
|
-
threadId?: string;
|
|
29
|
-
to?: string;
|
|
30
|
-
subject?: string;
|
|
31
|
-
inReplyTo?: string;
|
|
32
|
-
deleted?: string;
|
|
33
|
-
sentDraft?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Draft options */
|
|
37
|
-
export interface DraftOptions extends OutputOptions {
|
|
38
|
-
cc?: string;
|
|
39
|
-
bcc?: string;
|
|
40
|
-
attach?: string | string[];
|
|
41
|
-
limit?: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Create multipart message with attachment
|
|
46
|
-
*/
|
|
47
|
-
export function createMultipartMessage({ to, cc, bcc, subject, body, inReplyTo, references, attachments = [] }: {
|
|
48
|
-
to: string;
|
|
49
|
-
cc?: string;
|
|
50
|
-
bcc?: string;
|
|
51
|
-
subject: string;
|
|
52
|
-
body: string;
|
|
53
|
-
inReplyTo?: string;
|
|
54
|
-
references?: string;
|
|
55
|
-
attachments?: AttachmentPath[];
|
|
56
|
-
}): string {
|
|
57
|
-
const boundary = `boundary_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
58
|
-
|
|
59
|
-
const headers: string[] = [];
|
|
60
|
-
headers.push(`To: ${to}`);
|
|
61
|
-
if (cc) headers.push(`Cc: ${cc}`);
|
|
62
|
-
if (bcc) headers.push(`Bcc: ${bcc}`);
|
|
63
|
-
headers.push(`Subject: ${subject}`);
|
|
64
|
-
if (inReplyTo) {
|
|
65
|
-
headers.push(`In-Reply-To: ${inReplyTo}`);
|
|
66
|
-
headers.push(`References: ${references || inReplyTo}`);
|
|
67
|
-
}
|
|
68
|
-
headers.push('MIME-Version: 1.0');
|
|
69
|
-
headers.push(`Content-Type: multipart/mixed; boundary="${boundary}"`);
|
|
70
|
-
|
|
71
|
-
const parts: string[] = [];
|
|
72
|
-
|
|
73
|
-
// Text body part
|
|
74
|
-
parts.push(`--${boundary}`);
|
|
75
|
-
parts.push('Content-Type: text/plain; charset=utf-8');
|
|
76
|
-
parts.push('Content-Transfer-Encoding: 7bit');
|
|
77
|
-
parts.push('');
|
|
78
|
-
parts.push(body);
|
|
79
|
-
|
|
80
|
-
// Attachment parts
|
|
81
|
-
for (const att of attachments) {
|
|
82
|
-
const filename = path.basename(att.path);
|
|
83
|
-
const content = fs.readFileSync(att.path);
|
|
84
|
-
const base64Content = content.toString('base64');
|
|
85
|
-
const mimeType = att.mimeType || getMimeType(filename);
|
|
86
|
-
|
|
87
|
-
parts.push(`--${boundary}`);
|
|
88
|
-
parts.push(`Content-Type: ${mimeType}; name="${filename}"`);
|
|
89
|
-
parts.push('Content-Transfer-Encoding: base64');
|
|
90
|
-
parts.push(`Content-Disposition: attachment; filename="${filename}"`);
|
|
91
|
-
parts.push('');
|
|
92
|
-
// Split base64 into lines of 76 chars
|
|
93
|
-
for (let i = 0; i < base64Content.length; i += 76) {
|
|
94
|
-
parts.push(base64Content.slice(i, i + 76));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
parts.push(`--${boundary}--`);
|
|
99
|
-
|
|
100
|
-
const message = headers.join('\r\n') + '\r\n\r\n' + parts.join('\r\n');
|
|
101
|
-
return encodeBase64Url(message);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Simple message without attachments
|
|
106
|
-
*/
|
|
107
|
-
export function createSimpleMessage({ to, cc, bcc, subject, body, inReplyTo, references }: {
|
|
108
|
-
to: string;
|
|
109
|
-
cc?: string;
|
|
110
|
-
bcc?: string;
|
|
111
|
-
subject: string;
|
|
112
|
-
body: string;
|
|
113
|
-
inReplyTo?: string;
|
|
114
|
-
references?: string;
|
|
115
|
-
}): string {
|
|
116
|
-
const lines: string[] = [];
|
|
117
|
-
lines.push(`To: ${to}`);
|
|
118
|
-
if (cc) lines.push(`Cc: ${cc}`);
|
|
119
|
-
if (bcc) lines.push(`Bcc: ${bcc}`);
|
|
120
|
-
lines.push(`Subject: ${subject}`);
|
|
121
|
-
lines.push('Content-Type: text/plain; charset=utf-8');
|
|
122
|
-
if (inReplyTo) {
|
|
123
|
-
lines.push(`In-Reply-To: ${inReplyTo}`);
|
|
124
|
-
lines.push(`References: ${references || inReplyTo}`);
|
|
125
|
-
}
|
|
126
|
-
lines.push('');
|
|
127
|
-
lines.push(body);
|
|
128
|
-
return encodeBase64Url(lines.join('\r\n'));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Get MIME type from filename
|
|
133
|
-
*/
|
|
134
|
-
function getMimeType(filename: string): string {
|
|
135
|
-
const ext = path.extname(filename).toLowerCase();
|
|
136
|
-
const mimeTypes: Record<string, string> = {
|
|
137
|
-
'.pdf': 'application/pdf',
|
|
138
|
-
'.doc': 'application/msword',
|
|
139
|
-
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
140
|
-
'.xls': 'application/vnd.ms-excel',
|
|
141
|
-
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
142
|
-
'.png': 'image/png',
|
|
143
|
-
'.jpg': 'image/jpeg',
|
|
144
|
-
'.jpeg': 'image/jpeg',
|
|
145
|
-
'.gif': 'image/gif',
|
|
146
|
-
'.txt': 'text/plain',
|
|
147
|
-
'.csv': 'text/csv',
|
|
148
|
-
'.zip': 'application/zip',
|
|
149
|
-
'.json': 'application/json',
|
|
150
|
-
'.xml': 'application/xml',
|
|
151
|
-
'.html': 'text/html'
|
|
152
|
-
};
|
|
153
|
-
return mimeTypes[ext] || 'application/octet-stream';
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* List all drafts
|
|
158
|
-
*/
|
|
159
|
-
export async function list(options: DraftOptions = {}): Promise<DraftData[]> {
|
|
160
|
-
const gmail = await getGmail();
|
|
161
|
-
const limit = options.limit || 20;
|
|
162
|
-
|
|
163
|
-
const res = await gmail.users.drafts.list({
|
|
164
|
-
userId: 'me',
|
|
165
|
-
maxResults: limit
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
if (!res.data.drafts || res.data.drafts.length === 0) {
|
|
169
|
-
output([], options);
|
|
170
|
-
return [];
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Get details for each draft
|
|
174
|
-
const drafts = await Promise.all(
|
|
175
|
-
res.data.drafts.map(async (draft) => {
|
|
176
|
-
const draftRes = await gmail.users.drafts.get({
|
|
177
|
-
userId: 'me',
|
|
178
|
-
id: draft.id!,
|
|
179
|
-
format: 'metadata',
|
|
180
|
-
metadataHeaders: ['To', 'Subject', 'Date']
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const headers = extractHeaders(draftRes.data.message?.payload?.headers as Array<{ name: string; value: string }>);
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
id: draft.id!,
|
|
187
|
-
messageId: draftRes.data.message?.id,
|
|
188
|
-
to: headers.to || '(no recipient)',
|
|
189
|
-
subject: headers.subject || '(no subject)',
|
|
190
|
-
snippet: draftRes.data.message?.snippet || ''
|
|
191
|
-
};
|
|
192
|
-
})
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
output(drafts, options);
|
|
196
|
-
return drafts;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Get a draft
|
|
201
|
-
*/
|
|
202
|
-
export async function get(draftId: string, options: OutputOptions = {}): Promise<DraftData> {
|
|
203
|
-
const gmail = await getGmail();
|
|
204
|
-
|
|
205
|
-
const res = await gmail.users.drafts.get({
|
|
206
|
-
userId: 'me',
|
|
207
|
-
id: draftId,
|
|
208
|
-
format: 'full'
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
const headers = extractHeaders(res.data.message?.payload?.headers as Array<{ name: string; value: string }>);
|
|
212
|
-
const body = extractBody(res.data.message?.payload as MessagePayload);
|
|
213
|
-
|
|
214
|
-
const draft: DraftData = {
|
|
215
|
-
id: res.data.id!,
|
|
216
|
-
messageId: res.data.message?.id,
|
|
217
|
-
threadId: res.data.message?.threadId || undefined,
|
|
218
|
-
to: headers.to || '',
|
|
219
|
-
cc: headers.cc || null,
|
|
220
|
-
subject: headers.subject || '(no subject)',
|
|
221
|
-
body: body
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
output(draft, options);
|
|
225
|
-
return draft;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Create a new draft
|
|
230
|
-
*/
|
|
231
|
-
export async function create(to: string, subject: string, body: string, options: DraftOptions = {}): Promise<DraftResult> {
|
|
232
|
-
const gmail = await getGmail();
|
|
233
|
-
|
|
234
|
-
body = body.replace(/\\n/g, '\n');
|
|
235
|
-
|
|
236
|
-
let raw: string;
|
|
237
|
-
if (options.attach) {
|
|
238
|
-
const attachments = Array.isArray(options.attach) ? options.attach : [options.attach];
|
|
239
|
-
const attachmentPaths = attachments.map(a => ({ path: a }));
|
|
240
|
-
raw = createMultipartMessage({
|
|
241
|
-
to,
|
|
242
|
-
cc: options.cc,
|
|
243
|
-
bcc: options.bcc,
|
|
244
|
-
subject,
|
|
245
|
-
body,
|
|
246
|
-
attachments: attachmentPaths
|
|
247
|
-
});
|
|
248
|
-
} else {
|
|
249
|
-
raw = createSimpleMessage({
|
|
250
|
-
to,
|
|
251
|
-
cc: options.cc,
|
|
252
|
-
bcc: options.bcc,
|
|
253
|
-
subject,
|
|
254
|
-
body
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const res = await gmail.users.drafts.create({
|
|
259
|
-
userId: 'me',
|
|
260
|
-
requestBody: {
|
|
261
|
-
message: { raw }
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
const result: DraftResult = {
|
|
266
|
-
ok: true,
|
|
267
|
-
id: res.data.id || undefined,
|
|
268
|
-
messageId: res.data.message?.id,
|
|
269
|
-
to,
|
|
270
|
-
subject
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
output(result, options);
|
|
274
|
-
return result;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Create a draft reply to a message
|
|
279
|
-
*/
|
|
280
|
-
export async function createReply(messageId: string, body: string, options: DraftOptions = {}): Promise<DraftResult> {
|
|
281
|
-
const gmail = await getGmail();
|
|
282
|
-
|
|
283
|
-
// Get original message
|
|
284
|
-
const original = await gmail.users.messages.get({
|
|
285
|
-
userId: 'me',
|
|
286
|
-
id: messageId,
|
|
287
|
-
format: 'metadata',
|
|
288
|
-
metadataHeaders: ['From', 'To', 'Subject', 'Message-ID', 'References']
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
const headers = extractHeaders(original.data.payload?.headers as Array<{ name: string; value: string }>);
|
|
292
|
-
|
|
293
|
-
// Determine reply-to address
|
|
294
|
-
const replyTo = headers.from!;
|
|
295
|
-
let subject = headers.subject || '';
|
|
296
|
-
if (!subject.toLowerCase().startsWith('re:')) {
|
|
297
|
-
subject = `Re: ${subject}`;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
body = body.replace(/\\n/g, '\n');
|
|
301
|
-
|
|
302
|
-
let raw: string;
|
|
303
|
-
if (options.attach) {
|
|
304
|
-
const attachments = Array.isArray(options.attach) ? options.attach : [options.attach];
|
|
305
|
-
const attachmentPaths = attachments.map(a => ({ path: a }));
|
|
306
|
-
raw = createMultipartMessage({
|
|
307
|
-
to: replyTo,
|
|
308
|
-
subject,
|
|
309
|
-
body,
|
|
310
|
-
inReplyTo: headers.message_id,
|
|
311
|
-
references: headers.references ? `${headers.references} ${headers.message_id}` : headers.message_id,
|
|
312
|
-
attachments: attachmentPaths
|
|
313
|
-
});
|
|
314
|
-
} else {
|
|
315
|
-
raw = createSimpleMessage({
|
|
316
|
-
to: replyTo,
|
|
317
|
-
subject,
|
|
318
|
-
body,
|
|
319
|
-
inReplyTo: headers.message_id,
|
|
320
|
-
references: headers.references ? `${headers.references} ${headers.message_id}` : headers.message_id
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const res = await gmail.users.drafts.create({
|
|
325
|
-
userId: 'me',
|
|
326
|
-
requestBody: {
|
|
327
|
-
message: {
|
|
328
|
-
raw,
|
|
329
|
-
threadId: original.data.threadId || undefined
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
const result: DraftResult = {
|
|
335
|
-
ok: true,
|
|
336
|
-
id: res.data.id || undefined,
|
|
337
|
-
messageId: res.data.message?.id,
|
|
338
|
-
threadId: original.data.threadId || undefined,
|
|
339
|
-
to: replyTo,
|
|
340
|
-
subject,
|
|
341
|
-
inReplyTo: messageId
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
output(result, options);
|
|
345
|
-
return result;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Update a draft
|
|
350
|
-
*/
|
|
351
|
-
export async function update(draftId: string, to: string, subject: string, body: string, options: DraftOptions = {}): Promise<DraftResult> {
|
|
352
|
-
const gmail = await getGmail();
|
|
353
|
-
|
|
354
|
-
body = body.replace(/\\n/g, '\n');
|
|
355
|
-
|
|
356
|
-
let raw: string;
|
|
357
|
-
if (options.attach) {
|
|
358
|
-
const attachments = Array.isArray(options.attach) ? options.attach : [options.attach];
|
|
359
|
-
const attachmentPaths = attachments.map(a => ({ path: a }));
|
|
360
|
-
raw = createMultipartMessage({
|
|
361
|
-
to,
|
|
362
|
-
cc: options.cc,
|
|
363
|
-
subject,
|
|
364
|
-
body,
|
|
365
|
-
attachments: attachmentPaths
|
|
366
|
-
});
|
|
367
|
-
} else {
|
|
368
|
-
raw = createSimpleMessage({
|
|
369
|
-
to,
|
|
370
|
-
cc: options.cc,
|
|
371
|
-
subject,
|
|
372
|
-
body
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const res = await gmail.users.drafts.update({
|
|
377
|
-
userId: 'me',
|
|
378
|
-
id: draftId,
|
|
379
|
-
requestBody: {
|
|
380
|
-
message: { raw }
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
const result: DraftResult = {
|
|
385
|
-
ok: true,
|
|
386
|
-
id: res.data.id || undefined,
|
|
387
|
-
messageId: res.data.message?.id,
|
|
388
|
-
to,
|
|
389
|
-
subject
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
output(result, options);
|
|
393
|
-
return result;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Delete a draft
|
|
398
|
-
*/
|
|
399
|
-
export async function remove(draftId: string, options: OutputOptions = {}): Promise<DraftResult> {
|
|
400
|
-
const gmail = await getGmail();
|
|
401
|
-
|
|
402
|
-
await gmail.users.drafts.delete({
|
|
403
|
-
userId: 'me',
|
|
404
|
-
id: draftId
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
const result: DraftResult = { ok: true, deleted: draftId };
|
|
408
|
-
output(result, options);
|
|
409
|
-
return result;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Send a draft
|
|
414
|
-
*/
|
|
415
|
-
export async function send(draftId: string, options: OutputOptions = {}): Promise<DraftResult> {
|
|
416
|
-
const gmail = await getGmail();
|
|
417
|
-
|
|
418
|
-
const res = await gmail.users.drafts.send({
|
|
419
|
-
userId: 'me',
|
|
420
|
-
requestBody: {
|
|
421
|
-
id: draftId
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
const result: DraftResult = {
|
|
426
|
-
ok: true,
|
|
427
|
-
id: res.data.id || undefined,
|
|
428
|
-
threadId: res.data.threadId || undefined,
|
|
429
|
-
sentDraft: draftId
|
|
430
|
-
};
|
|
431
|
-
|
|
432
|
-
output(result, options);
|
|
433
|
-
return result;
|
|
434
|
-
}
|