package onlyoffice import ( "regexp" "strings" ) var multiSpace = regexp.MustCompile(`\s+`) // sloganSeparators split a company name from a trailing tagline/slogan. var sloganSeparators = []string{" — ", " – ", " - ", "—", "–"} // companyLegalSuffix matches trailing legal-form tokens (GmbH, & Co. KG, …). var companyLegalSuffix = regexp.MustCompile(`(?i)[\s,]*(&\s*co\.?\s*kg\.?|co\.?\s*kg\.?|gmbh|ag|ltd\.?|llc\.?|inc\.?|plc\.?|s\.?l\.?|sarl|b\.?v\.?)\s*$`) // StripSloganSuffix returns the part before an em/en dash tagline, e.g. // "Affirm — Fraud Engineering" → "Affirm". func StripSloganSuffix(s string) string { s = strings.TrimSpace(s) for _, sep := range sloganSeparators { if i := strings.Index(s, sep); i > 0 { return strings.TrimSpace(s[:i]) } } return s } // StripCompanyLegalSuffix removes trailing GmbH / & Co. KG / Ltd / … tokens. func StripCompanyLegalSuffix(s string) string { s = strings.TrimSpace(s) s = strings.TrimRight(s, ",; ") for { next := companyLegalSuffix.ReplaceAllString(s, "") next = strings.TrimRight(strings.TrimSpace(next), ",; ") if next == s || next == "" { return s } s = next } } // CompanyGroupingKey normalizes a company name for dedupe (ignores slogans and legal suffixes). func CompanyGroupingKey(s string) string { return NormalizeCompanyName(StripCompanyLegalSuffix(StripSloganSuffix(s))) } // NormalizeCompanyName lowercases and collapses whitespace for grouping. func NormalizeCompanyName(s string) string { return collapseKey(s) } // NormalizePersonKey builds a grouping key from first and last name. func NormalizePersonKey(first, last string) string { return collapseKey(strings.TrimSpace(first) + " " + strings.TrimSpace(last)) } // NormalizeOpportunityTitle lowercases and trims a deal title for exact dedupe. func NormalizeOpportunityTitle(s string) string { return collapseKey(s) } // StripCompanySuffix removes a trailing " @ Company" segment when present. func StripCompanySuffix(title string) string { title = strings.TrimSpace(title) if i := strings.LastIndex(title, " @ "); i >= 0 { return strings.TrimSpace(title[:i]) } return title } // FixDealTitle strips a leading @, normalizes separator spacing, and collapses // empty-position titles like " @ Acme" to "Acme". func FixDealTitle(s string) string { s = strings.TrimSpace(s) for strings.HasPrefix(s, "@") { s = strings.TrimSpace(strings.TrimPrefix(s, "@")) } if s == "" { return "" } if i := strings.Index(s, "@"); i >= 0 { left := strings.TrimSpace(s[:i]) right := strings.TrimSpace(s[i+1:]) if left == "" && right != "" { return right } if left != "" && right != "" { return left + " @ " + right } } return s } // ContactInfoKey groups contact info rows by type and normalized value. // ContactInfoKey builds a dedupe key for a contact-info row. func ContactInfoKey(infoType, value string) string { return NormalizeContactInfoType(infoType) + "|" + strings.ToLower(strings.TrimSpace(value)) } // NormalizeContactInfoType maps OO numeric infoType codes and string labels to // a canonical lowercase label (email, phone, …). func NormalizeContactInfoType(infoType string) string { t := strings.ToLower(strings.TrimSpace(infoType)) switch t { case "1", "email": return "email" case "2", "phone": return "phone" case "3", "website": return "website" case "4", "skype": return "skype" case "5", "twitter": return "twitter" case "6", "linkedin": return "linkedin" case "7", "facebook": return "facebook" case "8", "address": return "address" case "9", "livejournal": return "livejournal" case "10", "myspace": return "myspace" case "11", "gmail": return "gmail" case "12", "blogger": return "blogger" case "13", "yahoo": return "yahoo" case "14", "msn": return "msn" case "15", "icq": return "icq" case "16", "jabber": return "jabber" case "17", "aim": return "aim" default: return t } } // MemberDisplayKey normalizes a member displayName for duplicate detection. func MemberDisplayKey(displayName string) string { return CompanyGroupingKey(displayName) } func collapseKey(s string) string { s = strings.TrimSpace(s) s = multiSpace.ReplaceAllString(s, " ") return strings.ToLower(s) } // OpportunityTitlesMatch reports whether two deal titles refer to the same role+company. func OpportunityTitlesMatch(a, b string) bool { return DealTitleKey(a, false) == DealTitleKey(b, false) } func DealTitleForApplication(position, company string) string { position = strings.TrimSpace(position) company = strings.TrimSpace(company) if company == "" { return position } if position == "" { return company } return position + " @ " + company }