Skip to content

Latest commit

Β 

History

24 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Foody β€” Work hard, skip hangry

Β 

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.


CI License: AGPL v3 TypeScript Node.js Slack Bolt Puppeteer Slack AI Deterministic core


πŸ‘‰ 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


β–Ά Watch the Foody demo (60 seconds)


Why Foody

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.


The order flow

A real run, start to finish. Five messages, one shared basket, one tap to order.

Foody β€” group lunch ordering in Slack, from β€œlet's eat something” to a placed order

1 Β· Someone says β€œlet's eat something”

Foody greets the channel and confirms the delivery address. It's sticky per Slack user β€” asked once, remembered forever.

Greeting

2 Β· Pick a vibe

Eight one-tap cuisine categories. Pick one (or skip straight to the top spots).

Category picker

3 Β· Top 3 restaurants near you

Ranked and scored, each with rating, ETA, delivery fee and minimum β€” one tap to open the menu.

Top 3 restaurants

4 Β· React to build a shared basket

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.

Menu and shared cart

5 Β· One tap to order

Hit the green button and Foody builds the basket on takeaway.com β€” with a live, self-animating progress card while it works.

Build progress

6 Β· Done

A clean receipt lands in the thread: items, totals, and an ETA.

Receipt


πŸ€– The AI assistant β€” built on Slack AI capabilities

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

What makes it different

  • πŸͺ„ 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.

Architecture

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.

Request sequence

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):

Foody request sequence β€” today

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:

Foody request sequence β€” with official API

Print versions: Architecture β€” Today (PDF) Β· Architecture β€” With official API (PDF)


Quick start

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:slack

Then in Slack, in a channel where Foody is a member (/invite @Foody), type let's eat something.

Slack app setup

  1. Go to https://api.slack.com/apps β†’ Create New App β†’ From a manifest.
  2. Paste docs/slack-manifest.yml.
  3. Install to Workspace, copy the Bot User OAuth Token (xoxb-…) β†’ SLACK_BOT_TOKEN.
  4. Basic Information β†’ App-Level Tokens β†’ create one with connections:write (xapp-…) β†’ SLACK_APP_TOKEN.
  5. Basic Information β†’ Signing Secret β†’ SLACK_SIGNING_SECRET.
  6. Make sure Socket Mode is On.

Configuration knobs (.env)

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 real takeaway.com checkout (opt-in)

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.


CLI (debugging)

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>

Project docs

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.


Tech

TypeScript Β· Node.js 22 Β· @slack/bolt (Socket Mode) Β· Slack AI assistant surface Β· Claude (tool use) Β· Block Kit Β· Puppeteer + stealth Β· JSON disk-TTL cache Β· tsx


πŸš€ Going forward

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.

πŸ“¨ Reach out β€” let's make this real


Takeaway.com



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. 🍴

About

🍴 Foody β€” a Slack-native group food-ordering agent. Type "let's eat something" and the team builds one shared basket from emoji reactions. Work hard, skip hangry.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages