Β
A Slack-native group food-ordering agent that fronts Takeaway.com.
Someone in your channel types βlet's eat something.β Foody picks up, runs the whole order as a conversation, and builds one shared basket from everyone's emoji reactions.
π See it live: Foody runs in the Foody developer sandbox β open #foody-demo and type βlet's eat somethingβ, or ask the β¨ assistant.
Judges (slackhack@salesforce.com Β· testing@devpost.com) have collaborator invites; sandboxes are invite-only, so everyone else: watch the demo or email the maintainer for a seat. Β· full guide in DEMO.md
Every team knows the thread. It's 12:14, someone posts βlunch?β, and twenty minutes vanish into scrolling menus, pasting links, copy-pasting orders into a DM, and one poor soul becoming the human spreadsheet who tallies it all and pays.
The food is easy. The coordination is the tax.
Foody removes it. The team is already in Slack, and Slack already has a universal, zero-learning-curve input device: the emoji reaction. So that's the entire interface. No app to install, no link to chase, no spreadsheet.
A real run, start to finish. Five messages, one shared basket, one tap to order.
Foody greets the channel and confirms the delivery address. It's sticky per Slack user β asked once, remembered forever.
Eight one-tap cuisine categories. Pick one (or skip straight to the top spots).
Ranked and scored, each with rating, ETA, delivery fee and minimum β one tap to open the menu.
The top 10 dishes land as a single live message that Foody pre-reacts to. Anyone on the team taps an emoji to add a dish; un-reacting removes it. The running total updates in place for everyone.
Hit the green button and Foody builds the basket on takeaway.com β with a live, self-animating progress card while it works.
A clean receipt lands in the thread: items, totals, and an ETA.
Foody is a Slack AI app: it lives in the β¨ AI-apps split pane with a full assistant surface β assistant threads, live status updates ("is scanning takeaway.comβ¦"), and suggested prompts β powered by Claude with tool use.
Open Foody from the AI-apps pane (or DM it) and just talk:
"Something spicy for 6 people under β¬15, we're at Veldstraat 1, 9000 Gent" "What are the top pizza spots near the office, and what does the best one charge for a margherita?" "Start a photo order for burgers in #foody-demo"
The assistant compares restaurants, answers budget and menu questions, and remembers your delivery address. When you say go, it calls its start_group_order tool: the restaurant cards (or the menu, photos and all) land in the channel, and the classic emoji-reaction group flow takes over β AI plans the order, the whole team fills the basket.
| Slack AI capability | Where Foody uses it |
|---|---|
| Assistant surface (split pane) | The whole conversational ordering experience |
| Assistant threads & context | Multi-turn planning; knows which channel you came from |
| Live status | "is scanning takeaway.comβ¦" while tools run |
| Suggested prompts | One-tap starters like π Feed the team |
| Tool use (Claude) | start_group_order, restaurant search, address book |
- πͺ The interface is just emoji. No forms, no webview β group ordering is reactions on a message.
- π§ Reactions are the source of truth. The basket is reconciled from the actual reactions on the menu message, not a running tally of events β so a dropped Slack event never desyncs the cart. It self-heals.
- π§ AI where it helps, deterministic where it counts. The assistant pane is Claude with tools β plan, compare, negotiate budgets in natural language. The moment an order starts, the flow is fully deterministic: phrase matchers, reactions, reconciliation. Same input, same result, no surprises mid-lunch.
- π Private by default. Runs over Socket Mode β an outbound WebSocket, no public URL, nothing leaving the workspace.
- π A real checkout (opt-in). Beyond the mock flow, Foody can drive an actual takeaway.com basket in your signed-in Chrome, ready to confirm.
Foody is a small TypeScript app. Each layer owns one thing:
| Layer | Responsibility |
|---|---|
src/slack/app.ts |
Bolt + Socket Mode app. Message triggers, action buttons, reaction events, every Block Kit post & update. |
src/slack/blocks.ts |
Block Kit builders: restaurant cards, the unified menu+cart card, the animated build-progress card, the receipt. |
src/slack/assistant.ts |
The Slack AI assistant: assistant threads, live status, suggested prompts, Claude tool-use, and the start_group_order bridge into the channel flow. |
src/slack/intent.ts |
Phrase matchers for βlet's eatβ, βorder nowβ, βresetβ, βchange address to β¦β. A list, not a model. |
src/state.ts |
JSON state, keyed two ways: addr_<user> for the sticky address book, sess_<channel>_<thread> for the live cart. |
src/takeaway.ts + src/scrape-live.ts |
Restaurant/dish lookup with an in-memory + 24-hour disk-TTL cache and a daily background prewarm. |
src/checkout.ts |
Drives your already-running Chrome over the DevTools Protocol to fill the real basket in a background tab. |
src/emojis.ts |
The dish-emoji pool and the Unicode β Slack-shortcode mapping. |
β¨ AI assistant (Claude + tools) β start_group_order ββ
βΌ
let's eat β intent β session state β discovery (scrape + cache) β pre-reacted menu
β
react / un-react ββββββββββββββββ
β
reconcile cart from message reactions
β
Order β build basket (your Chrome) β receipt
There's a one-page visual of this in docs/Foody-Technical-One-Pager.pdf.
How one lunch order flows end to end. Foody is an installed Slack agent: it connects over Socket Mode (no public URL), so most steps are pure Slack β events in, Web API calls out. Only the takeaway hops differ between today and the future.
Today β discovery and checkout go through browser automation (amber steps 4 & 9):
With the official Just Eat Takeaway.com API β the Slack side is identical; the brittle browser steps become a partner API + a status webhook, and it runs fully headless:
Print versions: Architecture β Today (PDF) Β· Architecture β With official API (PDF)
npm install
cp .env.example .env
# fill in SLACK_BOT_TOKEN, SLACK_APP_TOKEN, SLACK_SIGNING_SECRET
# + ANTHROPIC_API_KEY for the AI assistant pane
npm run dev:slackThen in Slack, in a channel where Foody is a member (/invite @Foody), type let's eat something.
- Go to https://api.slack.com/apps β Create New App β From a manifest.
- Paste docs/slack-manifest.yml.
- Install to Workspace, copy the Bot User OAuth Token (
xoxb-β¦) βSLACK_BOT_TOKEN. - Basic Information β App-Level Tokens β create one with
connections:write(xapp-β¦) βSLACK_APP_TOKEN. - Basic Information β Signing Secret β
SLACK_SIGNING_SECRET. - Make sure Socket Mode is On.
| Variable | What it does |
|---|---|
FOODY_CHANNELS |
Comma-separated channel IDs to restrict the bot to. Empty = everywhere it's invited. |
FOODY_LOG_LEVEL |
debug Β· info (default) Β· warn Β· error. |
FOODY_DEBUG |
1 dumps a menu-page snapshot (HTML + screenshot + probe) per cart build to state/debug-checkout/. |
The mock flow posts a stubbed receipt. For a real basket, Foody connects to your already-running Chrome over the DevTools Protocol and builds the order in a background tab β so it lands in your real, signed-in session with your saved address and payment.
Why connect instead of launching headless? takeaway.com is fronted by Cloudflare Turnstile, which fingerprints and blocks bundled-Chromium Puppeteer. Your real Chrome already holds a valid clearance and session, so it sails through β and the βPayβ link opens in the same browser, basket intact.
Launch Chrome with the debug port (Foody can also do this for you from a Slack button):
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/foody-chrome
β οΈ Status: the live cart-build is experimental. DOM matching on takeaway.com is sensitive to a logged-out session and layout changes; sign into takeaway.com in that Chrome first. The Slack flow, shared cart, and receipt all work against mock data with zero setup.
State can be driven by hand β handy for poking at sessions without Slack:
npm run foody -- address <user> --set "Meir 1, Antwerpen"
npm run foody -- restaurants <user>
npm run foody -- menu <user> --index 1
npm run foody -- cart <user> --add "π,π"
npm run foody -- order <user>
npm run foody -- status <user>
npm run foody -- reset <user>| Doc | What it is |
|---|---|
| DEMO.md | How to try Foody live (join link) and how to host your own always-on demo. |
| docs/DEVPOST.md | The story: inspiration, what it does, how we built it, challenges, what's next. |
| docs/Foody-One-Pager.pdf | Commercial one-pager. |
| docs/Foody-Technical-One-Pager.pdf | Technical overview one-pager. |
| STYLE.md | Brand & style guide (color, type, voice, Block Kit usage). |
Screenshots above are generated from docs/screenshots/flow.html via node scripts/render-screenshots.mjs β faithful renderings of the real Block Kit messages.
TypeScript Β· Node.js 22 Β· @slack/bolt (Socket Mode) Β· Slack AI assistant surface Β· Claude (tool use) Β· Block Kit Β· Puppeteer + stealth Β· JSON disk-TTL cache Β· tsx
At the moment of writing, we did not have access to the Just Eat Takeaway.com API. Everything in Foody's runtime is real β but the food layer (restaurant discovery and checkout) currently goes through browser automation: a stealth headless Chrome scrapes menus, and checkout drives your own signed-in Chrome over the DevTools Protocol to get past Cloudflare. It works, but it's the one fragile, non-cloud part of the system.
Official API access would streamline this agent significantly. The Slack side wouldn't change at all β same install, same Socket Mode events, same emoji-reaction interface. Only the takeaway hops (steps 4 & 9 in the request sequence) would become clean API calls, which unlocks:
- π Reliable discovery β structured menus (modifiers, allergens, live availability); no scraping, never breaks on a layout change
- π³ Real orders + payment via API β no driving a browser, no manual confirm
- π¦ Live delivery tracking pushed back into the Slack thread (accepted β cooking β delivered)
- βοΈ Fully headless β deploy anywhere in the cloud, scale to many workspaces, no user's Chrome
- πΈ Accurate pricing & promos, address validation, reorder & history, true bill-splitting
Side by side: Architecture β Today vs Architecture β With the official API.
Are you on the Just Eat Takeaway.com team?
Foody is built to plug into an official partner API the moment access is available β turning this from a clever hackathon agent into a production-grade ordering experience for every Slack team. The hard part (the Slack-native UX) is done; we just need the front door to the menus and baskets.
π Open an issue on this repo, or reach the maintainer at timdries@hotmail.com
Foody Β· group food ordering for Slack Work hard, skip hangry. π΄








