Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions waifu-deal-sniper/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Discord Bot Token
# Get from: https://discord.com/developers/applications
DISCORD_TOKEN=your_discord_bot_token_here

# TinyFish Mino API Key
# Get from: https://mino.ai
MINO_API_KEY=your_mino_api_key_here
27 changes: 27 additions & 0 deletions waifu-deal-sniper/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Dependencies
node_modules/

# Environment variables
.env
.env.local
.env.production

# Database
*.db
*.sqlite

# OS files
.DS_Store
Thumbs.db

# IDE
.vscode/
.idea/

# Logs
*.log
npm-debug.log*

# Build
dist/
build/
232 changes: 232 additions & 0 deletions waifu-deal-sniper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
# 🎎 Waifu Deal Sniper

**Live Demo:** [https://discord.com/oauth2/authorize?client_id=1465346765611077871&permissions=277025508352&scope=bot]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Convert bare URLs to proper Markdown links.
This addresses the MD034 lint finding and improves readability.

📝 Suggested edit
-**Live Demo:** [https://discord.com/oauth2/authorize?client_id=1465346765611077871&permissions=277025508352&scope=bot]
+**Live Demo:** [Invite link](https://discord.com/oauth2/authorize?client_id=1465346765611077871&permissions=277025508352&scope=bot)

-https://github.com/user-attachments/assets/demo.mp4
+[Demo video](https://github.com/user-attachments/assets/demo.mp4)

Also applies to: 19-19

🤖 Prompt for AI Agents
In `@waifu-deal-sniper/README.md` at line 3, Replace bare URLs in the README with
proper Markdown link syntax to satisfy MD034: change the literal OAuth2 link at
the "Live Demo" line into a labeled link like "Live Demo:
[text](https://discord.com/...)" and similarly convert the other bare URL noted
at line 19 into a descriptive label with bracketed text followed by the URL in
parentheses; ensure each link uses the form [label](URL) and keep descriptive
labels (e.g., "Live Demo") for readability.


A Discord bot that helps anime figure collectors find discounted pre-owned figures by scraping deals in real-time from multiple sites using the TinyFish Mino API.

---

## 🎯 What It Does

Waifu Deal Sniper lets users search for anime figures across **AmiAmi**, **Mercari US**, and **Solaris Japan** directly from Discord. The bot uses TinyFish's Mino API to scrape real-time pricing, condition grades, and availability — then presents results with a fun, personality-driven interface including gacha mode, roast mode, and copium dispensary.

**Where TinyFish API is used:** The Mino API powers all figure searches by scraping e-commerce sites with natural language goals, extracting structured data (prices, conditions, images, stock status) from pages that don't have public APIs.

---

## 🎬 Demo

https://github.com/user-attachments/assets/demo.mp4

**Commands examples:**
- `rem bunny` - Search AmiAmi for Rem bunny figures
- `mercari miku` - Search Mercari US for Miku figures
- `all makima` - Search all 3 sites simultaneously
- `gacha rem` - Random figure gacha with rarity scoring
- `roast` - Get roasted for your figure taste

---

## 📦 TinyFish API Integration

```javascript
const MINO_ENDPOINT = 'https://mino.ai/v1/automation/run-sse';

async function searchSite(siteKey, query, maxPrice = null) {
const site = SITES[siteKey];
const searchUrl = site.searchUrl(query);

// Natural language goal for Mino
const goal = `Scrape pre-owned figure listings from this page.
For each product (max 8), extract:
- raw_title: Full product title
- price: Price (number only)
- url: Product link
- image: Image URL
- in_stock: true/false
- condition: Item condition
- manufacturer: Company name
Return JSON array.`;

const response = await fetch(MINO_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.MINO_API_KEY,
},
body: JSON.stringify({ url: searchUrl, goal }),
});

// Parse SSE response
const text = await response.text();
const lines = text.split('\n');

for (const line of lines) {
if (line.startsWith('data: ')) {
const event = JSON.parse(line.slice(6));
if (event.type === 'COMPLETE') {
return event.items || event.result;
}
}
}
}
```

---

## 🚀 How to Run

### Prerequisites
- Node.js 18+
- Discord Bot Token
- TinyFish Mino API Key

### 1. Clone the repository
```bash
git clone https://github.com/YOUR_USERNAME/TinyFish-cookbook.git
cd TinyFish-cookbook/waifu-deal-sniper
```

### 2. Install dependencies
```bash
npm install
```

### 3. Set environment variables
```bash
export DISCORD_TOKEN=your_discord_bot_token
export MINO_API_KEY=your_tinyfish_mino_api_key
```

Or create a `.env` file:
```env
DISCORD_TOKEN=your_discord_bot_token
MINO_API_KEY=your_tinyfish_mino_api_key
```

### 4. Run the bot
```bash
node bot.js
```

### 5. Invite the bot to your server
```
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=277025508352&scope=bot
```
Comment on lines +113 to +115
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language identifiers to fenced blocks for lint compliance.

📝 Suggested edit
-```
+```text
 https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=277025508352&scope=bot

- +text
┌─────────────────────────────────────────────────────────────────────────┐
│ DISCORD USER │
│ │
│ "mercari rem bunny" │
└─────────────────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────┐
│ DISCORD BOT (Node.js) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ Message │───▶│ Intent │───▶│ Site Router │ │
│ │ Parser │ │ Router │ │ (amiami/mercari/all) │ │
│ └──────────────┘ └──────────────┘ └───────────┬────────────┘ │
│ │ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────▼────────────┐ │
│ │ SQLite │◀──▶│ Rate │◀──▶│ Search Handler │ │
│ │ Database │ │ Limiter │ │ + Rarity Scoring │ │
│ └──────────────┘ └──────────────┘ └───────────┬────────────┘ │
│ │ │
└───────────────────────────────────────────────────────┼─────────────────┘


┌─────────────────────────────────────────────────────────────────────────┐
│ TINYFISH MINO API │
│ │
│ POST /v1/automation/run-sse │
│ { url: "https://mercari.com/search?keyword=rem", goal: "..." } │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Headless Browser → Navigate → Extract → Return Structured JSON │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 🇯🇵 │ │ 🇺🇸 │ │ ☀️ │
│ AmiAmi │ │ Mercari │ │ Solaris │
│ (JPY) │ │ (USD) │ │ (USD) │
└──────────┘ └──────────┘ └──────────┘


-```
+```text
waifu-deal-sniper/
├── bot.js          # Main bot logic (1,543 lines)
├── database.js     # SQLite database layer
├── templates.js    # 670+ personality responses
├── package.json    # Dependencies
└── README.md       # This file
</details>




Also applies to: 121-165, 185-192

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

113-113: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @waifu-deal-sniper/README.md around lines 113 - 115, The README contains
several fenced code blocks missing language identifiers which fails linting; add
a language tag (e.g., "text") to each triple-backtick fence for the blocks that
include the Discord OAuth URL
("https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID..."), the large
ASCII architecture diagram (the block starting with the DISCORD USER/ DISCORD
BOT diagram), and the repository file tree block that begins
"waifu-deal-sniper/"; ensure you update every matching fenced block (also the
other occurrences mentioned around the ASCII/art and file-tree sections) so each
opening becomestext.


</details>

<!-- fingerprinting:phantom:medusa:eagle -->

<!-- This is an auto-generated comment by CodeRabbit -->


---

## 🏗️ Architecture Diagram

```
┌─────────────────────────────────────────────────────────────────────────┐
│ DISCORD USER │
│ │
│ "mercari rem bunny" │
└─────────────────────────────────┬───────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ DISCORD BOT (Node.js) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ Message │───▶│ Intent │───▶│ Site Router │ │
│ │ Parser │ │ Router │ │ (amiami/mercari/all) │ │
│ └──────────────┘ └──────────────┘ └───────────┬────────────┘ │
│ │ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────▼────────────┐ │
│ │ SQLite │◀──▶│ Rate │◀──▶│ Search Handler │ │
│ │ Database │ │ Limiter │ │ + Rarity Scoring │ │
│ └──────────────┘ └──────────────┘ └───────────┬────────────┘ │
│ │ │
└───────────────────────────────────────────────────────┼─────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ TINYFISH MINO API │
│ │
│ POST /v1/automation/run-sse │
│ { url: "https://mercari.com/search?keyword=rem", goal: "..." } │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Headless Browser → Navigate → Extract → Return Structured JSON │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 🇯🇵 │ │ 🇺🇸 │ │ ☀️ │
│ AmiAmi │ │ Mercari │ │ Solaris │
│ (JPY) │ │ (USD) │ │ (USD) │
└──────────┘ └──────────┘ └──────────┘
```

---

## 📋 Features

| Feature | Description |
|---------|-------------|
| **Multi-Site Search** | AmiAmi, Mercari US, Solaris Japan |
| **Real-Time Scraping** | Live prices via Mino API |
| **Rarity Scoring** | SSR/SR/R/N based on scale, manufacturer, exclusivity |
| **Gacha Mode** | Random figure picks with dramatic reveals |
| **Roast Mode** | Get roasted for your waifu choices |
| **Copium Mode** | Consolation when figures are sold out |
| **Watchlist** | DM alerts when deals appear |
| **Rate Limiting** | Prevents API abuse |

---

## 📁 Project Structure

```
waifu-deal-sniper/
├── bot.js # Main bot logic (1,543 lines)
├── database.js # SQLite database layer
├── templates.js # 670+ personality responses
├── package.json # Dependencies
└── README.md # This file
```

---

## 🔧 Environment Variables

| Variable | Description | Required |
|----------|-------------|----------|
| `DISCORD_TOKEN` | Discord bot token | ✅ |
| `MINO_API_KEY` | TinyFish Mino API key | ✅ |

---

## 📜 Commands

| Command | Description |
|---------|-------------|
| `rem` | Search AmiAmi (default) |
| `mercari rem` | Search Mercari US |
| `solaris rem` | Search Solaris Japan |
| `all rem` | Search all sites |
| `gacha rem` | Random gacha pick |
| `roll` | Reroll gacha |
| `roast` | Get roasted |
| `copium` | Dispense cope |
| `watch rem under 15000` | Set price alert |
| `watchlist` | View alerts |
| `stats` | Your stats |
| `help` | Help message |

---

## 🙏 Credits

Built with [TinyFish Mino API](https://tinyfish.io) for web scraping.

---

## 📄 License

MIT
Binary file added waifu-deal-sniper/attachments/demo.mp4
Binary file not shown.
Loading