← back to Dear Bubbe Nextjs
lib/local-content-handler.ts
389 lines
/**
* Local Content Handler for Super Max Tier
*
* Integrates with existing news, weather, and sports APIs
* but adds hyper-local personalization and Bubbe commentary
*/
import { FeatureKey } from './bubbe-pricing';
import { canUserAccessFeature, getUserSession } from './user-session';
// City to team mapping for local sports
const CITY_SPORTS_TEAMS: Record<string, {
nba?: string[];
nfl?: string[];
mlb?: string[];
nhl?: string[];
}> = {
'new york': {
nba: ['knicks', 'nets'],
nfl: ['giants', 'jets'],
mlb: ['yankees', 'mets'],
nhl: ['rangers', 'islanders', 'devils']
},
'los angeles': {
nba: ['lakers', 'clippers'],
nfl: ['rams', 'chargers'],
mlb: ['dodgers', 'angels'],
nhl: ['kings', 'ducks']
},
'chicago': {
nba: ['bulls'],
nfl: ['bears'],
mlb: ['cubs', 'white sox'],
nhl: ['blackhawks']
},
'boston': {
nba: ['celtics'],
nfl: ['patriots'],
mlb: ['red sox'],
nhl: ['bruins']
},
'philadelphia': {
nba: ['76ers'],
nfl: ['eagles'],
mlb: ['phillies'],
nhl: ['flyers']
},
'san francisco': {
nba: ['warriors'],
nfl: ['49ers'],
mlb: ['giants'],
nhl: ['sharks']
},
'miami': {
nba: ['heat'],
nfl: ['dolphins'],
mlb: ['marlins'],
nhl: ['panthers']
},
'dallas': {
nba: ['mavericks'],
nfl: ['cowboys'],
mlb: ['rangers'],
nhl: ['stars']
},
'denver': {
nba: ['nuggets'],
nfl: ['broncos'],
mlb: ['rockies'],
nhl: ['avalanche']
},
'atlanta': {
nba: ['hawks'],
nfl: ['falcons'],
mlb: ['braves'],
nhl: []
}
};
// Get user's location from IP or headers
export async function getUserLocation(req: any): Promise<{
city?: string;
state?: string;
country?: string;
latitude?: number;
longitude?: number;
}> {
// Try to get from existing geolocation in your system
// This assumes you already have IP-based location detection
// as mentioned in your existing code
const ip = req.headers?.['x-forwarded-for'] ||
req.connection?.remoteAddress ||
'45.61.58.125'; // Default to server IP
try {
// Use existing geolocation API (Open-Meteo compatible)
const response = await fetch(`https://ipapi.co/${ip}/json/`);
const data = await response.json();
return {
city: data.city?.toLowerCase(),
state: data.region,
country: data.country_name,
latitude: data.latitude,
longitude: data.longitude
};
} catch (error) {
console.error('Failed to get location:', error);
// Default to Los Angeles (your existing default)
return {
city: 'los angeles',
state: 'CA',
country: 'United States',
latitude: 34.0522,
longitude: -118.2437
};
}
}
/**
* Get local news with Bubbe commentary
*/
export async function getLocalNews(userId: string, city: string): Promise<{
allowed: boolean;
data?: any;
bubbeResponse?: string;
}> {
// Check if user has access to local news
const access = canUserAccessFeature(userId, 'local_news');
if (!access.allowed) {
return { allowed: false, bubbeResponse: access.bubbeResponse };
}
try {
// Call your existing news endpoint but filter for local
const response = await fetch('http://localhost:3011/api/news', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source: 'local',
city: city
})
});
const newsData = await response.json();
// Add Bubbe's hot takes on local news
const bubbeCommentary = addBubbeNewsCommentary(newsData, city);
return {
allowed: true,
data: {
...newsData,
bubbeCommentary,
localizedFor: city
}
};
} catch (error) {
return {
allowed: true,
data: null,
bubbeResponse: `Oy vey, the news from ${city}? All bad! Trust me, you don't want to know. But since you're paying $54 a month, I'll find out for you!`
};
}
}
/**
* Get local sports with Bubbe commentary
*/
export async function getLocalSports(userId: string, city: string): Promise<{
allowed: boolean;
data?: any;
bubbeResponse?: string;
}> {
const access = canUserAccessFeature(userId, 'local_sports');
if (!access.allowed) {
return { allowed: false, bubbeResponse: access.bubbeResponse };
}
// Get local teams for this city
const teams = CITY_SPORTS_TEAMS[city.toLowerCase()] || {};
const allTeams = [
...(teams.nba || []),
...(teams.nfl || []),
...(teams.mlb || []),
...(teams.nhl || [])
];
if (allTeams.length === 0) {
return {
allowed: true,
bubbeResponse: `${city}? What teams? You live in a sports desert! Move to New York like a normal person!`
};
}
// Generate Bubbe commentary on local teams
const teamCommentary: Record<string, string> = {};
for (const team of allTeams) {
teamCommentary[team] = getBubbeTeamCommentary(team);
}
return {
allowed: true,
data: {
city,
teams,
commentary: teamCommentary,
bubbesSummary: `Your teams? Feh! The ${allTeams[0]} should be ashamed! For $54 a month I watch them lose so you don't have to!`
}
};
}
/**
* Get hyper-local weather with Bubbe guilt
*/
export async function getLocalWeather(userId: string, lat: number, lon: number, city: string): Promise<{
allowed: boolean;
data?: any;
bubbeResponse?: string;
}> {
const access = canUserAccessFeature(userId, 'local_weather');
if (!access.allowed) {
return { allowed: false, bubbeResponse: access.bubbeResponse };
}
try {
// Use your existing weather API but with exact coordinates
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t=temperature_2m,apparent_temperature,precipitation,weather_code,wind_speed_10m,relative_humidity_2m&temperature_unit=fahrenheit&timezone=auto`;
const response = await fetch(weatherUrl);
const weatherData = await response.json();
// Add Bubbe's weather guilt
const temp = weatherData.current.temperature_2m;
const feelsLike = weatherData.current.apparent_temperature;
let bubbeWeatherGuilt = '';
if (temp < 50) {
bubbeWeatherGuilt = `It's ${temp}°F in ${city}! FREEZING! Where's your coat? You'll catch pneumonia! And then who'll take care of you? Not me!`;
} else if (temp < 65) {
bubbeWeatherGuilt = `${temp}°F in ${city}? That's jacket weather! Don't come crying to me when you get sick!`;
} else if (temp > 85) {
bubbeWeatherGuilt = `Oy gevalt, ${temp}°F in ${city}! You're sweating like a schmuck! Drink water before you pass out!`;
} else {
bubbeWeatherGuilt = `${temp}°F in ${city}, feels like ${feelsLike}°F. Finally, decent weather! But you're probably inside playing video games anyway!`;
}
return {
allowed: true,
data: {
...weatherData,
city,
bubbeWeatherGuilt,
clothingAdvice: getClothingAdvice(temp),
localizedFor: `${city} (your exact neighborhood!)`
}
};
} catch (error) {
return {
allowed: true,
bubbeResponse: `The weather in ${city}? Look outside, genius! For $54 a month I shouldn't have to tell you it's raining!`
};
}
}
/**
* Add Bubbe commentary to news items
*/
function addBubbeNewsCommentary(newsData: any, city: string): string[] {
const commentary = [
`In ${city}? Everything's meshuga! The mayor's a putz, the traffic's terrible, and don't get me started on the rent!`,
`You want LOCAL news? Your neighbor's dog won't stop barking, the deli raised prices AGAIN, and that new restaurant? Terrible!`,
`${city} news? Oy vey! Construction everywhere, potholes like craters, and the schools? Don't make me laugh!`,
`Breaking news from ${city}: Everything costs too much, nobody knows how to drive, and the weather's always wrong!`
];
return commentary;
}
/**
* Get Bubbe's take on specific teams
*/
function getBubbeTeamCommentary(team: string): string {
const commentary: Record<string, string> = {
// New York
'yankees': "27 championships and they STILL disappoint me! For what they pay those players?",
'mets': "The Mets? Oy vey, why do you do this to yourself? Such suffering!",
'knicks': "The Knicks? Every year 'this is our year.' Every year I'm disappointed!",
'nets': "The Nets? They had everyone and won NOTHING! Such a waste!",
'giants': "The Giants? Living in the past! Those Super Bowls were a lifetime ago!",
'jets': "The Jets? Don't make me laugh! Or cry. Actually, both!",
// Los Angeles
'lakers': "The Lakers? They buy everyone and still can't win! LeBron's older than ME!",
'clippers': "The Clippers? Always second best in their own city! Such a shanda!",
'dodgers': "The Dodgers spend like drunken sailors! For what? One championship?",
// Chicago
'bulls': "The Bulls? Michael Jordan retired 30 years ago! Move on already!",
'bears': "The Bears? Still living off 1985! That was before you were born!",
'cubs': "The Cubs? One championship and they think they're special!",
// Boston
'celtics': "The Celtics? So many banners they're running out of room! Show-offs!",
'red sox': "The Red Sox? Fenway's falling apart like my knees!",
'patriots': "The Patriots? Brady left and took the magic with him!",
// Default
'default': "That team? Feh! Overpaid schmucks who can't win when it matters!"
};
return commentary[team.toLowerCase()] || commentary['default'];
}
/**
* Get clothing advice based on temperature
*/
function getClothingAdvice(temp: number): string {
if (temp < 32) return "Bundle up like an Eskimo! Coat, scarf, gloves, hat - EVERYTHING!";
if (temp < 50) return "Wear a proper coat! Not that thin shmate you call a jacket!";
if (temp < 65) return "At least bring a sweater! You'll thank me later!";
if (temp < 75) return "Perfect weather and you'll probably wear shorts like a meshuggeneh!";
if (temp < 85) return "Dress light but bring sunscreen! Skin cancer is no joke!";
return "It's HOT! Wear nothing if you want, but stay hydrated!";
}
/**
* Check if user has Super Max and can access all local features
*/
export function hasSuperMaxAccess(userId: string): boolean {
const session = getUserSession(userId);
return session.plan === 'supermax';
}
/**
* Generate a localized response based on user's area
*/
export async function getPersonalizedLocalResponse(
userId: string,
message: string,
req: any
): Promise<{
allowed: boolean;
response?: string;
localData?: any;
}> {
// Check Super Max access
if (!hasSuperMaxAccess(userId)) {
const access = canUserAccessFeature(userId, 'personalized_insights');
if (!access.allowed) {
return { allowed: false, response: access.bubbeResponse };
}
}
// Get user's location
const location = await getUserLocation(req);
const city = location.city || 'your city';
// Determine what local content they're asking for
const lowerMessage = message.toLowerCase();
if (lowerMessage.includes('news')) {
return getLocalNews(userId, city);
}
if (lowerMessage.includes('sport') || lowerMessage.includes('team')) {
return getLocalSports(userId, city);
}
if (lowerMessage.includes('weather') || lowerMessage.includes('temperature')) {
return getLocalWeather(userId, location.latitude!, location.longitude!, city);
}
// General local insight
return {
allowed: true,
response: `You want to know about ${city}? Oy, where do I start! The traffic's terrible, everything's expensive, the people are rude - but at least it's not Cleveland! For $54 a month I know EVERYTHING about your neighborhood. The best deli? Goldstein's on 3rd. The gossip? Mrs. Schwartz is dating the mailman! Want more? Just ask!`,
localData: {
city,
state: location.state,
country: location.country,
coordinates: {
latitude: location.latitude,
longitude: location.longitude
}
}
};
}