commune-ai 0.2.5 → 0.2.61
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/README.md +131 -2
- package/dist/client.d.ts +5 -2
- package/dist/client.js +5 -1
- package/dist/types.d.ts +23 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,6 +132,53 @@ const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
|
|
|
132
132
|
|
|
133
133
|
---
|
|
134
134
|
|
|
135
|
+
## Attachments
|
|
136
|
+
Access email attachments with secure, temporary download URLs.
|
|
137
|
+
|
|
138
|
+
Attachments are stored and available as `attachment_id`. These IDs are:
|
|
139
|
+
- Available through email events in the incoming webhook
|
|
140
|
+
- Available in metadata of semantic search results
|
|
141
|
+
|
|
142
|
+
Use the `attachment_id` to directly access the attachment, download it or read it.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
// Get attachment metadata
|
|
146
|
+
const attachment = await client.attachments.get("att_123");
|
|
147
|
+
console.log(attachment.filename, attachment.size);
|
|
148
|
+
|
|
149
|
+
// Get download URL (expires in 1 hour)
|
|
150
|
+
const { url } = await client.attachments.get("att_123", { url: true });
|
|
151
|
+
// Use the URL to download or display the file
|
|
152
|
+
|
|
153
|
+
// Custom expiration (2 hours)
|
|
154
|
+
const { url } = await client.attachments.get("att_123", { url: true, expiresIn: 7200 });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Handling attachments in webhook
|
|
158
|
+
```ts
|
|
159
|
+
const handler = createWebhookHandler({
|
|
160
|
+
onEvent: async (message, context) => {
|
|
161
|
+
const { attachments } = context.payload;
|
|
162
|
+
|
|
163
|
+
if (attachments && attachments.length > 0) {
|
|
164
|
+
for (const att of attachments) {
|
|
165
|
+
console.log(`Attachment: ${att.filename} (${att.size} bytes)`);
|
|
166
|
+
|
|
167
|
+
// Get download URL
|
|
168
|
+
const { url } = await client.attachments.get(att.attachment_id, { url: true });
|
|
169
|
+
|
|
170
|
+
// Download the file
|
|
171
|
+
const response = await fetch(url);
|
|
172
|
+
const buffer = await response.arrayBuffer();
|
|
173
|
+
// Process the file...
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
135
182
|
## Semantic Search
|
|
136
183
|
Commune provides powerful semantic search capabilities to help your agent find relevant conversations and context. The search is powered by embeddings and vector similarity, allowing natural language queries.
|
|
137
184
|
|
|
@@ -230,10 +277,75 @@ interface SearchResult {
|
|
|
230
277
|
participants: string[];
|
|
231
278
|
threadId: string;
|
|
232
279
|
timestamp: Date;
|
|
280
|
+
attachmentIds?: string[]; // Attachment IDs in this conversation
|
|
281
|
+
hasAttachments?: boolean; // Whether conversation has attachments
|
|
282
|
+
attachmentCount?: number; // Number of attachments
|
|
233
283
|
};
|
|
234
284
|
}
|
|
235
285
|
```
|
|
236
286
|
|
|
287
|
+
### Search with attachments
|
|
288
|
+
```ts
|
|
289
|
+
// Search for conversations with attachments
|
|
290
|
+
const results = await client.searchConversations(
|
|
291
|
+
"invoice with receipt",
|
|
292
|
+
{ organizationId: "org_123" }
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// Filter results that have attachments
|
|
296
|
+
const withAttachments = results.filter(r => r.metadata.hasAttachments);
|
|
297
|
+
|
|
298
|
+
// Access attachments from search results
|
|
299
|
+
for (const result of withAttachments) {
|
|
300
|
+
for (const attachmentId of result.metadata.attachmentIds || []) {
|
|
301
|
+
const { url, filename } = await client.attachments.get(attachmentId, { url: true });
|
|
302
|
+
console.log(`Download: ${filename} - ${url}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Spam Protection
|
|
308
|
+
Commune includes built-in spam detection and protection to keep your agent safe from malicious emails and prevent abuse.
|
|
309
|
+
|
|
310
|
+
### Automatic Spam Detection
|
|
311
|
+
All incoming emails are automatically analyzed for spam patterns:
|
|
312
|
+
- Content analysis (spam keywords, suspicious patterns)
|
|
313
|
+
- URL validation (broken links, phishing detection)
|
|
314
|
+
- Sender reputation tracking
|
|
315
|
+
- Domain blacklist checking
|
|
316
|
+
|
|
317
|
+
Spam emails are automatically rejected before reaching your webhook, protecting your agent from:
|
|
318
|
+
- Phishing attempts
|
|
319
|
+
- Malicious links
|
|
320
|
+
- Mass spam campaigns
|
|
321
|
+
- Fraudulent content
|
|
322
|
+
|
|
323
|
+
### Outbound Protection
|
|
324
|
+
Commune also protects your sending reputation:
|
|
325
|
+
- Rate limiting per organization tier
|
|
326
|
+
- Content validation for outbound emails
|
|
327
|
+
- Burst detection for mass mailing
|
|
328
|
+
- Automatic blocking of spam-like content
|
|
329
|
+
|
|
330
|
+
This ensures your domain maintains high deliverability and isn't flagged by email providers.
|
|
331
|
+
|
|
332
|
+
### Spam Reporting
|
|
333
|
+
If a spam email gets through, you can report it:
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
import { CommuneClient } from "@commune/sdk";
|
|
337
|
+
const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
|
|
338
|
+
|
|
339
|
+
// Report a message as spam
|
|
340
|
+
await client.reportSpam({
|
|
341
|
+
message_id: "msg_123",
|
|
342
|
+
reason: "Unsolicited marketing email",
|
|
343
|
+
classification: "spam" // or "phishing", "malware", "other"
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The system learns from reports and automatically blocks repeat offenders.
|
|
348
|
+
|
|
237
349
|
## Context (conversation state)
|
|
238
350
|
Commune stores conversation state so your agent can respond with context.
|
|
239
351
|
|
|
@@ -485,6 +597,7 @@ const handler = createWebhookHandler({
|
|
|
485
597
|
## Full example (single file)
|
|
486
598
|
A complete copy‑paste example that:
|
|
487
599
|
- receives webhook
|
|
600
|
+
- handles attachments
|
|
488
601
|
- replies by email
|
|
489
602
|
- replies in Slack thread
|
|
490
603
|
- fetches conversation history
|
|
@@ -497,7 +610,23 @@ const client = new CommuneClient({ apiKey: process.env.COMMUNE_API_KEY! });
|
|
|
497
610
|
|
|
498
611
|
const handler = createWebhookHandler({
|
|
499
612
|
onEvent: async (message, context) => {
|
|
500
|
-
// 1)
|
|
613
|
+
// 1) Handle attachments
|
|
614
|
+
const { attachments } = context.payload;
|
|
615
|
+
if (attachments && attachments.length > 0) {
|
|
616
|
+
console.log(`Received ${attachments.length} attachments`);
|
|
617
|
+
|
|
618
|
+
for (const att of attachments) {
|
|
619
|
+
// Get download URL
|
|
620
|
+
const { url, filename } = await client.attachments.get(att.attachment_id, { url: true });
|
|
621
|
+
console.log(`Attachment: ${filename} - ${url}`);
|
|
622
|
+
|
|
623
|
+
// Download if needed
|
|
624
|
+
// const response = await fetch(url);
|
|
625
|
+
// const buffer = await response.arrayBuffer();
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// 2) Email reply (same thread)
|
|
501
630
|
if (message.channel === "email") {
|
|
502
631
|
const sender = message.participants.find(p => p.role === "sender")?.identity;
|
|
503
632
|
if (!sender) return;
|
|
@@ -514,7 +643,7 @@ const handler = createWebhookHandler({
|
|
|
514
643
|
return;
|
|
515
644
|
}
|
|
516
645
|
|
|
517
|
-
//
|
|
646
|
+
// 3) Slack reply (same thread)
|
|
518
647
|
if (message.channel === "slack") {
|
|
519
648
|
const channelId = message.metadata.slack_channel_id;
|
|
520
649
|
if (!channelId) return;
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AttachmentRecord, CreateDomainPayload, DomainEntry, InboxEntry, MessageListParams, SendMessagePayload, UnifiedMessage, SearchFilter, SearchOptions, SearchResult, IndexConversationPayload } from './types.js';
|
|
1
|
+
import type { AttachmentRecord, AttachmentUrl, CreateDomainPayload, DomainEntry, InboxEntry, MessageListParams, SendMessagePayload, UnifiedMessage, SearchFilter, SearchOptions, SearchResult, IndexConversationPayload } from './types.js';
|
|
2
2
|
export type ClientOptions = {
|
|
3
3
|
baseUrl?: string;
|
|
4
4
|
apiKey: string;
|
|
@@ -72,6 +72,9 @@ export declare class CommuneClient {
|
|
|
72
72
|
}>;
|
|
73
73
|
};
|
|
74
74
|
attachments: {
|
|
75
|
-
get: (attachmentId: string
|
|
75
|
+
get: (attachmentId: string, options?: {
|
|
76
|
+
url?: boolean;
|
|
77
|
+
expiresIn?: number;
|
|
78
|
+
}) => Promise<AttachmentRecord | AttachmentUrl>;
|
|
76
79
|
};
|
|
77
80
|
}
|
package/dist/client.js
CHANGED
|
@@ -128,7 +128,11 @@ export class CommuneClient {
|
|
|
128
128
|
},
|
|
129
129
|
};
|
|
130
130
|
this.attachments = {
|
|
131
|
-
get: async (attachmentId) => {
|
|
131
|
+
get: async (attachmentId, options) => {
|
|
132
|
+
if (options?.url) {
|
|
133
|
+
const expiresIn = options.expiresIn || 3600;
|
|
134
|
+
return this.request(`/api/attachments/${encodeURIComponent(attachmentId)}/url?expiresIn=${expiresIn}`);
|
|
135
|
+
}
|
|
132
136
|
return this.request(`/api/attachments/${encodeURIComponent(attachmentId)}`);
|
|
133
137
|
},
|
|
134
138
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -42,6 +42,22 @@ export interface AttachmentRecord {
|
|
|
42
42
|
source: Channel;
|
|
43
43
|
source_url?: string | null;
|
|
44
44
|
download_error?: boolean;
|
|
45
|
+
storage_type?: 'cloudinary' | 'database';
|
|
46
|
+
cloudinary_url?: string | null;
|
|
47
|
+
cloudinary_public_id?: string | null;
|
|
48
|
+
}
|
|
49
|
+
export interface AttachmentMetadata {
|
|
50
|
+
attachment_id: string;
|
|
51
|
+
filename: string;
|
|
52
|
+
mime_type: string;
|
|
53
|
+
size: number;
|
|
54
|
+
}
|
|
55
|
+
export interface AttachmentUrl {
|
|
56
|
+
url: string;
|
|
57
|
+
expiresIn: number;
|
|
58
|
+
filename: string;
|
|
59
|
+
mimeType: string;
|
|
60
|
+
size: number;
|
|
45
61
|
}
|
|
46
62
|
export interface DomainWebhook {
|
|
47
63
|
id?: string;
|
|
@@ -142,6 +158,7 @@ export interface InboundEmailWebhookPayload {
|
|
|
142
158
|
email: unknown;
|
|
143
159
|
message: UnifiedMessage;
|
|
144
160
|
extractedData?: Record<string, any>;
|
|
161
|
+
attachments?: AttachmentMetadata[];
|
|
145
162
|
}
|
|
146
163
|
export interface ApiError {
|
|
147
164
|
message?: string;
|
|
@@ -175,6 +192,9 @@ export interface SearchResult {
|
|
|
175
192
|
participants: string[];
|
|
176
193
|
threadId: string;
|
|
177
194
|
timestamp: Date;
|
|
195
|
+
attachmentIds?: string[];
|
|
196
|
+
hasAttachments?: boolean;
|
|
197
|
+
attachmentCount?: number;
|
|
178
198
|
};
|
|
179
199
|
}
|
|
180
200
|
export interface ConversationMetadata {
|
|
@@ -185,6 +205,9 @@ export interface ConversationMetadata {
|
|
|
185
205
|
participants: string[];
|
|
186
206
|
threadId: string;
|
|
187
207
|
timestamp: Date;
|
|
208
|
+
attachmentIds?: string[];
|
|
209
|
+
hasAttachments?: boolean;
|
|
210
|
+
attachmentCount?: number;
|
|
188
211
|
}
|
|
189
212
|
export interface IndexConversationPayload {
|
|
190
213
|
id: string;
|