package router import ( "regexp" "strings" ) // Finding a URL in an utterance (Vikunja #259). // // This is deliberately strict: a scheme is required. "посмотри на example.org" // is not treated as a fetch request, because a bare dotted word is also how // people say file names, versions and Russian abbreviations, and the cost of a // false positive here is an outbound request nobody asked for. // // Note where this runs: an utterance from STT. Whisper will mangle a spoken URL, // which is fine — the URL that survives is one he pasted into the web chat, and // a mangled one simply fails to match. var urlRE = regexp.MustCompile(`(?i)\bhttps?://[^\s<>"']+`) // FirstURL returns the first http(s) URL in text. // // Trailing punctuation is trimmed: he ends sentences, and "…/page." is not a // path component. A closing bracket is only trimmed when it has no opener, // because a wikipedia URL legitimately ends in one. func FirstURL(text string) (string, bool) { m := urlRE.FindString(text) if m == "" { return "", false } m = strings.TrimRight(m, ".,;:!?…") if strings.HasSuffix(m, ")") && strings.Count(m, "(") == 0 { m = strings.TrimSuffix(m, ")") } // A scheme with nothing after it is not a URL. if rest := strings.SplitN(m, "//", 2); len(rest) < 2 || rest[1] == "" { return "", false } return m, true }