whatsapp-pi 1.0.51 → 1.0.52
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 +4 -6
- package/package.json +2 -1
- package/src/i18n.ts +4 -0
- package/src/services/incoming-media.service.ts +42 -0
- package/whatsapp-pi.ts +0 -11
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Pi is a powerful agentic AI coding assistant that operates in your terminal. Thi
|
|
|
27
27
|
- **Media Support**:
|
|
28
28
|
- **Vision Analysis**: Automatically forwards WhatsApp images to Pi for analysis.
|
|
29
29
|
- **Audio Transcription**: Transcribes voice notes when Whisper is installed.
|
|
30
|
-
- **Document Handling**: Downloads and stores documents (PDF, text) for agent access.
|
|
30
|
+
- **Document Handling**: Downloads and stores documents (PDF, text) for agent access; PDFs include a bounded text preview when readable.
|
|
31
31
|
|
|
32
32
|
## Prerequisites
|
|
33
33
|
|
|
@@ -36,10 +36,8 @@ To enable audio transcription features:
|
|
|
36
36
|
python -m pip install -U openai-whisper
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
- **macOS**: `brew install poppler`
|
|
42
|
-
- **Windows**: Install `poppler` (e.g., via Scoop) and add to PATH.
|
|
39
|
+
PDF documents are parsed locally and do not require extra system utilities.
|
|
40
|
+
If a PDF cannot be parsed automatically, it is still saved and forwarded with a clear fallback notice.
|
|
43
41
|
|
|
44
42
|
## Quick Start
|
|
45
43
|
|
|
@@ -164,7 +162,7 @@ npm test
|
|
|
164
162
|
- **Group-Only Mode**: Use `--whatsapp-group <jid>` to bind Pi to a single WhatsApp group. The group must also be present in Allowed Groups.
|
|
165
163
|
- **Recents Store**: Recent conversations and message history are persisted in `~/.pi/whatsapp-pi/recents/recents.json`.
|
|
166
164
|
- **Message Detail / Reply**: Open a message from history to inspect full content and reply with `R`.
|
|
167
|
-
- **Media Support**: Images are forwarded for vision analysis, audio is transcribed with Whisper, and
|
|
165
|
+
- **Media Support**: Images are forwarded for vision analysis, audio is transcribed with Whisper, and PDFs are saved under `./.pi-data/whatsapp/documents/` with local text preview when available.
|
|
168
166
|
- **Session Handling**: Saved state, allow list, and startup reconnects are restored automatically when available.
|
|
169
167
|
- **Intelligent Message Filtering**: Messages ending with `π` are ignored to prevent bot loops.
|
|
170
168
|
- **Storage Management**: Persistent data lives under `.pi-data/` plus the recents store in the user home directory.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whatsapp-pi",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.52",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "WhatsApp integration extension for Pi",
|
|
6
6
|
"main": "whatsapp-pi.ts",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"@llamaindex/liteparse": "^1.5.3",
|
|
35
36
|
"baileys": "^6.7.21",
|
|
36
37
|
"pino": "^10.3.1",
|
|
37
38
|
"qrcode-terminal": "^0.12.0"
|
package/src/i18n.ts
CHANGED
|
@@ -197,6 +197,8 @@ const fallback = {
|
|
|
197
197
|
"incoming.media.documentSize": "Size: {size}",
|
|
198
198
|
"incoming.media.documentLocation": "Location: {relativePath}",
|
|
199
199
|
"incoming.media.documentDescription": "Description: {caption}",
|
|
200
|
+
"incoming.media.documentPdfPreviewHeading": "PDF text preview:",
|
|
201
|
+
"incoming.media.documentPdfFallbackNotice": "PDF text was not extracted automatically. The file is saved at the path above.",
|
|
200
202
|
"incoming.media.documentDownloadFailed": "[WhatsApp-Pi] Failed to download document:",
|
|
201
203
|
"incoming.media.documentDownloadFailedText": "[Document: {fileName} (download failed)]",
|
|
202
204
|
"audio.emptyTranscription": "[Empty transcription]",
|
|
@@ -375,6 +377,8 @@ const translations: Record<Locale, Partial<Record<Key, string>>> = {
|
|
|
375
377
|
"incoming.media.documentSize": "Tamanho: {size}",
|
|
376
378
|
"incoming.media.documentLocation": "Local: {relativePath}",
|
|
377
379
|
"incoming.media.documentDescription": "Descrição: {caption}",
|
|
380
|
+
"incoming.media.documentPdfPreviewHeading": "Prévia do texto do PDF:",
|
|
381
|
+
"incoming.media.documentPdfFallbackNotice": "O texto do PDF não pôde ser extraído automaticamente. O arquivo foi salvo no caminho acima.",
|
|
378
382
|
"incoming.media.documentDownloadFailed": "[WhatsApp-Pi] Falha ao baixar documento:",
|
|
379
383
|
"incoming.media.documentDownloadFailedText": "[Documento: {fileName} (download falhou)]",
|
|
380
384
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { downloadContentFromMessage } from 'baileys';
|
|
2
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { LiteParse } from '@llamaindex/liteparse';
|
|
4
5
|
import { AudioService } from './audio.service.js';
|
|
5
6
|
import type { IncomingResolution } from './incoming-message.resolver.js';
|
|
6
7
|
import { WhatsAppPiLogger } from './whatsapp-pi.logger.js';
|
|
@@ -12,7 +13,11 @@ export interface ProcessedIncomingContent {
|
|
|
12
13
|
imageMimeType?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
const PDF_PREVIEW_LIMIT = 1200;
|
|
17
|
+
|
|
15
18
|
export class IncomingMediaService {
|
|
19
|
+
private readonly pdfParser = new LiteParse({ ocrEnabled: true });
|
|
20
|
+
|
|
16
21
|
constructor(
|
|
17
22
|
private readonly audioService: AudioService,
|
|
18
23
|
private readonly logger = new WhatsAppPiLogger(false)
|
|
@@ -80,6 +85,15 @@ export class IncomingMediaService {
|
|
|
80
85
|
+ t('incoming.media.documentSize', { size: this.formatFileSize(fileSize) }) + '\n'
|
|
81
86
|
+ t('incoming.media.documentLocation', { relativePath });
|
|
82
87
|
|
|
88
|
+
if (this.isPdfDocument(fileName, mimeType)) {
|
|
89
|
+
const preview = await this.extractPdfPreview(buffer);
|
|
90
|
+
if (preview) {
|
|
91
|
+
text += `\n\n${t('incoming.media.documentPdfPreviewHeading')}\n${preview}`;
|
|
92
|
+
} else {
|
|
93
|
+
text += `\n\n${t('incoming.media.documentPdfFallbackNotice')}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
83
97
|
if (documentMessage.caption) {
|
|
84
98
|
text += `\n\n${t('incoming.media.documentDescription', { caption: documentMessage.caption })}`;
|
|
85
99
|
}
|
|
@@ -91,6 +105,34 @@ export class IncomingMediaService {
|
|
|
91
105
|
}
|
|
92
106
|
}
|
|
93
107
|
|
|
108
|
+
private async extractPdfPreview(buffer: Buffer): Promise<string | null> {
|
|
109
|
+
try {
|
|
110
|
+
const result = await this.pdfParser.parse(buffer);
|
|
111
|
+
return this.formatPdfPreview(result.text);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
this.logger.warn('[WhatsApp-Pi] PDF parsing failed, falling back to storage-only behavior.', error);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private formatPdfPreview(text: string | undefined | null): string | null {
|
|
119
|
+
const normalized = (text || '').replace(/\r\n/g, '\n').trim();
|
|
120
|
+
if (!normalized) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (normalized.length <= PDF_PREVIEW_LIMIT) {
|
|
125
|
+
return normalized;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return `${normalized.slice(0, PDF_PREVIEW_LIMIT)}…`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private isPdfDocument(fileName: string, mimeType: string): boolean {
|
|
132
|
+
const normalizedMimeType = mimeType.toLowerCase().split(';')[0].trim();
|
|
133
|
+
return normalizedMimeType === 'application/pdf' || fileName.toLowerCase().endsWith('.pdf');
|
|
134
|
+
}
|
|
135
|
+
|
|
94
136
|
private async downloadMessage(message: any, type: 'image' | 'document'): Promise<Buffer> {
|
|
95
137
|
const stream = await downloadContentFromMessage(message, type);
|
|
96
138
|
let buffer = Buffer.from([]);
|
package/whatsapp-pi.ts
CHANGED
|
@@ -183,17 +183,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
ctx.ui.notify('WhatsApp: Session reset via /new is now fully supported.', 'info');
|
|
186
|
-
|
|
187
|
-
// Verify pdftotext availability for document support
|
|
188
|
-
try {
|
|
189
|
-
const { code } = await pi.exec('pdftotext', ['-v']);
|
|
190
|
-
if (code !== 0 && code !== 99) { // 99 is a common exit code for -v in some versions
|
|
191
|
-
throw new Error(`pdftotext returned code ${code}`);
|
|
192
|
-
}
|
|
193
|
-
} catch {
|
|
194
|
-
ctx.ui.notify('WhatsApp: pdftotext not found. PDF document support will be limited to storage only.', 'warning');
|
|
195
|
-
logger.warn('[WhatsApp-Pi] Warning: pdftotext not found in system PATH.');
|
|
196
|
-
}
|
|
197
186
|
});
|
|
198
187
|
|
|
199
188
|
// Track whether send_wa_message tool already sent a reply this turn
|