@zhin.js/plugin-calendar 0.0.0-stage → 0.1.1
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 +189 -2
- package/commands/calendar/auth/index.js +26 -0
- package/commands/calendar/auth/index.ts +29 -0
- package/commands/calendar/logout/index.js +18 -0
- package/commands/calendar/logout/index.ts +18 -0
- package/commands/calendar/status/index.js +10 -0
- package/commands/calendar/status/index.ts +10 -0
- package/lib/caldav.d.ts +18 -0
- package/lib/caldav.d.ts.map +1 -0
- package/lib/caldav.js +167 -0
- package/lib/caldav.js.map +1 -0
- package/lib/chat-auth.d.ts +49 -0
- package/lib/chat-auth.d.ts.map +1 -0
- package/lib/chat-auth.js +233 -0
- package/lib/chat-auth.js.map +1 -0
- package/lib/cli/auth.d.ts +3 -0
- package/lib/cli/auth.d.ts.map +1 -0
- package/lib/cli/auth.js +182 -0
- package/lib/cli/auth.js.map +1 -0
- package/lib/config.d.ts +87 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +95 -0
- package/lib/config.js.map +1 -0
- package/lib/credential-store.d.ts +30 -0
- package/lib/credential-store.d.ts.map +1 -0
- package/lib/credential-store.js +42 -0
- package/lib/credential-store.js.map +1 -0
- package/lib/free-time.d.ts +13 -0
- package/lib/free-time.d.ts.map +1 -0
- package/lib/free-time.js +96 -0
- package/lib/free-time.js.map +1 -0
- package/lib/http-callback.d.ts +13 -0
- package/lib/http-callback.d.ts.map +1 -0
- package/lib/http-callback.js +26 -0
- package/lib/http-callback.js.map +1 -0
- package/lib/index.d.ts +32 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +122 -0
- package/lib/index.js.map +1 -0
- package/lib/oauth-auth.d.ts +37 -0
- package/lib/oauth-auth.d.ts.map +1 -0
- package/lib/oauth-auth.js +91 -0
- package/lib/oauth-auth.js.map +1 -0
- package/lib/oauth-session.d.ts +27 -0
- package/lib/oauth-session.d.ts.map +1 -0
- package/lib/oauth-session.js +43 -0
- package/lib/oauth-session.js.map +1 -0
- package/lib/oauth.d.ts +28 -0
- package/lib/oauth.d.ts.map +1 -0
- package/lib/oauth.js +200 -0
- package/lib/oauth.js.map +1 -0
- package/lib/runtime.d.ts +11 -0
- package/lib/runtime.d.ts.map +1 -0
- package/lib/runtime.js +3 -0
- package/lib/runtime.js.map +1 -0
- package/lib/secrets.d.ts +3 -0
- package/lib/secrets.d.ts.map +1 -0
- package/lib/secrets.js +6 -0
- package/lib/secrets.js.map +1 -0
- package/lib/time.d.ts +37 -0
- package/lib/time.d.ts.map +1 -0
- package/lib/time.js +118 -0
- package/lib/time.js.map +1 -0
- package/lib/token-manager.d.ts +71 -0
- package/lib/token-manager.d.ts.map +1 -0
- package/lib/token-manager.js +239 -0
- package/lib/token-manager.js.map +1 -0
- package/lib/tool-errors.d.ts +6 -0
- package/lib/tool-errors.d.ts.map +1 -0
- package/lib/tool-errors.js +17 -0
- package/lib/tool-errors.js.map +1 -0
- package/lib/types.d.ts +28 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/package.json +112 -3
- package/plugin.js +72 -0
- package/skills/calendar-scheduling/SKILL.md +38 -0
- package/skills/calendar-scheduling/tools/create_event/index.js +38 -0
- package/skills/calendar-scheduling/tools/create_event/index.ts +41 -0
- package/skills/calendar-scheduling/tools/find_free_time/index.js +46 -0
- package/skills/calendar-scheduling/tools/find_free_time/index.ts +50 -0
- package/skills/calendar-scheduling/tools/list_events/index.js +32 -0
- package/skills/calendar-scheduling/tools/list_events/index.ts +42 -0
- package/src/caldav.ts +198 -0
- package/src/chat-auth.ts +336 -0
- package/src/cli/auth.ts +235 -0
- package/src/config.ts +108 -0
- package/src/credential-store.ts +79 -0
- package/src/free-time.ts +128 -0
- package/src/http-callback.ts +55 -0
- package/src/index.ts +193 -0
- package/src/oauth-auth.ts +145 -0
- package/src/oauth-session.ts +65 -0
- package/src/oauth.ts +298 -0
- package/src/runtime.ts +16 -0
- package/src/secrets.ts +5 -0
- package/src/time.ts +167 -0
- package/src/token-manager.ts +358 -0
- package/src/tool-errors.ts +18 -0
- package/src/types.ts +37 -0
package/README.md
CHANGED
|
@@ -1,3 +1,190 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zhin.js/plugin-calendar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
为 Zhin Agent 提供日历查询与创建:CalDAV(iCloud / Fastmail / Nextcloud),以及 **Google Calendar / Microsoft Outlook(Graph)**。
|
|
4
|
+
|
|
5
|
+
Google / Outlook 的授权在**聊天里完成**:master 发送 `calendar auth google`,点开机器人回复的链接登录,refresh_token 会保存到插件数据库,不需要手动改 `.env`。授权失效时,日历工具会提示重新授权,并主动通知上次完成授权的会话(只通知一次)。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @zhin.js/plugin-calendar
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
根 `package.json#zhin`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"features": [
|
|
18
|
+
{ "package": "@zhin.js/command" },
|
|
19
|
+
{ "package": "@zhin.js/tool" },
|
|
20
|
+
{ "package": "@zhin.js/skill" }
|
|
21
|
+
],
|
|
22
|
+
"plugins": [
|
|
23
|
+
{ "package": "@zhin.js/plugin-calendar", "instanceKey": "calendar" }
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
聊天授权依赖 zhin 的数据库(保存 refresh_token)、HTTP Host(接收 OAuth 回调)和消息出站(通知授权结果)。缺少数据库或 HTTP Host 时,`calendar auth` 会直接说明原因;CalDAV 不受影响。
|
|
29
|
+
|
|
30
|
+
## 使用流程(Google / Outlook)
|
|
31
|
+
|
|
32
|
+
1. 在 Google Cloud 或 Azure 创建 OAuth 客户端(见下文),把 Client ID / Secret 写进运行项目的 `.env`。
|
|
33
|
+
2. 在 `zhin.config.yml` 配置 `plugins.calendar`(见下文),重启或重载。
|
|
34
|
+
3. master 在聊天中发送 `calendar auth google`(或 `calendar auth microsoft`)。
|
|
35
|
+
4. 打开机器人回复的链接,登录并同意授权。链接 10 分钟内有效,只能用一次。
|
|
36
|
+
5. 浏览器显示「授权成功」,机器人在原会话回复授权成功。完成。
|
|
37
|
+
|
|
38
|
+
## 聊天命令(仅 master)
|
|
39
|
+
|
|
40
|
+
| 命令 | 说明 |
|
|
41
|
+
| ------------------------------------- | --------------------------------------------------------- |
|
|
42
|
+
| `calendar auth google` | 生成 Google 授权链接(PKCE + state,10 分钟有效、一次性) |
|
|
43
|
+
| `calendar auth microsoft` / `outlook` | 生成 Outlook(Microsoft Graph)授权链接 |
|
|
44
|
+
| `calendar status` | 查看当前 provider 与各平台授权状态(不会显示 token) |
|
|
45
|
+
| `calendar logout google\|microsoft` | 删除数据库中保存的授权 |
|
|
46
|
+
|
|
47
|
+
refresh_token 的读取顺序:**插件数据库(聊天授权写入)优先**,没有时才用配置里的 `refresh_token`(兼容旧的 `.env` 方式)。
|
|
48
|
+
|
|
49
|
+
## 配置(`zhin.config.yml`)
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
plugins:
|
|
53
|
+
calendar:
|
|
54
|
+
provider: google # none | caldav | google | microsoft
|
|
55
|
+
timezone: Asia/Manila
|
|
56
|
+
work_day_start: "09:00"
|
|
57
|
+
work_day_end: "18:00"
|
|
58
|
+
# OAuth 回调:默认使用 zhin HTTP Host 的本机地址,例如 http://127.0.0.1:8068/calendar/oauth/callback
|
|
59
|
+
oauth_redirect_base_url: "" # 浏览器和机器人不在同一台机器时,填外部可访问地址,如 https://bot.example.com
|
|
60
|
+
oauth_callback_path: /calendar/oauth/callback
|
|
61
|
+
google:
|
|
62
|
+
enabled: true
|
|
63
|
+
client_id: ${GOOGLE_CALENDAR_CLIENT_ID}
|
|
64
|
+
client_secret: ${GOOGLE_CALENDAR_CLIENT_SECRET}
|
|
65
|
+
refresh_token: "" # 可选后备;聊天授权后可以留空
|
|
66
|
+
calendar_id: primary
|
|
67
|
+
token_file: ""
|
|
68
|
+
microsoft:
|
|
69
|
+
enabled: false
|
|
70
|
+
client_id: ${MS_GRAPH_CLIENT_ID}
|
|
71
|
+
client_secret: ${MS_GRAPH_CLIENT_SECRET} # 公共客户端可留空
|
|
72
|
+
refresh_token: ""
|
|
73
|
+
tenant: common
|
|
74
|
+
calendar_id: "" # 空 / primary / me = 默认日历;否则 Graph calendar id
|
|
75
|
+
token_file: ""
|
|
76
|
+
caldav:
|
|
77
|
+
enabled: false
|
|
78
|
+
calendar_url: ${CALDAV_CALENDAR_URL}
|
|
79
|
+
username: ${CALDAV_USERNAME}
|
|
80
|
+
password: ${CALDAV_PASSWORD}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
运行项目的 `.env`(放在启动 zhin 的项目根目录,不是插件目录):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
GOOGLE_CALENDAR_CLIENT_ID=xxxx.apps.googleusercontent.com
|
|
87
|
+
GOOGLE_CALENDAR_CLIENT_SECRET=xxxx
|
|
88
|
+
# Outlook
|
|
89
|
+
MS_GRAPH_CLIENT_ID=xxxx
|
|
90
|
+
MS_GRAPH_CLIENT_SECRET= # 可选
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 回调地址与反向代理 / 隧道
|
|
94
|
+
|
|
95
|
+
回调地址 = `oauth_redirect_base_url`(为空时用 HTTP Host 本机地址)+ `oauth_callback_path`。这个地址必须同时满足:
|
|
96
|
+
|
|
97
|
+
- 打开授权链接的浏览器能访问到它;
|
|
98
|
+
- 已登记在 OAuth 客户端的「重定向 URI」里(必须**完全一致**)。
|
|
99
|
+
|
|
100
|
+
如果反向代理或 Cloudflare Tunnel 只转发部分路径(例如只放行 `/grokbot/*`),要么在隧道里额外放行 `/calendar/oauth/callback`,要么把 `oauth_callback_path` 改到已放行的前缀下,例如:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
oauth_redirect_base_url: https://bot.example.com
|
|
104
|
+
oauth_callback_path: /grokbot/calendar/oauth/callback
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
注意:Google「桌面应用」客户端只接受 `127.0.0.1` / `localhost` 回调。如果用公网域名回调,需要创建「Web 应用」类型的客户端,并把完整回调地址加入「已获授权的重定向 URI」。
|
|
108
|
+
|
|
109
|
+
## Google Cloud 设置
|
|
110
|
+
|
|
111
|
+
1. 打开 [Google Cloud Console](https://console.cloud.google.com/),创建或选择一个项目。
|
|
112
|
+
2. **API 和服务 → 库**,搜索并**启用 Google Calendar API**。
|
|
113
|
+
漏掉这一步,授权会成功,但读日历时报 `403 accessNotConfigured`(Google Calendar API has not been used in project … or it is disabled)。
|
|
114
|
+
3. **OAuth 同意屏幕**:用户类型选 External(Workspace 内部用可选 Internal)。
|
|
115
|
+
应用处于 **Testing(测试)** 状态时,必须在「测试用户」里**添加要授权的 Google 账号**,否则浏览器会显示「尚未完成 Google 验证流程」和 `403 access_denied`。
|
|
116
|
+
Testing 状态下 refresh_token 约 **7 天**过期,到期后再发一次 `calendar auth google` 即可;发布为 Production 后长期有效(仍受 Google 策略约束)。
|
|
117
|
+
4. **凭据 → 创建凭据 → OAuth 客户端 ID**:
|
|
118
|
+
- 机器人和浏览器在同一台电脑:选「**桌面应用**」,回调用默认的 `http://127.0.0.1:<HTTP 端口>/calendar/oauth/callback`。
|
|
119
|
+
- 通过公网域名回调:选「**Web 应用**」,在「已获授权的重定向 URI」加入完整回调地址。
|
|
120
|
+
5. 把 Client ID / Client Secret 写进 `.env` 的 `GOOGLE_CALENDAR_CLIENT_ID` / `GOOGLE_CALENDAR_CLIENT_SECRET`,重载后发送 `calendar auth google`。
|
|
121
|
+
|
|
122
|
+
申请的 scope:`calendar.events` + `calendar.readonly`(`access_type=offline&prompt=consent`)。
|
|
123
|
+
|
|
124
|
+
## Azure 应用注册(Outlook / Microsoft 365)
|
|
125
|
+
|
|
126
|
+
1. [Azure Portal](https://portal.azure.com/) → **Microsoft Entra ID → 应用注册 → 新注册**。
|
|
127
|
+
2. 支持的账户类型:个人 Outlook.com 账号选「任何组织目录中的帐户和个人 Microsoft 帐户」;仅公司账号可选单租户(此时 `tenant` 填租户 ID)。
|
|
128
|
+
3. **身份验证 → 添加平台**:
|
|
129
|
+
- 本机回调:选「移动和桌面应用程序」,添加 `http://127.0.0.1:<HTTP 端口>/calendar/oauth/callback`(或 `http://localhost`,以门户允许为准),并打开「允许公共客户端流」,这样不需要 secret。
|
|
130
|
+
- 公网回调:选「Web」,添加完整回调地址,并在「证书和密码」创建客户端密码,填到 `MS_GRAPH_CLIENT_SECRET`。
|
|
131
|
+
4. **API 权限 → Microsoft Graph → 委派的权限**:添加 `Calendars.ReadWrite`、`User.Read`、`offline_access`,组织账号按需「授予管理员同意」。
|
|
132
|
+
5. **概述**里复制「应用程序(客户端) ID」到 `MS_GRAPH_CLIENT_ID`。
|
|
133
|
+
6. `provider: microsoft`、`microsoft.enabled: true`,重载后发送 `calendar auth microsoft`。
|
|
134
|
+
|
|
135
|
+
授权请求的 scope:`offline_access Calendars.ReadWrite User.Read`。
|
|
136
|
+
|
|
137
|
+
## CalDAV
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
plugins:
|
|
141
|
+
calendar:
|
|
142
|
+
provider: caldav
|
|
143
|
+
caldav:
|
|
144
|
+
enabled: true
|
|
145
|
+
calendar_url: ${CALDAV_CALENDAR_URL} # 日历 collection URL
|
|
146
|
+
username: ${CALDAV_USERNAME}
|
|
147
|
+
password: ${CALDAV_PASSWORD} # iCloud 请使用 App 专用密码
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
CalDAV 不需要聊天授权。
|
|
151
|
+
|
|
152
|
+
## 故障排查
|
|
153
|
+
|
|
154
|
+
| 现象 | 原因与处理 |
|
|
155
|
+
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
156
|
+
| 读日历报 `403 accessNotConfigured` / API 未启用 | 该 Google Cloud 项目没有启用 Google Calendar API。到「API 和服务 → 库」启用,几分钟后重试。 |
|
|
157
|
+
| 浏览器显示「尚未完成 Google 验证流程」`403 access_denied` | 同意屏幕处于 Testing,账号不在测试用户里。添加测试用户后重新 `calendar auth google`。 |
|
|
158
|
+
| `redirect_uri_mismatch` | 回调地址与客户端登记的不一致。核对 `calendar auth` 回复里的「回调地址」,原样登记到客户端。 |
|
|
159
|
+
| 点击链接后浏览器打不开回调页 | 浏览器访问不到 zhin HTTP Host。配置 `oauth_redirect_base_url`,或让隧道放行 `oauth_callback_path`。 |
|
|
160
|
+
| 「授权链接无效、已使用或已过期」 | 链接超过 10 分钟、已用过,或 zhin 重启过。重新发送 `calendar auth <provider>`。 |
|
|
161
|
+
| 工具提示「日历授权已失效…(invalid_grant)」 | refresh_token 过期或被撤销(Google Testing 约 7 天、用户改密码、取消授权)。master 发送 `calendar auth <provider>`。 |
|
|
162
|
+
| 工具提示「日历尚未授权」 | 数据库和配置里都没有 refresh_token。发送 `calendar auth <provider>`。 |
|
|
163
|
+
| 日志提示「提供方返回了新的 refresh_token」 | Microsoft 会轮换 refresh_token。聊天授权时新值自动写回数据库;只用 `.env` 时请按日志更新,或配置 `token_file`。 |
|
|
164
|
+
|
|
165
|
+
## 可选:命令行授权(后备方式)
|
|
166
|
+
|
|
167
|
+
不方便走聊天回调时(例如没有 HTTP Host),可以用随包附带的命令行工具获取 refresh_token,再写入 `.env` 的 `GOOGLE_CALENDAR_REFRESH_TOKEN` / `MS_GRAPH_REFRESH_TOKEN`:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
export GOOGLE_CALENDAR_CLIENT_ID=...
|
|
171
|
+
export GOOGLE_CALENDAR_CLIENT_SECRET=...
|
|
172
|
+
npx zhin-calendar-auth google
|
|
173
|
+
|
|
174
|
+
export MS_GRAPH_CLIENT_ID=...
|
|
175
|
+
npx zhin-calendar-auth microsoft
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
它在本机监听 `http://127.0.0.1:53682/callback`(`CALENDAR_OAUTH_PORT` 可改端口),需要把这个地址登记为客户端的重定向 URI。工具只读取已 export 的环境变量,只打印 refresh_token,不写任何文件。在 zhin 仓库内也可以用 `pnpm --filter @zhin.js/plugin-calendar auth google`。
|
|
179
|
+
|
|
180
|
+
## Agent 技能
|
|
181
|
+
|
|
182
|
+
技能 `calendar-scheduling`:
|
|
183
|
+
|
|
184
|
+
| 工具 | 说明 |
|
|
185
|
+
| ---------------- | ------------------------------------- |
|
|
186
|
+
| `list_events` | 列出时间范围内事件 |
|
|
187
|
+
| `find_free_time` | 工作时段内找空档 |
|
|
188
|
+
| `create_event` | 创建事件(`role(master)` + 始终批准) |
|
|
189
|
+
|
|
190
|
+
创建前应向用户确认标题、时间与时区。
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from "zhin.js/command";
|
|
3
|
+
import { createDeliveryTarget } from "zhin.js";
|
|
4
|
+
import { parseOAuthProvider } from "../../../lib/chat-auth.js";
|
|
5
|
+
import { calendarRuntimeToken } from "../../../lib/runtime.js";
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: "通过聊天完成 Google / Outlook 日历 OAuth 授权(master 专用)",
|
|
8
|
+
params: {
|
|
9
|
+
provider: { type: "string", description: "google 或 microsoft(outlook)" },
|
|
10
|
+
},
|
|
11
|
+
permit: ["role(master)"],
|
|
12
|
+
async execute({ input, params, use }) {
|
|
13
|
+
const provider = parseOAuthProvider(params.provider);
|
|
14
|
+
if (!provider) {
|
|
15
|
+
return "用法:calendar auth google | calendar auth microsoft";
|
|
16
|
+
}
|
|
17
|
+
if (!input?.sender?.id) {
|
|
18
|
+
return "请在私聊或群聊中发送该命令";
|
|
19
|
+
}
|
|
20
|
+
const requester = {
|
|
21
|
+
sender_id: input.sender.id,
|
|
22
|
+
target: createDeliveryTarget(input),
|
|
23
|
+
};
|
|
24
|
+
return use(calendarRuntimeToken).auth.startAuthorization(provider, requester);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defineCommand } from "zhin.js/command";
|
|
2
|
+
import { createDeliveryTarget } from "zhin.js";
|
|
3
|
+
import { parseOAuthProvider } from "../../../src/chat-auth.js";
|
|
4
|
+
import { calendarRuntimeToken } from "../../../src/runtime.js";
|
|
5
|
+
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: "通过聊天完成 Google / Outlook 日历 OAuth 授权(master 专用)",
|
|
8
|
+
params: {
|
|
9
|
+
provider: { type: "string", description: "google 或 microsoft(outlook)" },
|
|
10
|
+
},
|
|
11
|
+
permit: ["role(master)"],
|
|
12
|
+
async execute({ input, params, use }) {
|
|
13
|
+
const provider = parseOAuthProvider(params.provider);
|
|
14
|
+
if (!provider) {
|
|
15
|
+
return "用法:calendar auth google | calendar auth microsoft";
|
|
16
|
+
}
|
|
17
|
+
if (!input?.sender?.id) {
|
|
18
|
+
return "请在私聊或群聊中发送该命令";
|
|
19
|
+
}
|
|
20
|
+
const requester = {
|
|
21
|
+
sender_id: input.sender.id,
|
|
22
|
+
target: createDeliveryTarget(input),
|
|
23
|
+
};
|
|
24
|
+
return use(calendarRuntimeToken).auth.startAuthorization(
|
|
25
|
+
provider,
|
|
26
|
+
requester
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from "zhin.js/command";
|
|
3
|
+
import { parseOAuthProvider } from "../../../lib/chat-auth.js";
|
|
4
|
+
import { calendarRuntimeToken } from "../../../lib/runtime.js";
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
description: "删除数据库中保存的日历授权(master 专用)",
|
|
7
|
+
params: {
|
|
8
|
+
provider: { type: "string", description: "google 或 microsoft(outlook)" },
|
|
9
|
+
},
|
|
10
|
+
permit: ["role(master)"],
|
|
11
|
+
async execute({ params, use }) {
|
|
12
|
+
const provider = parseOAuthProvider(params.provider);
|
|
13
|
+
if (!provider) {
|
|
14
|
+
return "用法:calendar logout google | calendar logout microsoft";
|
|
15
|
+
}
|
|
16
|
+
return use(calendarRuntimeToken).auth.logout(provider);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCommand } from "zhin.js/command";
|
|
2
|
+
import { parseOAuthProvider } from "../../../src/chat-auth.js";
|
|
3
|
+
import { calendarRuntimeToken } from "../../../src/runtime.js";
|
|
4
|
+
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
description: "删除数据库中保存的日历授权(master 专用)",
|
|
7
|
+
params: {
|
|
8
|
+
provider: { type: "string", description: "google 或 microsoft(outlook)" },
|
|
9
|
+
},
|
|
10
|
+
permit: ["role(master)"],
|
|
11
|
+
async execute({ params, use }) {
|
|
12
|
+
const provider = parseOAuthProvider(params.provider);
|
|
13
|
+
if (!provider) {
|
|
14
|
+
return "用法:calendar logout google | calendar logout microsoft";
|
|
15
|
+
}
|
|
16
|
+
return use(calendarRuntimeToken).auth.logout(provider);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from "zhin.js/command";
|
|
3
|
+
import { calendarRuntimeToken } from "../../../lib/runtime.js";
|
|
4
|
+
export default defineCommand({
|
|
5
|
+
description: "查看日历授权状态(master 专用)",
|
|
6
|
+
permit: ["role(master)"],
|
|
7
|
+
async execute({ use }) {
|
|
8
|
+
return use(calendarRuntimeToken).auth.describeStatus();
|
|
9
|
+
},
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineCommand } from "zhin.js/command";
|
|
2
|
+
import { calendarRuntimeToken } from "../../../src/runtime.js";
|
|
3
|
+
|
|
4
|
+
export default defineCommand({
|
|
5
|
+
description: "查看日历授权状态(master 专用)",
|
|
6
|
+
permit: ["role(master)"],
|
|
7
|
+
async execute({ use }) {
|
|
8
|
+
return use(calendarRuntimeToken).auth.describeStatus();
|
|
9
|
+
},
|
|
10
|
+
});
|
package/lib/caldav.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal CalDAV client (RFC 4791) for listing and creating VEVENTs.
|
|
3
|
+
* Works with iCloud, Fastmail and Nextcloud calendars.
|
|
4
|
+
*/
|
|
5
|
+
import type { CalendarEvent, CalendarProvider } from "./types.js";
|
|
6
|
+
export interface CalDavOptions {
|
|
7
|
+
readonly calendarUrl: string;
|
|
8
|
+
readonly username: string;
|
|
9
|
+
readonly password: string;
|
|
10
|
+
readonly timezone: string;
|
|
11
|
+
readonly fetchImpl?: typeof fetch;
|
|
12
|
+
}
|
|
13
|
+
declare function escapeXml(value: string): string;
|
|
14
|
+
/** Parses VEVENT components from a CalDAV multistatus response. */
|
|
15
|
+
export declare function parseCalDavEvents(xml: string, timezone: string): CalendarEvent[];
|
|
16
|
+
export declare function createCalDavProvider(options: CalDavOptions): CalendarProvider;
|
|
17
|
+
export { escapeXml };
|
|
18
|
+
//# sourceMappingURL=caldav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caldav.d.ts","sourceRoot":"","sources":["../src/caldav.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CACnC;AAMD,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMxC;AAuCD,mEAAmE;AACnE,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,aAAa,EAAE,CA2BjB;AAgCD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,gBAAgB,CAgE7E;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
package/lib/caldav.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { formatInZone } from "./time.js";
|
|
2
|
+
function basicAuth(username, password) {
|
|
3
|
+
return `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
|
|
4
|
+
}
|
|
5
|
+
function escapeXml(value) {
|
|
6
|
+
return value
|
|
7
|
+
.replace(/&/g, "&")
|
|
8
|
+
.replace(/</g, "<")
|
|
9
|
+
.replace(/>/g, ">")
|
|
10
|
+
.replace(/"/g, """);
|
|
11
|
+
}
|
|
12
|
+
function toUtcStamp(date) {
|
|
13
|
+
return date
|
|
14
|
+
.toISOString()
|
|
15
|
+
.replace(/[-:]/g, "")
|
|
16
|
+
.replace(/\.\d{3}/, "");
|
|
17
|
+
}
|
|
18
|
+
function parseIcsDate(value) {
|
|
19
|
+
const match = /^(\d{4})(\d{2})(\d{2})(?:T(\d{2})(\d{2})(\d{2})(Z)?)?/.exec(value.trim());
|
|
20
|
+
if (!match)
|
|
21
|
+
return new Date(NaN);
|
|
22
|
+
if (!match[4]) {
|
|
23
|
+
return new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
|
|
24
|
+
}
|
|
25
|
+
const utc = match[7] === "Z";
|
|
26
|
+
const parts = [
|
|
27
|
+
Number(match[1]),
|
|
28
|
+
Number(match[2]) - 1,
|
|
29
|
+
Number(match[3]),
|
|
30
|
+
Number(match[4]),
|
|
31
|
+
Number(match[5]),
|
|
32
|
+
Number(match[6] ?? 0),
|
|
33
|
+
];
|
|
34
|
+
return utc ? new Date(Date.UTC(...parts)) : new Date(...parts);
|
|
35
|
+
}
|
|
36
|
+
function extractField(block, name) {
|
|
37
|
+
const pattern = new RegExp(`^${name}(?:;[^:]*)?:(.*)$`, "im");
|
|
38
|
+
const match = pattern.exec(block);
|
|
39
|
+
return match
|
|
40
|
+
? match[1].trim().replace(/\\n/g, "\n").replace(/\\,/g, ",")
|
|
41
|
+
: undefined;
|
|
42
|
+
}
|
|
43
|
+
/** Parses VEVENT components from a CalDAV multistatus response. */
|
|
44
|
+
export function parseCalDavEvents(xml, timezone) {
|
|
45
|
+
const events = [];
|
|
46
|
+
const hrefs = [
|
|
47
|
+
...xml.matchAll(/<[^:>]*:?href[^>]*>([^<]+)<\/[^:>]*:?href>/gi),
|
|
48
|
+
].map((m) => m[1].trim());
|
|
49
|
+
const calendars = [...xml.matchAll(/BEGIN:VEVENT([\s\S]*?)END:VEVENT/g)];
|
|
50
|
+
calendars.forEach((match, index) => {
|
|
51
|
+
const block = match[1];
|
|
52
|
+
const uid = extractField(block, "UID") ?? hrefs[index] ?? `event-${index}`;
|
|
53
|
+
const summary = extractField(block, "SUMMARY") ?? "(无标题)";
|
|
54
|
+
const startRaw = extractField(block, "DTSTART");
|
|
55
|
+
const endRaw = extractField(block, "DTEND") ?? startRaw;
|
|
56
|
+
if (!startRaw || !endRaw)
|
|
57
|
+
return;
|
|
58
|
+
const start = parseIcsDate(startRaw);
|
|
59
|
+
const end = parseIcsDate(endRaw);
|
|
60
|
+
const allDay = !/T/.test(startRaw);
|
|
61
|
+
events.push({
|
|
62
|
+
id: uid,
|
|
63
|
+
title: summary,
|
|
64
|
+
start: formatInZone(start, timezone),
|
|
65
|
+
end: formatInZone(end, timezone),
|
|
66
|
+
location: extractField(block, "LOCATION"),
|
|
67
|
+
description: extractField(block, "DESCRIPTION"),
|
|
68
|
+
all_day: allDay,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
return events;
|
|
72
|
+
}
|
|
73
|
+
function buildVevent(input, uid) {
|
|
74
|
+
const lines = [
|
|
75
|
+
"BEGIN:VCALENDAR",
|
|
76
|
+
"VERSION:2.0",
|
|
77
|
+
"PRODID:-//zhin.js-assistant-toolkit//EN",
|
|
78
|
+
"BEGIN:VEVENT",
|
|
79
|
+
`UID:${uid}`,
|
|
80
|
+
`DTSTAMP:${toUtcStamp(new Date())}`,
|
|
81
|
+
input.all_day
|
|
82
|
+
? `DTSTART;VALUE=DATE:${input.start
|
|
83
|
+
.toISOString()
|
|
84
|
+
.slice(0, 10)
|
|
85
|
+
.replace(/-/g, "")}`
|
|
86
|
+
: `DTSTART:${toUtcStamp(input.start)}`,
|
|
87
|
+
input.all_day
|
|
88
|
+
? `DTEND;VALUE=DATE:${input.end
|
|
89
|
+
.toISOString()
|
|
90
|
+
.slice(0, 10)
|
|
91
|
+
.replace(/-/g, "")}`
|
|
92
|
+
: `DTEND:${toUtcStamp(input.end)}`,
|
|
93
|
+
`SUMMARY:${input.title.replace(/\n/g, " ")}`,
|
|
94
|
+
];
|
|
95
|
+
if (input.location)
|
|
96
|
+
lines.push(`LOCATION:${input.location.replace(/\n/g, " ")}`);
|
|
97
|
+
if (input.description)
|
|
98
|
+
lines.push(`DESCRIPTION:${input.description.replace(/\n/g, "\\n")}`);
|
|
99
|
+
lines.push("END:VEVENT", "END:VCALENDAR");
|
|
100
|
+
return lines.join("\r\n");
|
|
101
|
+
}
|
|
102
|
+
export function createCalDavProvider(options) {
|
|
103
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
104
|
+
const auth = basicAuth(options.username, options.password);
|
|
105
|
+
const base = options.calendarUrl.endsWith("/")
|
|
106
|
+
? options.calendarUrl
|
|
107
|
+
: `${options.calendarUrl}/`;
|
|
108
|
+
return {
|
|
109
|
+
name: "caldav",
|
|
110
|
+
async listEvents(from, to, signal) {
|
|
111
|
+
const body = `<?xml version="1.0" encoding="utf-8" ?>
|
|
112
|
+
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
|
113
|
+
<D:prop><D:getetag/><C:calendar-data/></D:prop>
|
|
114
|
+
<C:filter>
|
|
115
|
+
<C:comp-filter name="VCALENDAR">
|
|
116
|
+
<C:comp-filter name="VEVENT">
|
|
117
|
+
<C:time-range start="${toUtcStamp(from)}" end="${toUtcStamp(to)}"/>
|
|
118
|
+
</C:comp-filter>
|
|
119
|
+
</C:comp-filter>
|
|
120
|
+
</C:filter>
|
|
121
|
+
</C:calendar-query>`;
|
|
122
|
+
const response = await fetchImpl(base, {
|
|
123
|
+
method: "REPORT",
|
|
124
|
+
headers: {
|
|
125
|
+
authorization: auth,
|
|
126
|
+
depth: "1",
|
|
127
|
+
"content-type": "application/xml; charset=utf-8",
|
|
128
|
+
},
|
|
129
|
+
body,
|
|
130
|
+
signal,
|
|
131
|
+
});
|
|
132
|
+
if (!response.ok)
|
|
133
|
+
throw new Error(`CalDAV REPORT 失败:HTTP ${response.status}`);
|
|
134
|
+
return parseCalDavEvents(await response.text(), options.timezone);
|
|
135
|
+
},
|
|
136
|
+
async createEvent(input, signal) {
|
|
137
|
+
const uid = `${Date.now()}-${Math.random()
|
|
138
|
+
.toString(36)
|
|
139
|
+
.slice(2, 10)}@zhin`;
|
|
140
|
+
const url = new URL(`${uid}.ics`, base).toString();
|
|
141
|
+
const response = await fetchImpl(url, {
|
|
142
|
+
method: "PUT",
|
|
143
|
+
headers: {
|
|
144
|
+
authorization: auth,
|
|
145
|
+
"content-type": "text/calendar; charset=utf-8",
|
|
146
|
+
"if-none-match": "*",
|
|
147
|
+
},
|
|
148
|
+
body: buildVevent(input, uid),
|
|
149
|
+
signal,
|
|
150
|
+
});
|
|
151
|
+
if (response.status !== 201 && response.status !== 204) {
|
|
152
|
+
throw new Error(`CalDAV PUT 失败:HTTP ${response.status}`);
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
id: uid,
|
|
156
|
+
title: input.title,
|
|
157
|
+
start: formatInZone(input.start, options.timezone),
|
|
158
|
+
end: formatInZone(input.end, options.timezone),
|
|
159
|
+
location: input.location,
|
|
160
|
+
description: input.description,
|
|
161
|
+
all_day: input.all_day,
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export { escapeXml };
|
|
167
|
+
//# sourceMappingURL=caldav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caldav.js","sourceRoot":"","sources":["../src/caldav.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat-driven OAuth authorization: `calendar auth <provider>` creates a PKCE
|
|
3
|
+
* link, the HTTP callback exchanges the code, stores the refresh token in the
|
|
4
|
+
* plugin database and notifies the conversation that requested it.
|
|
5
|
+
*/
|
|
6
|
+
import type { CalendarConfig } from "./config.js";
|
|
7
|
+
import type { CalendarCredentialStore, CredentialOwner } from "./credential-store.js";
|
|
8
|
+
import { type OAuthProviderKind, type OAuthTokenManager } from "./token-manager.js";
|
|
9
|
+
export declare const DEFAULT_OAUTH_CALLBACK_PATH = "/calendar/oauth/callback";
|
|
10
|
+
export declare const OAUTH_PROVIDERS: readonly OAuthProviderKind[];
|
|
11
|
+
export type CalendarNotifier<Target> = (target: Target, text: string) => Promise<void>;
|
|
12
|
+
export interface CalendarAuthServiceOptions<Target> {
|
|
13
|
+
readonly config: CalendarConfig;
|
|
14
|
+
readonly managers: Partial<Record<OAuthProviderKind, OAuthTokenManager>>;
|
|
15
|
+
readonly credentials: CalendarCredentialStore | undefined;
|
|
16
|
+
readonly notify: CalendarNotifier<Target> | undefined;
|
|
17
|
+
/** Returns the full callback URL, or undefined when no HTTP host is available. */
|
|
18
|
+
readonly resolveRedirectUri: () => string | undefined;
|
|
19
|
+
readonly fetchImpl?: typeof fetch;
|
|
20
|
+
readonly now?: () => number;
|
|
21
|
+
}
|
|
22
|
+
export interface CallbackResult {
|
|
23
|
+
readonly statusCode: number;
|
|
24
|
+
readonly message: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function parseOAuthProvider(value: unknown): OAuthProviderKind | undefined;
|
|
27
|
+
/** Joins a base URL and the callback path, tolerating extra/missing slashes. */
|
|
28
|
+
export declare function buildRedirectUri(baseUrl: string, callbackPath: string): string;
|
|
29
|
+
/** Turns a listening address into a browser-reachable local origin. */
|
|
30
|
+
export declare function localOriginFromAddress(address: {
|
|
31
|
+
readonly protocol: string;
|
|
32
|
+
readonly host: string;
|
|
33
|
+
readonly port: number;
|
|
34
|
+
}): string;
|
|
35
|
+
export declare class CalendarAuthService<Target> {
|
|
36
|
+
#private;
|
|
37
|
+
constructor(options: CalendarAuthServiceOptions<Target>);
|
|
38
|
+
/** Handles `calendar auth <provider>` and returns the chat reply. */
|
|
39
|
+
startAuthorization(provider: OAuthProviderKind, requester: CredentialOwner<Target>): Promise<string>;
|
|
40
|
+
/** Handles the OAuth redirect back to the HTTP host. */
|
|
41
|
+
handleCallback(searchParams: URLSearchParams): Promise<CallbackResult>;
|
|
42
|
+
/** Handles `calendar status`. */
|
|
43
|
+
describeStatus(): Promise<string>;
|
|
44
|
+
/** Handles `calendar logout <provider>`: removes the stored token. */
|
|
45
|
+
logout(provider: OAuthProviderKind): Promise<string>;
|
|
46
|
+
/** Called by the token manager once when the refresh token is rejected. */
|
|
47
|
+
notifyAuthorizationLost(provider: OAuthProviderKind, message: string): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=chat-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-auth.d.ts","sourceRoot":"","sources":["../src/chat-auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,uBAAuB,EACvB,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAS/B,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,2BAA2B,6BAA6B,CAAC;AAEtE,eAAO,MAAM,eAAe,EAAE,SAAS,iBAAiB,EAGvD,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,MAAM,IAAI,CACrC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,0BAA0B,CAAC,MAAM;IAChD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACzE,QAAQ,CAAC,WAAW,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAC1D,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IACtD,kFAAkF;IAClF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACtD,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAaD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,GACb,iBAAiB,GAAG,SAAS,CAW/B;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,MAAM,CAIR;AAED,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,CAMT;AAED,qBAAa,mBAAmB,CAAC,MAAM;;gBASzB,OAAO,EAAE,0BAA0B,CAAC,MAAM,CAAC;IAUvD,qEAAqE;IAC/D,kBAAkB,CACtB,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC,GACjC,OAAO,CAAC,MAAM,CAAC;IA0ClB,wDAAwD;IAClD,cAAc,CAAC,YAAY,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAwE5E,iCAAiC;IAC3B,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAQvC,sEAAsE;IAChE,MAAM,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAU1D,2EAA2E;IACrE,uBAAuB,CAC3B,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;CAwEjB"}
|