bloby-bot 0.23.3 → 0.23.5
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/dist-bloby/assets/{bloby-C0a7vrLL.js → bloby-D5KNR5Eq.js} +77 -77
- package/dist-bloby/assets/{highlighted-body-OFNGDK62-CZjp0iMu.js → highlighted-body-OFNGDK62-DTNO5HAa.js} +1 -1
- package/dist-bloby/assets/mermaid-GHXKKRXX-BcQuheG7.js +1 -0
- package/dist-bloby/bloby.html +1 -1
- package/package.json +1 -1
- package/supervisor/channels/manager.ts +50 -2
- package/supervisor/channels/whatsapp.ts +17 -0
- package/supervisor/chat/src/components/Chat/BlobyImageCard.tsx +14 -1
- package/dist-bloby/assets/mermaid-GHXKKRXX-Br2SGJBW.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{n as r,r as i,t as a}from"./bloby-
|
|
1
|
+
import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{n as r,r as i,t as a}from"./bloby-D5KNR5Eq.js";var o=e(t(),1),s=n(),c=({code:e,language:t,raw:n,className:c,startLine:l,lineNumbers:u,...d})=>{let{shikiTheme:f}=(0,o.useContext)(i),p=r(),[m,h]=(0,o.useState)(n);return(0,o.useEffect)(()=>{if(!p){h(n);return}let r=p.highlight({code:e,language:t,themes:f},e=>{h(e)});r&&h(r)},[e,t,f,p,n]),(0,s.jsx)(a,{className:c,language:t,lineNumbers:u,result:m,startLine:l,...d})};export{c as HighlightedCodeBlockBody};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{i as e}from"./bloby-D5KNR5Eq.js";export{e as Mermaid};
|
package/dist-bloby/bloby.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content" />
|
|
6
6
|
<title>Bloby Chat</title>
|
|
7
|
-
<script type="module" crossorigin src="/bloby/assets/bloby-
|
|
7
|
+
<script type="module" crossorigin src="/bloby/assets/bloby-D5KNR5Eq.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/bloby/assets/jsx-runtime-C0W9Wf2W.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/bloby/assets/globals-F7yBpvwG.js">
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/bloby/assets/globals-CLC60WUS.css">
|
package/package.json
CHANGED
|
@@ -138,11 +138,59 @@ export class ChannelManager {
|
|
|
138
138
|
this.providers.clear();
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
/** Send a message via a specific channel */
|
|
141
|
+
/** Send a message via a specific channel, processing any custom UI tags */
|
|
142
142
|
async sendMessage(channel: ChannelType, to: string, text: string): Promise<void> {
|
|
143
143
|
const provider = this.providers.get(channel);
|
|
144
144
|
if (!provider) throw new Error(`Channel ${channel} not available`);
|
|
145
|
-
|
|
145
|
+
|
|
146
|
+
// Process custom tags for channel delivery
|
|
147
|
+
let processed = text;
|
|
148
|
+
|
|
149
|
+
// <BlobyImage> — send the image natively, remove tag from text
|
|
150
|
+
const imgRegex = /<BlobyImage\s+src="([^"]+)"(?:\s+alt="([^"]*)")?\s*\/>/g;
|
|
151
|
+
const images: { src: string; alt: string }[] = [];
|
|
152
|
+
let imgMatch;
|
|
153
|
+
while ((imgMatch = imgRegex.exec(text)) !== null) {
|
|
154
|
+
images.push({ src: imgMatch[1], alt: imgMatch[2] || '' });
|
|
155
|
+
}
|
|
156
|
+
processed = processed.replace(imgRegex, '').trim();
|
|
157
|
+
|
|
158
|
+
// <BlobyText> — flatten to plain title + content
|
|
159
|
+
processed = processed.replace(
|
|
160
|
+
/<BlobyText\s+title="([^"]+)">([\s\S]*?)<\/BlobyText>/g,
|
|
161
|
+
(_match, title, content) => `📄 *${title}*\n\n${content.trim()}`,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// Send text (if any remains after stripping tags)
|
|
165
|
+
if (processed) {
|
|
166
|
+
await provider.sendMessage(to, processed);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Send images natively via WhatsApp
|
|
170
|
+
if (images.length > 0 && provider instanceof WhatsAppChannel) {
|
|
171
|
+
for (const img of images) {
|
|
172
|
+
try {
|
|
173
|
+
// Resolve file path — try multiple locations since the bot might save anywhere
|
|
174
|
+
const relPath = img.src.replace(/^\/api\/files\//, '');
|
|
175
|
+
const candidates = [
|
|
176
|
+
path.join(WORKSPACE_DIR, 'files', relPath), // /api/files/images/x.png → workspace/files/images/x.png
|
|
177
|
+
path.join(WORKSPACE_DIR, relPath), // /x.png → workspace/x.png
|
|
178
|
+
path.join(WORKSPACE_DIR, 'client', 'public', relPath), // /x.png → workspace/client/public/x.png
|
|
179
|
+
];
|
|
180
|
+
const absPath = candidates.find((p) => fs.existsSync(p));
|
|
181
|
+
if (absPath) {
|
|
182
|
+
const buffer = fs.readFileSync(absPath);
|
|
183
|
+
const ext = path.extname(absPath).slice(1);
|
|
184
|
+
const mimeMap: Record<string, string> = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp' };
|
|
185
|
+
await provider.sendImage(to, buffer, img.alt || undefined, mimeMap[ext] || 'image/png');
|
|
186
|
+
} else {
|
|
187
|
+
log.warn(`[channels] Image file not found in any location: ${img.src}`);
|
|
188
|
+
}
|
|
189
|
+
} catch (err: any) {
|
|
190
|
+
log.warn(`[channels] Failed to send image via WhatsApp: ${err.message}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
146
194
|
}
|
|
147
195
|
|
|
148
196
|
/** Show "typing..." indicator in a chat */
|
|
@@ -114,6 +114,23 @@ export class WhatsAppChannel implements ChannelProvider {
|
|
|
114
114
|
log.info(`[whatsapp] Sent message to ${jid} (id=${result?.key?.id || 'unknown'})`);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/** Send an image via WhatsApp */
|
|
118
|
+
async sendImage(to: string, image: Buffer, caption?: string, mimetype?: string): Promise<void> {
|
|
119
|
+
if (!this.sock || !this.connected) {
|
|
120
|
+
log.warn('[whatsapp] Cannot send image — not connected');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const jid = to.includes('@') ? to : `${to.replace(/[^0-9]/g, '')}@s.whatsapp.net`;
|
|
124
|
+
this.stopTyping(jid);
|
|
125
|
+
|
|
126
|
+
const msg: any = { image, mimetype: mimetype || 'image/png' };
|
|
127
|
+
if (caption) msg.caption = caption;
|
|
128
|
+
|
|
129
|
+
const result = await this.sock.sendMessage(jid, msg);
|
|
130
|
+
if (result?.key?.id) this.trackSentId(result.key.id);
|
|
131
|
+
log.info(`[whatsapp] Sent image to ${jid} (id=${result?.key?.id || 'unknown'})`);
|
|
132
|
+
}
|
|
133
|
+
|
|
117
134
|
/** Show "typing..." indicator in a chat. Re-sends every 20s to keep it visible. */
|
|
118
135
|
startTyping(jid: string): void {
|
|
119
136
|
if (!this.sock || !this.connected) return;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Download, ImageOff } from 'lucide-react';
|
|
2
3
|
|
|
3
4
|
interface Props {
|
|
4
5
|
src: string;
|
|
@@ -11,6 +12,8 @@ function getFilename(src: string): string {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export default function BlobyImageCard({ src, alt }: Props) {
|
|
15
|
+
const [failed, setFailed] = useState(false);
|
|
16
|
+
|
|
14
17
|
const handleDownload = async () => {
|
|
15
18
|
try {
|
|
16
19
|
const res = await fetch(src);
|
|
@@ -28,6 +31,15 @@ export default function BlobyImageCard({ src, alt }: Props) {
|
|
|
28
31
|
}
|
|
29
32
|
};
|
|
30
33
|
|
|
34
|
+
if (failed) {
|
|
35
|
+
return (
|
|
36
|
+
<div className="my-2 flex items-center gap-2.5 px-3.5 py-2.5 rounded-xl border border-border/30 bg-black/10 text-muted-foreground/50 text-xs">
|
|
37
|
+
<ImageOff className="h-4 w-4 shrink-0" />
|
|
38
|
+
<span className="truncate">{alt || 'Image not found'}</span>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
return (
|
|
32
44
|
<div className="relative group/img my-2 rounded-xl overflow-hidden border border-border/30 bg-black/20 w-fit max-w-full">
|
|
33
45
|
<img
|
|
@@ -35,6 +47,7 @@ export default function BlobyImageCard({ src, alt }: Props) {
|
|
|
35
47
|
alt={alt || 'Generated image'}
|
|
36
48
|
className="max-w-full max-h-80 object-contain"
|
|
37
49
|
loading="lazy"
|
|
50
|
+
onError={() => setFailed(true)}
|
|
38
51
|
/>
|
|
39
52
|
<div className="absolute top-2 right-2 flex gap-1.5 opacity-0 group-hover/img:opacity-100 transition-opacity">
|
|
40
53
|
<button
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./bloby-C0a7vrLL.js";export{e as Mermaid};
|