from __future__ import annotations RATING_WEIGHTS = { "love": 2.5, "like": 1.55, "curious": 0.9, # Skip does not propagate along similarity edges (no negative pull on neighbors). "not_interested": 0.0, "hard_no": -2.5, } EDGE_TYPE_WEIGHTS = { "fetlife_collab": 1.35, "fetlife_similar": 1.2, "kinktionary_link": 1.0, # Long-tail scenario-style kink -> shorter canonical neighbor; curated or script-proposed. "scenario_bridge": 0.85, "catalog": 0.6, } FACET_IMPORTANCE = {"dynamic": 1.4, "modality": 1.2, "body_focus": 1.25, "tone": 0.8, "visual": 0.9} FACET_FIELDS = ["dynamic", "modality", "body_focus", "tone", "visual"] VALID_RATINGS = ["love", "like", "curious", "not_interested", "hard_no"] VALID_DIRECTIONS = ["to_me", "by_me", "together"] DISCOVERABLE_CLUSTERS = { "fetlife_fetish", "kink_activities", "sexual_activities", "body_focus", "fetishes", "materials", "objects", "body_modification", "power_exchange", "discipline_related", "discipline", } SUPPORTING_CLUSTERS = {"roles", "dynamics", "positions"} ROLE_CLUSTERS = SUPPORTING_CLUSTERS DYNAMIC_CLUSTERS = {"power_exchange", "discipline_related", "discipline", "dynamics", "positions"} STARTER_CLUSTERS = { "fetlife_fetish", "kink_activities", "sexual_activities", "body_focus", "fetishes", "materials", "objects", "body_modification", "power_exchange", "philia_fetish", "toys_equipment", "discipline_related", "discipline", } NOISY_CLUSTERS = { "holidays", "glossary", "mental_health", "identity", "genders", "orientations", "consent", "events", "communities", "terminology", } META_CLUSTERS = NOISY_CLUSTERS | { "relationships", "sexual_orientations", "romantic_orientations", "genders", "sexual_health", "scene_safety", "consent", "play_spaces", "pornography", "gay_culture", "safety_resources", "pop_culture", } ROLE_QUERY_HINTS = {"dom", "dominant", "sub", "submissive", "top", "bottom", "switch", "role"} ROLE_TERMS = { "dominant", "dom", "submissive", "sub", "top", "bottom", "switch", "master", "mistress", "slave", "pet", "handler", "brat", "daddy", "mommy", "owner", "princess", "boy", "girl", } EXACT_ROLE_NAMES = {term.lower() for term in ROLE_TERMS} | { "service top", "service bottom", "service submissive", "service switch", "service slave", "service daddy", "service pet", "service pup", "service rigger", "service sadist", "service masochist", "service brat", } STARTER_CORE_NAMES = { "anal sex", "blindfolds", "bondage", "breast nipple play", "cuddles", "hair pulling", "handcuffs", "kissing", "lingerie", "massages", "mutual masturbation", "oral sex", "spanking", "talking dirty", "toys", } STARTER_LATER_NAMES = { "ass play", "biting", "blow jobs", "butt plugs", "candle wax", "discipline", "domination", "eating pussy", "orgasm control", "role play", "strap ons", "submission", "threesomes", } STARTER_EXPLORE_ONLY_NAMES = { "abduction play", "asphyxiaphilia play", "bare bottom spanking", "bdsm", "blood play", "collar and lead leash", "deep throating", "exhibitionism", "face fucking", "fisting", "gagging choked by cock", "gangbangs", "master/slave", "outdoor sex", "rough sex", "sex in public", "voyeurism", "whips", } EXACT_DYNAMIC_NAMES = { "submission", "domination", "power exchange", "service-oriented submission", "complete submission", "male submission", "female submission", "affectionate submission", "dominance and submission", "d/s", "24/7 d/s", "obedience", "protocol", "service", "control", } FETLIFE_DYNAMIC_KEYWORDS = ( "submission", "domination", "dominant", "submissive", "d/s", "power exchange", "service-oriented", "obedience", "protocol", ) PLAY_OVERRIDE_NAMES = { "oral sex", "anal sex", "bondage", "spanking", "hair pulling", "talking dirty", "lingerie", "blindfolds", "handcuffs", "sex in public", "blow jobs", "eating pussy", "orgasm control", "threesomes", "gangbangs", "strap-ons", } DEFAULT_TYPE_OVERRIDES = [ ("bottom", "roles", "role", "Kinktionary role"), ("bottom", "fetlife_fetish", "play", "FetLife fetish page"), ("master", "roles", "role", "Kinktionary role"), ("master", "fetlife_fetish", "play", "FetLife fetish page"), ("submission", "*", "dynamic", "Submission is a dynamic, not a default play surface"), ("domination", "*", "dynamic", "Domination is a dynamic, not a default play surface"), ("power exchange", "*", "dynamic", "Power exchange is a dynamic, not a default play surface"), ] POSITIVE_RATINGS = {"love", "like", "curious"} STRONG_POSITIVE_RATINGS = {"love", "like"} SUPPRESSED_RATINGS = {"not_interested"} BLOCKED_RATINGS = {"hard_no"}