@neomei/opencode-feishu 0.1.2 → 0.2.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +29 -4
  2. package/LICENSE +0 -0
  3. package/README.md +114 -6
  4. package/dist/cli.js +154 -6
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/config.d.ts +30 -0
  7. package/dist/core/config.d.ts.map +1 -1
  8. package/dist/core/config.js +41 -1
  9. package/dist/core/config.js.map +1 -1
  10. package/dist/core/daemon.d.ts.map +1 -1
  11. package/dist/core/daemon.js +2 -1
  12. package/dist/core/daemon.js.map +1 -1
  13. package/dist/core/dedup.d.ts +29 -0
  14. package/dist/core/dedup.d.ts.map +1 -0
  15. package/dist/core/dedup.js +69 -0
  16. package/dist/core/dedup.js.map +1 -0
  17. package/dist/core/file-downloader.d.ts +24 -0
  18. package/dist/core/file-downloader.d.ts.map +1 -0
  19. package/dist/core/file-downloader.js +79 -0
  20. package/dist/core/file-downloader.js.map +1 -0
  21. package/dist/core/hook-manager.d.ts +14 -0
  22. package/dist/core/hook-manager.d.ts.map +1 -0
  23. package/dist/core/hook-manager.js +71 -0
  24. package/dist/core/hook-manager.js.map +1 -0
  25. package/dist/core/message-handler.d.ts +17 -1
  26. package/dist/core/message-handler.d.ts.map +1 -1
  27. package/dist/core/message-handler.js +151 -8
  28. package/dist/core/message-handler.js.map +1 -1
  29. package/dist/core/profile-manager.d.ts +27 -0
  30. package/dist/core/profile-manager.d.ts.map +1 -0
  31. package/dist/core/profile-manager.js +149 -0
  32. package/dist/core/profile-manager.js.map +1 -0
  33. package/dist/core/session-manager.d.ts +8 -1
  34. package/dist/core/session-manager.d.ts.map +1 -1
  35. package/dist/core/session-manager.js +27 -4
  36. package/dist/core/session-manager.js.map +1 -1
  37. package/dist/core/types.d.ts +13 -0
  38. package/dist/core/types.d.ts.map +1 -1
  39. package/dist/feishu/api.d.ts +17 -0
  40. package/dist/feishu/api.d.ts.map +1 -1
  41. package/dist/feishu/api.js +106 -0
  42. package/dist/feishu/api.js.map +1 -1
  43. package/dist/feishu/card.d.ts +4 -1
  44. package/dist/feishu/card.d.ts.map +1 -1
  45. package/dist/feishu/card.js +26 -6
  46. package/dist/feishu/card.js.map +1 -1
  47. package/dist/feishu/event-source.d.ts.map +1 -1
  48. package/dist/feishu/event-source.js +3 -0
  49. package/dist/feishu/event-source.js.map +1 -1
  50. package/dist/feishu/silent-logger.d.ts +5 -5
  51. package/dist/feishu/silent-logger.d.ts.map +1 -1
  52. package/dist/feishu/silent-logger.js +5 -5
  53. package/dist/feishu/silent-logger.js.map +1 -1
  54. package/dist/index.d.ts +3 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +3 -0
  57. package/dist/index.js.map +1 -1
  58. package/dist/opencode/client.d.ts +5 -1
  59. package/dist/opencode/client.d.ts.map +1 -1
  60. package/dist/opencode/client.js +15 -2
  61. package/dist/opencode/client.js.map +1 -1
  62. package/dist/opencode/event-handler.d.ts +6 -1
  63. package/dist/opencode/event-handler.d.ts.map +1 -1
  64. package/dist/opencode/event-handler.js +31 -3
  65. package/dist/opencode/event-handler.js.map +1 -1
  66. package/dist/plugin.d.ts.map +1 -1
  67. package/dist/plugin.js +4 -2
  68. package/dist/plugin.js.map +1 -1
  69. package/dist/services/approval-service.d.ts +20 -0
  70. package/dist/services/approval-service.d.ts.map +1 -0
  71. package/dist/services/approval-service.js +123 -0
  72. package/dist/services/approval-service.js.map +1 -0
  73. package/dist/services/base-service.d.ts +22 -0
  74. package/dist/services/base-service.d.ts.map +1 -0
  75. package/dist/services/base-service.js +47 -0
  76. package/dist/services/base-service.js.map +1 -0
  77. package/dist/services/calendar-service.d.ts +25 -0
  78. package/dist/services/calendar-service.d.ts.map +1 -0
  79. package/dist/services/calendar-service.js +220 -0
  80. package/dist/services/calendar-service.js.map +1 -0
  81. package/dist/services/chat-service.d.ts +52 -0
  82. package/dist/services/chat-service.d.ts.map +1 -0
  83. package/dist/services/chat-service.js +170 -0
  84. package/dist/services/chat-service.js.map +1 -0
  85. package/dist/services/contact-service.d.ts +40 -0
  86. package/dist/services/contact-service.d.ts.map +1 -0
  87. package/dist/services/contact-service.js +137 -0
  88. package/dist/services/contact-service.js.map +1 -0
  89. package/dist/services/doc-service.d.ts +137 -0
  90. package/dist/services/doc-service.d.ts.map +1 -0
  91. package/dist/services/doc-service.js +516 -0
  92. package/dist/services/doc-service.js.map +1 -0
  93. package/dist/services/im-service.d.ts +64 -0
  94. package/dist/services/im-service.d.ts.map +1 -0
  95. package/dist/services/im-service.js +215 -0
  96. package/dist/services/im-service.js.map +1 -0
  97. package/dist/services/index.d.ts +9 -0
  98. package/dist/services/index.d.ts.map +1 -0
  99. package/dist/services/index.js +10 -0
  100. package/dist/services/index.js.map +1 -0
  101. package/dist/services/task-service.d.ts +19 -0
  102. package/dist/services/task-service.d.ts.map +1 -0
  103. package/dist/services/task-service.js +126 -0
  104. package/dist/services/task-service.js.map +1 -0
  105. package/dist/setup/device-flow.d.ts +31 -0
  106. package/dist/setup/device-flow.d.ts.map +1 -0
  107. package/dist/setup/device-flow.js +133 -0
  108. package/dist/setup/device-flow.js.map +1 -0
  109. package/dist/setup/preflight.d.ts +1 -0
  110. package/dist/setup/preflight.d.ts.map +1 -1
  111. package/dist/setup/preflight.js +167 -1
  112. package/dist/setup/preflight.js.map +1 -1
  113. package/dist/setup/wizard.d.ts +3 -1
  114. package/dist/setup/wizard.d.ts.map +1 -1
  115. package/dist/setup/wizard.js +113 -125
  116. package/dist/setup/wizard.js.map +1 -1
  117. package/dist/standalone.d.ts.map +1 -1
  118. package/dist/standalone.js +49 -13
  119. package/dist/standalone.js.map +1 -1
  120. package/dist/types/extended.d.ts +196 -0
  121. package/dist/types/extended.d.ts.map +1 -0
  122. package/dist/types/extended.js +6 -0
  123. package/dist/types/extended.js.map +1 -0
  124. package/package.json +5 -3
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Simple in-memory message deduplicator with TTL.
3
+ * Prevents processing the same message multiple times (e.g. from WS reconnects).
4
+ */
5
+ export declare class MessageDeduplicator {
6
+ private seen;
7
+ private ttlMs;
8
+ private cleanupInterval?;
9
+ constructor(ttlMs?: number);
10
+ /**
11
+ * Check if a message has been seen before.
12
+ * If not, record it and return false.
13
+ * If yes, return true (it's a duplicate).
14
+ */
15
+ isDuplicate(messageId: string): boolean;
16
+ /**
17
+ * Get the current size of the dedup cache.
18
+ */
19
+ size(): number;
20
+ /**
21
+ * Clean up expired entries.
22
+ */
23
+ private cleanup;
24
+ /**
25
+ * Stop the cleanup interval.
26
+ */
27
+ stop(): void;
28
+ }
29
+ //# sourceMappingURL=dedup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../../src/core/dedup.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,IAAI,CAAiC;IAC7C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,eAAe,CAAC,CAAiB;gBAE7B,KAAK,SAAU;IAW3B;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAavC;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,OAAO,CAAC,OAAO;IAcf;;OAEG;IACH,IAAI,IAAI,IAAI;CAOb"}
@@ -0,0 +1,69 @@
1
+ import { createLogger } from './logger.js';
2
+ const log = createLogger('MessageDeduplicator');
3
+ /**
4
+ * Simple in-memory message deduplicator with TTL.
5
+ * Prevents processing the same message multiple times (e.g. from WS reconnects).
6
+ */
7
+ export class MessageDeduplicator {
8
+ seen = new Map();
9
+ ttlMs;
10
+ cleanupInterval;
11
+ constructor(ttlMs = 600_000) {
12
+ // Default 10 minutes
13
+ this.ttlMs = ttlMs;
14
+ // Run cleanup every TTL interval
15
+ this.cleanupInterval = setInterval(() => this.cleanup(), ttlMs);
16
+ // Ensure cleanup doesn't keep process alive
17
+ if (this.cleanupInterval.unref) {
18
+ this.cleanupInterval.unref();
19
+ }
20
+ }
21
+ /**
22
+ * Check if a message has been seen before.
23
+ * If not, record it and return false.
24
+ * If yes, return true (it's a duplicate).
25
+ */
26
+ isDuplicate(messageId) {
27
+ const now = Date.now();
28
+ const entry = this.seen.get(messageId);
29
+ if (entry && now - entry.timestamp < this.ttlMs) {
30
+ log.debug({ messageId, ageMs: now - entry.timestamp }, 'Duplicate message detected');
31
+ return true;
32
+ }
33
+ this.seen.set(messageId, { timestamp: now });
34
+ return false;
35
+ }
36
+ /**
37
+ * Get the current size of the dedup cache.
38
+ */
39
+ size() {
40
+ return this.seen.size;
41
+ }
42
+ /**
43
+ * Clean up expired entries.
44
+ */
45
+ cleanup() {
46
+ const now = Date.now();
47
+ let cleaned = 0;
48
+ for (const [id, entry] of this.seen.entries()) {
49
+ if (now - entry.timestamp > this.ttlMs) {
50
+ this.seen.delete(id);
51
+ cleaned++;
52
+ }
53
+ }
54
+ if (cleaned > 0) {
55
+ log.debug({ cleaned, remaining: this.seen.size }, 'Dedup cache cleaned');
56
+ }
57
+ }
58
+ /**
59
+ * Stop the cleanup interval.
60
+ */
61
+ stop() {
62
+ if (this.cleanupInterval) {
63
+ clearInterval(this.cleanupInterval);
64
+ this.cleanupInterval = undefined;
65
+ }
66
+ this.seen.clear();
67
+ }
68
+ }
69
+ //# sourceMappingURL=dedup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup.js","sourceRoot":"","sources":["../../src/core/dedup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,GAAG,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAMhD;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IACtB,IAAI,GAAG,IAAI,GAAG,EAAsB,CAAC;IACrC,KAAK,CAAS;IACd,eAAe,CAAkB;IAEzC,YAAY,KAAK,GAAG,OAAO;QACzB,qBAAqB;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,iCAAiC;QACjC,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;QAChE,4CAA4C;QAC5C,IAAI,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAC/B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,SAAiB;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAEvC,IAAI,KAAK,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAChD,GAAG,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,EAAE,4BAA4B,CAAC,CAAC;YACrF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7C,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACxB,CAAC;IAED;;OAEG;IACK,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9C,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBACvC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrB,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACpC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ export interface DownloadedFile {
2
+ filePath: string;
3
+ fileName: string;
4
+ mimeType: string;
5
+ }
6
+ /**
7
+ * Download files from Feishu to local storage.
8
+ * Files are saved to ~/.config/opencode/feishu-attachments/
9
+ */
10
+ export declare class FileDownloader {
11
+ private baseDir;
12
+ constructor(baseDir?: string);
13
+ /**
14
+ * Download a file from a URL and save it locally.
15
+ * Returns the local file path.
16
+ */
17
+ downloadFromUrl(url: string, fileName: string, mimeType: string): Promise<DownloadedFile>;
18
+ /**
19
+ * Save a buffer directly to a file.
20
+ */
21
+ saveBuffer(buffer: Buffer, fileName: string, mimeType: string): Promise<DownloadedFile>;
22
+ private sanitizeFileName;
23
+ }
24
+ //# sourceMappingURL=file-downloader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-downloader.d.ts","sourceRoot":"","sources":["../../src/core/file-downloader.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,CAAC,EAAE,MAAM;IAO5B;;;OAGG;IACG,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA0C/F;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAmB7F,OAAO,CAAC,gBAAgB;CAOzB"}
@@ -0,0 +1,79 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import { createLogger } from './logger.js';
5
+ const log = createLogger('FileDownloader');
6
+ const DEFAULT_ATTACHMENTS_DIR = join(homedir(), '.config', 'opencode', 'feishu-attachments');
7
+ const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB limit
8
+ /**
9
+ * Download files from Feishu to local storage.
10
+ * Files are saved to ~/.config/opencode/feishu-attachments/
11
+ */
12
+ export class FileDownloader {
13
+ baseDir;
14
+ constructor(baseDir) {
15
+ this.baseDir = baseDir || DEFAULT_ATTACHMENTS_DIR;
16
+ if (!existsSync(this.baseDir)) {
17
+ mkdirSync(this.baseDir, { recursive: true });
18
+ }
19
+ }
20
+ /**
21
+ * Download a file from a URL and save it locally.
22
+ * Returns the local file path.
23
+ */
24
+ async downloadFromUrl(url, fileName, mimeType) {
25
+ try {
26
+ log.info({ url, fileName }, 'Downloading file from URL');
27
+ const controller = new AbortController();
28
+ const timeout = setTimeout(() => controller.abort(), 30000); // 30s timeout
29
+ const response = await fetch(url, { signal: controller.signal });
30
+ clearTimeout(timeout);
31
+ if (!response.ok) {
32
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
33
+ }
34
+ const contentLength = response.headers.get('content-length');
35
+ if (contentLength && parseInt(contentLength) > MAX_FILE_SIZE) {
36
+ throw new Error(`File too large: ${contentLength} bytes (max ${MAX_FILE_SIZE})`);
37
+ }
38
+ const buffer = Buffer.from(await response.arrayBuffer());
39
+ if (buffer.length > MAX_FILE_SIZE) {
40
+ throw new Error(`File too large: ${buffer.length} bytes (max ${MAX_FILE_SIZE})`);
41
+ }
42
+ // Sanitize filename
43
+ const safeName = this.sanitizeFileName(fileName);
44
+ const timestamp = Date.now();
45
+ const uniqueName = `${timestamp}_${safeName}`;
46
+ const filePath = join(this.baseDir, uniqueName);
47
+ writeFileSync(filePath, buffer);
48
+ log.info({ filePath, size: buffer.length }, 'File downloaded successfully');
49
+ return { filePath, fileName: safeName, mimeType };
50
+ }
51
+ catch (err) {
52
+ log.error({ err, url, fileName }, 'Failed to download file');
53
+ throw err;
54
+ }
55
+ }
56
+ /**
57
+ * Save a buffer directly to a file.
58
+ */
59
+ async saveBuffer(buffer, fileName, mimeType) {
60
+ if (buffer.length > MAX_FILE_SIZE) {
61
+ throw new Error(`File too large: ${buffer.length} bytes (max ${MAX_FILE_SIZE})`);
62
+ }
63
+ const safeName = this.sanitizeFileName(fileName);
64
+ const timestamp = Date.now();
65
+ const uniqueName = `${timestamp}_${safeName}`;
66
+ const filePath = join(this.baseDir, uniqueName);
67
+ writeFileSync(filePath, buffer);
68
+ log.info({ filePath, size: buffer.length }, 'Buffer saved to file');
69
+ return { filePath, fileName: safeName, mimeType };
70
+ }
71
+ sanitizeFileName(name) {
72
+ // Remove path traversal characters and limit length
73
+ return name
74
+ .replace(/[\x00-\x1f\x7f]/g, '') // Control chars
75
+ .replace(/[/\\?%<>|":]/g, '_') // Invalid path chars
76
+ .substring(0, 200); // Limit length
77
+ }
78
+ }
79
+ //# sourceMappingURL=file-downloader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-downloader.js","sourceRoot":"","sources":["../../src/core/file-downloader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { HookConfig } from './types.js';
2
+ export interface HookContext {
3
+ sessionId: string;
4
+ opencodeUrl: string;
5
+ [key: string]: string | undefined;
6
+ }
7
+ export declare class HookManager {
8
+ private config;
9
+ private projectDir;
10
+ constructor(config: HookConfig, projectDir: string);
11
+ run(hookName: keyof HookConfig, ctx: HookContext): Promise<void>;
12
+ private execScript;
13
+ }
14
+ //# sourceMappingURL=hook-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-manager.d.ts","sourceRoot":"","sources":["../../src/core/hook-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM;IAK5C,GAAG,CAAC,QAAQ,EAAE,MAAM,UAAU,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IActE,OAAO,CAAC,UAAU;CAoDnB"}
@@ -0,0 +1,71 @@
1
+ import { spawn } from 'child_process';
2
+ import { createLogger } from './logger.js';
3
+ const log = createLogger('HookManager');
4
+ export class HookManager {
5
+ config;
6
+ projectDir;
7
+ constructor(config, projectDir) {
8
+ this.config = config;
9
+ this.projectDir = projectDir;
10
+ }
11
+ async run(hookName, ctx) {
12
+ const scriptPath = this.config[hookName];
13
+ if (!scriptPath)
14
+ return;
15
+ log.info({ hook: hookName, script: scriptPath, sessionId: ctx.sessionId }, 'Running hook');
16
+ try {
17
+ await this.execScript(scriptPath, ctx);
18
+ log.info({ hook: hookName, sessionId: ctx.sessionId }, 'Hook completed');
19
+ }
20
+ catch (err) {
21
+ log.error({ err, hook: hookName, sessionId: ctx.sessionId }, 'Hook failed');
22
+ }
23
+ }
24
+ execScript(scriptPath, ctx) {
25
+ return new Promise((resolve) => {
26
+ const absPath = scriptPath.startsWith('/')
27
+ ? scriptPath
28
+ : `${this.projectDir}/${scriptPath}`;
29
+ const env = {
30
+ ...process.env,
31
+ HOOK_SESSION_ID: ctx.sessionId,
32
+ HOOK_OPENCODE_URL: ctx.opencodeUrl,
33
+ };
34
+ // Pass additional context as HOOK_<KEY> env vars
35
+ for (const [key, value] of Object.entries(ctx)) {
36
+ if (value !== undefined) {
37
+ env[`HOOK_${key.toUpperCase()}`] = value;
38
+ }
39
+ }
40
+ const child = spawn(absPath, [], {
41
+ env,
42
+ stdio: ['ignore', 'pipe', 'pipe'],
43
+ timeout: 30000,
44
+ });
45
+ let stdout = '';
46
+ let stderr = '';
47
+ child.stdout?.on('data', (data) => {
48
+ stdout += data.toString();
49
+ });
50
+ child.stderr?.on('data', (data) => {
51
+ stderr += data.toString();
52
+ });
53
+ child.on('close', (code) => {
54
+ if (code === 0) {
55
+ if (stdout.trim())
56
+ log.info({ stdout: stdout.trim() }, 'Hook stdout');
57
+ resolve();
58
+ }
59
+ else {
60
+ log.warn({ code, stderr: stderr.trim(), stdout: stdout.trim() }, 'Hook exited non-zero');
61
+ resolve(); // Don't reject — hook failures shouldn't break the main flow
62
+ }
63
+ });
64
+ child.on('error', (err) => {
65
+ log.warn({ err }, 'Failed to spawn hook script');
66
+ resolve(); // Don't reject
67
+ });
68
+ });
69
+ }
70
+ }
71
+ //# sourceMappingURL=hook-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-manager.js","sourceRoot":"","sources":["../../src/core/hook-manager.ts"],"names":[],"mappings":"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"}
@@ -2,12 +2,28 @@ import type { FeishuConfig, FeishuMessage } from '../core/types.js';
2
2
  import type { SessionManager } from '../core/session-manager.js';
3
3
  import type { FeishuAPI } from '../feishu/api.js';
4
4
  import type { OpenCodeClient } from '../opencode/client.js';
5
+ import type { DocService } from '../services/doc-service.js';
5
6
  export declare class MessageHandler {
6
7
  private config;
7
8
  private sessionManager;
8
9
  private feishuApi;
9
10
  private opencode;
10
- constructor(config: FeishuConfig, sessionManager: SessionManager, feishuApi: FeishuAPI, opencode: OpenCodeClient);
11
+ private dedup;
12
+ private fileDownloader;
13
+ private docService?;
14
+ private botName;
15
+ private thinkingTimers;
16
+ constructor(config: FeishuConfig, sessionManager: SessionManager, feishuApi: FeishuAPI, opencode: OpenCodeClient, docService?: DocService, botName?: string);
11
17
  handleMessage(message: FeishuMessage): Promise<void>;
18
+ private stopThinkingAnimation;
19
+ /**
20
+ * Detect document creation intent from user message.
21
+ * Supports patterns like: "创建文档 XXX", "新建一个文档", "写个文档"
22
+ */
23
+ private detectDocCreationIntent;
24
+ /**
25
+ * Unified media download handler for images, files, audio, and video.
26
+ */
27
+ private downloadMedia;
12
28
  }
13
29
  //# sourceMappingURL=message-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"message-handler.d.ts","sourceRoot":"","sources":["../../src/core/message-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAM5D,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,QAAQ,CAAiB;gBAG/B,MAAM,EAAE,YAAY,EACpB,cAAc,EAAE,cAAc,EAC9B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,cAAc;IAQpB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;CA2I3D"}
1
+ {"version":3,"file":"message-handler.d.ts","sourceRoot":"","sources":["../../src/core/message-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAQ7D,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAqD;gBAGzE,MAAM,EAAE,YAAY,EACpB,cAAc,EAAE,cAAc,EAC9B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,cAAc,EACxB,UAAU,CAAC,EAAE,UAAU,EACvB,OAAO,SAAO;IAYV,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA4Q1D,OAAO,CAAC,qBAAqB;IAQ7B;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;OAEG;YACW,aAAa;CAsB5B"}
@@ -1,4 +1,6 @@
1
1
  import { FeishuCard } from '../feishu/card.js';
2
+ import { MessageDeduplicator } from './dedup.js';
3
+ import { FileDownloader } from './file-downloader.js';
2
4
  import { createLogger } from './logger.js';
3
5
  const log = createLogger('MessageHandler');
4
6
  export class MessageHandler {
@@ -6,11 +8,20 @@ export class MessageHandler {
6
8
  sessionManager;
7
9
  feishuApi;
8
10
  opencode;
9
- constructor(config, sessionManager, feishuApi, opencode) {
11
+ dedup;
12
+ fileDownloader;
13
+ docService;
14
+ botName;
15
+ thinkingTimers = new Map();
16
+ constructor(config, sessionManager, feishuApi, opencode, docService, botName = '点点') {
10
17
  this.config = config;
11
18
  this.sessionManager = sessionManager;
12
19
  this.feishuApi = feishuApi;
13
20
  this.opencode = opencode;
21
+ this.docService = docService;
22
+ this.botName = botName;
23
+ this.dedup = new MessageDeduplicator(config.dedupTtl || 600_000);
24
+ this.fileDownloader = new FileDownloader();
14
25
  }
15
26
  async handleMessage(message) {
16
27
  try {
@@ -30,6 +41,11 @@ export class MessageHandler {
30
41
  log.info('Skipping message from app/bot itself');
31
42
  return;
32
43
  }
44
+ // Deduplicate: skip if we've seen this message before
45
+ if (this.dedup.isDuplicate(message.message_id)) {
46
+ log.info({ messageId: message.message_id }, 'Duplicate message, skipping');
47
+ return;
48
+ }
33
49
  const chatId = message.chat_id;
34
50
  const chatType = message.chat_type;
35
51
  log.info({ chatType, sender: message.sender.sender_id?.union_id || 'unknown' }, 'Processing message');
@@ -69,16 +85,42 @@ export class MessageHandler {
69
85
  await this.feishuApi.sendText(chatId, '⏳ 正在处理上一条消息,请稍候...');
70
86
  return;
71
87
  }
72
- // Extract text content
88
+ // Resolve sender name (with cache)
89
+ const senderUnionId = message.sender.sender_id?.union_id || 'unknown';
90
+ const senderName = await this.feishuApi.getUserName(senderUnionId);
91
+ log.info({ senderName, senderId: senderUnionId }, 'Resolved sender name');
92
+ // Extract content and download files based on message type
73
93
  let text = '';
94
+ const files = [];
74
95
  try {
75
96
  const content = JSON.parse(message.content);
76
- text = content.text || '';
77
- log.info({ text: text.substring(0, 100) }, 'Extracted text');
97
+ switch (message.message_type) {
98
+ case 'text':
99
+ text = content.text || '';
100
+ break;
101
+ case 'image':
102
+ text = await this.downloadMedia(message.message_id, content.image_key, 'image', 'image.jpg', 'image/jpeg', '图片');
103
+ break;
104
+ case 'file':
105
+ text = await this.downloadMedia(message.message_id, content.file_key, 'file', content.file_name || 'unknown', 'application/octet-stream', '文件');
106
+ break;
107
+ case 'audio':
108
+ text = await this.downloadMedia(message.message_id, content.file_key, 'file', 'audio.opus', 'audio/opus', '语音');
109
+ break;
110
+ case 'media':
111
+ text = await this.downloadMedia(message.message_id, content.file_key, 'file', content.file_name || 'video.mp4', 'video/mp4', '视频');
112
+ break;
113
+ case 'sticker':
114
+ text = `[表情消息]`;
115
+ break;
116
+ default:
117
+ text = `[不支持的消息类型: ${message.message_type}]`;
118
+ }
119
+ log.info({ text: text.substring(0, 100), type: message.message_type, files: files.length }, 'Extracted content');
78
120
  }
79
121
  catch {
80
- log.warn({ content: message.content }, 'Failed to parse message content');
81
- text = '[不支持的消息类型]';
122
+ log.warn({ content: message.content, type: message.message_type }, 'Failed to parse message content');
123
+ text = `[不支持的消息类型: ${message.message_type}]`;
82
124
  }
83
125
  // Remove @mention from text if present
84
126
  if (message.mentions) {
@@ -87,11 +129,32 @@ export class MessageHandler {
87
129
  }
88
130
  log.info({ text: text.substring(0, 100) }, 'Text after removing mentions');
89
131
  }
132
+ // Prepend sender name in group chats for context
133
+ if (chatType === 'group' && senderName && senderName !== senderUnionId) {
134
+ text = `[${senderName}]: ${text}`;
135
+ }
90
136
  if (!text) {
91
137
  log.info('Empty text content, ignoring');
92
138
  return;
93
139
  }
94
140
  log.info({ chatType, chatId, text: text.substring(0, 100) }, 'Message content');
141
+ // Check for document creation intent
142
+ if (this.docService) {
143
+ const docIntent = this.detectDocCreationIntent(text);
144
+ if (docIntent.intent) {
145
+ log.info({ title: docIntent.title }, 'Document creation intent detected');
146
+ try {
147
+ const doc = await this.docService.createDocumentFromMarkdown(docIntent.title, `# ${docIntent.title}\n\n> 由 OpenCode AI 助手创建\n\n请在此处编辑文档内容...\n`);
148
+ await this.feishuApi.sendText(chatId, `✅ 已为您创建文档「${doc.title}」\n🔗 ${doc.url}\n\n您可以点击链接查看和编辑文档。`);
149
+ // Continue to OpenCode to let AI generate a friendly response
150
+ text = `${text}\n\n[系统提示:已为用户创建飞书文档「${doc.title}」,链接:${doc.url}。请告诉用户文档已创建,并简要说明可以如何使用。]`;
151
+ }
152
+ catch (err) {
153
+ log.error({ err, title: docIntent.title }, 'Failed to create document');
154
+ await this.feishuApi.sendText(chatId, '❌ 创建文档失败,请确认已开通文档相关权限(docx:document、docx:document:readonly)');
155
+ }
156
+ }
157
+ }
95
158
  // Atomically check and set busy status to prevent race conditions
96
159
  const currentSession = this.sessionManager.getSession(chatId);
97
160
  if (!currentSession || currentSession.status === 'busy') {
@@ -99,10 +162,38 @@ export class MessageHandler {
99
162
  return;
100
163
  }
101
164
  this.sessionManager.updateStatus(chatId, 'busy');
165
+ // Show thinking card immediately before sending prompt
166
+ if (!this.config.showProcess) {
167
+ const dots = ['.', '..', '...', '....'];
168
+ let frame = 0;
169
+ const thinkingCard = FeishuCard.createThinkingCard(this.botName, dots[frame]);
170
+ const msg = await this.feishuApi.sendCard(chatId, thinkingCard);
171
+ if (msg?.message_id) {
172
+ this.sessionManager.setCurrentMessage(chatId, msg.message_id);
173
+ // Animate the ellipsis until content arrives
174
+ const timer = setInterval(async () => {
175
+ const session = this.sessionManager.getSession(chatId);
176
+ if (!session?.currentMessageId || !this.thinkingTimers.has(chatId)) {
177
+ clearInterval(timer);
178
+ return;
179
+ }
180
+ if (session.currentContent) {
181
+ this.stopThinkingAnimation(chatId);
182
+ return;
183
+ }
184
+ frame = (frame + 1) % dots.length;
185
+ try {
186
+ await this.feishuApi.updateCard(session.currentMessageId, FeishuCard.createThinkingCard(this.botName, dots[frame]));
187
+ }
188
+ catch { /* best-effort */ }
189
+ }, 500);
190
+ this.thinkingTimers.set(chatId, timer);
191
+ }
192
+ }
102
193
  // Send message to OpenCode
103
194
  try {
104
- log.info({ sessionId: session.id }, 'Sending prompt to OpenCode');
105
- await this.opencode.sendPrompt(session.id, text);
195
+ log.info({ sessionId: session.id, files: files.length }, 'Sending prompt to OpenCode');
196
+ await this.opencode.sendPrompt(session.id, text, files.length > 0 ? files : undefined);
106
197
  log.info('Prompt sent successfully');
107
198
  }
108
199
  catch (err) {
@@ -110,6 +201,7 @@ export class MessageHandler {
110
201
  await this.feishuApi.sendCard(chatId, FeishuCard.createErrorCard(`发送消息失败: ${err instanceof Error ? err.message : String(err)}`));
111
202
  this.sessionManager.updateStatus(chatId, 'idle');
112
203
  this.sessionManager.clearCurrentMessage(chatId);
204
+ this.stopThinkingAnimation(chatId);
113
205
  }
114
206
  }
115
207
  catch (err) {
@@ -122,5 +214,56 @@ export class MessageHandler {
122
214
  }
123
215
  }
124
216
  }
217
+ stopThinkingAnimation(chatId) {
218
+ const timer = this.thinkingTimers.get(chatId);
219
+ if (timer) {
220
+ clearInterval(timer);
221
+ this.thinkingTimers.delete(chatId);
222
+ }
223
+ }
224
+ /**
225
+ * Detect document creation intent from user message.
226
+ * Supports patterns like: "创建文档 XXX", "新建一个文档", "写个文档"
227
+ */
228
+ detectDocCreationIntent(text) {
229
+ const patterns = [
230
+ /(?:创建|新建|写个?)(?:一个|个)?(?:飞书)?(?:云)?文档[,::]?\s*["「『【]([^"」』】]+)["」』】]/i,
231
+ /(?:创建|新建|写个?)(?:一个|个)?(?:飞书)?(?:云)?文档[,::]?\s*(.+?)(?:\n|$)/i,
232
+ /(?:创建|新建|写个?)(?:一个|个)?(.+?)(?:的|的)?(?:飞书)?(?:云)?文档/i,
233
+ /(?:帮我|请)?(?:创建|新建|写)(?:一个|个)?(.+)/i,
234
+ ];
235
+ for (const pattern of patterns) {
236
+ const match = text.match(pattern);
237
+ if (match && match[1]) {
238
+ const title = match[1].trim().replace(/^(?:一个|个|这份|这个|的)\s*/, '');
239
+ if (title && title.length > 0 && title.length < 100) {
240
+ return { intent: true, title };
241
+ }
242
+ }
243
+ }
244
+ // Simple keyword detection as fallback
245
+ if (/^(?:创建|新建|写个?)文档/.test(text.trim())) {
246
+ return { intent: true, title: '未命名文档' };
247
+ }
248
+ return { intent: false, title: '' };
249
+ }
250
+ /**
251
+ * Unified media download handler for images, files, audio, and video.
252
+ */
253
+ async downloadMedia(messageId, fileKey, resourceType, fileName, mimeType, typeLabel) {
254
+ if (!fileKey) {
255
+ return `[${typeLabel}消息]`;
256
+ }
257
+ try {
258
+ log.info({ messageId, fileKey, fileName }, `Downloading ${typeLabel}...`);
259
+ const buffer = await this.feishuApi.downloadMedia(messageId, fileKey, resourceType);
260
+ const downloaded = await this.fileDownloader.saveBuffer(buffer, fileName, mimeType);
261
+ return `[${typeLabel}已上传: ${downloaded.filePath}]`;
262
+ }
263
+ catch (err) {
264
+ log.error({ err, messageId, fileKey }, `Failed to download ${typeLabel}`);
265
+ return `[${typeLabel}消息(下载失败)]`;
266
+ }
267
+ }
125
268
  }
126
269
  //# sourceMappingURL=message-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"message-handler.js","sourceRoot":"","sources":["../../src/core/message-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message-handler.js","sourceRoot":"","sources":["../../src/core/message-handler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,GAAG,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;AAE3C,MAAM,OAAO,cAAc;IACjB,MAAM,CAAe;IACrB,cAAc,CAAiB;IAC/B,SAAS,CAAY;IACrB,QAAQ,CAAiB;IACzB,KAAK,CAAsB;IAC3B,cAAc,CAAiB;IAC/B,UAAU,CAAc;IACxB,OAAO,CAAS;IAChB,cAAc,GAAG,IAAI,GAAG,EAA0C,CAAC;IAE3E,YACE,MAAoB,EACpB,cAA8B,EAC9B,SAAoB,EACpB,QAAwB,EACxB,UAAuB,EACvB,OAAO,GAAG,IAAI;QAEd,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC,CAAC;QACjE,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAsB;QACxC,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC;gBACP,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW;gBACxC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;aAC5C,EAAE,kBAAkB,CAAC,CAAC;YAEvB,sBAAsB;YACtB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACpB,GAAG,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YAED,oCAAoC;YACpC,IAAI,OAAO,CAAC,MAAM,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;gBACzC,GAAG,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;gBACjD,OAAO;YACT,CAAC;YAED,sDAAsD;YACtD,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/C,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,6BAA6B,CAAC,CAAC;gBAC3E,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;YAEnC,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,IAAI,SAAS,EAAE,EAAE,oBAAoB,CAAC,CAAC;YAEtG,uCAAuC;YACvC,IAAI,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;gBACvD,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,mBAAmB,CAAC,CAAC;gBAC9D,qDAAqD;gBACrD,yDAAyD;gBACzD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC;gBAChD,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,CACxC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,EAAE,OAAO,KAAK,SAAS,CAChD,CAAC;gBAEF,IAAI,CAAC,WAAW,EAAE,CAAC;oBACjB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,2CAA2C,CAAC,CAAC;oBAClE,OAAO;gBACT,CAAC;gBACD,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAChC,CAAC;YAED,qBAAqB;YACrB,IAAI,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;gBACnE,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;gBACpC,OAAO;YACT,CAAC;YAED,kBAAkB;YAClB,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC;gBACpD,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC3D,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,yBAAyB,CAAC,CAAC;oBAClD,OAAO;gBACT,CAAC;YACH,CAAC;YAED,wBAAwB;YACxB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,6BAA6B,CAAC,CAAC;YACpD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC/E,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC;YAE7E,kDAAkD;YAClD,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9B,GAAG,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;gBAClD,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAC3B,MAAM,EACN,oBAAoB,CACrB,CAAC;gBACF,OAAO;YACT,CAAC;YAED,mCAAmC;YACnC,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,IAAI,SAAS,CAAC;YACtE,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;YACnE,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,sBAAsB,CAAC,CAAC;YAE1E,2DAA2D;YAC3D,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,MAAM,KAAK,GAAoE,EAAE,CAAC;YAElF,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC5C,QAAQ,OAAO,CAAC,YAAY,EAAE,CAAC;oBAC7B,KAAK,MAAM;wBACT,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;wBAC1B,MAAM;oBACR,KAAK,OAAO;wBACV,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAC7B,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,SAAS,EACjB,OAAO,EACP,WAAW,EACX,YAAY,EACZ,IAAI,CACL,CAAC;wBACF,MAAM;oBACR,KAAK,MAAM;wBACT,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAC7B,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,QAAQ,EAChB,MAAM,EACN,OAAO,CAAC,SAAS,IAAI,SAAS,EAC9B,0BAA0B,EAC1B,IAAI,CACL,CAAC;wBACF,MAAM;oBACR,KAAK,OAAO;wBACV,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAC7B,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,QAAQ,EAChB,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,IAAI,CACL,CAAC;wBACF,MAAM;oBACR,KAAK,OAAO;wBACV,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAC7B,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,QAAQ,EAChB,MAAM,EACN,OAAO,CAAC,SAAS,IAAI,WAAW,EAChC,WAAW,EACX,IAAI,CACL,CAAC;wBACF,MAAM;oBACR,KAAK,SAAS;wBACZ,IAAI,GAAG,QAAQ,CAAC;wBAChB,MAAM;oBACR;wBACE,IAAI,GAAG,cAAc,OAAO,CAAC,YAAY,GAAG,CAAC;gBACjD,CAAC;gBACD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC;YACnH,CAAC;YAAC,MAAM,CAAC;gBACP,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,EAAE,iCAAiC,CAAC,CAAC;gBACtG,IAAI,GAAG,cAAc,OAAO,CAAC,YAAY,GAAG,CAAC;YAC/C,CAAC;YAED,uCAAuC;YACvC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACrB,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACvC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC9C,CAAC;gBACD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,8BAA8B,CAAC,CAAC;YAC7E,CAAC;YAED,iDAAiD;YACjD,IAAI,QAAQ,KAAK,OAAO,IAAI,UAAU,IAAI,UAAU,KAAK,aAAa,EAAE,CAAC;gBACvE,IAAI,GAAG,IAAI,UAAU,MAAM,IAAI,EAAE,CAAC;YACpC,CAAC;YAED,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;gBACzC,OAAO;YACT,CAAC;YAED,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAC;YAEhF,qCAAqC;YACrC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACpB,MAAM,SAAS,GAAG,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC;gBACrD,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;oBACrB,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,EAAE,mCAAmC,CAAC,CAAC;oBAC1E,IAAI,CAAC;wBACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,0BAA0B,CAC1D,SAAS,CAAC,KAAK,EACf,KAAK,SAAS,CAAC,KAAK,6CAA6C,CAClE,CAAC;wBAEF,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAC3B,MAAM,EACN,aAAa,GAAG,CAAC,KAAK,SAAS,GAAG,CAAC,GAAG,qBAAqB,CAC5D,CAAC;wBAEF,8DAA8D;wBAC9D,IAAI,GAAG,GAAG,IAAI,wBAAwB,GAAG,CAAC,KAAK,QAAQ,GAAG,CAAC,GAAG,2BAA2B,CAAC;oBAC5F,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,EAAE,2BAA2B,CAAC,CAAC;wBACxE,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAC3B,MAAM,EACN,6DAA6D,CAC9D,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YAED,kEAAkE;YAClE,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAC9D,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACxD,GAAG,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YACD,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAEjD,uDAAuD;YACvD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;gBACxC,IAAI,KAAK,GAAG,CAAC,CAAC;gBACd,MAAM,YAAY,GAAG,UAAU,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC9E,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;gBAChE,IAAI,GAAG,EAAE,UAAU,EAAE,CAAC;oBACpB,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;oBAC9D,6CAA6C;oBAC7C,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;wBACnC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;wBACvD,IAAI,CAAC,OAAO,EAAE,gBAAgB,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;4BACnE,aAAa,CAAC,KAAK,CAAC,CAAC;4BACrB,OAAO;wBACT,CAAC;wBACD,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;4BAC3B,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;4BACnC,OAAO;wBACT,CAAC;wBACD,KAAK,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;wBAClC,IAAI,CAAC;4BACH,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAC7B,OAAO,CAAC,gBAAgB,EACxB,UAAU,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CACzD,CAAC;wBACJ,CAAC;wBAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;oBAC/B,CAAC,EAAE,GAAG,CAAC,CAAC;oBACR,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;YAED,2BAA2B;YAC3B,IAAI,CAAC;gBACH,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,4BAA4B,CAAC,CAAC;gBACvF,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBACvF,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,uBAAuB,CAAC,CAAC;gBAE5C,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAC3B,MAAM,EACN,UAAU,CAAC,eAAe,CACxB,WAAW,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC9D,CACF,CAAC;gBAEF,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBACjD,IAAI,CAAC,cAAc,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;gBAChD,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;YACrC,CAAC;QAEH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,wBAAwB,CAAC,CAAC;YAE7C,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAC3B,OAAO,CAAC,OAAO,EACf,iBAAiB,CAClB,CAAC;YACJ,CAAC;YAAC,OAAO,OAAO,EAAE,CAAC;gBACjB,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,8BAA8B,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IAGO,qBAAqB,CAAC,MAAc;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,KAAK,EAAE,CAAC;YACV,aAAa,CAAC,KAAK,CAAC,CAAC;YACrB,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,uBAAuB,CAAC,IAAY;QAC1C,MAAM,QAAQ,GAAG;YACf,uEAAuE;YACvE,8DAA8D;YAC9D,qDAAqD;YACrD,oCAAoC;SACrC,CAAC;QAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;gBAClE,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;oBACpD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;gBACjC,CAAC;YACH,CAAC;QACH,CAAC;QAED,uCAAuC;QACvC,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAC1C,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACtC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa,CACzB,SAAiB,EACjB,OAA2B,EAC3B,YAA8B,EAC9B,QAAgB,EAChB,QAAgB,EAChB,SAAiB;QAEjB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,SAAS,KAAK,CAAC;QAC5B,CAAC;QAED,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,SAAS,KAAK,CAAC,CAAC;YAC1E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;YACpF,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACpF,OAAO,IAAI,SAAS,QAAQ,UAAU,CAAC,QAAQ,GAAG,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,sBAAsB,SAAS,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,SAAS,WAAW,CAAC;QAClC,CAAC;IACH,CAAC;CACF"}