@jiexiaoyin/wecom-api 0.0.2

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 (58) hide show
  1. package/README.md +228 -0
  2. package/config.example.json +7 -0
  3. package/config.js +76 -0
  4. package/docs/approval-templates.example.json +11 -0
  5. package/docs/nginx-mirror.md +193 -0
  6. package/openclaw.plugin.json +15 -0
  7. package/package.json +34 -0
  8. package/plugin.cjs +172 -0
  9. package/plugin.ts +136 -0
  10. package/skills/wecom-api/SKILL.md +40 -0
  11. package/skills/wecom-api/index.js +288 -0
  12. package/skills/wecom-api/openclaw.plugin.json +10 -0
  13. package/src/callback-helper.js +198 -0
  14. package/src/config.cjs +286 -0
  15. package/src/core/permission.js +479 -0
  16. package/src/crypto.js +130 -0
  17. package/src/index.js +199 -0
  18. package/src/modules/addressbook/index.js +413 -0
  19. package/src/modules/addressbook_cache/index.js +365 -0
  20. package/src/modules/advanced/index.js +159 -0
  21. package/src/modules/app/index.js +102 -0
  22. package/src/modules/approval/index.js +146 -0
  23. package/src/modules/auth/index.js +103 -0
  24. package/src/modules/callback/index.js +1180 -0
  25. package/src/modules/chain/index.js +193 -0
  26. package/src/modules/checkin/index.js +142 -0
  27. package/src/modules/checkin_rules/index.js +251 -0
  28. package/src/modules/contact/index.js +481 -0
  29. package/src/modules/contact_stats/index.js +349 -0
  30. package/src/modules/custom/index.js +140 -0
  31. package/src/modules/customer/index.js +51 -0
  32. package/src/modules/disk/index.js +245 -0
  33. package/src/modules/document/index.js +282 -0
  34. package/src/modules/hr/index.js +93 -0
  35. package/src/modules/intelligence/index.js +346 -0
  36. package/src/modules/kf/index.js +74 -0
  37. package/src/modules/live/index.js +122 -0
  38. package/src/modules/media/index.js +183 -0
  39. package/src/modules/meeting/index.js +665 -0
  40. package/src/modules/message/index.js +402 -0
  41. package/src/modules/messenger/index.js +208 -0
  42. package/src/modules/moments/index.js +161 -0
  43. package/src/modules/msgaudit/index.js +24 -0
  44. package/src/modules/notify/index.js +81 -0
  45. package/src/modules/oceanengine/index.js +199 -0
  46. package/src/modules/openchat/index.js +197 -0
  47. package/src/modules/phone/index.js +45 -0
  48. package/src/modules/room/index.js +178 -0
  49. package/src/modules/schedule/index.js +246 -0
  50. package/src/modules/school/index.js +199 -0
  51. package/src/modules/security/index.js +223 -0
  52. package/src/modules/sensitive/index.js +170 -0
  53. package/src/modules/thirdparty/index.js +145 -0
  54. package/src/sdk/index.js +269 -0
  55. package/src/utils/callback-helper.js +198 -0
  56. package/test/callback-crypto.test.js +55 -0
  57. package/test/crypto.test.js +85 -0
  58. package/test/permission.test.js +115 -0
@@ -0,0 +1,183 @@
1
+ /**
2
+ * 素材管理模块
3
+ * API 章节:十 - 素材管理
4
+ * 包含:临时素材、图片、高清语音
5
+ */
6
+
7
+ const WeComSDK = require('../../sdk');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ class Media extends WeComSDK {
12
+ constructor(config) {
13
+ super(config);
14
+ }
15
+
16
+ /**
17
+ * 上传临时素材
18
+ * @param {string} filePath 文件路径
19
+ * @param {string} type 媒体类型: image, voice, video, file
20
+ */
21
+ async uploadMedia(filePath, type = 'image') {
22
+ if (!fs.existsSync(filePath)) {
23
+ throw new Error(`File not found: ${filePath}`);
24
+ }
25
+
26
+ const fileName = path.basename(filePath);
27
+ const fileBuffer = fs.readFileSync(filePath);
28
+
29
+ const formData = {
30
+ media: {
31
+ value: fileBuffer,
32
+ options: {
33
+ filename: fileName,
34
+ contentType: this.getContentType(fileName)
35
+ }
36
+ }
37
+ };
38
+
39
+ return this.post('/media/upload', { media: formData }, {
40
+ type,
41
+ apiType: 'upload'
42
+ });
43
+ }
44
+
45
+ /**
46
+ * 获取临时素材
47
+ * @param {string} mediaId 媒体 id
48
+ * @param {string} savePath 保存路径
49
+ */
50
+ async getMedia(mediaId, savePath) {
51
+ const response = await this.request({
52
+ method: 'GET',
53
+ url: '/media/get',
54
+ params: { media_id: mediaId },
55
+ responseType: 'stream'
56
+ });
57
+
58
+ if (savePath) {
59
+ const writer = fs.createWriteStream(savePath);
60
+ response.data.pipe(writer);
61
+ return new Promise((resolve, reject) => {
62
+ writer.on('finish', () => resolve({ savePath }));
63
+ writer.on('error', reject);
64
+ });
65
+ }
66
+
67
+ return response.data;
68
+ }
69
+
70
+ /**
71
+ * 上传图片
72
+ * @param {string} filePath 图片路径
73
+ */
74
+ async uploadImage(filePath) {
75
+ if (!fs.existsSync(filePath)) {
76
+ throw new Error(`File not found: ${filePath}`);
77
+ }
78
+
79
+ const fileName = path.basename(filePath);
80
+ const fileBuffer = fs.readFileSync(filePath);
81
+
82
+ const formData = {
83
+ media: {
84
+ value: fileBuffer,
85
+ options: {
86
+ filename: fileName,
87
+ contentType: 'image/jpeg'
88
+ }
89
+ }
90
+ };
91
+
92
+ return this.post('/media/uploadimg', { media: formData }, {
93
+ apiType: 'upload'
94
+ });
95
+ }
96
+
97
+ /**
98
+ * 获取高清语音素材
99
+ * @param {string} mediaId 媒体 id
100
+ * @param {string} savePath 保存路径
101
+ */
102
+ async getHighDefinitionVoice(mediaId, savePath) {
103
+ const response = await this.request({
104
+ method: 'GET',
105
+ url: '/media/get/fcvoice',
106
+ params: { media_id: mediaId },
107
+ responseType: 'stream'
108
+ });
109
+
110
+ if (savePath) {
111
+ const writer = fs.createWriteStream(savePath);
112
+ response.data.pipe(writer);
113
+ return new Promise((resolve, reject) => {
114
+ writer.on('finish', () => resolve({ savePath }));
115
+ writer.on('error', reject);
116
+ });
117
+ }
118
+
119
+ return response.data;
120
+ }
121
+
122
+ /**
123
+ * 异步上传临时素材
124
+ * @param {string} filePath 文件路径
125
+ * @param {string} type 媒体类型
126
+ */
127
+ async asyncUploadMedia(filePath, type = 'image') {
128
+ if (!fs.existsSync(filePath)) {
129
+ throw new Error(`File not found: ${filePath}`);
130
+ }
131
+
132
+ const fileName = path.basename(filePath);
133
+ const fileBuffer = fs.readFileSync(filePath);
134
+
135
+ const formData = {
136
+ media: {
137
+ value: fileBuffer,
138
+ options: {
139
+ filename: fileName,
140
+ contentType: this.getContentType(fileName)
141
+ }
142
+ }
143
+ };
144
+
145
+ return this.post('/media/async_upload', { media: formData }, {
146
+ type,
147
+ apiType: 'upload'
148
+ });
149
+ }
150
+
151
+ /**
152
+ * 根据文件扩展名获取 Content-Type
153
+ * @param {string} fileName 文件名
154
+ */
155
+ getContentType(fileName) {
156
+ const ext = path.extname(fileName).toLowerCase();
157
+ const types = {
158
+ '.jpg': 'image/jpeg',
159
+ '.jpeg': 'image/jpeg',
160
+ '.png': 'image/png',
161
+ '.gif': 'image/gif',
162
+ '.bmp': 'image/bmp',
163
+ '.webp': 'image/webp',
164
+ '.mp3': 'audio/mpeg',
165
+ '.wma': 'audio/x-wav',
166
+ '.wav': 'audio/x-wav',
167
+ '.amr': 'audio/amr',
168
+ '.mp4': 'video/mp4',
169
+ '.avi': 'video/x-msvideo',
170
+ '.pdf': 'application/pdf',
171
+ '.doc': 'application/msword',
172
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
173
+ '.xls': 'application/vnd.ms-excel',
174
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
175
+ '.ppt': 'application/vnd.ms-powerpoint',
176
+ '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
177
+ '.zip': 'application/zip'
178
+ };
179
+ return types[ext] || 'application/octet-stream';
180
+ }
181
+ }
182
+
183
+ module.exports = Media;