Download apps/api/src/services/whatsapp-utils.ts from safetrack/edtech: direct link, hf CLI and curl.
- Browser
- Download file 3.14 kB
-
https://huggingface.co/spaces/safetrack/edtech/resolve/main/apps/api/src/services/whatsapp-utils.ts
- Command line
-
hf download hf://spaces/safetrack/edtech/apps/api/src/services/whatsapp-utils.ts
-
curl -L -o whatsapp-utils.ts https://huggingface.co/spaces/safetrack/edtech/resolve/main/apps/api/src/services/whatsapp-utils.ts
3.14 kB
| import axios from 'axios'; | |
| import { Readable } from 'stream'; | |
| export function normalizeCommand(text: string): string { | |
| return text | |
| .trim() | |
| .toLowerCase() | |
| .replace(/[.,!?;:]+$/, "") // Remove trailing punctuation | |
| .toUpperCase(); | |
| } | |
| export function detectIntent(text: string): 'YES' | 'NO' | 'UNKNOWN' { | |
| const normalized = text.trim().toLowerCase().replace(/[.,!?;:]+$/, ""); | |
| const yesWords = ['oui', 'ouais', 'wi', 'waaw', 'yes', 'yep', 'ok', 'd’accord', 'daccord', 'da’accord']; | |
| const noWords = ['non', 'déet', 'deet', 'no', 'nah', 'nein']; | |
| if (yesWords.some(w => normalized.includes(w))) return 'YES'; | |
| if (noWords.some(w => normalized.includes(w))) return 'NO'; | |
| return 'UNKNOWN'; | |
| } | |
| export function levenshteinDistance(a: string, b: string): number { | |
| const matrix: number[][] = []; | |
| for (let i = 0; i <= b.length; i++) matrix[i] = [i]; | |
| for (let j = 0; j <= a.length; j++) matrix[0][j] = j; | |
| for (let i = 1; i <= b.length; i++) { | |
| for (let j = 1; j <= a.length; j++) { | |
| if (b.charAt(i - 1) === a.charAt(j - 1)) { | |
| matrix[i][j] = matrix[i - 1][j - 1]; | |
| } else { | |
| matrix[i][j] = Math.min( | |
| matrix[i - 1][j - 1] + 1, // substitution | |
| matrix[i][j - 1] + 1, // insertion | |
| matrix[i - 1][j] + 1 // deletion | |
| ); | |
| } | |
| } | |
| } | |
| return matrix[b.length][a.length]; | |
| } | |
| export function isFuzzyMatch(text: string, target: string, threshold = 0.8): boolean { | |
| const normalized = text.trim().toUpperCase(); | |
| const tar = target.toUpperCase(); | |
| if (normalized === tar) return true; | |
| if (normalized.includes(tar) || tar.includes(normalized)) return true; | |
| const distance = levenshteinDistance(normalized, tar); | |
| const maxLength = Math.max(normalized.length, tar.length); | |
| const similarity = 1 - distance / maxLength; | |
| return similarity >= threshold; | |
| } | |
| /** | |
| * Download a WhatsApp media file from the Graph API as a stream. | |
| * @param mediaId - The media ID from the WhatsApp webhook payload | |
| * @param accessToken - WHATSAPP_ACCESS_TOKEN | |
| * @returns { stream, mimeType, fileSize } | |
| */ | |
| export async function downloadMedia( | |
| mediaId: string, | |
| accessToken: string | |
| ): Promise<{ stream: Readable; mimeType: string; fileSize: number }> { | |
| // Step 1: Get the media URL | |
| const metaRes = await axios.get( | |
| `https://graph.facebook.com/${process.env.META_GRAPH_API_VERSION || 'v22.0'}/${mediaId}`, | |
| { headers: { Authorization: `Bearer ${accessToken}` } } | |
| ); | |
| const { url, mime_type, file_size } = metaRes.data; | |
| if (!url) throw new Error(`[downloadMedia] No URL returned for media ${mediaId}`); | |
| // Step 2: Download the binary content as a stream | |
| const mediaRes = await axios.get(url, { | |
| headers: { Authorization: `Bearer ${accessToken}` }, | |
| responseType: 'stream', | |
| timeout: 30_000 | |
| }); | |
| return { | |
| stream: mediaRes.data, | |
| mimeType: mime_type || 'application/octet-stream', | |
| fileSize: file_size || 0 | |
| }; | |
| } | |