dooray-mail-cli 0.1.1 → 0.1.2
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/SKILL.md +24 -0
- package/dist/mail-client.js +7 -2
- package/dooray-cli.js +115 -0
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -122,6 +122,30 @@ You'll be prompted for:
|
|
|
122
122
|
- Subject: email subject
|
|
123
123
|
- Body: email content (press Ctrl+D when done)
|
|
124
124
|
|
|
125
|
+
### Reply to an Email
|
|
126
|
+
|
|
127
|
+
**Non-interactive (recommended for automation):**
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
dooray-cli reply 123 --body "Thank you for your email."
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Interactive mode:**
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
dooray-cli reply 123
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**How it works:**
|
|
140
|
+
1. First, read the email using `dooray-cli read <uid>`
|
|
141
|
+
2. The email information (Message-ID, sender, subject) is cached automatically
|
|
142
|
+
3. Use `reply <uid>` to send a reply
|
|
143
|
+
4. The reply will:
|
|
144
|
+
- Automatically set recipient to original sender
|
|
145
|
+
- Add "Re:" prefix to subject (if not already present)
|
|
146
|
+
- Include proper email threading headers (In-Reply-To, References)
|
|
147
|
+
- Appear in the same conversation thread in email clients
|
|
148
|
+
|
|
125
149
|
### Search Emails
|
|
126
150
|
|
|
127
151
|
Search for emails by keywords in subject:
|
package/dist/mail-client.js
CHANGED
|
@@ -232,7 +232,9 @@ class DoorayMailClient {
|
|
|
232
232
|
html: params.html ? params.body : undefined,
|
|
233
233
|
cc: params.cc?.join(', '),
|
|
234
234
|
bcc: params.bcc?.join(', '),
|
|
235
|
-
attachments: params.attachments
|
|
235
|
+
attachments: params.attachments,
|
|
236
|
+
inReplyTo: params.inReplyTo || undefined,
|
|
237
|
+
references: params.references || undefined
|
|
236
238
|
};
|
|
237
239
|
const info = await this.smtpTransporter.sendMail(mailOptions);
|
|
238
240
|
console.log('[Dooray] Mail sent:', info.messageId);
|
|
@@ -320,6 +322,7 @@ class DoorayMailClient {
|
|
|
320
322
|
const parsed = await (0, mailparser_1.simpleParser)(all.body);
|
|
321
323
|
return {
|
|
322
324
|
id: uid.toString(),
|
|
325
|
+
messageId: parsed.messageId || '',
|
|
323
326
|
subject: parsed.subject || '(제목 없음)',
|
|
324
327
|
from: {
|
|
325
328
|
name: parsed.from?.value[0]?.name || parsed.from?.text || '알 수 없음',
|
|
@@ -333,7 +336,9 @@ class DoorayMailClient {
|
|
|
333
336
|
bodyHtml: parsed.html || undefined,
|
|
334
337
|
receivedAt: parsed.date?.toISOString() || new Date().toISOString(),
|
|
335
338
|
isRead: flags.includes('\\Seen'),
|
|
336
|
-
hasAttachments: (parsed.attachments?.length || 0) > 0
|
|
339
|
+
hasAttachments: (parsed.attachments?.length || 0) > 0,
|
|
340
|
+
inReplyTo: parsed.inReplyTo || '',
|
|
341
|
+
references: parsed.references || []
|
|
337
342
|
};
|
|
338
343
|
}
|
|
339
344
|
catch (error) {
|
package/dooray-cli.js
CHANGED
|
@@ -12,6 +12,7 @@ const path = require('path');
|
|
|
12
12
|
const os = require('os');
|
|
13
13
|
|
|
14
14
|
const CONFIG_PATH = path.join(os.homedir(), '.dooray-config.json');
|
|
15
|
+
const CACHE_PATH = path.join(os.homedir(), '.dooray-mail-cache.json');
|
|
15
16
|
|
|
16
17
|
// CLI 명령어 파서
|
|
17
18
|
const args = process.argv.slice(2);
|
|
@@ -31,6 +32,23 @@ function saveConfig(config) {
|
|
|
31
32
|
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
// 메일 캐시 로드
|
|
36
|
+
function loadCache() {
|
|
37
|
+
if (!fs.existsSync(CACHE_PATH)) {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(fs.readFileSync(CACHE_PATH, 'utf-8'));
|
|
42
|
+
} catch {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 메일 캐시 저장
|
|
48
|
+
function saveCache(cache) {
|
|
49
|
+
fs.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2), 'utf-8');
|
|
50
|
+
}
|
|
51
|
+
|
|
34
52
|
// 메일 클라이언트 생성
|
|
35
53
|
async function createClient(config) {
|
|
36
54
|
const security = new SecurityManager();
|
|
@@ -62,6 +80,9 @@ async function handleCommand() {
|
|
|
62
80
|
case 'send':
|
|
63
81
|
await sendCommand(args.slice(1));
|
|
64
82
|
break;
|
|
83
|
+
case 'reply':
|
|
84
|
+
await replyCommand(args[1], args.slice(2));
|
|
85
|
+
break;
|
|
65
86
|
case 'search':
|
|
66
87
|
await searchCommand(args.slice(1));
|
|
67
88
|
break;
|
|
@@ -184,12 +205,23 @@ async function readCommand(uid) {
|
|
|
184
205
|
process.exit(1);
|
|
185
206
|
}
|
|
186
207
|
|
|
208
|
+
// 메일 정보 캐시에 저장 (답장용)
|
|
209
|
+
const cache = loadCache();
|
|
210
|
+
cache[uid] = {
|
|
211
|
+
messageId: mail.messageId,
|
|
212
|
+
subject: mail.subject,
|
|
213
|
+
from: mail.from,
|
|
214
|
+
references: mail.references || []
|
|
215
|
+
};
|
|
216
|
+
saveCache(cache);
|
|
217
|
+
|
|
187
218
|
const fromText = typeof mail.from === 'object' ? `${mail.from.name} <${mail.from.email}>` : mail.from;
|
|
188
219
|
const toText = Array.isArray(mail.to)
|
|
189
220
|
? mail.to.map(t => typeof t === 'object' ? `${t.name} <${t.email}>` : t).join(', ')
|
|
190
221
|
: mail.to;
|
|
191
222
|
|
|
192
223
|
console.log('\n📧 Mail Details\n');
|
|
224
|
+
console.log(`UID: ${uid}`);
|
|
193
225
|
console.log(`From: ${fromText}`);
|
|
194
226
|
console.log(`To: ${toText}`);
|
|
195
227
|
console.log(`Subject: ${mail.subject}`);
|
|
@@ -204,6 +236,84 @@ async function readCommand(uid) {
|
|
|
204
236
|
}
|
|
205
237
|
}
|
|
206
238
|
|
|
239
|
+
// reply: 답장 보내기
|
|
240
|
+
async function replyCommand(uid, args) {
|
|
241
|
+
if (!uid) {
|
|
242
|
+
console.error('❌ Usage: dooray-cli reply <uid> [--body "message"]');
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 캐시에서 원본 메일 정보 로드
|
|
247
|
+
const cache = loadCache();
|
|
248
|
+
const originalMail = cache[uid];
|
|
249
|
+
|
|
250
|
+
if (!originalMail) {
|
|
251
|
+
console.error('❌ Mail not found in cache. Please read the mail first using: dooray-cli read ' + uid);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// --body 옵션 파싱
|
|
256
|
+
let body;
|
|
257
|
+
for (let i = 0; i < args.length; i++) {
|
|
258
|
+
if (args[i] === '--body' && args[i + 1]) {
|
|
259
|
+
body = args[++i];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const config = loadConfig();
|
|
264
|
+
const client = await createClient(config);
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
// 인터랙티브 모드
|
|
268
|
+
if (!body) {
|
|
269
|
+
const readline = require('readline').createInterface({
|
|
270
|
+
input: process.stdin,
|
|
271
|
+
output: process.stdout
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
console.log(`\n💬 Reply to: ${originalMail.from.name} <${originalMail.from.email}>`);
|
|
275
|
+
console.log(` Subject: Re: ${originalMail.subject}\n`);
|
|
276
|
+
console.log('Body (type message, press Ctrl+D when done):');
|
|
277
|
+
|
|
278
|
+
body = '';
|
|
279
|
+
readline.on('line', (line) => {
|
|
280
|
+
body += line + '\n';
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
await new Promise(resolve => readline.on('close', resolve));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 답장 제목 생성
|
|
287
|
+
const replySubject = originalMail.subject.startsWith('Re:')
|
|
288
|
+
? originalMail.subject
|
|
289
|
+
: `Re: ${originalMail.subject}`;
|
|
290
|
+
|
|
291
|
+
// References 헤더 생성
|
|
292
|
+
const references = [...(originalMail.references || [])];
|
|
293
|
+
if (originalMail.messageId && !references.includes(originalMail.messageId)) {
|
|
294
|
+
references.push(originalMail.messageId);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 답장 발송
|
|
298
|
+
await client.sendMail({
|
|
299
|
+
to: originalMail.from.email,
|
|
300
|
+
subject: replySubject,
|
|
301
|
+
text: body.trim(),
|
|
302
|
+
inReplyTo: originalMail.messageId,
|
|
303
|
+
references: references.join(' ')
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
console.log('\n✅ Reply sent successfully');
|
|
307
|
+
console.log(` To: ${originalMail.from.name} <${originalMail.from.email}>`);
|
|
308
|
+
console.log(` Subject: ${replySubject}`);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error('❌ Error:', error.message);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
} finally {
|
|
313
|
+
await client.close();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
207
317
|
// send: 메일 발송
|
|
208
318
|
async function sendCommand(args) {
|
|
209
319
|
// 비인터랙티브 모드: --to, --subject, --body 옵션 사용
|
|
@@ -353,6 +463,9 @@ Commands:
|
|
|
353
463
|
send Send a new mail (interactive)
|
|
354
464
|
send --to <email> --subject <subject> --body <body>
|
|
355
465
|
Send a mail (non-interactive)
|
|
466
|
+
reply <uid> Reply to a mail (interactive)
|
|
467
|
+
reply <uid> --body <body>
|
|
468
|
+
Reply to a mail (non-interactive)
|
|
356
469
|
search <keywords> Search mails by keywords
|
|
357
470
|
unread Show unread mail count
|
|
358
471
|
test Test IMAP/SMTP connection
|
|
@@ -364,6 +477,8 @@ Examples:
|
|
|
364
477
|
dooray-cli recent
|
|
365
478
|
dooray-cli read 123
|
|
366
479
|
dooray-cli send --to "user@example.com" --subject "Hello" --body "Test message"
|
|
480
|
+
dooray-cli reply 123
|
|
481
|
+
dooray-cli reply 123 --body "Thank you for your email."
|
|
367
482
|
dooray-cli search "meeting" "report"
|
|
368
483
|
dooray-cli unread
|
|
369
484
|
|