Follow Modes (neutral / pocket_veto / pocket_boost):
- Alembic migration 0013 adds follow_mode column to follows table
- FollowButton rewritten as mode-aware dropdown for bills; simple toggle for members/topics
- PATCH /api/follows/{id}/mode endpoint with validation
- Dispatcher filters pocket_veto follows (suppress new_document/new_amendment events)
- Dispatcher adds ntfy Actions header for pocket_boost follows
Change-driven (milestone) Alerts:
- New notification_utils.py with shared emit helpers and 30-min dedup
- congress_poller emits bill_updated events on milestone action text
- llm_processor replaced with shared emit util (also notifies member/topic followers)
Notification Enhancements:
- ntfy priority levels (high for bill_updated, default for others)
- Quiet hours (UTC): dispatcher holds events outside allowed window
- Digest mode (daily/weekly): send_notification_digest Celery beat task
- Notification history endpoint + Recent Alerts UI section
- Enriched following page (bill titles, member photos/details via sub-components)
- Follow mode test buttons in admin settings panel
Infrastructure:
- nginx: switch upstream blocks to set $variable proxy_pass so Docker DNS
re-resolves upstream IPs after container rebuilds (valid=10s)
- TROUBLESHOOTING.md documenting common Docker/nginx/postgres gotchas
Authored-By: Jack Levy
187 lines
3.9 KiB
TypeScript
187 lines
3.9 KiB
TypeScript
export interface MemberTerm {
|
|
congress?: number;
|
|
chamber?: string;
|
|
partyName?: string;
|
|
stateCode?: string;
|
|
stateName?: string;
|
|
startYear?: number;
|
|
endYear?: number;
|
|
district?: number;
|
|
}
|
|
|
|
export interface MemberLeadership {
|
|
type?: string;
|
|
congress?: number;
|
|
current?: boolean;
|
|
}
|
|
|
|
export interface MemberTrendScore {
|
|
score_date: string;
|
|
newsapi_count: number;
|
|
gnews_count: number;
|
|
gtrends_score: number;
|
|
composite_score: number;
|
|
}
|
|
|
|
export interface MemberNewsArticle {
|
|
id: number;
|
|
source?: string;
|
|
headline?: string;
|
|
url?: string;
|
|
published_at?: string;
|
|
relevance_score?: number;
|
|
}
|
|
|
|
export interface Member {
|
|
bioguide_id: string;
|
|
name: string;
|
|
first_name?: string;
|
|
last_name?: string;
|
|
party?: string;
|
|
state?: string;
|
|
chamber?: string;
|
|
district?: string;
|
|
photo_url?: string;
|
|
official_url?: string;
|
|
congress_url?: string;
|
|
birth_year?: string;
|
|
address?: string;
|
|
phone?: string;
|
|
terms_json?: MemberTerm[];
|
|
leadership_json?: MemberLeadership[];
|
|
sponsored_count?: number;
|
|
cosponsored_count?: number;
|
|
latest_trend?: MemberTrendScore;
|
|
}
|
|
|
|
export interface CitedPoint {
|
|
text: string;
|
|
citation: string;
|
|
quote: string;
|
|
}
|
|
|
|
export interface BriefSchema {
|
|
id: number;
|
|
brief_type?: string;
|
|
summary?: string;
|
|
key_points?: (string | CitedPoint)[];
|
|
risks?: (string | CitedPoint)[];
|
|
deadlines?: { date: string | null; description: string }[];
|
|
topic_tags?: string[];
|
|
llm_provider?: string;
|
|
llm_model?: string;
|
|
govinfo_url?: string;
|
|
created_at?: string;
|
|
}
|
|
|
|
export interface TrendScore {
|
|
score_date: string;
|
|
newsapi_count: number;
|
|
gnews_count: number;
|
|
gtrends_score: number;
|
|
composite_score: number;
|
|
}
|
|
|
|
export interface BillAction {
|
|
id: number;
|
|
action_date?: string;
|
|
action_text?: string;
|
|
action_type?: string;
|
|
chamber?: string;
|
|
}
|
|
|
|
export interface NewsArticle {
|
|
id: number;
|
|
source?: string;
|
|
headline?: string;
|
|
url?: string;
|
|
published_at?: string;
|
|
relevance_score?: number;
|
|
}
|
|
|
|
export interface Bill {
|
|
bill_id: string;
|
|
congress_number: number;
|
|
bill_type: string;
|
|
bill_number: number;
|
|
title?: string;
|
|
short_title?: string;
|
|
introduced_date?: string;
|
|
latest_action_date?: string;
|
|
latest_action_text?: string;
|
|
status?: string;
|
|
chamber?: string;
|
|
congress_url?: string;
|
|
sponsor?: Member;
|
|
latest_brief?: BriefSchema;
|
|
latest_trend?: TrendScore;
|
|
updated_at?: string;
|
|
}
|
|
|
|
export interface BillDetail extends Bill {
|
|
actions: BillAction[];
|
|
news_articles: NewsArticle[];
|
|
trend_scores: TrendScore[];
|
|
briefs: BriefSchema[];
|
|
has_document: boolean;
|
|
}
|
|
|
|
export interface PaginatedResponse<T> {
|
|
items: T[];
|
|
total: number;
|
|
page: number;
|
|
per_page: number;
|
|
pages: number;
|
|
}
|
|
|
|
export interface Follow {
|
|
id: number;
|
|
follow_type: "bill" | "member" | "topic";
|
|
follow_value: string;
|
|
follow_mode: "neutral" | "pocket_veto" | "pocket_boost";
|
|
created_at: string;
|
|
}
|
|
|
|
export interface DashboardData {
|
|
feed: Bill[];
|
|
trending: Bill[];
|
|
follows: { bills: number; members: number; topics: number };
|
|
}
|
|
|
|
export interface SettingsData {
|
|
llm_provider: string;
|
|
llm_model: string;
|
|
congress_poll_interval_minutes: number;
|
|
newsapi_enabled: boolean;
|
|
pytrends_enabled: boolean;
|
|
}
|
|
|
|
export interface NotificationSettings {
|
|
ntfy_topic_url: string;
|
|
ntfy_auth_method: string; // "none" | "token" | "basic"
|
|
ntfy_token: string;
|
|
ntfy_username: string;
|
|
ntfy_password: string;
|
|
ntfy_enabled: boolean;
|
|
rss_enabled: boolean;
|
|
rss_token: string | null;
|
|
digest_enabled: boolean;
|
|
digest_frequency: "daily" | "weekly";
|
|
quiet_hours_start: number | null;
|
|
quiet_hours_end: number | null;
|
|
}
|
|
|
|
export interface NotificationEvent {
|
|
id: number;
|
|
bill_id: string;
|
|
event_type: "new_document" | "new_amendment" | "bill_updated";
|
|
payload: {
|
|
bill_title?: string;
|
|
bill_label?: string;
|
|
brief_summary?: string;
|
|
bill_url?: string;
|
|
} | null;
|
|
dispatched_at: string | null;
|
|
created_at: string;
|
|
}
|