Initial Marktplaats CLI scaffold

Create mrktplaats-cli from twdehands-cli, updating module/import names, command branding, and token paths while preserving command behavior and payload formats.
This commit is contained in:
2026-04-15 23:45:49 +02:00
commit 7470bf1baa
9 changed files with 2753 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
mrktplaats
mrktplaats.exe
tmp_ocr_*/

40
IDENTITY.md Normal file
View File

@@ -0,0 +1,40 @@
Role & Identity:
You are Jonathan, a real human user buying computer hardware on the Belgian marketplace "marktplaats.nl". You are NOT an AI, bot, or automated script. You write messages exactly like a normal person typing quickly on a smartphone.
Language & Tone:
Language: Casual, conversational Dutch (Flemish nuances are fine).
Tone: Friendly, polite, but brief and direct. Do not sound corporate, overly formal, or robotic.
Strict Formatting Rules (CRITICAL):
NO Emojis: Never use emojis under any circumstances.
NO Em-dashes or En-dashes: Never use — or . Use regular commas or periods.
NO IDs or Metadata: Never include profile IDs (like "40921019"), URNs, reference numbers, or brackets in your messages to sellers.
NO Bullet points in chat: Write in short, normal paragraphs or single lines.
NO Passive Voice: Avoid robotic phrases like "Dit bericht is door mij verstuurd." Use active, human phrasing like "Ik stuurde dit per ongeluk."
Message Structure:
Greeting: Start with "Hoi", "Hallo", "Beste", or "Dag [Naam]".
Body: Keep it strictly to 1 or 2 short sentences. Get straight to the point (e.g., asking about SMART data, making an offer, or apologizing).
Sign-off: End simply with "Groeten, Jonathan", "Mvg, Jonathan", or just "Groeten".
Examples of GOOD output:
"Hoi, oei sorry, dat vorige bericht was een foutje en niet voor jou bedoeld. Mag je negeren! Groeten, Jonathan"
"Hallo, ik heb wel interesse. Heb je toevallig een recente SMART screenshot van de schijf? Mvg, Jonathan"
Examples of BAD output (NEVER DO THIS):
"Hi — sorry, dat bericht was vroegtijdig verstuurd door mij. Trek mijn huidige bieding in. (40921019)"
"Beste verkoper, hierbij wil ik u informeren dat..."

437
README.md Normal file
View File

@@ -0,0 +1,437 @@
# mrktplaats CLI
Command-line interface to [marktplaats.nl](https://www.marktplaats.nl), built on the
[mrktplaats Go SDK](../mrktplaats). Designed to be used by both humans and LLM agents.
---
## Build
```bash
cd mrktplaats-cli
go build -o mrktplaats .
# optional: install globally
go install .
```
---
## Authentication
Run `login` once to authenticate. The token is saved to
`~/.config/mrktplaats/token` and reused automatically.
```bash
mrktplaats login --email you@example.com --password yourpassword
```
If the account has SMS 2FA enabled the first call returns a verification prompt.
Re-run with the code you received:
```bash
mrktplaats login --email you@example.com --password yourpassword --code 123456
```
**Alternative: environment variable**
```bash
export MRKTPLAATS_TOKEN=your-token-here
mrktplaats me
```
---
## Output format
All commands write **pretty-printed JSON** to stdout.
Errors are written to stderr and exit with code 1.
---
## Command reference
### `login`
```
mrktplaats login --email EMAIL --password PASS [--code 2FA_CODE]
```
Authenticate and persist the token. With 2FA enabled, the first call returns
`"status": "verification_required"` — re-run with `--code` after receiving the SMS.
**Output (success)**
```json
{
"status": "ok",
"token_saved": "/home/you/.config/mrktplaats/token",
"access_token": "8fc90f77-..."
}
```
**Output (2FA required)**
```json
{
"status": "verification_required",
"method": "sms",
"message": "We sent a code to +32 4xx xxx xxx",
"request_id": "abc123",
"next_step": "re-run with --code CODE after receiving the SMS"
}
```
---
### `search`
```
mrktplaats search --query Q [--category ID] [--page N] [--size N] [--sort FIELD]
```
Search listings. No authentication required.
| Flag | Default | Description |
|------|---------|-------------|
| `--query` | *(required)* | Search query |
| `--category` | 0 (all) | Category ID filter |
| `--page` | 1 | Page number |
| `--size` | 20 | Results per page (max 100) |
| `--sort` | `SORT_INDEX` | `SORT_INDEX` \| `DATE_DESC` \| `PRICE_ASC` \| `PRICE_DESC` |
| `--seller` | — | Filter by seller ID |
**Output**
```json
{
"total": 2573,
"page": 1,
"size": 3,
"listings": [
{
"urn": "m2372861012",
"title": "Seiko SARV001",
"price_cents": 25000,
"price_label": "€ 250,00",
"price_type": "FIXED",
"city": "Gent",
"seller_name": "Jonathan",
"url": "https://..."
}
]
}
```
`price_type` values: `FIXED`, `MIN_BID`, `FAST_BID`, `FREE`, `ON_REQUEST`.
---
### `listing`
```
mrktplaats listing --urn URN
```
Fetch full details for a listing. Requires authentication.
**Output** — full listing object including:
- `adCore.title`, `.description`, `.price`, `.adAddress`, `.pictures`, `.attributes`
- `sellerInformation.id`, `.name` — seller's ID is needed for `Relevant.Get`
- `bids` — array of current bids
- `currentMinimumBid` — minimum next bid in cents
---
### `conversations`
```
mrktplaats conversations [--limit N]
```
List all messaging conversations.
**Output**
```json
{
"total": 2,
"unread": 0,
"conversations": [
{
"id": "1cgx:5qgvx3h:2p1gxt95d",
"item_urn": "m2372861012",
"title": "NVIDEA 100000 V2",
"other_party": "Mattia",
"other_party_id": 57506580,
"unread": 0,
"latest_message": "Is this still available?"
}
]
}
```
---
### `messages`
```
mrktplaats messages --conv CONV_ID [--limit N] [--offset N]
```
Read messages in a conversation. Use `conversations` to find IDs.
**Output**
```json
{
"total": 4,
"messages": [
{
"id": "abc123",
"sender_id": 40921019,
"text": "Is this still available?",
"date": "2026-03-03T18:48:00Z",
"type": "chat",
"read": false
}
]
}
```
---
### `send`
```
mrktplaats send --urn URN --text TEXT
```
Start a **new** conversation with the seller of a listing.
Use `reply` to respond to an existing conversation.
**Output**
```json
{
"conversation_id": "1cgx:5qgvx3h:2p1gxt95d",
"item_urn": "m2372861012",
"status": "sent"
}
```
---
### `reply`
```
mrktplaats reply --conv CONV_ID --text TEXT
```
Send a message in an **existing** conversation.
**Output**
```json
{
"message_id": "029c3014-...",
"conversation_id": "1cgx:5qgvx3h:2p1gxt95d",
"status": "sent"
}
```
---
### `bid`
```
mrktplaats bid --urn URN --amount EUROS [--message TEXT]
```
Place a bid. The listing must have `price_type` of `MIN_BID` or `FAST_BID`.
Amount is in **euros** (e.g. `--amount 20` for €20.00).
**Output**
```json
{
"bids": [
{"id": 1533844324, "value": 2000, "date": "...", "user": {"id": 40921019, "nickname": "Jonathan"}}
],
"current_minimum_bid_cents": 2000,
"current_minimum_bid_euros": 20.0
}
```
---
### `remove-bid`
```
mrktplaats remove-bid --id BID_ID
```
Remove a bid. `BID_ID` is the numeric `id` field from the `bids` array.
**Output**
```json
{
"bids": [],
"current_minimum_bid_cents": 2000,
"current_minimum_bid_euros": 20.0
}
```
---
### `favorites`
```
mrktplaats favorites [--limit N]
```
List saved listings.
**Output**
```json
{
"total": 3,
"more": false,
"items": [
{"item_urn": "m2372861012", "saved_at": ""}
]
}
```
---
### `add-favorite` / `remove-favorite`
```
mrktplaats add-favorite --urn URN
mrktplaats remove-favorite --urn URN
```
**Output**
```json
{"status": "added", "item_urn": "m2372861012"}
{"status": "removed", "item_urn": "m2372861012"}
```
---
### `me`
```
mrktplaats me
```
Get the authenticated user's profile (id, name, email, postcode, etc.).
---
### `my-ads`
```
mrktplaats my-ads [--status active|inactive|sold] [--limit N]
```
List own listings.
**Output**
```json
{
"total": 1,
"ads": [
{
"item_id": "m2373048475",
"title": "Seiko SARV001 - SDK integratietest",
"status": "active",
"price_cents": 25000,
"price_euros": 250.0
}
]
}
```
---
### `delete-ad`
```
mrktplaats delete-ad --urn URN [--reason N]
```
Delete one of your own listings.
| Reason | Meaning |
|--------|---------|
| 1 | Sold on marktplaats.nl |
| 2 | Sold elsewhere |
| 3 | No longer selling |
| 4 | Other (default) |
**Output**
```json
{"status": "deleted", "item_urn": "m2373048475"}
```
---
## LLM agent usage guide
This tool is designed to be called by an LLM agent as a subprocess. All output
is structured JSON so the agent can parse results directly.
### Typical workflow for buying
```
1. search for items
mrktplaats search --query "laptop" --size 10
2. inspect a promising listing
mrktplaats listing --urn <urn from search>
3. check conversations to see if you already contacted this seller
mrktplaats conversations
4. contact the seller (new conversation)
mrktplaats send --urn <urn> --text "Is this still available? Can you do €X?"
OR reply to an existing conversation
mrktplaats reply --conv <conv_id> --text "..."
5. if the listing supports bidding (price_type = MIN_BID or FAST_BID)
mrktplaats bid --urn <urn> --amount <euros>
# to cancel: mrktplaats remove-bid --id <bid_id>
6. save interesting listings for later
mrktplaats add-favorite --urn <urn>
```
### Typical workflow for selling
```
1. check own listings
mrktplaats my-ads
2. delete a listing
mrktplaats delete-ad --urn <urn>
3. check and reply to incoming messages
mrktplaats conversations
mrktplaats messages --conv <conv_id>
mrktplaats reply --conv <conv_id> --text "..."
```
### Key fields to extract from responses
| Task | Command | Field to read |
|------|---------|---------------|
| Get listing URN from search | `search` | `listings[].urn` |
| Get seller ID (for bids/contact) | `listing` | `sellerInformation.id` |
| Get bid ID (to cancel) | `listing` or `bid` | `bids[].id` |
| Get conversation ID | `conversations` or `send` | `conversations[].id` / `conversation_id` |
| Check if bid-enabled | `listing` | `adCore.price.priceType == "MIN_BID"` |
| Get minimum bid | `listing` | `currentMinimumBid` (cents) |
### Error handling
On error the tool exits with code 1 and writes to stderr:
```
error: listing m9999: mrktplaats: API error 404: ...
```
The agent should check the exit code and parse the error message from stderr.

153
SKILLS.md Normal file
View File

@@ -0,0 +1,153 @@
# mrktplaats CLI — Skill Guide for AI Agents
## What You Can Do
You have a `mrktplaats` CLI tool to interact with marktplaats.nl (Belgian marketplace). Use it to:
- **Search** for items (phones, RAM, electronics, etc.)
- **Get details** on listings (price, condition, seller info)
- **Check seller ratings** before buying
- **Contact sellers** via marktplaats messaging
- **Check your messages** and conversations
- **Bid** on auction items
- **Post listings** to sell items
## When to Use This Tool
**Use it when the user asks:**
- "Are there any messages?" → Run `conversations`
- "Find me a cheap Samsung phone" → Run `search`
- "What's the condition of listing X?" → Run `listing --urn X`
- "Is seller Y trustworthy?" → Run `reviews --seller Y`
- "Message the seller about X" → Run `send` or `reply`
- "Send those links to Discord" → Use curl to webhook
**Don't use it for:**
- General web searches (use websearch)
- Code questions
- Anything unrelated to marktplaats.nl
## Quick Reference
```bash
# Search (most common)
./mrktplaats search --query "TERMS" --size 50 --sort PRICE_ASC
# Get details
./mrktplaats listing --urn URN
# Check seller
./mrktplaats reviews --seller ID
# Messages
./mrktplaats conversations --limit 10
./mrktplaats messages --conv CONV_ID --limit 10
# Contact seller (NEW listing)
./mrktplaats send --urn URN --text "MESSAGE"
# Contact seller (EXISTING conversation)
./mrktplaats reply --conv CONV_ID --text "MESSAGE"
# Discord webhook
curl -X POST WEBHOOK_URL -H "Content-Type: application/json" -d '{"content": "..."}'
```
## Writing Messages (IMPORTANT)
When contacting sellers, follow these rules:
**Language:** Casual Dutch (Flemish)
**Tone:** Friendly, brief, direct — like texting
**NO:** emojis, em-dashes (—), IDs/URNs, bullet points
**Structure:**
1. Greeting: "Hoi", "Hallo", "Beste [Naam]"
2. Body: 1-2 short sentences
3. Sign-off: "Groeten, Jonathan"
**Good examples:**
- "Hoi, is die Samsung S22 nog beschikbaar? Groeten, Jonathan"
- "Hoi, kan ik hem morgen ophalen? Groeten, Jonathan"
**Never do:**
- "Hi — I'm interested in your listing (URN: m123456). Please respond."
- Include profile IDs, prices in subject, or formal language
## Finding Deals
User's benchmarks:
- DDR4 RAM: ~€2-3/GB is good
- Used phones: depends on model/condition
- Always check: price, condition (in description), seller reviews
**Workflow:**
1. Search with broad terms: `./mrktplaats search --query "ram sodimm" --size 50 --sort PRICE_ASC`
2. Analyze top results
3. Get details: `./mrktplaats listing --urn URN`
4. Check seller: `./mrktplaats reviews --seller ID`
5. Present findings with links
**Links format:** `https://link.marktplaats.nl/{URN}`
## Discord Integration
**Webhook URL:**
```
https://discord.com/api/webhooks/1478131681033588859/xjPSaVsePMWrmXI1jGtvnvAaQ2L4jeBVZt9KzXY2LAH3KmHATsLIkqsgjzcMa31oYcnY
```
**Send message:**
```bash
curl -X POST WEBHOOK_URL \
-H "Content-Type: application/json" \
-d '{"content": "Your message here"}'
```
## Errors & Solutions
| Error | Meaning | Solution |
|-------|---------|----------|
| `401 Unauthorized` | Token expired | Ask user to login |
| `403 VERIFICATION_NOT_FOUND` | 2FA blocked | Wait hours, then retry login |
| `dial tcp: lookup app.marktplaats.nl` | Network error | Retry later |
| No results returned | Bad search terms | Try different query |
## Login Flow
1. Ask user for email/password (or token)
2. Run: `./mrktplaats login --email EMAIL --password PASS`
3. If 2FA required → ask for SMS code
4. Re-run with code: `./mrktplaats login --email EMAIL --password PASS --code CODE`
## Complete Workflow Examples
### Example 1: User wants to find cheap DDR4 RAM
```
You: ./mrktplaats search --query "DDR4 sodimm" --size 50 --sort PRICE_ASC
→ Results: [...]
You: Analyze and calculate €/GB for top items
You: Present table with best deals
```
### Example 2: User wants to contact a seller
```
You: ./mrktplaats send --urn m1234567890 --text "Hoi, is die nog beschikbaar? Groeten, Jonathan"
→ {"status": "sent", ...}
```
### Example 3: User wants to check messages
```
You: ./mrktplaats conversations --limit 10
→ Shows conversations with unread count
You: ./mrktplaats messages --conv CONV_ID --limit 5
→ Shows message history
```
### Example 4: User wants to send deal links to Discord
```
You: curl -X POST WEBHOOK_URL -H "Content-Type: application/json" \
-d '{"content": "DDR4 RAM deals:\n- 16GB - €50: https://link.marktplaats.nl/m123"}'
```

291
TOOLS.md Normal file
View File

@@ -0,0 +1,291 @@
# mrktplaats CLI — Technical Reference
## Installation & Setup
The CLI is in `/home/joren/dev/marktplaatsApi/mrktplaats-cli/`. Run commands from there or add to PATH.
## Authentication
```bash
# Login (starts 2FA flow)
./mrktplaats login --email EMAIL --password PASS
# If 2FA triggered, re-run with code
./mrktplaats login --email EMAIL --password PASS --code 123456
# Token location: ~/.config/mrktplaats/token
# Or set env var: MRKTPLAATS_TOKEN=...
```
## Commands
### Search Listings
```bash
./mrktplaats search --query "SEARCH_TERMS" [--category ID] [--size N] [--sort SORT]
```
| Flag | Values | Description |
|------|--------|-------------|
| `--query` | string | Search terms (required) |
| `--category` | ID | Category ID (optional) |
| `--size` | 1-100 | Number of results (default 20) |
| `--sort` | PRICE_ASC, PRICE_DESC, DATE_DESC | Sort order |
**Example:**
```bash
./mrktplaats search --query "Samsung Galaxy S22" --size 30 --sort PRICE_ASC
```
**Output:**
```json
{
"listings": [
{
"urn": "m2375361894",
"title": "Samsung Galaxy s22, 128g, 8g ram",
"price_cents": 16000,
"price_label": "€ 160,00",
"price_type": "FIXED",
"city": "Halle",
"seller_name": "Tom"
}
],
"page": 1,
"size": 30,
"total": 403
}
```
---
### Get Listing Details
```bash
./mrktplaats listing --urn URN
```
**Output:**
```json
{
"adCore": {
"urn": "m2375361894",
"title": "Samsung Galaxy s22, 128g, 8g ram",
"description": "Is ongeveer een tweetal jaar gebruikt...",
"price": {"priceAmount": 16000, "priceType": "FIXED"},
"pictures": [...],
"attributes": [
{"key": "condition", "name": "Conditie", "values": [{"name": "Gebruikt"}]},
{"key": "Opslagcapaciteit", "values": [{"name": "128 GB"}]}
],
"link": "https://link.marktplaats.nl/m2375361894"
},
"sellerInformation": {
"id": 34593982,
"name": "Tom",
"type": "CONSUMER",
"activeSince": {"label": "8½ jaar"},
"kycState": {"twoFactorVerificationState": "VERIFIED"}
}
}
```
---
### Seller Reviews
```bash
./mrktplaats reviews --seller ID
# or from a listing
./mrktplaats reviews --urn URN
```
**Output:**
```json
{
"reviews": [
{
"id": 125540237,
"score": 5,
"reviewer_name": "Kakoje",
"subject": "Monitoren",
"direction": "S2B",
"date": "2024-06-26",
"details": [{"category": "general", "score": 5, "traits": ["+Reageert snel", "+Vriendelijk"]}]
}
],
"seller_id": 34593982,
"summary": {"average_score": 4.5, "count": 14}
}
```
---
### Conversations
```bash
./mrktplaats conversations --limit N
```
**Output:**
```json
{
"conversations": [
{
"id": "5b6x:2p0q1r6:2p16t6r09",
"item_urn": "m2366146019",
"title": "Ebike FIETSBATRERIJ",
"other_party": "linde.de",
"other_party_id": 32557926,
"unread": 1,
"latest_message": "Wilrijk"
}
],
"total": 3,
"unread": 1
}
```
---
### Messages
```bash
./mrktplaats messages --conv CONV_ID --limit N
```
**Output:**
```json
{
"messages": [
{
"id": "7ae205d4-18c6-11f1-977b-25f14ffc6a81",
"sender_id": 32557926,
"text": "Wilrijk",
"date": "2026-03-05T19:06:56.942Z",
"type": "chat"
}
],
"total": 6
}
```
---
### Send Message (New Conversation)
```bash
./mrktplaats send --urn URN --text "MESSAGE"
```
**Output:**
```json
{
"item_urn": "m2372861012",
"seller_id": "57506580",
"seller_name": "Mattia",
"status": "sent"
}
```
---
### Reply to Conversation
```bash
./mrktplaats reply --conv CONV_ID --text "MESSAGE"
```
**Output:**
```json
{
"conversation_id": "1cgx:5qgvx3h:2p1gxt95d",
"message_id": "448b34fe-1806-11f1-9484-e9655a940eaf",
"status": "sent"
}
```
---
### Place Bid
```bash
./mrktplaats bid --urn URN --amount EUROS [--message TEXT]
```
---
### Other Commands
```bash
# Seller's other listings
./mrktplaats seller-listings --seller ID
# Similar listings
./mrktplaats similar --urn URN
# Search autocomplete
./mrktplaats suggest --prefix TEXT
# Favorites
./mrktplaats favorites
./mrktplaats add-favorite --urn URN
./mrktplaats remove-favorite --urn URN
# User info
./mrktplaats me
./mrktplaats notifications
./mrktplaats my-ads
# Sell item
./mrktplaats recognize --file PATH
./mrktplaats price-suggestion --category ID --title TEXT
./mrktplaats create-ad --category ID --title T --description D --price EUROS
```
## Error Messages
| Error | Cause | Fix |
|-------|-------|-----|
| `401 Unauthorized` | Token expired/missing | Run login |
| `403 VERIFICATION_NOT_FOUND` | Too many wrong 2FA codes | Wait hours |
| `403 VERIFICATION_REQUESTS_BLOCKED` | 2FA blocked | Wait hours |
| `dial tcp: lookup app.marktplaats.nl: server misbehaving` | DNS/network | Retry later |
| `No results found` | Search terms not matching | Try broader terms |
## Common Patterns
### Finding Deals
1. Search: `./mrktplaats search --query "DDR4 sodimm" --size 50 --sort PRICE_ASC`
2. Parse results (max 100)
3. Fetch details for promising URNs: `./mrktplaats listing --urn URN`
4. Calculate price-per-GB for memory
5. Check seller reviews
### Checking Messages
1. `./mrktplaats conversations --limit 10` → shows unread count
2. If unread > 0, fetch: `./mrktplaats messages --conv CONV_ID --limit 10`
### Contacting Seller
1. Use `./mrktplaats send --urn URN --text "..."` for new listings
2. Use `./mrktplaats reply --conv CONV_ID --text "..."` for existing threads
### Sending to Discord
```bash
curl -X POST "https://discord.com/api/webhooks/1478131681033588859/xjPSaVsePMWrmXI1jGtvnvAaQ2L4jeBVZt9KzXY2LAH3KmHATsLIkqsgjzcMa31oYcnY" \
-H "Content-Type: application/json" \
-d '{"content": "Samsung S22 - €160: https://link.marktplaats.nl/m2375361894"}'
```
## IDENTITY.md Reminder
When writing messages to sellers:
- Use casual Dutch (Flemish)
- Keep it short (1-2 sentences)
- NO emojis, NO em-dashes, NO IDs/URNs
- Sign off with "Groeten, Jonathan"
**Example:**
```
Hoi, is die Samsung S22 nog beschikbaar? Groeten, Jonathan
```

7
go.mod Normal file
View File

@@ -0,0 +1,7 @@
module github.com/joren/mrktplaats-cli
go 1.25.0
require github.com/joren/mrktplaats v0.0.0
replace github.com/joren/mrktplaats => ../mrktplaats

1711
main.go Normal file

File diff suppressed because it is too large Load Diff

BIN
mrktplaats-cli Executable file

Binary file not shown.

111
scripts/poll_nvidia_reply.py Executable file
View File

@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""
Long-running poller for a conversation on the mrktplaats-cli.
Usage: python3 scripts/poll_nvidia_reply.py --conv CONV_ID --user USER_ID [--interval 900]
The script runs in a loop, calling `go run main.go messages --conv CONV_ID --limit 50`
in the repo directory, parses the JSON output and exits when it finds any message
whose `sender_id` is not the provided USER_ID (i.e. a seller reply).
When a reply is found the script writes a JSON file with the matching messages to
/home/joren/.local/share/opencode/tool-output/nvidia_reply_found.json and exits with
code 0. It logs progress to stdout/stderr so you can follow the `nohup` log file.
"""
import argparse
import json
import subprocess
import time
import os
from datetime import datetime
def now():
return datetime.utcnow().isoformat() + "Z"
def fetch_messages(conv, workdir):
# Run the mrktplaats-cli to fetch the conversation messages
cmd = ["go", "run", "main.go", "messages", "--conv", conv, "--limit", "50"]
proc = subprocess.run(cmd, cwd=workdir, capture_output=True, text=True)
if proc.returncode != 0:
raise RuntimeError(f"messages command failed: {proc.stderr.strip()}")
return proc.stdout
def find_foreign_messages(json_text, user_id):
data = json.loads(json_text)
msgs = data.get("messages") or []
foreign = [m for m in msgs if int(m.get("sender_id", 0)) != int(user_id)]
return foreign
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--conv", required=True, help="Conversation id to poll")
parser.add_argument("--user", required=True, help="Our user id (numeric)")
parser.add_argument(
"--interval",
type=int,
default=900,
help="Poll interval in seconds (default 900 = 15m)",
)
parser.add_argument(
"--workdir",
default="/home/joren/dev/marktplaatsApi/mrktplaats-cli",
help="Path to mrktplaats-cli repo",
)
args = parser.parse_args()
out_dir = os.path.expanduser("/home/joren/.local/share/opencode/tool-output")
os.makedirs(out_dir, exist_ok=True)
found_path = os.path.join(out_dir, "nvidia_reply_found.json")
print(
f"{now()} Poller started for conv={args.conv}, user={args.user}, interval={args.interval}s"
)
# write pid file
pid_path = os.path.join(out_dir, "nvidia_poll.pid")
with open(pid_path, "w") as f:
f.write(str(os.getpid()))
try:
while True:
try:
raw = fetch_messages(args.conv, args.workdir)
except Exception as e:
print(f"{now()} Error fetching messages: {e}")
time.sleep(30)
continue
try:
foreign = find_foreign_messages(raw, args.user)
except Exception as e:
print(f"{now()} Error parsing messages JSON: {e}")
time.sleep(30)
continue
if foreign:
result = {"conv": args.conv, "detected_at": now(), "messages": foreign}
with open(found_path, "w") as f:
json.dump(result, f, indent=2)
print(
f"{now()} Found {len(foreign)} foreign message(s) — wrote {found_path}"
)
# also print the messages for immediate visibility
print(json.dumps(result, indent=2))
return 0
print(f"{now()} No reply yet; sleeping {args.interval} seconds")
time.sleep(args.interval)
finally:
# cleanup pid file
try:
os.remove(pid_path)
except Exception:
pass
if __name__ == "__main__":
main()