mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-28 18:36:23 +02:00
feat(detection): implement multi-page token detection system
- Add extractTwitterTokens() to detect $TOKEN mentions (e.g., $BONK, $SOL) - Add extractContractAddresses() for Solana (base58) and Ethereum (0x) addresses - Add extractTradingPairs() to detect TOKEN/SOL, TOKEN/USDT patterns - Update extractPageContext() to use new detection functions - Add detectedTokens field to PageContext interface - Create DetectedTokensList component to display detected tokens - Integrate DetectedTokensList into ChatInterface - Add handleDetectedTokenClick to analyze selected tokens - Support auto-detection on Twitter, generic pages, and DexScreener Implements Task 2: Multi-Page Token Detection Part of hybrid token detection system (manual search + auto-detect)
This commit is contained in:
parent
cb879fca37
commit
e89824db0f
4 changed files with 251 additions and 0 deletions
|
|
@ -82,6 +82,122 @@ function extractDexScreenerData(): TokenData | undefined {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract token mentions from Twitter/X
|
||||
* Detects $TOKEN format (e.g., $BONK, $SOL)
|
||||
*/
|
||||
function extractTwitterTokens(): TokenData[] {
|
||||
const tokens: TokenData[] = [];
|
||||
const pageText = document.body.innerText;
|
||||
|
||||
// Match $TOKEN pattern (e.g., $BONK, $SOL, $PEPE)
|
||||
const tokenPattern = /\$([A-Z]{2,10})\b/g;
|
||||
const matches = pageText.matchAll(tokenPattern);
|
||||
|
||||
const uniqueTokens = new Set<string>();
|
||||
for (const match of matches) {
|
||||
const symbol = match[1];
|
||||
if (!uniqueTokens.has(symbol)) {
|
||||
uniqueTokens.add(symbol);
|
||||
tokens.push({
|
||||
chain: "solana", // Default to Solana, can be enhanced
|
||||
pairAddress: "", // Will be resolved via API
|
||||
tokenSymbol: symbol,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract contract addresses from page content
|
||||
* Supports Solana and Ethereum address formats
|
||||
*/
|
||||
function extractContractAddresses(): TokenData[] {
|
||||
const tokens: TokenData[] = [];
|
||||
const pageText = document.body.innerText;
|
||||
|
||||
// Solana address pattern (base58, 32-44 characters)
|
||||
const solanaPattern = /\b([1-9A-HJ-NP-Za-km-z]{32,44})\b/g;
|
||||
|
||||
// Ethereum address pattern (0x followed by 40 hex characters)
|
||||
const ethPattern = /\b(0x[a-fA-F0-9]{40})\b/g;
|
||||
|
||||
// Extract Ethereum addresses
|
||||
const ethMatches = pageText.matchAll(ethPattern);
|
||||
for (const match of ethMatches) {
|
||||
const address = match[1];
|
||||
tokens.push({
|
||||
chain: "ethereum",
|
||||
pairAddress: address,
|
||||
tokenSymbol: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// Extract Solana addresses (more selective to avoid false positives)
|
||||
const solanaMatches = pageText.matchAll(solanaPattern);
|
||||
const uniqueSolanaAddresses = new Set<string>();
|
||||
|
||||
for (const match of solanaMatches) {
|
||||
const address = match[1];
|
||||
// Basic validation: should not be all same character, should have variety
|
||||
if (address.length >= 32 &&
|
||||
address.length <= 44 &&
|
||||
new Set(address).size > 10 &&
|
||||
!uniqueSolanaAddresses.has(address)) {
|
||||
uniqueSolanaAddresses.add(address);
|
||||
tokens.push({
|
||||
chain: "solana",
|
||||
pairAddress: address,
|
||||
tokenSymbol: undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return tokens.slice(0, 5); // Limit to first 5 to avoid spam
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract trading pairs from page content
|
||||
* Detects patterns like TOKEN/SOL, TOKEN/USDT, etc.
|
||||
*/
|
||||
function extractTradingPairs(): TokenData[] {
|
||||
const tokens: TokenData[] = [];
|
||||
const pageText = document.body.innerText;
|
||||
|
||||
// Match trading pair patterns (e.g., BONK/SOL, PEPE/USDT)
|
||||
const pairPattern = /\b([A-Z]{2,10})\/([A-Z]{2,10})\b/g;
|
||||
const matches = pageText.matchAll(pairPattern);
|
||||
|
||||
const uniquePairs = new Set<string>();
|
||||
for (const match of matches) {
|
||||
const baseToken = match[1];
|
||||
const quoteToken = match[2];
|
||||
const pairKey = `${baseToken}/${quoteToken}`;
|
||||
|
||||
if (!uniquePairs.has(pairKey)) {
|
||||
uniquePairs.add(pairKey);
|
||||
tokens.push({
|
||||
chain: "solana", // Default to Solana
|
||||
pairAddress: "", // Will be resolved via API
|
||||
tokenSymbol: baseToken,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return tokens.slice(0, 3); // Limit to first 3 pairs
|
||||
}
|
||||
|
||||
interface PageContext {
|
||||
url: string;
|
||||
title: string;
|
||||
pageType: PageType;
|
||||
tokenData?: TokenData;
|
||||
/** Detected tokens from page content (Twitter mentions, addresses, pairs) */
|
||||
detectedTokens?: TokenData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract page context based on page type
|
||||
*/
|
||||
|
|
@ -99,6 +215,37 @@ function extractPageContext(): PageContext {
|
|||
// Add page-specific data
|
||||
if (pageType === "dexscreener") {
|
||||
context.tokenData = extractDexScreenerData();
|
||||
} else if (pageType === "twitter") {
|
||||
// Extract Twitter token mentions
|
||||
const twitterTokens = extractTwitterTokens();
|
||||
const contractAddresses = extractContractAddresses();
|
||||
const tradingPairs = extractTradingPairs();
|
||||
|
||||
// Combine all detected tokens
|
||||
context.detectedTokens = [
|
||||
...twitterTokens,
|
||||
...contractAddresses,
|
||||
...tradingPairs,
|
||||
];
|
||||
|
||||
// Set primary token if available
|
||||
if (context.detectedTokens.length > 0) {
|
||||
context.tokenData = context.detectedTokens[0];
|
||||
}
|
||||
} else if (pageType === "generic") {
|
||||
// For generic pages, try to detect contract addresses and trading pairs
|
||||
const contractAddresses = extractContractAddresses();
|
||||
const tradingPairs = extractTradingPairs();
|
||||
|
||||
context.detectedTokens = [
|
||||
...contractAddresses,
|
||||
...tradingPairs,
|
||||
];
|
||||
|
||||
// Set primary token if available
|
||||
if (context.detectedTokens.length > 0) {
|
||||
context.tokenData = context.detectedTokens[0];
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue