1 | |
2 |
|
3 | export type DynSelect_folder = string |
4 |
|
5 | export async function folder(auth: RT.MsOutlook, mailbox: string | undefined) { |
6 | const base = `https://graph.microsoft.com/v1.0/${ |
7 | mailbox != null && mailbox !== "" |
8 | ? `users/${encodeURIComponent(mailbox)}` |
9 | : "me" |
10 | }` |
11 | const r = await fetch(`${base}/mailFolders?$top=100&$select=id,displayName`, { |
12 | headers: { |
13 | Authorization: `Bearer ${auth.token}`, |
14 | Accept: "application/json", |
15 | }, |
16 | }) |
17 | if (!r.ok) throw new Error(`${r.status} ${await r.text()}`) |
18 | const { value } = (await r.json()) as { |
19 | value: { id: string; displayName: string }[] |
20 | } |
21 | return value.map((f) => ({ value: f.id, label: f.displayName })) |
22 | } |
23 |
|
24 | |
25 | * Find Emails |
26 | * List or search messages, newest first by default, in the whole mailbox or one folder. `search` takes Outlook search syntax (e.g. `from:alice@example.com hasAttachments:true`) and cannot be combined with `filter` or `order_by`; `filter` takes OData (e.g. `isRead eq false`, `importance eq 'high'`, `receivedDateTime ge 2026-01-01T00:00:00Z`). Pass the returned `@odata.nextLink` as `next_link` for the next page. Requires Mail.Read (covered by the default Mail.ReadWrite) (a shared mailbox needs Mail.Read.Shared, not a default). |
27 | */ |
28 | export async function main( |
29 | auth: RT.MsOutlook, |
30 | folder: DynSelect_folder | undefined, |
31 | search: string | undefined, |
32 | filter: string | undefined, |
33 | order_by: string | undefined, |
34 | select: string | undefined, |
35 | top: number = 25, |
36 | next_link: string | undefined, |
37 | body_format: "html" | "text" = "html", |
38 | mailbox: string | undefined |
39 | ) { |
40 | const base = `https://graph.microsoft.com/v1.0/${ |
41 | mailbox != null && mailbox !== "" |
42 | ? `users/${encodeURIComponent(mailbox)}` |
43 | : "me" |
44 | }` |
45 | if ( |
46 | next_link != null && |
47 | next_link !== "" && |
48 | !next_link.startsWith("https://graph.microsoft.com/") |
49 | ) { |
50 | throw new Error( |
51 | "next_link must be an @odata.nextLink returned by Microsoft Graph" |
52 | ) |
53 | } |
54 | let url: URL |
55 | if (next_link != null && next_link !== "") { |
56 | url = new URL(next_link) |
57 | } else { |
58 | url = new URL( |
59 | folder != null && folder !== "" |
60 | ? `${base}/mailFolders/${encodeURIComponent(folder)}/messages` |
61 | : `${base}/messages` |
62 | ) |
63 | url.searchParams.append("$top", String(top)) |
64 | if (search != null && search !== "") { |
65 | url.searchParams.append("$search", `"${search.replace(/"/g, '\\"')}"`) |
66 | } else if ( |
67 | (filter == null || filter === "") && |
68 | (order_by == null || order_by === "") |
69 | ) { |
70 | url.searchParams.append("$orderby", "receivedDateTime desc") |
71 | } |
72 | if (filter != null && filter !== "") { |
73 | url.searchParams.append("$filter", filter) |
74 | } |
75 | if (order_by != null && order_by !== "") { |
76 | url.searchParams.append("$orderby", order_by) |
77 | } |
78 | if (select != null && select !== "") { |
79 | url.searchParams.append("$select", select) |
80 | } |
81 | } |
82 |
|
83 | const response = await fetch(url, { |
84 | headers: { |
85 | Authorization: `Bearer ${auth.token}`, |
86 | Accept: "application/json", |
87 | Prefer: `outlook.body-content-type="${body_format}"`, |
88 | }, |
89 | }) |
90 |
|
91 | if (!response.ok) { |
92 | throw new Error(`${response.status} ${await response.text()}`) |
93 | } |
94 |
|
95 | return await response.json() |
96 | } |
97 | |