Generate AI-powered summaries of Denton, TX city government meetings and publish them to a Facebook page.
- Scans a meeting archive containing
metadata.json,whisper_transcript.txt, andagenda.pdfper meeting - Sends the full transcript + agenda to a local LLM (llama.cpp) for structured summarization
- Formats the summary as a Facebook post with key decisions, community impact, and relevant hashtags
- Publishes to a Facebook Page via the Graph API, tracking posted meetings to avoid duplicates
# Clone the repo
git clone https://github.com/yourusername/city2facebook.git
cd city2facebook
# Create and activate a virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r city2facebook/requirements.txt
# Copy example config and fill in your values
cp city2facebook/config.example.json city2facebook/config.json
# Edit config.json with your archive path, LLM details, and Facebook credentialsTo get a valid page access token:
- Go to Facebook Graph API Explorer
- Select your app, click "Get Token" → "Get User Access Token"
- Ensure
pages_manage_postsandpages_read_engagementpermissions are checked - Generate the user token, then call
/me/accounts?fields=id,access_tokenin the explorer - Copy the
access_tokenfrom your page's entry in the response — this is the page token - Also note the page
idfrom the same response - Place both in
config.json
The page token lasts ~60 days. When it expires, repeat the process.
The tool expects a local LLM running the OpenAI-compatible API (e.g., llama.cpp):
./server -m /path/to/model.gguf --port 8000Update llm_base_url, llm_model, and llm_key in config.json accordingly.
python city2facebook/main.py list
python city2facebook/main.py list --search "zoning"
python city2facebook/main.py list --body "City Council"
python city2facebook/main.py list --limit 20Dry run (generate summary, don't post):
python city2facebook/main.py process --meeting-id 108037 --dry-runInteractive mode (review before posting):
python city2facebook/main.py process --meeting-id 108037
# Then choose: [p]ost, [s]kip, or [e]xport draft to fileAuto-post (generate and publish immediately):
python city2facebook/main.py process --meeting-id 108037 --autoForce reprocess (re-generate and re-post even if already in the posted log):
python city2facebook/main.py process --meeting-id 108037 --auto --forceAdd -v to any command for debug-level output:
python city2facebook/main.py -v process --meeting-id 108037 --dry-runUse --config to point to a different config file (e.g., for local dev):
python city2facebook/main.py --config config.dev.json process --meeting-id 108037 --dry-runExpected directory layout per meeting:
archive/
bag-108037/
data/
metadata.json # Meeting title, date, chapters
whisper_transcript.txt # Full meeting transcript
agenda.pdf # Meeting agenda document
video.mp4 # (optional, ignored by the tool)
| Key | Description |
|---|---|
archive_dir |
Path to the root archive directory containing bag-* folders |
llm_base_url |
Base URL of the OpenAI-compatible LLM server |
llm_model |
Name of the model to use |
llm_key |
API key for the LLM server |
fb_page_id |
Facebook Page numeric ID |
fb_token |
Facebook Page Access Token |
posted_log |
Path to the JSONL file tracking posted meetings (default: posted.jsonl) |
city2facebook/
main.py # CLI entry point, argument parsing, workflow orchestration
meeting.py # Meeting data loading from archive bags
extract_agenda.py # PDF text extraction (PyMuPDF)
summarizer.py # LLM prompt building, API call, JSON parsing
poster.py # Post formatting, Facebook Graph API, duplicate tracking
config.example.json # Template config (copy to config.json)
requirements.txt # Python dependencies
- LLM summarization typically takes 3–5 minutes per meeting due to full transcript + agenda context
- Verbose mode (
-v) logs token counts and generation time - The
listcommand uses optimized directory scanning to handle large archives (1,900+ meetings)