Add consented social milestone posting
This commit is contained in:
69
src/lib/social-milestones.ts
Normal file
69
src/lib/social-milestones.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
export const DEFAULT_SOCIAL_MILESTONE_THRESHOLDS = [1000, 10000] as const;
|
||||
|
||||
export type SocialMilestoneKind = `unique_scans_${number}`;
|
||||
|
||||
/**
|
||||
* Staging can set SOCIAL_MILESTONE_THRESHOLDS=1 (or e.g. 1,2) so the complete
|
||||
* flow is testable without fabricating thousands of scans. Production keeps
|
||||
* the conservative defaults unless its environment explicitly changes them.
|
||||
*/
|
||||
export function getSocialMilestoneThresholds(): number[] {
|
||||
const configured = process.env.SOCIAL_MILESTONE_THRESHOLDS;
|
||||
if (!configured) return [...DEFAULT_SOCIAL_MILESTONE_THRESHOLDS];
|
||||
|
||||
const thresholds = Array.from(new Set(
|
||||
configured.split(',')
|
||||
.map(value => Number(value.trim()))
|
||||
.filter(value => Number.isInteger(value) && value > 0 && value <= 1_000_000)
|
||||
)).sort((a, b) => a - b);
|
||||
|
||||
return thresholds.length ? thresholds : [...DEFAULT_SOCIAL_MILESTONE_THRESHOLDS];
|
||||
}
|
||||
|
||||
const useCaseLabels: Record<string, string> = {
|
||||
menu_pdf: 'menu QR code',
|
||||
marketing_campaign: 'campaign QR code',
|
||||
vcard: 'digital business-card QR code',
|
||||
event: 'event QR code',
|
||||
feedback: 'feedback QR code',
|
||||
};
|
||||
|
||||
export function milestoneKind(threshold: number): SocialMilestoneKind {
|
||||
return `unique_scans_${threshold}` as SocialMilestoneKind;
|
||||
}
|
||||
|
||||
export function milestoneThreshold(kind: string): number | null {
|
||||
const result = /^unique_scans_(\d+)$/.exec(kind);
|
||||
return result ? Number(result[1]) : null;
|
||||
}
|
||||
|
||||
export type SocialLocale = 'en' | 'de';
|
||||
|
||||
export function socialLocale(value?: string | null): SocialLocale {
|
||||
return value === 'de' ? 'de' : 'en';
|
||||
}
|
||||
|
||||
export function usageLabel(primaryUseCase: string | null, locale: SocialLocale = 'en'): string {
|
||||
if (locale === 'de') {
|
||||
const german: Record<string, string> = { menu_pdf: 'Speisekarten-QR-Code', marketing_campaign: 'Kampagnen-QR-Code', vcard: 'Visitenkarten-QR-Code', event: 'Event-QR-Code', feedback: 'Feedback-QR-Code' };
|
||||
return (primaryUseCase && german[primaryUseCase]) || 'QR-Code';
|
||||
}
|
||||
return (primaryUseCase && useCaseLabels[primaryUseCase]) || 'QR code';
|
||||
}
|
||||
|
||||
export function buildMilestonePost(primaryUseCase: string | null, threshold: number, xHandle?: string | null, locale: SocialLocale = 'en'): string {
|
||||
const count = threshold.toLocaleString(locale === 'de' ? 'de-DE' : 'en-US');
|
||||
const base = locale === 'de'
|
||||
? `Ein ${usageLabel(primaryUseCase, locale)} hat gerade ${count} eindeutige Scans erreicht. 🎉`
|
||||
: `A ${usageLabel(primaryUseCase, locale)} just reached ${count} unique scans. 🎉`;
|
||||
return xHandle ? `${base} By @${xHandle.replace(/^@/, '')}.` : base;
|
||||
}
|
||||
|
||||
export function buildMilestoneCard(primaryUseCase: string | null, threshold: number, locale: SocialLocale) {
|
||||
return { version: 'milestone-card-v1', language: locale, threshold, label: usageLabel(primaryUseCase, locale), title: locale === 'de' ? 'Erfolgsmeilenstein' : 'Success milestone' };
|
||||
}
|
||||
|
||||
export function normalizeXHandle(value: string): string | null {
|
||||
const handle = value.trim().replace(/^@/, '');
|
||||
return /^[A-Za-z0-9_]{1,15}$/.test(handle) ? handle : null;
|
||||
}
|
||||
Reference in New Issue
Block a user