@izhimu/qq 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.
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Utility functions for QQ NapCat plugin
3
+ */
4
+ /**
5
+ * Generate a unique message ID for OpenClaw
6
+ */
7
+ export declare function generateMessageId(): string;
8
+ /**
9
+ * Generate a unique echo ID for request correlation
10
+ */
11
+ export declare function generateEchoId(): string;
12
+ /**
13
+ * Convert NapCat integer message ID to string
14
+ */
15
+ export declare function messageIdToString(messageId: number | string): string;
16
+ /**
17
+ * Map common QQ face IDs to emoji
18
+ */
19
+ export declare const FACE_ID_TO_EMOJI: Record<string, string>;
20
+ /**
21
+ * Get emoji for QQ face ID
22
+ * For unknown IDs, shows the ID for reference
23
+ */
24
+ export declare function getEmojiForFaceId(faceId: string): string;
25
+ /**
26
+ * Check if a string is a valid URL
27
+ */
28
+ export declare function isValidUrl(str: string): boolean;
29
+ /**
30
+ * Extract URL from image data
31
+ * Returns the URL if valid, otherwise returns undefined
32
+ */
33
+ export declare function extractImageUrl(data: {
34
+ url?: string;
35
+ file?: string;
36
+ }): string | undefined;
37
+ /**
38
+ * Calculate exponential backoff delay
39
+ */
40
+ export declare function calculateBackoff(attempt: number, baseMs?: number, maxMs?: number): number;
41
+ /**
42
+ * Chunk an array into smaller arrays
43
+ */
44
+ export declare function chunk<T>(array: T[], size: number): T[][];
45
+ /**
46
+ * Get a human-readable message for a WebSocket close code
47
+ */
48
+ export declare function getCloseCodeMessage(code: number): string;
49
+ export { CQCodeUtils, CQNode } from './cqcode.js';
50
+ export { Logger } from './log.js';
51
+ export { MarkdownToText, markdownToText, } from './markdown.js';
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Utility functions for QQ NapCat plugin
3
+ */
4
+ // =============================================================================
5
+ // ID Generation
6
+ // =============================================================================
7
+ let idCounter = 0;
8
+ /**
9
+ * Generate a unique message ID for OpenClaw
10
+ */
11
+ export function generateMessageId() {
12
+ return `qq-${Date.now()}-${++idCounter}`;
13
+ }
14
+ /**
15
+ * Generate a unique echo ID for request correlation
16
+ */
17
+ export function generateEchoId() {
18
+ return `echo-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
19
+ }
20
+ // =============================================================================
21
+ // Message ID Conversion
22
+ // =============================================================================
23
+ /**
24
+ * Convert NapCat integer message ID to string
25
+ */
26
+ export function messageIdToString(messageId) {
27
+ return String(messageId);
28
+ }
29
+ // =============================================================================
30
+ // Face/Emoji Mapping
31
+ // =============================================================================
32
+ /**
33
+ * Map common QQ face IDs to emoji
34
+ */
35
+ export const FACE_ID_TO_EMOJI = {
36
+ '0': '😊',
37
+ '1': 'šŸ˜…',
38
+ '2': 'ā˜ŗļø',
39
+ '3': 'šŸ˜„',
40
+ '4': '😁',
41
+ '5': 'šŸ˜†',
42
+ '6': '😃',
43
+ '7': 'šŸ˜‚',
44
+ '8': '🤣',
45
+ '9': '😊',
46
+ '10': 'šŸ˜',
47
+ '11': '🄰',
48
+ '12': '😘',
49
+ '13': 'šŸ˜—',
50
+ '14': 'šŸ˜™',
51
+ '15': '😚',
52
+ '16': '🄲',
53
+ '17': 'šŸ™‚',
54
+ '18': 'šŸ™ƒ',
55
+ '19': 'šŸ˜‰',
56
+ '20': '😌',
57
+ '21': 'šŸ˜',
58
+ '22': '🄰',
59
+ '23': '😘',
60
+ '24': 'šŸ˜—',
61
+ '25': 'šŸ˜™',
62
+ '26': '😚',
63
+ '27': 'šŸ˜‹',
64
+ '28': 'šŸ˜›',
65
+ '29': 'šŸ˜',
66
+ '30': '😜',
67
+ '31': '🤪',
68
+ '32': '🤨',
69
+ '33': '🧐',
70
+ '34': 'šŸ¤“',
71
+ '35': 'šŸ˜Ž',
72
+ '36': '🤩',
73
+ '37': '🄳',
74
+ '38': 'šŸ˜',
75
+ '39': 'šŸ˜’',
76
+ '40': 'šŸ˜ž',
77
+ '41': 'šŸ˜”',
78
+ '42': '😟',
79
+ '43': 'šŸ˜•',
80
+ '44': 'šŸ™',
81
+ '45': '😣',
82
+ '46': 'šŸ˜–',
83
+ '47': '😫',
84
+ '48': '😩',
85
+ '49': '🄺',
86
+ '50': '😢',
87
+ '51': '😭',
88
+ '52': '😤',
89
+ '53': '😠',
90
+ '54': '😔',
91
+ '55': '🤬',
92
+ '56': '🤯',
93
+ '57': '😳',
94
+ '58': '🄵',
95
+ '59': '🄶',
96
+ '60': '😱',
97
+ '61': '😨',
98
+ '62': '😰',
99
+ '63': '😄',
100
+ '64': 'šŸ˜“',
101
+ '65': 'šŸ¤—',
102
+ '66': 'šŸ¤”',
103
+ '67': '🤭',
104
+ '68': '🤫',
105
+ '69': '🤄',
106
+ '70': '😶',
107
+ '71': '😐',
108
+ '72': 'šŸ˜‘',
109
+ '73': '😬',
110
+ '74': 'šŸ™„',
111
+ '75': '😯',
112
+ '76': '😦',
113
+ '77': '😧',
114
+ '78': '😮',
115
+ '79': '😲',
116
+ '80': '🄱',
117
+ '81': '😓',
118
+ '82': '🤤',
119
+ '83': '😪',
120
+ '84': '😵',
121
+ '85': '🤐',
122
+ '86': '🄓',
123
+ '87': '🤢',
124
+ '88': '🤮',
125
+ '89': '🤧',
126
+ '90': '😷',
127
+ '91': 'šŸ¤’',
128
+ '92': 'šŸ¤•',
129
+ '93': 'šŸ¤‘',
130
+ '94': '🤠',
131
+ '95': '😈',
132
+ '96': 'šŸ‘æ',
133
+ '97': 'šŸ‘¹',
134
+ '98': 'šŸ‘ŗ',
135
+ '99': '🤔',
136
+ '100': 'šŸ’©',
137
+ '101': 'šŸ‘»',
138
+ '102': 'šŸ’€',
139
+ '103': 'ā˜ ļø',
140
+ '104': 'šŸ‘½',
141
+ '105': 'šŸ‘¾',
142
+ '106': 'šŸ¤–',
143
+ '107': 'šŸŽƒ',
144
+ '108': '😺',
145
+ '109': '😸',
146
+ '110': '😹',
147
+ '111': '😻',
148
+ '112': '😼',
149
+ '113': '😽',
150
+ '114': 'šŸ™€',
151
+ '115': '😿',
152
+ '116': '😾',
153
+ };
154
+ /**
155
+ * Get emoji for QQ face ID
156
+ * For unknown IDs, shows the ID for reference
157
+ */
158
+ export function getEmojiForFaceId(faceId) {
159
+ return FACE_ID_TO_EMOJI[faceId] || `[č”Øęƒ…:${faceId}]`;
160
+ }
161
+ // =============================================================================
162
+ // URL Validation
163
+ // =============================================================================
164
+ /**
165
+ * Check if a string is a valid URL
166
+ */
167
+ export function isValidUrl(str) {
168
+ try {
169
+ new URL(str);
170
+ return true;
171
+ }
172
+ catch {
173
+ return false;
174
+ }
175
+ }
176
+ /**
177
+ * Extract URL from image data
178
+ * Returns the URL if valid, otherwise returns undefined
179
+ */
180
+ export function extractImageUrl(data) {
181
+ if (data.url && isValidUrl(data.url)) {
182
+ return data.url;
183
+ }
184
+ if (data.file && isValidUrl(data.file)) {
185
+ return data.file;
186
+ }
187
+ return undefined;
188
+ }
189
+ // =============================================================================
190
+ // Delay Helpers
191
+ // =============================================================================
192
+ /**
193
+ * Calculate exponential backoff delay
194
+ */
195
+ export function calculateBackoff(attempt, baseMs = 1000, maxMs = 30000) {
196
+ const delay = baseMs * Math.pow(2, attempt);
197
+ return Math.min(delay, maxMs);
198
+ }
199
+ // =============================================================================
200
+ // Array Helpers
201
+ // =============================================================================
202
+ /**
203
+ * Chunk an array into smaller arrays
204
+ */
205
+ export function chunk(array, size) {
206
+ const chunks = [];
207
+ for (let i = 0; i < array.length; i += size) {
208
+ chunks.push(array.slice(i, i + size));
209
+ }
210
+ return chunks;
211
+ }
212
+ // =============================================================================
213
+ // WebSocket Helpers
214
+ // =============================================================================
215
+ /**
216
+ * Get a human-readable message for a WebSocket close code
217
+ */
218
+ export function getCloseCodeMessage(code) {
219
+ const messages = {
220
+ 1000: 'Normal closure',
221
+ 1001: 'Going away',
222
+ 1002: 'Protocol error',
223
+ 1003: 'Unsupported data',
224
+ 1004: 'Reserved',
225
+ 1005: 'No status received',
226
+ 1006: 'Abnormal closure',
227
+ 1007: 'Invalid frame payload data',
228
+ 1008: 'Policy violation',
229
+ 1009: 'Message too big',
230
+ 1010: 'Missing extension',
231
+ 1011: 'Internal error',
232
+ 1012: 'Service restart',
233
+ 1013: 'Try again later',
234
+ 1014: 'Bad gateway',
235
+ 1015: 'TLS handshake',
236
+ };
237
+ return messages[code] ?? `Unknown close code: ${code}`;
238
+ }
239
+ // =============================================================================
240
+ // CQ Code Utilities
241
+ // =============================================================================
242
+ export { CQCodeUtils } from './cqcode.js';
243
+ // =============================================================================
244
+ // Log Utilities
245
+ // =============================================================================
246
+ export { Logger } from './log.js';
247
+ // =============================================================================
248
+ // Markdown Utilities
249
+ // =============================================================================
250
+ export { MarkdownToText, markdownToText, } from './markdown.js';
@@ -0,0 +1,6 @@
1
+ export declare class Logger {
2
+ static debug(category: string, message: string, ...args: unknown[]): void;
3
+ static info(category: string, message: string, ...args: unknown[]): void;
4
+ static warn(category: string, message: string, ...args: unknown[]): void;
5
+ static error(category: string, message: string, ...args: unknown[]): void;
6
+ }
@@ -0,0 +1,23 @@
1
+ import { getRuntime } from "../core/runtime.js";
2
+ function log() {
3
+ return getRuntime()?.logging.getChildLogger({ module: 'channel/qq' }) ?? console;
4
+ }
5
+ function param(args) {
6
+ if (args.length === 0)
7
+ return '';
8
+ return ' ' + args.map(arg => typeof arg === 'string' ? arg : JSON.stringify(arg)).join(' ');
9
+ }
10
+ export class Logger {
11
+ static debug(category, message, ...args) {
12
+ log().debug?.(`[${category}] ${message}${param(args)}`);
13
+ }
14
+ static info(category, message, ...args) {
15
+ log().info?.(`[${category}] ${message}${param(args)}`);
16
+ }
17
+ static warn(category, message, ...args) {
18
+ log().warn?.(`[${category}] ${message}${param(args)}`);
19
+ }
20
+ static error(category, message, ...args) {
21
+ log().error?.(`[${category}] ${message}${param(args)}`);
22
+ }
23
+ }
@@ -0,0 +1,29 @@
1
+ export declare class MarkdownToText {
2
+ private codeBlockStore;
3
+ private maskPrefix;
4
+ private maskCounter;
5
+ /**
6
+ * äø»å…„å£ļ¼šå°† Markdown č½¬ę¢äøŗēŗÆę–‡ęœ¬
7
+ * @param markdown 原始 Markdown 字符串
8
+ */
9
+ convert(markdown: string): string;
10
+ /**
11
+ * äæęŠ¤ä»£ē å—
12
+ * ę”ÆęŒ ```language 和 ~~~ äø¤ē§å†™ę³•
13
+ */
14
+ private maskCodeBlocks;
15
+ /**
16
+ * äæęŠ¤č”Œå†…ä»£ē 
17
+ * `code` -> ä½æē”Øå•å¼•å·åŒ…č£¹ļ¼ŒåŒŗåˆ«äŗŽę™®é€šę–‡ęœ¬
18
+ */
19
+ private maskInlineCode;
20
+ /**
21
+ * čæ˜åŽŸč¢«ęŽ©ē ēš„å†…å®¹
22
+ */
23
+ private unmaskContent;
24
+ /**
25
+ * HTML å®žä½“č§£ē 
26
+ */
27
+ private decodeHtmlEntities;
28
+ }
29
+ export declare const markdownToText: (md: string) => string;
@@ -0,0 +1,137 @@
1
+ export class MarkdownToText {
2
+ // ē”ØäŗŽęš‚å­˜č¢«äæęŠ¤ēš„ä»£ē å—ļ¼Œé˜²ę­¢č¢«ę­£åˆ™čÆÆä¼¤
3
+ codeBlockStore = new Map();
4
+ maskPrefix = '%%MD_MASK_';
5
+ maskCounter = 0;
6
+ /**
7
+ * äø»å…„å£ļ¼šå°† Markdown č½¬ę¢äøŗēŗÆę–‡ęœ¬
8
+ * @param markdown 原始 Markdown 字符串
9
+ */
10
+ convert(markdown) {
11
+ if (!markdown)
12
+ return '';
13
+ // 1. 初始化
14
+ this.codeBlockStore.clear();
15
+ this.maskCounter = 0;
16
+ let text = markdown;
17
+ // --- 阶ꮵ 1: äæęŠ¤ę€§é¢„å¤„ē† (Protect) ---
18
+ // åæ…é”»ęœ€å…ˆę‰§č”Œļ¼Œé˜²ę­¢ä»£ē é‡Œēš„ę³Øé‡Š # č¢«å½“ęˆę ‡é¢˜ļ¼Œęˆ– ** č¢«å½“ęˆē²—ä½“
19
+ text = this.maskCodeBlocks(text);
20
+ text = this.maskInlineCode(text);
21
+ // --- 阶ꮵ 2: ē»“ęž„åŒ–č½¬ę¢ (Structure) ---
22
+ // 2.1 清理 HTML 标签 (äæē•™ <br> ēš„ę¢č”Œę•ˆęžœ)
23
+ text = text.replace(/<br\s*\/?>/gi, '\n');
24
+ text = text.replace(/<hr\s*\/?>/gi, '\n────────────────────\n');
25
+ text = text.replace(/<[^>]+>/g, ''); // ē§»é™¤å‰©ä½™ę‰€ęœ‰ę ‡ē­¾
26
+ // 2.2 ę ‡é¢˜ (Headers) -> č½¬ę¢äøŗč§†č§‰é†’ē›®ēš„ę–‡ęœ¬
27
+ // H1/H2 ä½æē”ØåŒēŗæ/å•ēŗæåˆ†éš”ļ¼ŒH3+ ä½æē”Øę‹¬å·åŒ…č£¹
28
+ text = text.replace(/^#\s+(.*)$/gm, '\n$1\n════════════════════\n');
29
+ text = text.replace(/^##\s+(.*)$/gm, '\n$1\n────────────────────\n');
30
+ text = text.replace(/^(#{3,6})\s+(.*)$/gm, '\n怐 $2 怑\n');
31
+ // 2.3 ę°“å¹³åˆ†å‰²ēŗæ (---, ***, ___)
32
+ text = text.replace(/^(-\s*?|\*\s*?|_\s*?){3,}\s*$/gm, '────────────────────');
33
+ // 2.4 引用 (Blockquotes) -> ä½æē”Øē«–ēŗæå‰ē¼€
34
+ // å¤„ē†å¤šēŗ§å¼•ē”Ø >> Text
35
+ text = text.replace(/^(>+)\s?(.*)$/gm, (_match, _arrows, content) => {
36
+ return `ā–Ž ${content}`;
37
+ });
38
+ // 2.5 ä»»åŠ”åˆ—č”Ø (Task Lists)
39
+ text = text.replace(/^(\s*)-\s\[x]\s/gim, '$1āœ… '); // 完成
40
+ text = text.replace(/^(\s*)-\s\[\s]\s/gim, '$1⬜ '); // 未完成
41
+ // 2.6 ę— åŗäøŽęœ‰åŗåˆ—č”Ø (Lists)
42
+ // äæē•™ $1 (缩进空格)ļ¼Œå°† -/*/+ ę›æę¢äøŗ •
43
+ text = text.replace(/^(\s*)[-*+]\s+(.*)$/gm, '$1• $2');
44
+ // ęœ‰åŗåˆ—č”Ø 1. 2. é€šåøøäøéœ€č¦ę”¹åŠØļ¼Œäæē•™åŽŸę ·å³åÆ
45
+ // 2.7 蔨格 (Tables)
46
+ // ē§»é™¤åÆ¹é½č”Œ |---|---|
47
+ text = text.replace(/^\s*\|?[\s\-:|]+\|?\s*$/gm, '');
48
+ // 将 | Cell | Cell | č½¬ę¢äøŗē©ŗę ¼åˆ†éš”ļ¼Œå°½é‡äæęŒäø€č”Œ
49
+ text = text.replace(/^\|(.*)\|$/gm, (_match, content) => {
50
+ // ē§»é™¤é¦–å°¾ē®”é“ē¬¦ļ¼Œäø­é—“ē®”é“ē¬¦å˜äøŗē©ŗę ¼
51
+ return content.split('|').map((s) => s.trim()).join(' ');
52
+ });
53
+ // --- 阶ꮵ 3: č”Œå†…ę ¼å¼ęø…ē† (Inline Formatting) ---
54
+ // 3.1 粗体 (Bold) -> ä½æē”Øäø­ę–‡å¼•å·ęˆ–åŒę˜Ÿå·å¼ŗč°ƒ
55
+ // 使用 [\s\S] ē”®äæåŒ¹é…č·Øč”Œē²—ä½“
56
+ text = text.replace(/(\*\*|__)([\s\S]*?)\1/g, 'ā€œ$2ā€');
57
+ // 3.2 ę–œä½“ (Italic) -> ē›“ęŽ„ē§»é™¤ē¬¦å·ļ¼ŒēŗÆę–‡ęœ¬å¾ˆéš¾č”ØēŽ°ę–œä½“
58
+ // ę³Øę„ļ¼šåæ…é”»åœØå¤„ē†å®Œē²—ä½“åŽå¤„ē†ę–œä½“
59
+ text = text.replace(/([*_])([\s\S]*?)\1/g, '$2');
60
+ // 3.3 åˆ é™¤ēŗæ (Strikethrough) -> ē§»é™¤å†…å®¹ęˆ–ä»…ē§»é™¤ē¬¦å·ļ¼Ÿé€šåøøä»…ē§»é™¤ē¬¦å·
61
+ text = text.replace(/~~([\s\S]*?)~~/g, '$1');
62
+ // 3.4 图片 (Images) -> č½¬ę¢äøŗå ä½ē¬¦
63
+ text = text.replace(/!\[([^\]]*)]\(([^)]+)\)/g, (_match, alt) => {
64
+ return `[图片: ${alt || 'Image'}]`;
65
+ });
66
+ // 3.5 é“¾ęŽ„ (Links) -> Text (URL)
67
+ // ęŽ’é™¤é”šē‚¹é“¾ęŽ„ęˆ–ē©ŗé“¾ęŽ„
68
+ text = text.replace(/\[([^\]]+)]\(([^)]+)\)/g, '$1 ($2)');
69
+ // å¤„ē†č‡ŖåŠØé“¾ęŽ„ <http://example.com>
70
+ text = text.replace(/<((?:https?|ftp|email):[^>]+)>/g, '$1');
71
+ // --- 阶ꮵ 4: čæ˜åŽŸäøŽę”¶å°¾ (Restore & Finalize) ---
72
+ // 4.1 čæ˜åŽŸä»£ē å—
73
+ text = this.unmaskContent(text);
74
+ // 4.2 解码 HTML 实体 (&amp; -> &)
75
+ text = this.decodeHtmlEntities(text);
76
+ // 4.3 ęœ€ē»ˆęŽ’ē‰ˆä¼˜åŒ–
77
+ // ē§»é™¤ę®µé¦–ę®µå°¾å¤šä½™ē©ŗē™½ļ¼Œå°†čæžē»­3äøŖä»„äøŠę¢č”ŒåŽ‹ē¼©äøŗ2äøŖļ¼ˆę®µč½é—“č·ļ¼‰
78
+ text = text.replace(/\n{3,}/g, '\n\n').trim();
79
+ return text;
80
+ }
81
+ /**
82
+ * äæęŠ¤ä»£ē å—
83
+ * ę”ÆęŒ ```language 和 ~~~ äø¤ē§å†™ę³•
84
+ */
85
+ maskCodeBlocks(text) {
86
+ // 匹配 3äøŖęˆ–ę›“å¤šåå¼•å·/波浪线
87
+ const codeBlockRegex = /(`{3,}|~{3,})(\w*)\n([\s\S]*?)\1/g;
88
+ return text.replace(codeBlockRegex, (_match, _fence, lang, code) => {
89
+ const key = `${this.maskPrefix}BLOCK_${this.maskCounter++}`;
90
+ const langTag = lang ? ` [${lang}]` : '';
91
+ // ęž„é€ ē¾Žč§‚ēš„ä»£ē å—ę ·å¼
92
+ const formatted = `\n────────────────────${langTag}\n${code.replace(/^\n+|\n+$/g, '')}\n────────────────────\n`;
93
+ this.codeBlockStore.set(key, formatted);
94
+ return key;
95
+ });
96
+ }
97
+ /**
98
+ * äæęŠ¤č”Œå†…ä»£ē 
99
+ * `code` -> ä½æē”Øå•å¼•å·åŒ…č£¹ļ¼ŒåŒŗåˆ«äŗŽę™®é€šę–‡ęœ¬
100
+ */
101
+ maskInlineCode(text) {
102
+ return text.replace(/`([^`]+)`/g, (_match, code) => {
103
+ const key = `${this.maskPrefix}INLINE_${this.maskCounter++}`;
104
+ this.codeBlockStore.set(key, ` ā€˜${code}’ `); // åŠ ē©ŗę ¼é˜²ę­¢ē²˜čæž
105
+ return key;
106
+ });
107
+ }
108
+ /**
109
+ * čæ˜åŽŸč¢«ęŽ©ē ēš„å†…å®¹
110
+ */
111
+ unmaskContent(text) {
112
+ // ä½æē”Øę­£åˆ™å…Øå±€åŒ¹é…ę‰€ęœ‰ mask key
113
+ const maskRegex = new RegExp(`${this.maskPrefix}\\w+_\\d+`, 'g');
114
+ return text.replace(maskRegex, (key) => {
115
+ return this.codeBlockStore.get(key) || '';
116
+ });
117
+ }
118
+ /**
119
+ * HTML å®žä½“č§£ē 
120
+ */
121
+ decodeHtmlEntities(text) {
122
+ const entities = {
123
+ '&nbsp;': ' ',
124
+ '&amp;': '&',
125
+ '&lt;': '<',
126
+ '&gt;': '>',
127
+ '&quot;': '"',
128
+ '&apos;': "'",
129
+ '&#39;': "'",
130
+ '&copy;': 'Ā©',
131
+ '&reg;': 'Ā®'
132
+ };
133
+ return text.replace(/&[a-z0-9#]+;/gi, (entity) => entities[entity] || entity);
134
+ }
135
+ }
136
+ // åÆ¼å‡ŗå•ä¾‹č¾…åŠ©å‡½ę•°ļ¼Œę–¹ä¾æē›“ęŽ„č°ƒē”Ø
137
+ export const markdownToText = (md) => new MarkdownToText().convert(md);
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "qq",
3
+ "name": "QQ",
4
+ "channels": ["qq"],
5
+ "description": "QQ channel plugin for OpenClaw using NapCat WebSocket API",
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {}
10
+ }
11
+ }
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@izhimu/qq",
3
+ "version": "0.1.1",
4
+ "description": "A QQ channel plugin for OpenClaw using NapCat WebSocket",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "LICENSE",
12
+ "openclaw.plugin.json"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18.0.0"
16
+ },
17
+ "scripts": {
18
+ "dev": "tsc --watch",
19
+ "build": "tsc",
20
+ "prepublishOnly": "npm run build",
21
+ "release:patch": "npm version patch && npm publish --access public",
22
+ "release:minor": "npm version minor && npm publish --access public",
23
+ "release:major": "npm version major && npm publish --access public",
24
+ "release": "npm run release:patch"
25
+ },
26
+ "keywords": [
27
+ "openclaw",
28
+ "plugin",
29
+ "qq"
30
+ ],
31
+ "author": "izhimu",
32
+ "license": "MIT",
33
+ "openclaw": {
34
+ "extensions": [
35
+ "./dist/index.js"
36
+ ],
37
+ "install": {
38
+ "npmSpec": "@izhimu/qq",
39
+ "localPath": "extensions/qq",
40
+ "defaultChoice": "npm"
41
+ }
42
+ },
43
+ "peerDependencies": {
44
+ "openclaw": "^2026.2.1"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "openclaw": {
48
+ "optional": true
49
+ }
50
+ },
51
+ "dependencies": {
52
+ "ws": "^8.19.0",
53
+ "zod": "^4.3.6"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^22.0.0",
57
+ "@types/ws": "^8.18.0",
58
+ "openclaw": "^2026.2.1",
59
+ "typescript": "^5.0.0"
60
+ }
61
+ }