WhatsApp fact-checking bot for Cameroon. Forward a message, voice note, image, or video; it extracts the claim, searches published fact-checks, and replies with sourced evidence in English, French, or Cameroonian Pidgin. Never guesses a verdict.
# Kongosa Check
*"Kongossa"* is Cameroonian Pidgin for gossip, rumor, the thing that spreads
in a WhatsApp group faster than anyone can check it. Kongosa Check is the
WhatsApp companion described in the concept note: forward it a message, a
voice note, an image, or a video, it extracts the claim, checks it against
real fact-check sources, and replies with a sourced credibility badge
instead of a bare verdict.
This is a real, runnable FastAPI server, not a mockup. It's built against
Meta's actual WhatsApp Business Cloud API, Groq, Google's Fact Check
Tools API (text and image search), and the open C2PA Content Credentials
standard, so once you've done the setup below, it will send and receive
real WhatsApp messages, including transcribing voice notes and checking
forwarded images and video.
## How a message gets answered
1. **Cache** — has this exact claim been checked in the last 3 days? If so, reuse the answer.
2. **Community layer** — has a trusted local fact-checker submitted a correction for this claim? Corrections always win over the automated pipeline (`app/corrections_store.py`), matched by meaning, not exact wording (`app/cache.py::word_overlap`).
3. **Agent tool search** — a bounded tool-calling loop (`app/agent.py`) tries the Google Fact Check Tools API first, and only if that finds nothing does it fall back to general web search (`app/web_search.py`, backed by Tavily) for context, not a verdict, on claims nobody has formally fact-checked yet.
4. **No match anywhere** — reply "unverified," never a guessed verdict.
If the message contains a link, it's fetched first (`app/link_reader.py`) and the headline and opening text become what gets checked — forwarding a bare article link is one of the commonest shapes a claim arrives in, and without this the bot sees only a URL string. Fetching a stranger's URL from your own server is an SSRF surface, so every hostname is resolved and refused if it lands on a private, loopback, or link-local address, and every r …