291 lines
6.9 KiB
Markdown
291 lines
6.9 KiB
Markdown
|
|
# Plusvibe Thread Extraction Debugging Guide
|
||
|
|
|
||
|
|
## Quick Start Testing
|
||
|
|
|
||
|
|
Open the browser console on Plusvibe and run these commands:
|
||
|
|
|
||
|
|
### 1. Test DOM Analysis
|
||
|
|
```javascript
|
||
|
|
// Analyze the page structure
|
||
|
|
PlusvibeExtractor.analyzeDOMStructure();
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Test API Endpoints
|
||
|
|
```javascript
|
||
|
|
// Test which API endpoints work
|
||
|
|
await PlusvibeExtractor.testAPIEndpoints();
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Test Thread Extraction
|
||
|
|
```javascript
|
||
|
|
// Try to extract messages
|
||
|
|
const messages = await PlusvibeExtractor.extractThreadMessages();
|
||
|
|
console.log('Extracted messages:', messages);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Force DOM Extraction
|
||
|
|
```javascript
|
||
|
|
// Skip API and test DOM extraction
|
||
|
|
const domMessages = PlusvibeExtractor.extractViaDOM();
|
||
|
|
console.log('DOM messages:', domMessages);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Finding the Right Selectors
|
||
|
|
|
||
|
|
### Step 1: Identify Message Containers
|
||
|
|
|
||
|
|
1. **Right-click on a message** in Plusvibe
|
||
|
|
2. Select **"Inspect Element"**
|
||
|
|
3. Look for patterns in the HTML structure
|
||
|
|
4. Note the class names, IDs, and data attributes
|
||
|
|
|
||
|
|
Common patterns to look for:
|
||
|
|
- Classes containing: `message`, `email`, `thread`, `conversation`
|
||
|
|
- Data attributes: `data-message-id`, `data-thread-id`, `data-email`
|
||
|
|
- Container elements: `<article>`, `<section>`, `<li>`, `<div>`
|
||
|
|
|
||
|
|
### Step 2: Test Selectors
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Test a specific selector
|
||
|
|
const elements = document.querySelectorAll('.your-selector-here');
|
||
|
|
console.log(`Found ${elements.length} elements`);
|
||
|
|
elements.forEach((el, i) => {
|
||
|
|
console.log(`Element ${i}:`, {
|
||
|
|
text: el.textContent.substring(0, 100),
|
||
|
|
classes: el.className,
|
||
|
|
hasEmail: el.textContent.includes('@')
|
||
|
|
});
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 3: Find Thread ID Location
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Search for thread IDs in various places
|
||
|
|
const locations = [
|
||
|
|
// URL
|
||
|
|
window.location.href,
|
||
|
|
// Data attributes
|
||
|
|
...[...document.querySelectorAll('[data-id], [data-thread-id], [data-conversation-id]')].map(el => ({
|
||
|
|
element: el,
|
||
|
|
id: el.getAttribute('data-id') || el.getAttribute('data-thread-id') || el.getAttribute('data-conversation-id')
|
||
|
|
})),
|
||
|
|
// Text content
|
||
|
|
document.body.innerText.match(/ID:\s*([a-zA-Z0-9\-_]+)/gi)
|
||
|
|
];
|
||
|
|
console.log('Thread ID locations:', locations);
|
||
|
|
```
|
||
|
|
|
||
|
|
## API Integration
|
||
|
|
|
||
|
|
### Testing API Authentication
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Test if your API key works
|
||
|
|
fetch('https://app.pipl.ai/api/v2/me', {
|
||
|
|
headers: {
|
||
|
|
'Authorization': 'Bearer YOUR_API_KEY_HERE',
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
}
|
||
|
|
}).then(r => r.json()).then(console.log);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Finding the Right Endpoints
|
||
|
|
|
||
|
|
Try these common patterns:
|
||
|
|
1. `/api/v2/threads/{threadId}`
|
||
|
|
2. `/api/v2/conversations/{conversationId}`
|
||
|
|
3. `/api/v2/messages?thread={threadId}`
|
||
|
|
4. `/api/v2/inbox/messages`
|
||
|
|
5. `/api/v1/emails`
|
||
|
|
|
||
|
|
### API Response Formats
|
||
|
|
|
||
|
|
Look for these common structures:
|
||
|
|
```javascript
|
||
|
|
// Format 1: Direct messages array
|
||
|
|
{
|
||
|
|
"messages": [
|
||
|
|
{
|
||
|
|
"id": "123",
|
||
|
|
"from": "sender@email.com",
|
||
|
|
"content": "Message text",
|
||
|
|
"timestamp": "2024-01-01T12:00:00Z"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
|
||
|
|
// Format 2: Nested in data
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"messages": [...]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Format 3: Thread with messages
|
||
|
|
{
|
||
|
|
"thread": {
|
||
|
|
"id": "thread123",
|
||
|
|
"messages": [...]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Advanced DOM Extraction
|
||
|
|
|
||
|
|
### Visual Proximity Method
|
||
|
|
|
||
|
|
This method finds emails and looks for content nearby:
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Find all email addresses on the page
|
||
|
|
const emailRegex = /[\w.+-]+@[\w.-]+\.\w+/g;
|
||
|
|
const walker = document.createTreeWalker(
|
||
|
|
document.body,
|
||
|
|
NodeFilter.SHOW_TEXT,
|
||
|
|
null,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
const emailNodes = [];
|
||
|
|
let node;
|
||
|
|
while (node = walker.nextNode()) {
|
||
|
|
if (emailRegex.test(node.textContent)) {
|
||
|
|
emailNodes.push({
|
||
|
|
node: node,
|
||
|
|
parent: node.parentElement,
|
||
|
|
email: node.textContent.match(emailRegex)[0]
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('Email nodes found:', emailNodes);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Pattern-Based Extraction
|
||
|
|
|
||
|
|
Test different patterns:
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const patterns = {
|
||
|
|
// Pattern 1: "email wrote:"
|
||
|
|
wrote: /([\w.+-]+@[\w.-]+\.\w+)\s+wrote:/gi,
|
||
|
|
|
||
|
|
// Pattern 2: Timestamp followed by content
|
||
|
|
timestamp: /(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{4}.*?)\n([\s\S]+?)(?=\d{1,2}[\/\-]\d{1,2}[\/\-]\d{4}|$)/gi,
|
||
|
|
|
||
|
|
// Pattern 3: Email in header
|
||
|
|
header: /From:\s*([\w.+-]+@[\w.-]+\.\w+)/gi
|
||
|
|
};
|
||
|
|
|
||
|
|
Object.entries(patterns).forEach(([name, pattern]) => {
|
||
|
|
const matches = [...document.body.innerText.matchAll(pattern)];
|
||
|
|
console.log(`Pattern "${name}" found ${matches.length} matches`);
|
||
|
|
matches.slice(0, 3).forEach((match, i) => {
|
||
|
|
console.log(` Match ${i + 1}:`, match[0].substring(0, 100));
|
||
|
|
});
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Common Issues and Solutions
|
||
|
|
|
||
|
|
### Issue 1: No Messages Found
|
||
|
|
|
||
|
|
**Solution**: Check if messages are in an iframe
|
||
|
|
```javascript
|
||
|
|
const iframes = document.querySelectorAll('iframe');
|
||
|
|
iframes.forEach((iframe, i) => {
|
||
|
|
try {
|
||
|
|
const iframeDoc = iframe.contentDocument;
|
||
|
|
if (iframeDoc && iframeDoc.body.textContent.includes('@')) {
|
||
|
|
console.log(`Iframe ${i} contains email content`);
|
||
|
|
// You may need to run extraction inside the iframe
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
console.log(`Iframe ${i} is cross-origin`);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Issue 2: Dynamic Content Loading
|
||
|
|
|
||
|
|
**Solution**: Wait for content to load
|
||
|
|
```javascript
|
||
|
|
// Observer to detect when messages load
|
||
|
|
const observer = new MutationObserver((mutations) => {
|
||
|
|
const hasNewEmails = mutations.some(m =>
|
||
|
|
[...m.addedNodes].some(n =>
|
||
|
|
n.textContent && n.textContent.includes('@')
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
if (hasNewEmails) {
|
||
|
|
console.log('New email content detected');
|
||
|
|
// Re-run extraction
|
||
|
|
PlusvibeExtractor.extractThreadMessages();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
observer.observe(document.body, {
|
||
|
|
childList: true,
|
||
|
|
subtree: true
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Issue 3: Messages in Shadow DOM
|
||
|
|
|
||
|
|
**Solution**: Check for shadow roots
|
||
|
|
```javascript
|
||
|
|
function findShadowRoots(root = document.body) {
|
||
|
|
const elements = [];
|
||
|
|
|
||
|
|
function traverse(node) {
|
||
|
|
if (node.shadowRoot) {
|
||
|
|
elements.push(node.shadowRoot);
|
||
|
|
traverse(node.shadowRoot);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const child of node.children || []) {
|
||
|
|
traverse(child);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
traverse(root);
|
||
|
|
console.log('Found shadow roots:', elements);
|
||
|
|
return elements;
|
||
|
|
}
|
||
|
|
|
||
|
|
findShadowRoots();
|
||
|
|
```
|
||
|
|
|
||
|
|
## Reporting Issues
|
||
|
|
|
||
|
|
When reporting issues, please provide:
|
||
|
|
|
||
|
|
1. **Console output** from:
|
||
|
|
```javascript
|
||
|
|
PlusvibeExtractor.analyzeDOMStructure();
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Sample HTML** of a message element:
|
||
|
|
```javascript
|
||
|
|
// Right-click message → Inspect → Copy → Copy outerHTML
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **API test results** (if using API):
|
||
|
|
```javascript
|
||
|
|
await PlusvibeExtractor.testAPIEndpoints();
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Current URL format**:
|
||
|
|
```javascript
|
||
|
|
console.log({
|
||
|
|
href: window.location.href,
|
||
|
|
pathname: window.location.pathname,
|
||
|
|
search: window.location.search,
|
||
|
|
hash: window.location.hash
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
This information will help identify the correct selectors and methods for your Plusvibe instance.
|