บทนำ — ข้อผิดพลาดจริงที่ทำให้ต้องเขียนบทความนี้

ผมเคยเสียเวลาหลายชั่วโมงกับข้อผิดพลาดนี้:

Error: Could not load manifest.json
Details: serviceworker.js file not found or invalid
Manifest version 2 is deprecated. 
You must use Manifest V3 for new extensions.

วันนี้ผมจะสอนทุกขั้นตอนการสร้าง Chrome Extension ที่เชื่อมต่อกับ AI API แบบครบวงจร โดยใช้ HolySheep AI ซึ่งมีอัตราเพียง ¥1=$1 ประหยัดมากกว่า 85% เมื่อเทียบกับผู้ให้บริการอื่น

Manifest V3 คืออะไร และทำไมต้องเปลี่ยน

Manifest V3 เป็นเวอร์ชันใหม่ของ Chrome Extension API ที่ Google บังคับใช้ตั้งแต่ปี 2023 โดยมีการเปลี่ยนแปลงสำคัญ:

โครงสร้างโปรเจกต์

my-ai-extension/
├── manifest.json
├── background.js
├── content.js
├── popup.html
├── popup.js
├── styles.css
└── icons/
    ├── icon16.png
    ├── icon48.png
    └── icon128.png

1. สร้าง manifest.json

{
  "manifest_version": 3,
  "name": "AI Assistant powered by HolySheep",
  "version": "1.0.0",
  "description": "AI Assistant โดยใช้ HolySheep API",
  "permissions": [
    "storage",
    "activeTab",
    "scripting"
  ],
  "host_permissions": [
    "https://api.holysheep.ai/*"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "content_scripts": [
    {
      "matches": [""],
      "js": ["content.js"]
    }
  ],
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

2. background.js — Service Worker สำหรับจัดการ API

// การตั้งค่า API ของ HolySheep
const HOLYSHEEP_CONFIG = {
  baseUrl: 'https://api.holysheep.ai/v1',
  model: 'gpt-4.1'
};

// ฟังก์ชันเรียกใช้ HolySheep API
async function callHolySheepAI(messages, apiKey) {
  const response = await fetch(${HOLYSHEEP_CONFIG.baseUrl}/chat/completions, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': Bearer ${apiKey}
    },
    body: JSON.stringify({
      model: HOLYSHEEP_CONFIG.model,
      messages: messages,
      max_tokens: 2000,
      temperature: 0.7
    })
  });

  if (!response.ok) {
    const errorData = await response.json().catch(() => ({}));
    throw new Error(API Error ${response.status}: ${errorData.error?.message || response.statusText});
  }

  return await response.json();
}

// รับข้อความจาก Content Script หรือ Popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'getAIResponse') {
    chrome.storage.local.get(['apiKey'], async (result) => {
      try {
        const apiKey = result.apiKey;
        
        if (!apiKey) {
          sendResponse({ error: 'กรุณาตั้งค่า API Key ก่อน' });
          return;
        }

        const data = await callHolySheepAI(request.messages, apiKey);
        sendResponse({ data: data });
      } catch (error) {
        sendResponse({ error: error.message });
      }
    });
    return true; // บอก Chrome ว่าจะส่ง response แบบ async
  }
});

// ตั้งค่า API Key
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'setApiKey') {
    chrome.storage.local.set({ apiKey: request.apiKey }, () => {
      sendResponse({ success: true });
    });
    return true;
  }
});

3. popup.html และ popup.js — UI สำหรับผู้ใช้

<!DOCTYPE html>
<html lang="th">
<head>
  <meta charset="UTF-8">
  <title>AI Assistant</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="container">
    <h2>🤖 HolySheep AI</h2>
    
    <div id="setupSection">
      <label>API Key:</label>
      <input type="password" id="apiKeyInput" placeholder="ใส่ API Key ของคุณ">
      <button id="saveKeyBtn">บันทึก</button>
      <p class="hint">สมัครที่ <a href="https://www.holysheep.ai/register" target="_blank">holysheep.ai</a></p>
    </div>

    <div id="chatSection" style="display:none;">
      <textarea id="userInput" rows="4" placeholder="พิมพ์คำถามของคุณ..."></textarea>
      <button id="sendBtn">ส่ง</button>
      <div id="response"></div>
    </div>
  </div>
  <script src="popup.js"></script>
</body>
</html>
// popup.js
document.addEventListener('DOMContentLoaded', () => {
  const setupSection = document.getElementById('setupSection');
  const chatSection = document.getElementById('chatSection');
  const apiKeyInput = document.getElementById('apiKeyInput');
  const saveKeyBtn = document.getElementById('saveKeyBtn');
  const userInput = document.getElementById('userInput');
  const sendBtn = document.getElementById('sendBtn');
  const responseDiv = document.getElementById('response');

  // โหลด API Key ที่บันทึกไว้
  chrome.storage.local.get(['apiKey'], (result) => {
    if (result.apiKey) {
      setupSection.style.display = 'none';
      chatSection.style.display = 'block';
    }
  });

  // บันทึก API Key
  saveKeyBtn.addEventListener('click', () => {
    const apiKey = apiKeyInput.value.trim();
    if (!apiKey) {
      alert('กรุณาใส่ API Key');
      return;
    }

    chrome.runtime.sendMessage({
      action: 'setApiKey',
      apiKey: apiKey
    }, (response) => {
      if (response.success) {
        setupSection.style.display = 'none';
        chatSection.style.display = 'block';
      }
    });
  });

  // ส่งข้อความ
  sendBtn.addEventListener('click', async () => {
    const message = userInput.value.trim();
    if (!message) return;

    responseDiv.innerHTML = '⏳ กำลังประมวลผล...';
    
    chrome.runtime.sendMessage({
      action: 'getAIResponse',
      messages: [{ role: 'user', content: message }]
    }, (response) => {
      if (response.error) {
        responseDiv.innerHTML = ❌ ข้อผิดพลาด: ${response.error};
      } else {
        const answer = response.data.choices[0].message.content;
        responseDiv.innerHTML = ✅ ${answer};
      }
    });
  });
});

4. content.js — ดึงข้อความจากเว็บไซต์

// content.js - ทำงานในหน้าเว็บที่ผู้ใช้เปิดอยู่
let selectedText = '';

// ตรวจจับการเลือกข้อความ
document.addEventListener('mouseup', () => {
  const selection = window.getSelection();
  selectedText = selection.toString().trim();
});

// สร้าง Context Menu
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'getSelectedText') {
    sendResponse({ text: selectedText });
  }
});

// เพิ่มปุ่มลัดในเว็บ
const aiButton = document.createElement('button');
aiButton.innerHTML = '🤖 ถาม AI';
aiButton.style.cssText = `
  position: fixed;
  bottom: 20px;
  right: 20px;
  padding: 12px 20px;
  background: #6366f1;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  z-index: 999999;
`;
document.body.appendChild(aiButton);

aiButton.addEventListener('click', () => {
  if (!selectedText) {
    alert('กรุณาเลือกข้อความที่ต้องการถามก่อน');
    return;
  }

  chrome.runtime.sendMessage({
    action: 'getAIResponse',
    messages: [
      { role: 'system', content: 'คุณเป็นผู้ช่วย AI ที่ตอบคำถามจากเนื้อหาที่ผู้ใช้เลือก' },
      { role: 'user', content: เนื้อหาที่เลือก: "${selectedText}" }
    ]
  }, (response) => {
    if (response.error) {
      alert(ข้อผิดพลาด: ${response.error});
    } else {
      const answer = response.data.choices[0].message.content;
      alert(🤖 AI: ${answer});
    }
  });
});

5. styles.css

.container {
  width: 350px;
  padding: 20px;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

h2 {
  margin: 0 0 15px 0;
  color: #1f2937;
}

label {
  display: block;
  margin-bottom: 5px;
  font-weight: 600;
}

input {
  width: 100%;
  padding: 10px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  margin-bottom: 10px;
  box-sizing: border-box;
}

button {
  width: 100%;
  padding: 10px;
  background: #6366f1;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 600;
}

button:hover {
  background: #4f46e5;
}

textarea {
  width: 100%;
  padding: 10px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  margin-bottom: 10px;
  resize: none;
  box-sizing: border-box;
}

#response {
  margin-top: 15px;
  padding: 12px;
  background: #f3f4f6;
  border-radius: 6px;
  white-space: pre-wrap;
  font-size: 14px;
}

.hint {
  font-size: 12px;
  color: #6b7280;
  margin-top: 5px;
}

ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข

1. 401 Unauthorized — API Key ไม่ถูกต้อง

// ❌ ข้อผิดพลาดที่พบ
{
  "error": {
    "message": "Invalid API key provided",
    "type": "invalid_request_error",
    "code": "invalid_api_key"
  }
}

// ✅ วิธีแก้ไข
// 1. ตรวจสอบว่า API Key ถูกส่งอย่างถูกต้อง
const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
  headers: {
    'Authorization': Bearer ${apiKey}, // ต้องมี 'Bearer ' นำหน้า
    'Content-Type': 'application/json'
  }
});

// 2. ตรวจสอบว่า API Key ยังไม่หมดอายุ
// ไปที่ https://www.holysheep.ai/register เพื่อตรวจสอบ

2. ConnectionError: timeout — เชื่อมต่อไม่ได้

// ❌ ข้อผิดพลาดที่พบ
TypeError: Failed to fetch
NetworkError: Connection timeout

// ✅ วิธีแก้ไข
async function callAPIWithRetry(messages, apiKey, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 วินาที
      
      const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': Bearer ${apiKey}
        },
        body: JSON.stringify({
          model: 'gpt-4.1',
          messages: messages
        }),
        signal: controller.signal
      });
      
      clearTimeout(timeoutId);
      return await response.json();
      
    } catch (error) {
      if (error.name === 'AbortError') {
        console.log(ครั้งที่ ${i + 1}: Timeout, ลองใหม่...);
      } else if (i === maxRetries - 1) {
        throw new Error(เชื่อมต่อไม่ได้หลังลอง ${maxRetries} ครั้ง);
      }
    }
  }
}

3. Manifest V3 Service Worker ไม่ทำงาน

// ❌ ข้อผิดพลาดที่พบ
Service worker registration failed
Error: serviceworker.js not found

// ✅ วิธีแก้ไข
// 1. ตรวจสอบว่าไฟล์อยู่ในตำแหน่งที่ถูกต้อง
// manifest.json ต้องระบุชื่อไฟล์ที่ตรงกัน
"background": {
  "service_worker": "background.js" // ไม่ใช่ background.ts หรือ background/sw.js
}

// 2. เปิด Chrome ไปที่ chrome://extensions/
// คลิก "Service Worker" link ใต้ extension ของคุณ
// ตรวจสอบ Console สำหรับ error logs

// 3. ถ้าใช้ ES Modules ต้องระบุ type ใน manifest
"background": {
  "service_worker": "background.js",
  "type": "module"  // เพิ่มบรรทัดนี้ถ้าใช้ import
}

4. CORS Error — ข้อจำกัดของ Manifest V3

// ❌ ข้อผิดพลาดที่พบ
Access to fetch at 'https://api.holysheep.ai/v1/chat/completions' 
from origin 'https://example.com' has been blocked by CORS policy

// ✅ วิธีแก้ไข
// ใน Manifest V3 ต้องเพิ่ม host_permissions ใน manifest.json
{
  "manifest_version": 3,
  "host_permissions": [
    "https://api.holysheep.ai/*"  // เพิ่ม domain ที่ต้องการเรียก
  ]
}

// หรือเรียก API จาก background.js แทน content.js
// เพราะ background service worker ไม่มี CORS restriction

ราคาและข้อมูลสำคัญของ HolySheep AI

โมเดล ราคา ($/MTok) ราคาจริง
GPT-4.1 $8.00 ประหยัด 85%+
Claude Sonnet 4.5 $15.00 ประหยัด 85%+
Gemini 2.5 Flash $2.50 ประหยัด 85%+
DeepSeek V3.2 $0.42 ประหยัด 85%+

จุดเด่นของ HolySheep:

วิธีทดสอบ Extension

  1. เปิด Chrome ไปที่ chrome://extensions/
  2. เปิด Developer mode ที่มุมขวาบน
  3. คลิก "Load unpacked" แล้วเลือกโฟลเดอร์ project
  4. คลิกที่ไอคอน Extension แล้วตั้งค่า API Key จาก HolySheep AI
  5. ทดสอบโดยเลือกข้อความบนเว็บไซต์ใดก็ได้แล้วกดปุ่ม AI

สรุป

การสร้าง Chrome Extension ที่เชื่อมต่อกับ AI API ใน Manifest V3 ต้องระวังเรื่อง Service Worker, Message Passing และ CORS Policy โดยเฉพาะ การใช้ HolySheep AI ทำให้ประหยัดค่าใช้จ่ายได้มากกว่า 85% พร้อมความเร็วตอบสนองน้อยกว่า 50ms

👉 สมัคร HolySheep AI — รับเครดิตฟรีเมื่อลงทะเบียน