Files
archy/packages/app/src/composables/useBitcoinDetector.ts
T

115 lines
3.0 KiB
TypeScript
Raw Normal View History

export interface DetectedBitcoinAddress {
address: string
type: 'bech32' | 'bech32m' | 'p2pkh' | 'p2sh' | 'testnet'
}
export interface DetectedLightningInvoice {
invoice: string
amount?: number
description?: string
}
export interface DetectedBolt12Offer {
offer: string
}
export interface DetectedTxId {
txid: string
}
// Bitcoin address patterns
const BECH32_REGEX = /\b(bc1q[a-z0-9]{38,62})\b/gi
const BECH32M_REGEX = /\b(bc1p[a-z0-9]{38,62})\b/gi
const P2PKH_REGEX = /\b(1[13456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{25,34})\b/g
const P2SH_REGEX = /\b(3[13456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{25,34})\b/g
const TESTNET_REGEX = /\b(tb1[a-z0-9]{38,62})\b/gi
// Lightning patterns
const BOLT11_REGEX = /\b(lnbc[a-z0-9]+)\b/gi
const BOLT12_REGEX = /\b(lno1[a-z0-9]+)\b/gi
// Transaction ID (64 hex chars)
const TXID_REGEX = /\b([a-f0-9]{64})\b/gi
export function detectBitcoinAddresses(text: string): DetectedBitcoinAddress[] {
const addresses: DetectedBitcoinAddress[] = []
const seen = new Set<string>()
for (const match of text.matchAll(BECH32_REGEX)) {
if (!seen.has(match[1].toLowerCase())) {
seen.add(match[1].toLowerCase())
addresses.push({ address: match[1], type: 'bech32' })
}
}
for (const match of text.matchAll(BECH32M_REGEX)) {
if (!seen.has(match[1].toLowerCase())) {
seen.add(match[1].toLowerCase())
addresses.push({ address: match[1], type: 'bech32m' })
}
}
for (const match of text.matchAll(P2PKH_REGEX)) {
if (!seen.has(match[1])) {
seen.add(match[1])
addresses.push({ address: match[1], type: 'p2pkh' })
}
}
for (const match of text.matchAll(P2SH_REGEX)) {
if (!seen.has(match[1])) {
seen.add(match[1])
addresses.push({ address: match[1], type: 'p2sh' })
}
}
for (const match of text.matchAll(TESTNET_REGEX)) {
if (!seen.has(match[1].toLowerCase())) {
seen.add(match[1].toLowerCase())
addresses.push({ address: match[1], type: 'testnet' })
}
}
return addresses
}
export function detectBolt11Invoices(text: string): DetectedLightningInvoice[] {
const invoices: DetectedLightningInvoice[] = []
const seen = new Set<string>()
for (const match of text.matchAll(BOLT11_REGEX)) {
const lower = match[1].toLowerCase()
if (!seen.has(lower)) {
seen.add(lower)
invoices.push({ invoice: match[1] })
}
}
return invoices
}
export function detectBolt12Offers(text: string): DetectedBolt12Offer[] {
const offers: DetectedBolt12Offer[] = []
const seen = new Set<string>()
for (const match of text.matchAll(BOLT12_REGEX)) {
const lower = match[1].toLowerCase()
if (!seen.has(lower)) {
seen.add(lower)
offers.push({ offer: match[1] })
}
}
return offers
}
export function detectTxIds(text: string): DetectedTxId[] {
const txids: DetectedTxId[] = []
const seen = new Set<string>()
for (const match of text.matchAll(TXID_REGEX)) {
if (!seen.has(match[1])) {
seen.add(match[1])
txids.push({ txid: match[1] })
}
}
return txids
}