restaurant-booking

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Restaurant Booking

餐厅预订

Setup

设置

Read your credentials from ~/.gooseworks/credentials.json:
bash
export GOOSEWORKS_API_KEY=$(python3 -c "import json;print(json.load(open('$HOME/.gooseworks/credentials.json'))['api_key'])")
export GOOSEWORKS_API_BASE=$(python3 -c "import json;print(json.load(open('$HOME/.gooseworks/credentials.json')).get('api_base','https://api.gooseworks.ai'))")
If ~/.gooseworks/credentials.json does not exist, tell the user to run:
npx gooseworks login
All endpoints use Bearer auth:
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
Book reservations using Notte browser automation via Orthogonal.
从~/.gooseworks/credentials.json读取您的凭证:
bash
export GOOSEWORKS_API_KEY=$(python3 -c "import json;print(json.load(open('$HOME/.gooseworks/credentials.json'))['api_key'])")
export GOOSEWORKS_API_BASE=$(python3 -c "import json;print(json.load(open('$HOME/.gooseworks/credentials.json')).get('api_base','https://api.gooseworks.ai'))")
如果~/.gooseworks/credentials.json不存在,请告知用户运行:
npx gooseworks login
所有端点均使用Bearer认证:
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
通过Orthogonal的Notte浏览器自动化工具预订座位。

Requirements

要求

  • Orthogonal CLI (
    npm install -g @orth/cli
    ) or API key
  • Guest info: name, email, phone
  • Orthogonal CLI(
    npm install -g @orth/cli
    )或API密钥
  • 宾客信息:姓名、邮箱、电话

Quick Flow

快速流程

  1. Start Notte session
  2. Navigate to booking site (OpenTable preferred)
  3. Select date/time/party size
  4. Fill contact form
  5. Submit and confirm
  1. 启动Notte会话
  2. 导航至预订网站(优先选择OpenTable)
  3. 选择日期/时间/用餐人数
  4. 填写联系表单
  5. 提交并确认

CLI Method (Recommended)

CLI方法(推荐)

1. Start a Notte Session

1. 启动Notte会话

bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/start"}'
  --body '{"browser_type":"chromium","headless":true,"solve_captchas":true,"idle_timeout_minutes":10}'
Save the
session_id
from the response.
bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/start"}'
  --body '{"browser_type":"chromium","headless":true,"solve_captchas":true,"idle_timeout_minutes":10}'
保存响应中的
session_id

2. Navigate to OpenTable

2. 导航至OpenTable

bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"goto","url":"https://www.opentable.com/r/{restaurant}?datetime=2026-02-17T19:00&covers=2"}'
bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"goto","url":"https://www.opentable.com/r/{restaurant}?datetime=2026-02-17T19:00&covers=2"}'

3. Click Time Slot

3. 点击时间段

bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"click","selector":"button:has-text(\"7:00 PM\")"}'
bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"click","selector":"button:has-text(\"7:00 PM\")"}'

4. Select Seating (if prompted)

4. 选择座位(如有提示)

bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"click","selector":"button:has-text(\"Select\")"}'
bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"click","selector":"button:has-text(\"Select\")"}'

5. Fill the Form

5. 填写表单

bash
undefined
bash
undefined

First name

名字

curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
-H "Content-Type: application/json"
-d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}' --body '{"type":"fill","selector":"input#firstName","value":"John"}'
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
-H "Content-Type: application/json"
-d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}' --body '{"type":"fill","selector":"input#firstName","value":"John"}'

Last name

姓氏

curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
-H "Content-Type: application/json"
-d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}' --body '{"type":"fill","selector":"input#lastName","value":"Doe"}'
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
-H "Content-Type: application/json"
-d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}' --body '{"type":"fill","selector":"input#lastName","value":"Doe"}'

Email

邮箱

curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
-H "Content-Type: application/json"
-d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}' --body '{"type":"fill","selector":"input#email","value":"john@example.com"}'
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
-H "Content-Type: application/json"
-d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}' --body '{"type":"fill","selector":"input#email","value":"john@example.com"}'

Phone

电话

curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
-H "Content-Type: application/json"
-d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}' --body '{"type":"fill","selector":"input#phoneNumber","value":"4155551234"}'
undefined
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run
-H "Authorization: Bearer $GOOSEWORKS_API_KEY"
-H "Content-Type: application/json"
-d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}' --body '{"type":"fill","selector":"input#phoneNumber","value":"4155551234"}'
undefined

6. Accept Terms

6. 接受条款

bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"click","selector":"text=I agree to the restaurant"}'
bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"click","selector":"text=I agree to the restaurant"}'

7. Submit Reservation

7. 提交预订

bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"click","selector":"button:has-text(\"Complete reservation\")"}'
bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/execute"}'
  --body '{"type":"click","selector":"button:has-text(\"Complete reservation\")"}'

8. Verify Confirmation

8. 验证确认信息

bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/scrape"}'
  --body '{"only_main_content":true}'
Look for "confirmed" in the response.
bash
curl -s -X POST $GOOSEWORKS_API_BASE/v1/proxy/orthogonal/run \
  -H "Authorization: Bearer $GOOSEWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api":"notte","path":"/sessions/{session_id}/page/scrape"}'
  --body '{"only_main_content":true}'
在响应中查找“confirmed”字样。

API Method (curl)

API方法(curl)

bash
undefined
bash
undefined

Start session

启动会话

-H "Content-Type: application/json"
-d '{ "api": "notte", "path": "/sessions/start", "body": { "browser_type": "chromium", "headless": true, "solve_captchas": true, "idle_timeout_minutes": 10 } }'
-H "Content-Type: application/json"
-d '{ "api": "notte", "path": "/sessions/start", "body": { "browser_type": "chromium", "headless": true, "solve_captchas": true, "idle_timeout_minutes": 10 } }'

Execute actions (same pattern)

执行操作(相同模式)

-H "Content-Type: application/json"
-d '{ "api": "notte", "path": "/sessions/{session_id}/page/execute", "body": {"type":"goto","url":"https://www.opentable.com/..."} }'
undefined
-H "Content-Type: application/json"
-d '{ "api": "notte", "path": "/sessions/{session_id}/page/execute", "body": {"type":"goto","url":"https://www.opentable.com/..."} }'
undefined

Key Selectors (OpenTable)

关键选择器(OpenTable)

FieldSelector
First name
input#firstName
Last name
input#lastName
Email
input#email
Phone
input#phoneNumber
Terms checkbox
text=I agree to the restaurant
Submit
button:has-text('Complete reservation')
Time slots
button:has-text('7:00 PM')
Seating select
button:has-text('Select')
字段选择器
名字
input#firstName
姓氏
input#lastName
邮箱
input#email
电话
input#phoneNumber
条款复选框
text=I agree to the restaurant
提交
button:has-text('Complete reservation')
时间段
button:has-text('7:00 PM')
座位选择
button:has-text('Select')

Finding Restaurant IDs

查找餐厅ID

Search OpenTable and extract from URL:
  • restref=1906
    → Foreign Cinema
  • Restaurant slug in URL path
Example URL format:
https://www.opentable.com/r/{restaurant-slug}?restref={id}&datetime={YYYY-MM-DDTHH:MM}&covers={n}
搜索OpenTable并从URL中提取:
  • restref=1906
    → Foreign Cinema
  • URL路径中的餐厅别名
示例URL格式:
https://www.opentable.com/r/{restaurant-slug}?restref={id}&datetime={YYYY-MM-DDTHH:MM}&covers={n}

Tips

提示

  • OpenTable holds table for 5 minutes - move fast
  • Use
    fill
    action with
    value
    param (not
    type
    with
    text
    )
  • Click terms via label text, not checkbox directly
  • No credit card needed - reservations are free
  • Confirmation email sent automatically
  • OpenTable会为您保留座位5分钟,请尽快操作
  • 使用带
    value
    参数的
    fill
    操作(而非带
    text
    type
    操作)
  • 通过标签文本点击条款,而非直接点击复选框
  • 无需信用卡预订,免费预约
  • 确认邮件会自动发送

Resy Alternative

Resy替代方案

If restaurant uses Resy:
https://resy.com/cities/{city}/venues/{restaurant}?date={YYYY-MM-DD}&seats={n}
Similar flow but different selectors. Scrape page first to identify form fields.
如果餐厅使用Resy:
https://resy.com/cities/{city}/venues/{restaurant}?date={YYYY-MM-DD}&seats={n}
流程类似但选择器不同。先抓取页面以识别表单字段。

After Booking

预订后操作

  1. Create calendar event with
    gog calendar create
  2. Add attendees and location
  3. Include confirmation number in description
  1. 使用
    gog calendar create
    创建日历事件
  2. 添加参会者和地点
  3. 在描述中包含确认号码