@bifos/dooray-cli 0.1.1 → 0.2.0
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/README.md +26 -0
- package/dist/index.js +374 -14
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -82,6 +82,32 @@ dooray wiki pages tc-ocr # 페이지 목록
|
|
|
82
82
|
dooray wiki page get tc-ocr <page-id> # 페이지 상세
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
### 메일
|
|
86
|
+
|
|
87
|
+
IMAP을 통해 Dooray 메일을 조회할 수 있습니다.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# 초기 설정 (서버 정보는 기본값 제공)
|
|
91
|
+
dooray config set imap-username your@email.com
|
|
92
|
+
dooray config set imap-password <IMAP_APP_PASSWORD>
|
|
93
|
+
|
|
94
|
+
# 메일 조회
|
|
95
|
+
dooray mail list # 최근 메일 목록
|
|
96
|
+
dooray mail list --unread # 안읽은 메일만
|
|
97
|
+
dooray mail list --search "키워드" # 제목 검색
|
|
98
|
+
dooray mail list --size 50 # 조회 개수 지정
|
|
99
|
+
dooray mail get <uid> # 메일 상세
|
|
100
|
+
dooray mail get <uid> --json # JSON 출력
|
|
101
|
+
|
|
102
|
+
# 메일 발송
|
|
103
|
+
dooray mail send --to "user@example.com" --subject "제목" --body "본문"
|
|
104
|
+
dooray mail send --to "a@b.com" --cc "c@d.com" --subject "제목" --body-file ./content.md
|
|
105
|
+
dooray mail send --to "a@b.com" --subject "HTML 메일" --body "<h1>Hello</h1>" --html
|
|
106
|
+
|
|
107
|
+
# 메일 답장 (스레드 유지)
|
|
108
|
+
dooray mail reply <uid> --body "답장 내용"
|
|
109
|
+
```
|
|
110
|
+
|
|
85
111
|
## 출력 모드
|
|
86
112
|
|
|
87
113
|
| 플래그 | 설명 | 용도 |
|
package/dist/index.js
CHANGED
|
@@ -24,8 +24,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
27
|
+
var import_commander27 = require("commander");
|
|
28
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
29
29
|
|
|
30
30
|
// src/commands/config.ts
|
|
31
31
|
var import_commander = require("commander");
|
|
@@ -52,6 +52,15 @@ var EXIT_AUTH_ERROR = 2;
|
|
|
52
52
|
var EXIT_PARAM_ERROR = 3;
|
|
53
53
|
var EXIT_CONFIG_ERROR = 4;
|
|
54
54
|
|
|
55
|
+
// src/config/types.ts
|
|
56
|
+
var DEFAULTS = {
|
|
57
|
+
baseUrl: "https://api.dooray.com",
|
|
58
|
+
imapHost: "imap.dooray.com",
|
|
59
|
+
imapPort: 993,
|
|
60
|
+
smtpHost: "smtp.dooray.com",
|
|
61
|
+
smtpPort: 465
|
|
62
|
+
};
|
|
63
|
+
|
|
55
64
|
// src/config/store.ts
|
|
56
65
|
var DOORAY_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".dooray");
|
|
57
66
|
var CONFIG_PATH = (0, import_node_path.join)(DOORAY_DIR, "config.json");
|
|
@@ -81,7 +90,7 @@ async function setConfigValue(key, value) {
|
|
|
81
90
|
const config = await getConfig() ?? {
|
|
82
91
|
version: 1,
|
|
83
92
|
apiKey: "",
|
|
84
|
-
baseUrl:
|
|
93
|
+
baseUrl: DEFAULTS.baseUrl
|
|
85
94
|
};
|
|
86
95
|
switch (key) {
|
|
87
96
|
case "api-key":
|
|
@@ -90,10 +99,28 @@ async function setConfigValue(key, value) {
|
|
|
90
99
|
case "base-url":
|
|
91
100
|
config.baseUrl = value;
|
|
92
101
|
break;
|
|
102
|
+
case "imap-host":
|
|
103
|
+
config.imapHost = value;
|
|
104
|
+
break;
|
|
105
|
+
case "imap-port":
|
|
106
|
+
config.imapPort = parseInt(value, 10);
|
|
107
|
+
break;
|
|
108
|
+
case "imap-username":
|
|
109
|
+
config.imapUsername = value;
|
|
110
|
+
break;
|
|
111
|
+
case "imap-password":
|
|
112
|
+
config.imapPassword = value;
|
|
113
|
+
break;
|
|
114
|
+
case "smtp-host":
|
|
115
|
+
config.smtpHost = value;
|
|
116
|
+
break;
|
|
117
|
+
case "smtp-port":
|
|
118
|
+
config.smtpPort = parseInt(value, 10);
|
|
119
|
+
break;
|
|
93
120
|
default:
|
|
94
121
|
throw new DoorayCliError(
|
|
95
122
|
`\uC54C \uC218 \uC5C6\uB294 \uC124\uC815 \uD0A4: ${key}
|
|
96
|
-
\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uD0A4: api-key, base-url`,
|
|
123
|
+
\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uD0A4: api-key, base-url, imap-host, imap-port, imap-username, imap-password, smtp-host, smtp-port`,
|
|
97
124
|
EXIT_CONFIG_ERROR
|
|
98
125
|
);
|
|
99
126
|
}
|
|
@@ -1555,20 +1582,347 @@ var wikiPageEditCommand = new import_commander22.Command("edit").description("\u
|
|
|
1555
1582
|
`);
|
|
1556
1583
|
});
|
|
1557
1584
|
|
|
1585
|
+
// src/commands/mail/list.ts
|
|
1586
|
+
var import_commander23 = require("commander");
|
|
1587
|
+
|
|
1588
|
+
// src/api/imapClient.ts
|
|
1589
|
+
var import_imapflow = require("imapflow");
|
|
1590
|
+
var import_mailparser = require("mailparser");
|
|
1591
|
+
function getImapConfigOrThrow(config) {
|
|
1592
|
+
if (!config.imapUsername || !config.imapPassword) {
|
|
1593
|
+
throw new DoorayCliError(
|
|
1594
|
+
"IMAP \uC124\uC815\uC774 \uC644\uB8CC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uC124\uC815\uC744 \uC9C4\uD589\uD558\uC138\uC694:\n dooray config set imap-username <YOUR_EMAIL>\n dooray config set imap-password <YOUR_IMAP_PASSWORD>",
|
|
1595
|
+
EXIT_CONFIG_ERROR
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
return {
|
|
1599
|
+
host: config.imapHost ?? DEFAULTS.imapHost,
|
|
1600
|
+
port: config.imapPort ?? DEFAULTS.imapPort,
|
|
1601
|
+
username: config.imapUsername,
|
|
1602
|
+
password: config.imapPassword
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
function createClient(config) {
|
|
1606
|
+
const imap = getImapConfigOrThrow(config);
|
|
1607
|
+
return new import_imapflow.ImapFlow({
|
|
1608
|
+
host: imap.host,
|
|
1609
|
+
port: imap.port,
|
|
1610
|
+
secure: true,
|
|
1611
|
+
auth: { user: imap.username, pass: imap.password },
|
|
1612
|
+
logger: false
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
async function listMails(config, opts) {
|
|
1616
|
+
const client = createClient(config);
|
|
1617
|
+
const limit = opts.limit ?? 20;
|
|
1618
|
+
try {
|
|
1619
|
+
await client.connect();
|
|
1620
|
+
const lock = await client.getMailboxLock("INBOX");
|
|
1621
|
+
try {
|
|
1622
|
+
const query = {};
|
|
1623
|
+
if (opts.unread) query.seen = false;
|
|
1624
|
+
if (opts.search) query.subject = opts.search;
|
|
1625
|
+
if (Object.keys(query).length === 0) query.all = true;
|
|
1626
|
+
const uids = await client.search(query, { uid: true });
|
|
1627
|
+
if (uids.length === 0) return [];
|
|
1628
|
+
const sorted = uids.sort((a, b) => b - a).slice(0, limit);
|
|
1629
|
+
const uidSet = sorted.join(",");
|
|
1630
|
+
const messages = [];
|
|
1631
|
+
for await (const msg of client.fetch(uidSet, {
|
|
1632
|
+
uid: true,
|
|
1633
|
+
flags: true,
|
|
1634
|
+
envelope: true
|
|
1635
|
+
}, { uid: true })) {
|
|
1636
|
+
messages.push({
|
|
1637
|
+
uid: msg.uid,
|
|
1638
|
+
subject: msg.envelope.subject ?? "(\uC81C\uBAA9 \uC5C6\uC74C)",
|
|
1639
|
+
from: msg.envelope.from?.[0] ? `${msg.envelope.from[0].name || ""} <${msg.envelope.from[0].address || ""}>` : "(unknown)",
|
|
1640
|
+
to: (msg.envelope.to ?? []).map(
|
|
1641
|
+
(t) => `${t.name || ""} <${t.address || ""}>`
|
|
1642
|
+
),
|
|
1643
|
+
date: msg.envelope.date ?? null,
|
|
1644
|
+
isRead: msg.flags.has("\\Seen")
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
messages.sort((a, b) => b.uid - a.uid);
|
|
1648
|
+
return messages;
|
|
1649
|
+
} finally {
|
|
1650
|
+
lock.release();
|
|
1651
|
+
}
|
|
1652
|
+
} finally {
|
|
1653
|
+
await client.logout();
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
async function getMail(config, uid) {
|
|
1657
|
+
const client = createClient(config);
|
|
1658
|
+
try {
|
|
1659
|
+
await client.connect();
|
|
1660
|
+
const lock = await client.getMailboxLock("INBOX");
|
|
1661
|
+
try {
|
|
1662
|
+
const msg = await client.fetchOne(String(uid), {
|
|
1663
|
+
uid: true,
|
|
1664
|
+
flags: true,
|
|
1665
|
+
envelope: true,
|
|
1666
|
+
source: true
|
|
1667
|
+
}, { uid: true });
|
|
1668
|
+
if (!msg) {
|
|
1669
|
+
throw new DoorayCliError(`\uBA54\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: UID ${uid}`, 1);
|
|
1670
|
+
}
|
|
1671
|
+
const parsed = await (0, import_mailparser.simpleParser)(msg.source);
|
|
1672
|
+
const body = parsed.text ?? parsed.html ?? "(\uBCF8\uBB38 \uC5C6\uC74C)";
|
|
1673
|
+
return {
|
|
1674
|
+
uid: msg.uid,
|
|
1675
|
+
subject: msg.envelope.subject ?? "(\uC81C\uBAA9 \uC5C6\uC74C)",
|
|
1676
|
+
from: msg.envelope.from?.[0] ? `${msg.envelope.from[0].name || ""} <${msg.envelope.from[0].address || ""}>` : "(unknown)",
|
|
1677
|
+
to: (msg.envelope.to ?? []).map(
|
|
1678
|
+
(t) => `${t.name || ""} <${t.address || ""}>`
|
|
1679
|
+
),
|
|
1680
|
+
date: msg.envelope.date ?? null,
|
|
1681
|
+
isRead: msg.flags.has("\\Seen"),
|
|
1682
|
+
body
|
|
1683
|
+
};
|
|
1684
|
+
} finally {
|
|
1685
|
+
lock.release();
|
|
1686
|
+
}
|
|
1687
|
+
} finally {
|
|
1688
|
+
await client.logout();
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
// src/commands/mail/list.ts
|
|
1693
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
1694
|
+
var mailListCommand = new import_commander23.Command("list").description("\uBA54\uC77C \uBAA9\uB85D \uC870\uD68C").option("--unread", "\uC548\uC77D\uC740 \uBA54\uC77C\uB9CC").option("--search <keyword>", "\uC81C\uBAA9 \uAC80\uC0C9").option("--size <number>", "\uC870\uD68C \uAC1C\uC218", "20").action(async (opts) => {
|
|
1695
|
+
const globalOpts = mailListCommand.optsWithGlobals();
|
|
1696
|
+
const config = await getConfigOrThrow();
|
|
1697
|
+
startSpinner("\uBA54\uC77C \uC870\uD68C \uC911...");
|
|
1698
|
+
const mails = await listMails(config, {
|
|
1699
|
+
unread: opts.unread,
|
|
1700
|
+
search: opts.search,
|
|
1701
|
+
limit: Number(opts.size)
|
|
1702
|
+
});
|
|
1703
|
+
stopSpinner(true, `\uBA54\uC77C ${mails.length}\uAC74 \uC870\uD68C \uC644\uB8CC`);
|
|
1704
|
+
output(globalOpts, {
|
|
1705
|
+
headers: ["UID", "\uC77D\uC74C", "\uB0A0\uC9DC", "\uBCF4\uB0B8\uC0AC\uB78C", "\uC81C\uBAA9"],
|
|
1706
|
+
rows: mails.map((m) => [
|
|
1707
|
+
String(m.uid),
|
|
1708
|
+
m.isRead ? "\u2713" : import_chalk4.default.bold("\u25CF"),
|
|
1709
|
+
m.date ? m.date.toLocaleDateString("ko-KR") : "",
|
|
1710
|
+
m.from.replace(/<.*>/, "").trim() || m.from,
|
|
1711
|
+
m.subject
|
|
1712
|
+
]),
|
|
1713
|
+
raw: mails.map((m) => ({
|
|
1714
|
+
uid: m.uid,
|
|
1715
|
+
subject: m.subject,
|
|
1716
|
+
from: m.from,
|
|
1717
|
+
to: m.to,
|
|
1718
|
+
date: m.date?.toISOString() ?? null,
|
|
1719
|
+
isRead: m.isRead
|
|
1720
|
+
})),
|
|
1721
|
+
ids: mails.map((m) => String(m.uid))
|
|
1722
|
+
});
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
// src/commands/mail/get.ts
|
|
1726
|
+
var import_commander24 = require("commander");
|
|
1727
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
1728
|
+
var mailGetCommand = new import_commander24.Command("get").description("\uBA54\uC77C \uC0C1\uC138 \uC870\uD68C").argument("<uid>", "\uBA54\uC77C UID").action(async (uid) => {
|
|
1729
|
+
const globalOpts = mailGetCommand.optsWithGlobals();
|
|
1730
|
+
const config = await getConfigOrThrow();
|
|
1731
|
+
startSpinner("\uBA54\uC77C \uC870\uD68C \uC911...");
|
|
1732
|
+
const mail = await getMail(config, Number(uid));
|
|
1733
|
+
stopSpinner(true, "\uBA54\uC77C \uC870\uD68C \uC644\uB8CC");
|
|
1734
|
+
if (globalOpts.json) {
|
|
1735
|
+
printJson({
|
|
1736
|
+
uid: mail.uid,
|
|
1737
|
+
subject: mail.subject,
|
|
1738
|
+
from: mail.from,
|
|
1739
|
+
to: mail.to,
|
|
1740
|
+
date: mail.date?.toISOString() ?? null,
|
|
1741
|
+
isRead: mail.isRead,
|
|
1742
|
+
body: mail.body
|
|
1743
|
+
});
|
|
1744
|
+
} else {
|
|
1745
|
+
process.stdout.write(
|
|
1746
|
+
`${import_chalk5.default.bold("\uC81C\uBAA9:")} ${mail.subject}
|
|
1747
|
+
${import_chalk5.default.bold("\uBCF4\uB0B8\uC0AC\uB78C:")} ${mail.from}
|
|
1748
|
+
${import_chalk5.default.bold("\uBC1B\uB294\uC0AC\uB78C:")} ${mail.to.join(", ")}
|
|
1749
|
+
${import_chalk5.default.bold("\uB0A0\uC9DC:")} ${mail.date?.toLocaleString("ko-KR") ?? ""}
|
|
1750
|
+
${import_chalk5.default.bold("\uC77D\uC74C:")} ${mail.isRead ? "\uC608" : "\uC544\uB2C8\uC624"}
|
|
1751
|
+
|
|
1752
|
+
${mail.body}
|
|
1753
|
+
`
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
// src/commands/mail/send.ts
|
|
1759
|
+
var import_commander25 = require("commander");
|
|
1760
|
+
var import_promises9 = require("fs/promises");
|
|
1761
|
+
|
|
1762
|
+
// src/api/smtpClient.ts
|
|
1763
|
+
var import_nodemailer = __toESM(require("nodemailer"));
|
|
1764
|
+
function getSmtpConfigOrThrow(config) {
|
|
1765
|
+
if (!config.imapUsername || !config.imapPassword) {
|
|
1766
|
+
throw new DoorayCliError(
|
|
1767
|
+
"\uBA54\uC77C \uC124\uC815\uC774 \uC644\uB8CC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uC124\uC815\uC744 \uC9C4\uD589\uD558\uC138\uC694:\n dooray config set imap-username <YOUR_EMAIL>\n dooray config set imap-password <YOUR_IMAP_PASSWORD>",
|
|
1768
|
+
EXIT_CONFIG_ERROR
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
return {
|
|
1772
|
+
host: config.smtpHost ?? DEFAULTS.smtpHost,
|
|
1773
|
+
port: config.smtpPort ?? DEFAULTS.smtpPort,
|
|
1774
|
+
username: config.imapUsername,
|
|
1775
|
+
password: config.imapPassword
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
async function sendMail(config, opts) {
|
|
1779
|
+
const smtp = getSmtpConfigOrThrow(config);
|
|
1780
|
+
const transporter = import_nodemailer.default.createTransport({
|
|
1781
|
+
host: smtp.host,
|
|
1782
|
+
port: smtp.port,
|
|
1783
|
+
secure: true,
|
|
1784
|
+
auth: { user: smtp.username, pass: smtp.password }
|
|
1785
|
+
});
|
|
1786
|
+
const mailOptions = {
|
|
1787
|
+
from: smtp.username,
|
|
1788
|
+
to: opts.to.join(", "),
|
|
1789
|
+
subject: opts.subject,
|
|
1790
|
+
inReplyTo: opts.inReplyTo,
|
|
1791
|
+
references: opts.references
|
|
1792
|
+
};
|
|
1793
|
+
if (opts.cc?.length) mailOptions.cc = opts.cc.join(", ");
|
|
1794
|
+
if (opts.bcc?.length) mailOptions.bcc = opts.bcc.join(", ");
|
|
1795
|
+
if (opts.html) {
|
|
1796
|
+
mailOptions.html = opts.body;
|
|
1797
|
+
} else {
|
|
1798
|
+
mailOptions.text = opts.body;
|
|
1799
|
+
}
|
|
1800
|
+
const info = await transporter.sendMail(mailOptions);
|
|
1801
|
+
return {
|
|
1802
|
+
messageId: info.messageId,
|
|
1803
|
+
accepted: info.accepted ?? [],
|
|
1804
|
+
rejected: info.rejected ?? []
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// src/commands/mail/send.ts
|
|
1809
|
+
var mailSendCommand = new import_commander25.Command("send").description("\uBA54\uC77C \uBC1C\uC1A1").requiredOption("--to <addresses...>", "\uBC1B\uB294\uC0AC\uB78C \uC774\uBA54\uC77C (\uBCF5\uC218 \uAC00\uB2A5)").requiredOption("--subject <title>", "\uC81C\uBAA9").option("--body <text>", "\uBCF8\uBB38").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C").option("--cc <addresses...>", "\uCC38\uC870").option("--bcc <addresses...>", "\uC228\uC740\uCC38\uC870").option("--html", "\uBCF8\uBB38\uC744 HTML\uB85C \uC804\uC1A1").action(async (opts) => {
|
|
1810
|
+
const globalOpts = mailSendCommand.optsWithGlobals();
|
|
1811
|
+
const config = await getConfigOrThrow();
|
|
1812
|
+
let body = opts.body ?? "";
|
|
1813
|
+
if (opts.bodyFile) {
|
|
1814
|
+
body = await (0, import_promises9.readFile)(opts.bodyFile, "utf-8");
|
|
1815
|
+
}
|
|
1816
|
+
if (!body) {
|
|
1817
|
+
process.stderr.write("\uC624\uB958: --body \uB610\uB294 --body-file\uC744 \uC9C0\uC815\uD558\uC138\uC694\n");
|
|
1818
|
+
process.exit(3);
|
|
1819
|
+
}
|
|
1820
|
+
startSpinner("\uBA54\uC77C \uBC1C\uC1A1 \uC911...");
|
|
1821
|
+
const result = await sendMail(config, {
|
|
1822
|
+
to: opts.to,
|
|
1823
|
+
cc: opts.cc,
|
|
1824
|
+
bcc: opts.bcc,
|
|
1825
|
+
subject: opts.subject,
|
|
1826
|
+
body,
|
|
1827
|
+
html: opts.html
|
|
1828
|
+
});
|
|
1829
|
+
stopSpinner(true, "\uBA54\uC77C \uBC1C\uC1A1 \uC644\uB8CC");
|
|
1830
|
+
if (globalOpts.json) {
|
|
1831
|
+
printJson(result);
|
|
1832
|
+
} else {
|
|
1833
|
+
process.stdout.write(
|
|
1834
|
+
`\uBA54\uC77C \uBC1C\uC1A1 \uC644\uB8CC
|
|
1835
|
+
Message-ID: ${result.messageId}
|
|
1836
|
+
\uC218\uC2E0: ${result.accepted.join(", ")}
|
|
1837
|
+
` + (result.rejected.length ? ` \uAC70\uBD80: ${result.rejected.join(", ")}
|
|
1838
|
+
` : "")
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
});
|
|
1842
|
+
|
|
1843
|
+
// src/commands/mail/reply.ts
|
|
1844
|
+
var import_commander26 = require("commander");
|
|
1845
|
+
var import_promises10 = require("fs/promises");
|
|
1846
|
+
var import_imapflow2 = require("imapflow");
|
|
1847
|
+
async function getMessageId(config, uid) {
|
|
1848
|
+
const imap = getImapConfigOrThrow(config);
|
|
1849
|
+
const client = new import_imapflow2.ImapFlow({
|
|
1850
|
+
host: imap.host,
|
|
1851
|
+
port: imap.port,
|
|
1852
|
+
secure: true,
|
|
1853
|
+
auth: { user: imap.username, pass: imap.password },
|
|
1854
|
+
logger: false
|
|
1855
|
+
});
|
|
1856
|
+
try {
|
|
1857
|
+
await client.connect();
|
|
1858
|
+
const lock = await client.getMailboxLock("INBOX");
|
|
1859
|
+
try {
|
|
1860
|
+
const msg = await client.fetchOne(String(uid), {
|
|
1861
|
+
uid: true,
|
|
1862
|
+
envelope: true
|
|
1863
|
+
}, { uid: true });
|
|
1864
|
+
return msg?.envelope.messageId ?? null;
|
|
1865
|
+
} finally {
|
|
1866
|
+
lock.release();
|
|
1867
|
+
}
|
|
1868
|
+
} finally {
|
|
1869
|
+
await client.logout();
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
var mailReplyCommand = new import_commander26.Command("reply").description("\uBA54\uC77C \uB2F5\uC7A5").argument("<uid>", "\uC6D0\uBCF8 \uBA54\uC77C UID").option("--body <text>", "\uB2F5\uC7A5 \uBCF8\uBB38").option("--body-file <path>", "\uB2F5\uC7A5 \uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C").option("--cc <addresses...>", "\uCC38\uC870").option("--html", "\uBCF8\uBB38\uC744 HTML\uB85C \uC804\uC1A1").action(async (uid, opts) => {
|
|
1873
|
+
const globalOpts = mailReplyCommand.optsWithGlobals();
|
|
1874
|
+
const config = await getConfigOrThrow();
|
|
1875
|
+
let body = opts.body ?? "";
|
|
1876
|
+
if (opts.bodyFile) {
|
|
1877
|
+
body = await (0, import_promises10.readFile)(opts.bodyFile, "utf-8");
|
|
1878
|
+
}
|
|
1879
|
+
if (!body) {
|
|
1880
|
+
process.stderr.write("\uC624\uB958: --body \uB610\uB294 --body-file\uC744 \uC9C0\uC815\uD558\uC138\uC694\n");
|
|
1881
|
+
process.exit(3);
|
|
1882
|
+
}
|
|
1883
|
+
startSpinner("\uC6D0\uBCF8 \uBA54\uC77C \uC870\uD68C \uC911...");
|
|
1884
|
+
const original = await getMail(config, Number(uid));
|
|
1885
|
+
const messageId = await getMessageId(config, Number(uid));
|
|
1886
|
+
const fromMatch = original.from.match(/<(.+?)>/);
|
|
1887
|
+
const replyTo = fromMatch ? fromMatch[1] : original.from;
|
|
1888
|
+
stopSpinner(true, "\uC6D0\uBCF8 \uBA54\uC77C \uC870\uD68C \uC644\uB8CC");
|
|
1889
|
+
startSpinner("\uB2F5\uC7A5 \uBC1C\uC1A1 \uC911...");
|
|
1890
|
+
const result = await sendMail(config, {
|
|
1891
|
+
to: [replyTo],
|
|
1892
|
+
cc: opts.cc,
|
|
1893
|
+
subject: original.subject.startsWith("Re: ") ? original.subject : `Re: ${original.subject}`,
|
|
1894
|
+
body,
|
|
1895
|
+
html: opts.html,
|
|
1896
|
+
inReplyTo: messageId ?? void 0,
|
|
1897
|
+
references: messageId ?? void 0
|
|
1898
|
+
});
|
|
1899
|
+
stopSpinner(true, "\uB2F5\uC7A5 \uBC1C\uC1A1 \uC644\uB8CC");
|
|
1900
|
+
if (globalOpts.json) {
|
|
1901
|
+
printJson(result);
|
|
1902
|
+
} else {
|
|
1903
|
+
process.stdout.write(
|
|
1904
|
+
`\uB2F5\uC7A5 \uBC1C\uC1A1 \uC644\uB8CC
|
|
1905
|
+
To: ${replyTo}
|
|
1906
|
+
Message-ID: ${result.messageId}
|
|
1907
|
+
`
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1558
1912
|
// src/index.ts
|
|
1559
|
-
var program = new
|
|
1560
|
-
program.name("dooray").description("Dooray REST API CLI").version("0.
|
|
1913
|
+
var program = new import_commander27.Command();
|
|
1914
|
+
program.name("dooray").description("Dooray REST API CLI").version("0.2.0").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").option("--quiet", "ID\uB9CC \uCD9C\uB825").option("--no-color", "\uC0C9\uC0C1 \uBE44\uD65C\uC131\uD654");
|
|
1561
1915
|
program.hook("preAction", () => {
|
|
1562
1916
|
const opts = program.opts();
|
|
1563
1917
|
if (opts.color === false || process.env.NO_COLOR) {
|
|
1564
|
-
|
|
1918
|
+
import_chalk6.default.level = 0;
|
|
1565
1919
|
}
|
|
1566
1920
|
});
|
|
1567
|
-
var projectCommand = new
|
|
1921
|
+
var projectCommand = new import_commander27.Command("project").description("\uD504\uB85C\uC81D\uD2B8 \uAD00\uB828 \uBA85\uB839");
|
|
1568
1922
|
projectCommand.addCommand(projectListCommand);
|
|
1569
1923
|
projectCommand.addCommand(projectMembersCommand);
|
|
1570
1924
|
projectCommand.addCommand(projectWorkflowsCommand);
|
|
1571
|
-
var postCommand = new
|
|
1925
|
+
var postCommand = new import_commander27.Command("post").description("\uC5C5\uBB34 \uAD00\uB828 \uBA85\uB839");
|
|
1572
1926
|
postCommand.addCommand(postListCommand);
|
|
1573
1927
|
postCommand.addCommand(postSearchCommand);
|
|
1574
1928
|
postCommand.addCommand(postGetCommand);
|
|
@@ -1576,31 +1930,37 @@ postCommand.addCommand(postEditCommand);
|
|
|
1576
1930
|
postCommand.addCommand(postCreateCommand);
|
|
1577
1931
|
postCommand.addCommand(postDoneCommand);
|
|
1578
1932
|
postCommand.addCommand(postWorkflowCommand);
|
|
1579
|
-
var commentCommand = new
|
|
1933
|
+
var commentCommand = new import_commander27.Command("comment").description("\uB313\uAE00 \uAD00\uB828 \uBA85\uB839");
|
|
1580
1934
|
commentCommand.addCommand(commentListCommand);
|
|
1581
1935
|
commentCommand.addCommand(commentAddCommand);
|
|
1582
1936
|
commentCommand.addCommand(commentEditCommand);
|
|
1583
1937
|
commentCommand.addCommand(commentDeleteCommand);
|
|
1584
1938
|
postCommand.addCommand(commentCommand);
|
|
1585
|
-
var wikiCommand = new
|
|
1939
|
+
var wikiCommand = new import_commander27.Command("wiki").description("\uC704\uD0A4 \uAD00\uB828 \uBA85\uB839");
|
|
1586
1940
|
wikiCommand.addCommand(wikiListCommand);
|
|
1587
1941
|
wikiCommand.addCommand(wikiPagesCommand);
|
|
1588
|
-
var wikiPageCommand = new
|
|
1942
|
+
var wikiPageCommand = new import_commander27.Command("page").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uAD00\uB828 \uBA85\uB839");
|
|
1589
1943
|
wikiPageCommand.addCommand(wikiPageGetCommand);
|
|
1590
1944
|
wikiPageCommand.addCommand(wikiPageCreateCommand);
|
|
1591
1945
|
wikiPageCommand.addCommand(wikiPageEditCommand);
|
|
1592
1946
|
wikiCommand.addCommand(wikiPageCommand);
|
|
1947
|
+
var mailCommand = new import_commander27.Command("mail").description("\uBA54\uC77C \uAD00\uB828 \uBA85\uB839");
|
|
1948
|
+
mailCommand.addCommand(mailListCommand);
|
|
1949
|
+
mailCommand.addCommand(mailGetCommand);
|
|
1950
|
+
mailCommand.addCommand(mailSendCommand);
|
|
1951
|
+
mailCommand.addCommand(mailReplyCommand);
|
|
1593
1952
|
program.addCommand(configCommand);
|
|
1594
1953
|
program.addCommand(cacheCommand);
|
|
1595
1954
|
program.addCommand(doctorCommand);
|
|
1596
1955
|
program.addCommand(projectCommand);
|
|
1597
1956
|
program.addCommand(postCommand);
|
|
1598
1957
|
program.addCommand(wikiCommand);
|
|
1958
|
+
program.addCommand(mailCommand);
|
|
1599
1959
|
program.parseAsync().catch((err) => {
|
|
1600
1960
|
if (err instanceof DoorayCliError) {
|
|
1601
|
-
process.stderr.write(
|
|
1961
|
+
process.stderr.write(import_chalk6.default.red(`\uC624\uB958: ${err.message}`) + "\n");
|
|
1602
1962
|
process.exit(err.exitCode);
|
|
1603
1963
|
}
|
|
1604
|
-
process.stderr.write(
|
|
1964
|
+
process.stderr.write(import_chalk6.default.red(`\uC624\uB958: ${err.message}`) + "\n");
|
|
1605
1965
|
process.exit(1);
|
|
1606
1966
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bifos/dooray-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI tool for Dooray project management — AI agent & terminal friendly",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dooray",
|
|
@@ -36,14 +36,19 @@
|
|
|
36
36
|
"chalk": "^5.6.2",
|
|
37
37
|
"cli-table3": "^0.6.5",
|
|
38
38
|
"commander": "^14.0.3",
|
|
39
|
+
"imapflow": "^1.2.18",
|
|
39
40
|
"js-yaml": "^4.1.1",
|
|
40
41
|
"ky": "^1.14.3",
|
|
42
|
+
"mailparser": "^3.9.6",
|
|
43
|
+
"nodemailer": "^8.0.4",
|
|
41
44
|
"ora": "^9.3.0",
|
|
42
45
|
"tmp": "^0.2.5"
|
|
43
46
|
},
|
|
44
47
|
"devDependencies": {
|
|
45
48
|
"@types/js-yaml": "^4.0.9",
|
|
49
|
+
"@types/mailparser": "^3.4.6",
|
|
46
50
|
"@types/node": "^25.5.0",
|
|
51
|
+
"@types/nodemailer": "^7.0.11",
|
|
47
52
|
"@types/tmp": "^0.2.6",
|
|
48
53
|
"tsup": "^8.5.1",
|
|
49
54
|
"typescript": "^6.0.2"
|