LINE Bot ที่ใช้ Gemini API (จาก Google) ในการประมวลผลและตอบ

สมัคร apikey Gemini
https://aistudio.google.com/apikey

กิจกรรมที่ 1 ถามตอบ Gemini แบบไม่ต่อเนื่อง

const LINE_TOKEN = 'xxxxx';
const GEMINI_API_KEY = 'xxxxxx';
const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent';

function doPost(e) {
  const data = JSON.parse(e.postData.contents);
  const event = data.events[0];

  if (event.type === 'message' && event.message.type === 'text') {
    const userText = event.message.text;
    const replyToken = event.replyToken;
    
    // Call Gemini API
    const payload = {
      contents: [{
        role: 'user',
        parts: [{ text: userText }]
      }]
    };
    const response = UrlFetchApp.fetch(GEMINI_API_URL + '?key=' + GEMINI_API_KEY, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify(payload)
    });
    const responseData = JSON.parse(response.getContentText());
    let messageText = 'ขอโทษครับ ไม่สามารถตอบได้ตอนนี้';
    if (responseData.candidates && responseData.candidates.length > 0) {
      messageText = responseData.candidates[0].content.parts[0].text;
    }

    // Send reply
    sendMessage(replyToken, { type: 'text', text: messageText.substring(0, 2000) });
  }
}

function sendMessage(replyToken, message) {
  const url = 'https://api.line.me/v2/bot/message/reply';
  UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + LINE_TOKEN },
    payload: JSON.stringify({ replyToken, messages: [message] })
  });
}

กิจกรรมที่ 2 ถามตอบ Gemini แบบต่อเนื่อง

const LINE_TOKEN = 'xxxxxx';
const GEMINI_API_KEY = 'xxxxx';
const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent';

function doPost(e) {
  const data = JSON.parse(e.postData.contents);
  const event = data.events[0];

  if (event.type === 'message' && event.message.type === 'text') {
    const userId = event.source.userId;
    const userText = event.message.text;
    const replyToken = event.replyToken;

    // ดึง history เดิม
    const history = getChatHistory(userId);
    history.push({ role: 'user', parts: [{ text: userText }] });

    // เตรียม payload
    const payload = { contents: history };
    const response = UrlFetchApp.fetch(GEMINI_API_URL + '?key=' + GEMINI_API_KEY, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify(payload)
    });

    const responseData = JSON.parse(response.getContentText());
    let messageText = 'ขอโทษครับ ไม่สามารถตอบได้ตอนนี้';
    
    if (responseData.candidates && responseData.candidates.length > 0) {
      const messageText = responseData.candidates[0].content.parts[0].text;

      // เพิ่มคำตอบของ bot ลงใน history
      history.push({ role: 'model', parts: [{ text: messageText }] });

      // เก็บ history ใหม่ (จำกัดไม่เกิน 10 รายการ)
      saveChatHistory(userId, history.slice(-10));
    }

    // ส่งข้อความกลับไปยังผู้ใช้
    sendMessage(replyToken, { type: 'text', text: messageText.substring(0, 2000) });
  }
}

function sendMessage(replyToken, message) {
  const url = 'https://api.line.me/v2/bot/message/reply';
  UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + LINE_TOKEN },
    payload: JSON.stringify({ replyToken, messages: [message] })
  });
}

function getChatHistory(userId) {
  const props = PropertiesService.getUserProperties();
  const key = `history_${userId}`;
  const historyJson = props.getProperty(key);
  return historyJson ? JSON.parse(historyJson) : [];
}

function saveChatHistory(userId, history) {
  const props = PropertiesService.getUserProperties();
  const key = `history_${userId}`;
  props.setProperty(key, JSON.stringify(history));
}

🎯 Use Case 1: ผูวยตอบคำถามนักเรียนเกียวกับบทเรียน
📌 สถานการณ์:
คุณสราง LINE Bot สำหรับนักเรียนชั .6 ทีใชถามปัญหาคณิตศาสตรพืนฐาน เช "เศษส่วน", "การหาร", หรือ "การคิดเปอร์เซ็นต์"

🧠 บริบททีกำหนดเอง:
{ "role": "system", "parts": [{ "text": "คุณคือครูผู้ช่วยสอนคณิตศาสตร์ระดับประถม ให้คำอธิบายง่าย ใช้ตัวอย่างจริง" }] }
👦 นักเรียน:
ครูครับ เศษ 2/5 บวกกับ 1/10 ยังไงครับ

🤖 Bot:
ใหหาคานิยมรวมกอนครับ 5 กับ 10 คูณกันได10
2/5 = 4/10 แล + 1/10 = 5/10 หรือ 1/2 ครับ

🎯 Use Case 2: ทีปรึกษานวัตกรรมการเรียนรูใหครู
📌 สถานการณ์:
ครูในศูนยการศึกษาพิเศษถามบอทผาน LINE าจะจัดกิจกรรมฝึกกลามเนือมือใหเดกออทิสติกอยางไรดี

🧠 บริบททีกำหนดเอง:
{ "role": "system", "parts": [{ "text": "คุณคือผู้เชี่ยวชาญด้านการจัดกิจกรรมเพื่อพัฒนาทักษะเด็กพิเศษ โดยเฉพาะกล้ามเนื้อมือและสายตา" }] }
👩‍🏫 ครู:
กิจกรรมแบบไหนทีวยใหเดกออทิสติกฝึกตา-มือดีครับ

🤖 Bot:
แนะนำกิจกรรมจับคูภาพ ตัดกระดาษดวยกรรไกร เลนเกมจับลูกบอลหลากสีผานทอใสครับ
กิจกรรมเหลานีวยใหเดกโฟกัสสายตาและใชมือสัมพันธกันได

กิจกรรมที่ 3 ถามตอบ Gemini เข้าใจบริบท

const LINE_TOKEN = 'xxxxxx';
const GEMINI_API_KEY = 'xxxxx';
const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent';

function doPost(e) {
  const data = JSON.parse(e.postData.contents);
  const event = data.events[0];

  if (event.type === 'message' && event.message.type === 'text') {
    const userId = event.source.userId;
    const userText = event.message.text;
    const replyToken = event.replyToken;

    // กำหนดบริบทเริ่มต้น เช่น ข้อความที่ให้กับระบบ
    let context = getChatHistory(userId);
    
    // ตัวอย่างบริบทที่กำหนดเอง
    if (context.length === 0) {
      context.push({ role: 'system', parts: [{ text: 'คุณคือผู้ช่วยตอบคำถามที่มีความรู้เกี่ยวกับร้านอาหาร' }] });
    }

    // เพิ่มข้อความของผู้ใช้ในบริบท
    context.push({ role: 'user', parts: [{ text: userText }] });

    // ส่งคำขอไปที่ Gemini API
    const payload = { contents: context };
    const response = UrlFetchApp.fetch(GEMINI_API_URL + '?key=' + GEMINI_API_KEY, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify(payload)
    });

    const responseData = JSON.parse(response.getContentText());
    let messageText = 'ขอโทษครับ ไม่สามารถตอบได้ตอนนี้';

    if (responseData.candidates && responseData.candidates.length > 0) {
      messageText = responseData.candidates[0].content.parts[0].text;
    }

    // เพิ่มคำตอบของบอทลงในบริบท
    context.push({ role: 'model', parts: [{ text: messageText }] });

    // เก็บบริบทใหม่กลับไปใน properties
    saveChatHistory(userId, context);

    // ส่งข้อความกลับไปยังผู้ใช้
    sendMessage(replyToken, { type: 'text', text: messageText.substring(0, 2000) });
  }
}

function sendMessage(replyToken, message) {
  const url = 'https://api.line.me/v2/bot/message/reply';
  UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + LINE_TOKEN },
    payload: JSON.stringify({ replyToken, messages: [message] })
  });
}

function getChatHistory(userId) {
  const props = PropertiesService.getUserProperties();
  const key = `history_${userId}`;
  const historyJson = props.getProperty(key);
  return historyJson ? JSON.parse(historyJson) : [];
}

function saveChatHistory(userId, history) {
  const props = PropertiesService.getUserProperties();
  const key = `history_${userId}`;
  props.setProperty(key, JSON.stringify(history));
}

กิจกรรมที่ 4 ถามตอบ Gemini กำหนดบริบทเอง

const LINE_TOKEN = 'xxxx';
const GEMINI_API_KEY = 'xxxx';
const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent';

function doPost(e) {
  const data = JSON.parse(e.postData.contents);
  const event = data.events[0];

  if (event.type === 'message' && event.message.type === 'text') {
    const userText = event.message.text;
    const replyToken = event.replyToken;

    // กำหนดบริบทที่ต้องการ (ตัวอย่างบริบทที่กำหนดเอง)
    const context = [
      { role: 'model', parts: [{ text: 'คุณคือผู้ช่วยตอบคำถามที่มีความรู้เกี่ยวกับร้านอาหาร' }] },
      { role: 'user', parts: [{ text: 'คุณเป็นบอทร้านอาหารชื่อ ครัวคุณป้า เมนูมี: ก๋วยเตี๋ยวต้มยำ(80บาท), ข้าวผัด(60บาท)' }] },
    ];

    // เพิ่มข้อความของผู้ใช้ลงไปในบริบท
    context.push({ role: 'user', parts: [{ text: userText }] });

    // ส่งคำขอไปที่ Gemini API
    const payload = { contents: context };
    const response = UrlFetchApp.fetch(GEMINI_API_URL + '?key=' + GEMINI_API_KEY, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify(payload)
    });

    const responseData = JSON.parse(response.getContentText());
    let messageText = 'ขอโทษครับ ไม่สามารถตอบได้ตอนนี้';

    if (responseData.candidates && responseData.candidates.length > 0) {
      messageText = responseData.candidates[0].content.parts[0].text;
    }

    // ส่งข้อความกลับไปยังผู้ใช้
    sendMessage(replyToken, { type: 'text', text: messageText.substring(0, 2000) });
  }
}

function sendMessage(replyToken, message) {
  const url = 'https://api.line.me/v2/bot/message/reply';
  UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + LINE_TOKEN },
    payload: JSON.stringify({ replyToken, messages: [message] })
  });
}

กิจกรรมที่ 5 ถามตอบ Gemini วิเคราะห์ภาพ

const LINE_TOKEN = 'xxxx';
const GEMINI_API_KEY = 'xxxx';
const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent';

function doPost(e) {
    const data = JSON.parse(e.postData.contents);
    const event = data.events[0];

    if (event.type === 'message') {
      const userId = event.source.userId;
      const replyToken = event.replyToken;
      const history = getChatHistory(userId);

      // รับข้อความจากผู้ใช้
      if (event.message.type === 'text') {
        const userText = event.message.text;
        history.push({ role: 'user', parts: [{ text: userText }] });

      // รับรูปภาพจากผู้ใช้
      } else if (event.message.type === 'image') {
          const imageData = getLineImage(event.message.id);
          const base64Image = Utilities.base64Encode(imageData);
          history.push({
            role: 'user',
            parts: [
              { text: 'วิเคราะห์รูปภาพนี้:' },
              { inlineData: { mimeType: 'image/jpeg', data: base64Image } }
            ]
          });
      }

      // เรียก Gemini API ตรงนี้
      const payload = { contents: history, generationConfig: { maxOutputTokens: 2000 } };
      const options = {
        method: 'post',
        contentType: 'application/json',
        payload: JSON.stringify(payload),
        muteHttpExceptions: true
      };

      let botReply = 'ขอโทษครับ ไม่สามารถตอบได้ตอนนี้';
      try {
        const response = UrlFetchApp.fetch(GEMINI_API_URL + '?key=' + GEMINI_API_KEY, options);
        const data = JSON.parse(response.getContentText());
        if (data.candidates && data.candidates.length > 0) {
          botReply = data.candidates[0].content.parts.map(p => p.text).join('');
        }
      } catch (err) {
        Logger.log('Gemini API Error: ' + err);
        botReply = 'เกิดข้อผิดพลาดในการติดต่อ AI';
      }

      // เก็บและส่งข้อความกลับ
      history.push({ role: 'model', parts: [{ text: botReply }] });
      saveChatHistory(userId, history.slice(-10));
      sendMessage(replyToken, { type: 'text', text: botReply.substring(0, 2000) });
    }
}

function sendMessage(replyToken, message) {
  const url = 'https://api.line.me/v2/bot/message/reply';
  UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + LINE_TOKEN },
    payload: JSON.stringify({ replyToken, messages: [message] })
  });
}

function getLineImage(messageId) {
  const url = `https://api-data.line.me/v2/bot/message/${messageId}/content`;
  const options = {
    headers: { Authorization: 'Bearer ' + LINE_TOKEN, 'Content-Type': 'application/octet-stream' },
    muteHttpExceptions: true
  };
  const response = UrlFetchApp.fetch(url, options);
  if (response.getResponseCode() !== 200) {
    throw new Error('ไม่สามารถดึงข้อมูลรูปภาพได้');
  }
  return response.getBlob().getBytes();
}

function getChatHistory(userId) {
  const props = PropertiesService.getUserProperties();
  const data = props.getProperty(`history_${userId}`);
  return data ? JSON.parse(data) : [];
}

function saveChatHistory(userId, history) {
  PropertiesService.getUserProperties().setProperty(`history_${userId}`, JSON.stringify(history));
}

ตัวอย่างบันทึกข้อมูลลง google sheet

const SHEET_ID = '1zxl-4z7gqOO92IqM_IYdpKs0FYhN68r3sHMfjoX_fd8'; // Google Sheet ID
logToSheet(state.name, state.phone, state.location);
function logToSheet(name, phone, location) {
  const sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName('Repairs');
  sheet.appendRow([new Date(), name, phone, location]);
}

กิจกรรมที่ 6 ตัวอย่างระบบแจ้งซ่อม

const LINE_TOKEN = 'xxxx';
const GEMINI_API_KEY = 'xxxx';
const GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent';
const SHEET_ID = 'xxxx'; // Google Sheet ID

function doPost(e) {
  const data = JSON.parse(e.postData.contents);
  const event = data.events[0];
  const userId = event.source.userId;
  const replyToken = event.replyToken;

  let state = getUserState(userId) || { step: 0 };

  if (event.type === 'message') {
    if (event.message.type === 'image' && state.step === 0) {
      const imageData = getLineImage(event.message.id);
      const base64Image = Utilities.base64Encode(imageData);

      // วิเคราะห์ก่อน
      const analysis = analyzeImage(base64Image);

      state.image = base64Image;
      state.step = 'wait_confirm_analysis';
      saveUserState(userId, state);

      // ส่งผลการวิเคราะห์ + Quick Reply
      sendMessage(replyToken, {
        type: 'text',
        text: `ผลการวิเคราะห์ภาพ:\n\n${analysis}\n\nต้องการแจ้งซ่อมหรือไม่?`,
        quickReply: {
          items: [
            {
              type: 'action',
              action: { type: 'message', label: 'แจ้งซ่อม', text: 'แจ้งซ่อม' }
            },
            {
              type: 'action',
              action: { type: 'message', label: 'ไม่แจ้งซ่อม', text: 'ไม่แจ้งซ่อม' }
            }
          ]
        }
      });

    } else if (event.message.type === 'text') {
      const text = event.message.text.trim();

      // หลังวิเคราะห์เสร็จ รอเลือกว่าจะซ่อมหรือไม่
      if (state.step === 'wait_confirm_analysis') {
        if (text === 'แจ้งซ่อม') {
          state.step = 1;
          saveUserState(userId, state);
          sendMessage(replyToken, { type: 'text', text: 'กรุณาพิมพ์ชื่อผู้แจ้ง:' });
        } else if (text === 'ไม่แจ้งซ่อม') {
          sendMessage(replyToken, { type: 'text', text: 'ยกเลิกการแจ้งซ่อม ✅' });
          clearUserState(userId);
        } else {
          sendMessage(replyToken, { type: 'text', text: 'กรุณาเลือก "แจ้งซ่อม" หรือ "ไม่แจ้งซ่อม"' });
        }

      } else if (state.step === 1) {
        state.name = text;
        state.step = 2;
        saveUserState(userId, state);
        sendMessage(replyToken, { type: 'text', text: 'กรุณาพิมพ์เบอร์โทร:' });

      } else if (state.step === 2) {
        state.phone = text;
        state.step = 3;
        saveUserState(userId, state);
        sendMessage(replyToken, { type: 'text', text: 'กรุณาพิมพ์สถานที่ซ่อม:' });

      } else if (state.step === 3) {
        state.location = text;
        state.step = 4;
        saveUserState(userId, state);

        const summary =
          `📋 สรุปข้อมูลแจ้งซ่อม:\n` +
          `ชื่อผู้แจ้ง: ${state.name}\n` +
          `เบอร์โทร: ${state.phone}\n` +
          `สถานที่: ${state.location}\n\n` +
          `พิมพ์ "ยืนยัน" เพื่อบันทึก หรือ "ยกเลิก" เพื่อยกเลิก`;

        sendMessage(replyToken, { type: 'text', text: summary });

      } else if (state.step === 4) {
        if (text === 'ยืนยัน') {
          logToSheet(state.name, state.phone, state.location);
          sendMessage(replyToken, { type: 'text', text: 'บันทึกการแจ้งซ่อมเรียบร้อย ✅' });
          clearUserState(userId);
        } else if (text === 'ยกเลิก') {
          sendMessage(replyToken, { type: 'text', text: 'ยกเลิกการแจ้งซ่อมแล้ว ❌' });
          clearUserState(userId);
        } else {
          sendMessage(replyToken, { type: 'text', text: 'กรุณาพิมพ์ "ยืนยัน" หรือ "ยกเลิก"' });
        }

      } else {
        sendMessage(replyToken, { type: 'text', text: 'กรุณาส่งภาพเพื่อเริ่มต้นแจ้งซ่อม' });
      }
    }
  }
}

// วิเคราะห์ภาพด้วย Gemini
function analyzeImage(base64Image) {
  const instruction = `
    คุณเป็นช่างผู้เชี่ยวชาญ
    วิเคราะห์ปัญหาจากภาพนี้ บอก:
    1. ปัญหาคืออะไร
    2. สาเหตุที่เป็นไปได้
    3. แนวทางแก้ไขทีละขั้นตอน
  `;

  const history = [{
    role: 'user',
    parts: [
      { text: instruction },
      { inlineData: { mimeType: 'image/jpeg', data: base64Image } }
    ]
  }];

  const payload = { contents: history, generationConfig: { maxOutputTokens: 1000 } };
  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload)
  };

  try {
    const response = UrlFetchApp.fetch(GEMINI_API_URL + '?key=' + GEMINI_API_KEY, options);
    const data = JSON.parse(response.getContentText());
    if (data.candidates && data.candidates.length > 0) {
      return data.candidates[0].content.parts.map(p => p.text).join('');
    }
    return 'ไม่สามารถวิเคราะห์ภาพได้';
  } catch (err) {
    return 'เกิดข้อผิดพลาดในการวิเคราะห์ภาพ';
  }
}

// ดึงรูปจาก LINE
function getLineImage(messageId) {
  const url = `https://api-data.line.me/v2/bot/message/${messageId}/content`;
  const options = {
    headers: { Authorization: 'Bearer ' + LINE_TOKEN, 'Content-Type': 'application/octet-stream' }
  };
  const response = UrlFetchApp.fetch(url, options);
  return response.getBlob().getBytes();
}


// ส่งข้อความไป LINE
function sendMessage(replyToken, message) {
  UrlFetchApp.fetch('https://api.line.me/v2/bot/message/reply', {
    method: 'post',
    contentType: 'application/json',
    headers: { Authorization: 'Bearer ' + LINE_TOKEN },
    payload: JSON.stringify({ replyToken, messages: [message] })
  });
}


// จัดการ State ผู้ใช้
function getUserState(userId) {
  const props = PropertiesService.getUserProperties();
  const state = props.getProperty(`state_${userId}`);
  return state ? JSON.parse(state) : null;
}

function saveUserState(userId, state) {
  PropertiesService.getUserProperties().setProperty(`state_${userId}`, JSON.stringify(state));
}

function clearUserState(userId) {
  PropertiesService.getUserProperties().deleteProperty(`state_${userId}`);
}


// บันทึกลง Google Sheets
function logToSheet(name, phone, location) {
  const sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName('Repairs');
  sheet.appendRow([new Date(), name, phone, location]);
}

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *