a moon tracking bot for Bluesky

feat(moon-phase): add phase aliases and normalization logic

Add PHASE_ALIASES constant to map alternative phase names to standard ones
Implement normalizePhase method to handle phase name variations and improve error messaging

+44 -13
+13 -1
src/core/moonPhaseConstants.ts
··· 8 8 "July", "August", "September", "October", "November", "December" 9 9 ] as const; 10 10 11 + // Map alternative phase names to standard phases 12 + export const PHASE_ALIASES: Record<string, string> = { 13 + "Dark Moon": "New Moon", 14 + "New": "New Moon", 15 + "Waxing": "Waxing Crescent", 16 + "Full": "Full Moon", 17 + "Waning": "Waning Crescent", 18 + "First": "First Quarter", 19 + "Last": "Last Quarter", 20 + "Third Quarter": "Last Quarter" 21 + } as const; 22 + 11 23 export const LYCANTHROPIC_PHRASES = [ 12 24 "Awooo!", 13 25 "Call of the wild is strong tonight.", ··· 148 160 MONTH_FLAIR_CHANCE: 0.5, 149 161 BRITISH_REFERENCE_CHANCE: 0.5, 150 162 PRIDE_REFERENCE_CHANCE_JUNE: 0.7 151 - } as const; 163 + } as const;
+31 -12
src/core/moonPhaseMessages.ts
··· 5 5 PRIDE_REFERENCES, 6 6 MONTH_FLAIRS, 7 7 PHASE_CONFIG, 8 - MESSAGE_CONFIG 8 + MESSAGE_CONFIG, 9 + PHASE_ALIASES 9 10 } from './moonPhaseConstants'; 10 11 import { getRandomElement, shuffleArray } from '../utils/arrayUtils'; 11 12 import type { MoonMessage } from '../types/moonPhase'; 12 13 13 14 export class MoonMessageGenerator { 15 + private normalizePhase(phase: string): string { 16 + // Check if the phase is already a standard phase 17 + if (phase in PHASE_CONFIG) { 18 + return phase; 19 + } 20 + 21 + // Check for direct alias match 22 + if (phase in PHASE_ALIASES) { 23 + return PHASE_ALIASES[phase]; 24 + } 25 + 26 + // Try case-insensitive matching 27 + const upperPhase = phase.toUpperCase(); 28 + for (const [alias, standardPhase] of Object.entries(PHASE_ALIASES)) { 29 + if (alias.toUpperCase() === upperPhase) { 30 + return standardPhase; 31 + } 32 + } 33 + 34 + // If no match found, throw error with helpful message 35 + throw new Error(`Unknown moon phase: "${phase}". Supported phases are: ${Object.keys(PHASE_CONFIG).join(', ')}`); 36 + } 37 + 14 38 private getBaseMessage(phase: string, illumination: number): string { 39 + const normalizedPhase = this.normalizePhase(phase); 15 40 const illuminationFixed = illumination.toFixed(1); 16 - const config = PHASE_CONFIG[phase as keyof typeof PHASE_CONFIG]; 17 - 18 - if (!config) { 19 - throw new Error(`Unknown moon phase: ${phase}`); 20 - } 41 + const config = PHASE_CONFIG[normalizedPhase as keyof typeof PHASE_CONFIG]; 21 42 22 43 const messages = { 23 44 "New Moon": `It's a New Moon, barely a whisper! Illumination: ${illuminationFixed}%.`, ··· 30 51 "Waning Crescent": `Waning Crescent, tiny sliver, ${illuminationFixed}% lit.` 31 52 }; 32 53 33 - const baseMessage = messages[phase as keyof typeof messages]; 54 + const baseMessage = messages[normalizedPhase as keyof typeof messages]; 34 55 const lycanthropicPhrase = getRandomElement(LYCANTHROPIC_PHRASES); 35 56 36 57 return `${config.emoji} ${baseMessage} ${lycanthropicPhrase}`; ··· 72 93 throw new Error(`Invalid month index: ${monthIndex}. Must be between 0 and 11.`); 73 94 } 74 95 75 - const config = PHASE_CONFIG[phase as keyof typeof PHASE_CONFIG]; 76 - if (!config) { 77 - throw new Error(`Unknown moon phase: ${phase}`); 78 - } 96 + const normalizedPhase = this.normalizePhase(phase); 97 + const config = PHASE_CONFIG[normalizedPhase as keyof typeof PHASE_CONFIG]; 79 98 80 99 const baseMessage = this.getBaseMessage(phase, illumination); 81 100 const additionalMessages = this.getAdditionalMessages(monthIndex); ··· 97 116 ): MoonMessage { 98 117 const generator = new MoonMessageGenerator(); 99 118 return generator.generateMessage(phase, illumination, monthIndex); 100 - } 119 + }