@alemonjs/bubble 2.1.0 → 2.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,4 @@
1
+ import type { DataEnums, DataMarkDown } from 'alemonjs';
2
+ export declare const markdownToBubbleText: (items: DataMarkDown["value"]) => string;
3
+ export declare const markdownRawToBubbleText: (raw: string) => string;
4
+ export declare const dataEnumToBubbleText: (item: DataEnums) => string;
package/lib/format.js ADDED
@@ -0,0 +1,81 @@
1
+ const markdownToBubbleText = (items) => {
2
+ return items
3
+ .map(item => {
4
+ switch (item.type) {
5
+ case 'MD.text':
6
+ return item.value;
7
+ case 'MD.title':
8
+ return `# ${item.value}\n`;
9
+ case 'MD.subtitle':
10
+ return `## ${item.value}\n`;
11
+ case 'MD.bold':
12
+ return `**${item.value}**`;
13
+ case 'MD.italic':
14
+ case 'MD.italicStar':
15
+ return `*${item.value}*`;
16
+ case 'MD.strikethrough':
17
+ return `~~${item.value}~~`;
18
+ case 'MD.link': {
19
+ const v = item.value;
20
+ return `[${v.text}](${v.url})`;
21
+ }
22
+ case 'MD.image':
23
+ return `![image](${item.value})`;
24
+ case 'MD.list':
25
+ return (item.value
26
+ .map(li => {
27
+ if (typeof li.value === 'object') {
28
+ return `${li.value.index}. ${li.value.text ?? ''}`;
29
+ }
30
+ return `- ${li.value}`;
31
+ })
32
+ .join('\n') + '\n');
33
+ case 'MD.blockquote':
34
+ return `> ${item.value}\n`;
35
+ case 'MD.divider':
36
+ return '\n————————\n';
37
+ case 'MD.newline':
38
+ return '\n';
39
+ case 'MD.code': {
40
+ const lang = item?.options?.language || '';
41
+ return `\`\`\`${lang}\n${item.value}\n\`\`\`\n`;
42
+ }
43
+ case 'MD.mention':
44
+ if (item.value === 'everyone') {
45
+ return '<@everyone>';
46
+ }
47
+ return `<@${item.value ?? ''}>`;
48
+ case 'MD.content':
49
+ return item.value;
50
+ case 'MD.button': {
51
+ const options = item?.options;
52
+ const autoEnter = options?.autoEnter ?? false;
53
+ const label = typeof item.value === 'object' ? item.value.title : item.value;
54
+ const command = options?.data || label;
55
+ return `<btn variant="borderless" command="${command}" enter="${String(autoEnter)}" >${label}</btn> `;
56
+ }
57
+ default:
58
+ return String(item?.value ?? '');
59
+ }
60
+ })
61
+ .join('');
62
+ };
63
+ const markdownRawToBubbleText = (raw) => {
64
+ return raw;
65
+ };
66
+ const dataEnumToBubbleText = (item) => {
67
+ switch (item.type) {
68
+ case 'MarkdownOriginal':
69
+ return markdownRawToBubbleText(String(item.value));
70
+ case 'Attachment':
71
+ return `[附件${item.options?.filename ? ': ' + item.options.filename : ''}]`;
72
+ case 'Audio':
73
+ return '[音频]';
74
+ case 'Video':
75
+ return '[视频]';
76
+ default:
77
+ return '';
78
+ }
79
+ };
80
+
81
+ export { dataEnumToBubbleText, markdownRawToBubbleText, markdownToBubbleText };
package/lib/index.js CHANGED
@@ -14,9 +14,12 @@ const main = () => {
14
14
  const client = new BubbleClient();
15
15
  void client.connect();
16
16
  let botId = '';
17
- client.getMe().then(res => {
17
+ client
18
+ .getMe()
19
+ .then(res => {
18
20
  botId = String(res?.id ?? '');
19
- }).catch(() => { });
21
+ })
22
+ .catch(() => { });
20
23
  const createUserAvatar = (_UserId, avatar) => {
21
24
  if (!avatar) {
22
25
  return '';
package/lib/send.js CHANGED
@@ -1,10 +1,101 @@
1
1
  import { createResult, ResultCode } from 'alemonjs';
2
2
  import { readFileSync } from 'fs';
3
+ import { dataEnumToBubbleText } from './format.js';
3
4
 
4
5
  const ImageURLToBuffer = async (url) => {
5
6
  const arrayBuffer = await fetch(url).then(res => res.arrayBuffer());
6
7
  return Buffer.from(arrayBuffer);
7
8
  };
9
+ const buildBubbleMdContent = (mdAndButtons) => {
10
+ let contentMd = '';
11
+ if (mdAndButtons && mdAndButtons.length > 0) {
12
+ mdAndButtons.forEach(item => {
13
+ if (item.type === 'Markdown' && typeof item.value !== 'string') {
14
+ const md = item.value;
15
+ const map = {
16
+ 'MD.title': value => `# ${value}`,
17
+ 'MD.subtitle': value => `## ${value}`,
18
+ 'MD.text': value => `${value} `,
19
+ 'MD.bold': value => `**${value}** `,
20
+ 'MD.divider': () => '\n————————\n',
21
+ 'MD.italic': value => `_${value}_ `,
22
+ 'MD.italicStar': value => `*${value}* `,
23
+ 'MD.strikethrough': value => `~~${value}~~ `,
24
+ 'MD.blockquote': value => `\n> ${value}`,
25
+ 'MD.newline': () => '\n',
26
+ 'MD.link': value => `[🔗${value.text}](${value.url}) `,
27
+ 'MD.image': value => `\n![${value}](${value})\n`,
28
+ 'MD.mention': (value, options) => {
29
+ const { belong } = options || {};
30
+ if (value === 'everyone' || value === 'all' || value === '' || typeof value !== 'string') {
31
+ return '<@everyone> ';
32
+ }
33
+ if (belong === 'user') {
34
+ return `<@${value}> `;
35
+ }
36
+ else if (belong === 'channel') {
37
+ return `<#${value}> `;
38
+ }
39
+ return '';
40
+ },
41
+ 'MD.button': (value, options) => {
42
+ const autoEnter = options?.autoEnter ?? false;
43
+ const label = typeof value === 'object' ? value.title : value;
44
+ const command = options?.data || label;
45
+ return `<btn variant="borderless" command="${command}" enter="${String(autoEnter)}" >${label}</btn> `;
46
+ },
47
+ 'MD.content': value => `${value}`
48
+ };
49
+ md.forEach(line => {
50
+ if (map[line.type]) {
51
+ const value = 'value' in line ? line.value : undefined;
52
+ const options = 'options' in line ? line.options : {};
53
+ contentMd += map[line.type](value, options);
54
+ return;
55
+ }
56
+ if (line.type === 'MD.list') {
57
+ const listStr = line.value.map(listItem => {
58
+ if (typeof listItem.value === 'object') {
59
+ return `\n${listItem.value.index}. ${listItem.value.text}`;
60
+ }
61
+ return `\n- ${listItem.value}`;
62
+ });
63
+ contentMd += `${listStr.join('')}\n`;
64
+ }
65
+ else if (line.type === 'MD.code') {
66
+ const language = line?.options?.language || '';
67
+ contentMd += `\n\`\`\`${language}\n${line.value}\n\`\`\`\n`;
68
+ }
69
+ else {
70
+ const value = line['value'] || '';
71
+ contentMd += String(value);
72
+ }
73
+ });
74
+ }
75
+ else if (item.type === 'BT.group' && item.value.length > 0 && typeof item.value !== 'string') {
76
+ contentMd += `<box classWind="mt-2" variant="borderless" >${item.value
77
+ ?.map(row => {
78
+ const val = row.value;
79
+ if (val.length === 0) {
80
+ return '';
81
+ }
82
+ return `<flex>${val
83
+ .map(button => {
84
+ const value = button?.value || {};
85
+ const options = button.options;
86
+ const autoEnter = options?.autoEnter ?? false;
87
+ const label = value;
88
+ const command = options?.data || label;
89
+ return `<btn command="${command}" enter="${String(autoEnter)}" >${label}</btn>`;
90
+ })
91
+ .join('')}</flex>`;
92
+ })
93
+ .join('')}</box>`;
94
+ }
95
+ });
96
+ }
97
+ return contentMd;
98
+ };
8
99
  const sendToRoom = async (client, param, val) => {
9
100
  try {
10
101
  if (!val || val.length <= 0) {
@@ -15,6 +106,9 @@ const sendToRoom = async (client, param, val) => {
15
106
  const messageId = param?.message_id ? String(param?.message_id) : undefined;
16
107
  const images = val.filter(item => item.type === 'Image' || item.type === 'ImageURL' || item.type === 'ImageFile');
17
108
  const mdAndButtons = val.filter(item => item.type === 'Markdown' || item.type === 'BT.group');
109
+ const nativeTypes = new Set(['Image', 'ImageURL', 'ImageFile', 'Markdown', 'BT.group', 'Mention', 'Text', 'Link']);
110
+ const unsupportedItems = val.filter(item => !nativeTypes.has(item.type));
111
+ const fallbackText = unsupportedItems.map(item => dataEnumToBubbleText(item)).filter(Boolean).join('\n');
18
112
  const content = val
19
113
  .filter(item => item.type === 'Mention' || item.type === 'Text' || item.type === 'Link')
20
114
  .map(item => {
@@ -51,6 +145,8 @@ const sendToRoom = async (client, param, val) => {
51
145
  return '';
52
146
  })
53
147
  .join('');
148
+ const contentMd = buildBubbleMdContent(mdAndButtons);
149
+ const finalContent = [content, contentMd, fallbackText].filter(Boolean).join('\n');
54
150
  if (images.length > 0) {
55
151
  let bufferData = null;
56
152
  for (let i = 0; i < images.length; i++) {
@@ -89,7 +185,7 @@ const sendToRoom = async (client, param, val) => {
89
185
  }
90
186
  if (channelId) {
91
187
  const res = await client.sendMessage(channelId, {
92
- content: content,
188
+ content: finalContent,
93
189
  type: 'image',
94
190
  attachments: [fileAttachment]
95
191
  });
@@ -97,7 +193,7 @@ const sendToRoom = async (client, param, val) => {
97
193
  }
98
194
  if (threadId) {
99
195
  const res = await client.sendDm(threadId, {
100
- content: content,
196
+ content: finalContent,
101
197
  type: 'image',
102
198
  attachments: [fileAttachment]
103
199
  });
@@ -105,100 +201,13 @@ const sendToRoom = async (client, param, val) => {
105
201
  }
106
202
  return [createResult(ResultCode.Ok, '完成', null)];
107
203
  }
108
- let contentMd = '';
109
- if (mdAndButtons && mdAndButtons.length > 0) {
110
- mdAndButtons.forEach(item => {
111
- if (item.type === 'Markdown' && typeof item.value !== 'string') {
112
- const md = item.value;
113
- const map = {
114
- 'MD.title': value => `# ${value}`,
115
- 'MD.subtitle': value => `## ${value}`,
116
- 'MD.text': value => `${value} `,
117
- 'MD.bold': value => `**${value}** `,
118
- 'MD.divider': () => '\n————————\n',
119
- 'MD.italic': value => `_${value}_ `,
120
- 'MD.italicStar': value => `*${value}* `,
121
- 'MD.strikethrough': value => `~~${value}~~ `,
122
- 'MD.blockquote': value => `\n> ${value}`,
123
- 'MD.newline': () => '\n',
124
- 'MD.link': value => `[🔗${value.text}](${value.url}) `,
125
- 'MD.image': value => `\n![${value}](${value})\n`,
126
- 'MD.mention': (value, options) => {
127
- const { belong } = options || {};
128
- if (value === 'everyone' || value === 'all' || value === '' || typeof value !== 'string') {
129
- return '<@everyone> ';
130
- }
131
- if (belong === 'user') {
132
- return `<@${value}> `;
133
- }
134
- else if (belong === 'channel') {
135
- return `<#${value}> `;
136
- }
137
- return '';
138
- },
139
- 'MD.button': (value, options) => {
140
- const autoEnter = options?.autoEnter ?? false;
141
- const label = typeof value === 'object' ? value.title : value;
142
- const command = options?.data || label;
143
- return `<btn variant="borderless" command="${command}" enter="${String(autoEnter)}" >${label}</btn> `;
144
- },
145
- 'MD.content': value => `${value}`
146
- };
147
- md.forEach(line => {
148
- if (map[line.type]) {
149
- const value = line?.value;
150
- const options = line?.options;
151
- contentMd += map[line.type](value, options);
152
- return;
153
- }
154
- if (line.type === 'MD.list') {
155
- const listStr = line.value.map(listItem => {
156
- if (typeof listItem.value === 'object') {
157
- return `\n${listItem.value.index}. ${listItem.value.text}`;
158
- }
159
- return `\n- ${listItem.value}`;
160
- });
161
- contentMd += `${listStr.join('')}\n`;
162
- }
163
- else if (line.type === 'MD.code') {
164
- const language = line?.options?.language || '';
165
- contentMd += `\n\`\`\`${language}\n${line.value}\n\`\`\`\n`;
166
- }
167
- else {
168
- const value = line['value'] || '';
169
- contentMd += String(value);
170
- }
171
- });
172
- }
173
- else if (item.type === 'BT.group' && item.value.length > 0 && typeof item.value !== 'string') {
174
- contentMd += `<box classWind="mt-2" variant="borderless" >${item.value
175
- ?.map(row => {
176
- const val = row.value;
177
- if (val.length === 0) {
178
- return '';
179
- }
180
- return `<flex>${val
181
- .map(button => {
182
- const value = button?.value || {};
183
- const options = button.options;
184
- const autoEnter = options?.autoEnter ?? false;
185
- const label = value;
186
- const command = options?.data || label;
187
- return `<btn command="${command}" enter="${String(autoEnter)}" >${label}</btn>`;
188
- })
189
- .join('')}</flex>`;
190
- })
191
- .join('')}</box>`;
192
- }
193
- });
194
- }
195
- if ((content && content.length > 0) || (contentMd && contentMd.length > 0)) {
204
+ if (finalContent && finalContent.length > 0) {
196
205
  if (channelId) {
197
- const res = await client.sendMessage(channelId, { content: content !== '' ? content : contentMd, type: 'text' });
206
+ const res = await client.sendMessage(channelId, { content: finalContent, type: 'text' });
198
207
  return [createResult(ResultCode.Ok, '完成', res)];
199
208
  }
200
209
  if (threadId) {
201
- const res = await client.sendDm(threadId, { content: content !== '' ? content : contentMd, type: 'text' });
210
+ const res = await client.sendDm(threadId, { content: finalContent, type: 'text' });
202
211
  return [createResult(ResultCode.Ok, '完成', res)];
203
212
  }
204
213
  return [createResult(ResultCode.Ok, '完成', null)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alemonjs/bubble",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "bubble platform",
5
5
  "author": "lemonade",
6
6
  "license": "MIT",