All widgets
Uber Eats Order
Shows when your Uber Eats delivery arrives, read from the Uber Eats order-tracking tab in Chrome or Safari.
Chopstick shows you everything in it before installing, and it starts turned off. Or download the .zip to import it yourself.
Type it in the notch's search
- Uber Eats order
- Refresh Uber Eats order
- Open Uber Eats tracking
What it runs
A widget is a folder of plain files. Here is all of its code.
widget.json
{
"id": "uber-eats-order",
"name": "Uber Eats Order",
"description": "Shows when your Uber Eats delivery arrives, read from the Uber Eats order-tracking tab in Chrome or Safari.",
"icon": "icon.png",
"show_when": {
"ubereats.active": true
},
"anytime": true,
"watch": [
{
"script": "order.sh",
"every": 30,
"on": [
"app"
],
"as": "ubereats"
}
],
"initial": "tracking",
"states": {
"tracking": {
"compact": {
"leading": {
"image": "logo.svg",
"tint": "#06C167"
},
"trailing": {
"text": "{{ubereats.eta | Uber Eats}}"
}
},
"expanded": {
"size": "compact",
"header": {
"tint": "#06C167",
"title": "{{ubereats.venue | Uber Eats order}}",
"image": "logo.svg"
},
"blocks": [
{
"text": "{{ubereats.eta | No active order}}",
"style": "title"
},
{
"text": "{{ubereats.status | Open your order's tracking page in Chrome or Safari}}",
"style": "body"
},
{
"note": "{{ubereats.detail | }}"
},
{
"row": [
{
"button": {
"symbol": "arrow.up.right",
"label": "Open tracking",
"style": "plain",
"do": [
{
"open_url": "{{ubereats.url | https://www.ubereats.com/orders}}"
}
]
}
},
"spacer",
{
"button": {
"symbol": "arrow.clockwise",
"label": "Refresh",
"style": "plain",
"do": [
{
"refresh": "ubereats"
}
]
}
}
]
}
]
}
}
},
"commands": {
"show": {
"description": "Show when the current Uber Eats order arrives.",
"phrase": "Uber Eats order",
"do": [
{
"refresh": "ubereats"
},
{
"go": "tracking"
}
]
},
"refresh": {
"description": "Re-read the Uber Eats tracking page now.",
"phrase": "Refresh Uber Eats order",
"do": [
{
"refresh": "ubereats"
}
]
},
"open": {
"description": "Open the Uber Eats order-tracking page.",
"phrase": "Open Uber Eats tracking",
"do": [
{
"open_url": "{{ubereats.url | https://www.ubereats.com/orders}}"
}
]
}
},
"preview": {
"states": [
"tracking"
],
"data": {
"ubereats": {
"active": true,
"venue": "Sushi Corner",
"status": "On the way",
"eta": "8–13 min",
"minutes": 8,
"detail": "Arrives around 19:45",
"url": "https://www.ubereats.com/orders"
}
}
}
}
order.js
// Finds an Uber Eats order-tracking tab in Chrome or Safari and reads its ETA.
// Prints { active, status, eta, minutes, venue, url, detail, needs_js }.
ObjC.import('stdlib')
function isRunning(name) {
return Application('System Events').processes.whose({ name: name }).length > 0
}
function findTab() {
const isOrder = u => /ubereats\.com\/(?:[a-z-]+\/)?orders\/[0-9a-f-]{8,}/i.test(u || '')
if (isRunning('Google Chrome')) {
const chrome = Application('Google Chrome')
for (const w of chrome.windows()) for (const t of w.tabs()) {
if (isOrder(t.url())) return {
url: t.url(), title: t.title(),
text: () => t.execute({ javascript: 'document.body.innerText' })
}
}
}
if (isRunning('Safari')) {
const safari = Application('Safari')
for (const w of safari.windows()) for (const t of w.tabs()) {
if (isOrder(t.url())) return {
url: t.url(), title: t.name(),
text: () => safari.doJavaScript('document.body.innerText', { in: t })
}
}
}
return null
}
function parse(text) {
const out = {}
const lower = text.toLowerCase()
const statuses = [
[/order (was )?delivered|delivered|enjoy your (order|meal)|zugestellt|geliefert/, 'Delivered'],
[/arriving now|almost there|nearby|is at your door|fast da/, 'Arriving'],
[/on (its|the) way|heading (to you|your way)|picked up|unterwegs|abgeholt/, 'On the way'],
[/preparing|being prepared|wird zubereitet|zubereitung/, 'Being prepared'],
[/checking your order|order received|confirmed|bestätigt|angenommen/, 'Order received'],
]
for (const [re, label] of statuses) if (re.test(lower)) { out.status = label; break }
const range = text.match(/(\d{1,3})\s*[–-]\s*(\d{1,3})\s*min/i)
const single = text.match(/(\d{1,3})\s*min/i)
if (range) { out.minutes = Number(range[1]); out.eta = range[1] + '–' + range[2] + ' min' }
else if (single) { out.minutes = Number(single[1]); out.eta = single[1] + ' min' }
const clock = text.match(/(?:arriv\w*|estimated|latest|ankunft|lieferung)[^\n\d]{0,30}(\d{1,2}[:.]\d{2}(?:\s*[ap]m)?)/i)
|| text.match(/(\d{1,2}:\d{2}(?:\s*[ap]m)?)\s*[–-]\s*\d{1,2}:\d{2}/i)
if (clock) out.at = clock[1].replace('.', ':')
if (out.at) out.detail = 'Arrives around ' + out.at
if (!out.eta && out.at) out.eta = out.at
return out
}
function run() {
const tab = findTab()
if (!tab) return JSON.stringify({ active: false })
const venue = (tab.title || '').replace(/\s*[|–-]\s*Uber\s*Eats.*$/i, '').replace(/^Uber\s*Eats\s*[|–-]?\s*/i, '')
const result = { active: true, url: tab.url, venue: venue && !/^order/i.test(venue) ? venue : 'Uber Eats' }
try {
Object.assign(result, parse(String(tab.text() || '')))
} catch (e) {
result.needs_js = true
result.status = 'Tracking tab found'
result.detail = 'To read the ETA, turn on View › Developer › Allow JavaScript from Apple Events (Chrome) or Develop › Allow JavaScript from Apple Events (Safari).'
}
if (result.status === 'Delivered') result.active = false, result.delivered = true
return JSON.stringify(result)
}
order.sh
osascript -l JavaScript order.js 2>/dev/null || echo "{\"active\": false}"