สร้าง Formula สำหรับติดตามพัสดุของไปรษณีย์ไทยใน Google Sheets กันเถอะ

Cover image

เมื่อไปรษณีย์ไทยเปิด API ให้เราเข้าไปดึงข้อมูลพัสดุได้แล้ว วันนี้เรามาลองเขียน Fomular สำหรับดึงเอาสถานะของพัสดุไปรษณีย์ไทยมาอยู่ใน Google Sheets กันดีกว่า

=ThailandPostTrack("หมายเลข EMS", "recent")

บอกไว้ก่อน

ก่อนจะเข้าเนื้อหาผมอยากให้ทุกคนศึกษาพื้นฐานของสิ่งเหล่านี้ก่อนนะครับ

ทำความรู้จัก Thailand Post API

เรียกได้ว่าสิ้นสุดการรอคอยกับ API ที่นักพัฒนาชาวไทยตั้งหน้าตั้งตารอมากที่สุด ตอนนี้ไปรษณีย์ไทยก็เปิดให้ทุกคนเข้าไปใช้งานได้แล้วที่นี่ครับ https://track.thailandpost.co.th

Thailand Post API

เริ่มจากสมัครสมาชิก

เข้าไปที่ https://track.thailandpost.co.th/register แล้วเลือกประเภทของการใช้งานของคุณเลยครับ สำหรับผมคือ "ลูกค้าทั่วไป" เพราะเราเอามาใช้ส่วนตัวทดลองอะไรเล็กๆน้อยๆเท่านั้นเอง ส่วนนี้ผมจะขอข้ามนะครับเพราะกรอกฟอร์มตามปกติไม่มีอะไรซับซ้อนทุกคนน่าจะเข้าใจกันอยู่แล้ว

สมัครสมาชิก

มีอะไรให้ใช้บ้าง

หลังจากสมัครสมาชิกเรียบร้อยแล้วให้ทำการ "เข้าสู่ระบบ" แล้วคลิกที่เมนูด้านซ้ายที่เขียนว่า "สำหรับนักพัฒนา"

มีอะไรให้ใช้บ้าง

ภายในนี้จะมีหัวข้อหลักๆ 2 หัวข้อดังนี้ครับ

  • API เป็นส่วนที่เราจะใช้ครับ เพราะเราจะใช้ Google Apps Script ยิง request มาที่นี่เพื่อขอข้อมูลสถานะพัสดุ
  • Webhook อันนี้ผมไม่ได้ใช้แต่จะขออธิบายให้เข้าใจง่ายๆว่ามันเป็นการที่เราบอกไปรษณีย์ว่าถ้ามีข้อมูลเปลี่ยนแปลงให้ทำการยิงไปที่ url ที่เรากำหนดไว้ด้วยนะ ตัวนี้จะมีประโยชน์ตรงที่เราไม่ต้องคอยยิงไปถามเรื่อยๆเพื่อตรวจสอบว่ามีอะไร update ไหม ไปรษณีย์จะเป็นคนบอกเราเองเวลามีอะไร update เหมาะกับการเอาไปทำ chatbot ที่มัน Notify ผู้ใช้งานว่าของไปถึงไหนแล้ว

ก่อนใช้ API ต้องสร้าง Token

กดที่ "Create Token" หลังจากนั้นกด "สร้าง" อีกทีครับเพียงเท่านี้เราก็จะใช้ Token ชุดแรกเอาไว้ไปใช้งาน API แล้ว สำหรับใครที่สร้างไปแล้วไม่จำเป็นต้องสร้างใหม่อีกนะครับ เพียงแค่กด "Create Token" เว็บจะแสดง Current Token ให้ด้วย

ก่อนใช้ API ต้องสร้าง Token ก่อน

ใครได้แล้วก็อย่าลืม copy ตัว token เอาไว้นะครับ ของผมก็จะเป็นตัวนี้

ZfPSL:MzEfEmTdMaGTSoZPJLDYE-KE[email protected]HOUT

ต่อไปก็คลิกที่ "เอกสารสำหรับการพัฒนา" หรือเข้าไปที่ url https://track.thailandpost.co.th/developerGuide ก็ได้เช่นกันครับ

API มีอะไรบ้าง

ใน API Documentation จะมีอธิบายทุกอย่างเกี่ยวกับ API เอาไว้นะครับตั้งแต่ความหมายของ Status Item การเรียกใช้แบบต่างๆ ทั้ง SOAP และ REST ไปจนถึงการใช้งาน Web hook นะครับ แต่เพื่อไม่ให้บทความนี้ยาวเกินไปเราจะดูเฉพาะส่วนที่เราจะใช้คือ API REST นะครับ

API มีอะไรบ้าง

ภายใน REST ก็จะประกอบด้วย endpoint หลักๆ 3 ตัวดังนี้ครับ

  • GetToken ใช้สำหรับขอ AccessToken อีกชุดเพื่อใช้ใน GetItems และ ReqiestItems แต่ว่าการขอต้องใช้ Token ที่เรากดมาเมื่อกี้นี้ไปขอด้วยนะครับ ตัวนี้มีอายุ 1 เดือน หมดอายุก็ต้องขอใหม่
  • GetItems ใช้ GET ข้อมูลพัสดุแต่ครั้งละไม่เกิน 100 ชิ้นครับ API จะ return กลับมาให้ใน HTTPResponse เลย ซึ่งเราจะใช้ตัวนี้
  • RequestItems ใช้ GET ข้อมูลพัสดุเหมือนกันแต่สำหรับจำนวนมากๆ 100 ชิ้นขึ้นไปครับ ตัวนี้ API จะส่ง link ของไฟล์ข้อมูลไปให้ใน Email แทนนะครับ

สรุปเรื่อง Token อีกรอบเผื่องง

  1. ให้ไป Create Token ในหน้าเว็บแล้วจะได้ Token มา 1 ชุด คือ Static Token
  2. นำ Static Token ไปขอ Token ใหม่ที่ endpoint GetToken จะได้ AccessToken ใหม่อีกชุดมีอายุ 1 เดือน
  3. นำ AccessToken ไปใช้ขอ GetItems

สร้าง GAS Project

เตรียม Apps Script Starter

เริ่มจากการ clone Apps Script Starter แล้วก็ติดตั้ง dependency package ก่อนเลยครับ

git clone https://github.com/labnol/apps-script-starter thailand-post-tracker
cd thailand-post-tracker
npm install

สร้าง Project บน Apps Script บ้าง โดยเลือก type เป็น sheets นะครับ

npx clasp create --type sheets --title "thailand-post-tracker" --rootDir ./dist

เตรียม sheets ให้สำหรับทำงาน

เปิด sheets ชื่อ "thailand-post-tracker" ใน Google Drive ขึ้นมา แล้วสร้าง sheet ใหม่ขึ้นมาชื่อ "Token" เผื่อเอาไว้เก็บ Token ที่เราจะใช้กันนะครับ

เตรียม sheets ให้สำหรับทำงาน 1

เพื่อความสะดวกในการอ้างอิงถึง cell ผมจะทำการตั้งชื่อให้มันซะเลย โดยคลิกขวาที่ cell แล้วเลือก Define named range

เตรียม sheets ให้สำหรับทำงาน 2

ผมตั้งไว้ 2 cells คือ StaticToken และ AccessToken ตามภาพนะครับเป็น B1 กับ B2

เตรียม sheets ให้สำหรับทำงาน 3

สร้างส่วนติดต่อกับ API ใน Google Apps Script

ผมจะเขียนส่วนของ service แยกออกมาครับ เริ่มจาก getToken กันก่อนเลย

src/service/index.js

const url = 'https://trackapi.thailandpost.co.th/post/api/v1';

export default {
  getToken(staticToken) {
    // เตรียม option ในการส่งตาม API Documentation
    const options = {
      method: 'POST',
      contentType: 'application/json',
      //กำหนด headers ตามที่ document บอกให้ครบด้วยนะครับ
      headers: {
        Authorization: `Token ${staticToken}`,
        'Content-Type': 'application/json'
      },
      validateHttpsCertificates: false //เนื่องจาก https ของ API ยังไม่ certify เราจะ skip มันซะ
    };

    //fecth ไปยัง API ของ Thailand Post 
    const data = UrlFetchApp.fetch(`${url}/authenticate/token`, options);

    //ได้ token ไปใช้ต่อ
    const { token } = JSON.parse(data);
    return token;
  }
};

สร้างเมนูสำหรับเพื่อความสะดวกในการขอ AccessToken

ที่ src/index.js ผมสร้างเมนูไว้ใน onOpen() ครับเพราะมันจะถูก call ทันทีตอนที่เปิด sheet ส่วน getNewAccessToken() ตรงนี้ผมจะเรียก cell จาก Named range ที่ตั้งชื่อเอาไว้นะครับ ซึ่งผมจะใช้ staticToken ที่ได้มาจาก sheet ส่งไปขอ ThailandPost.getToken() แล้วหลังจากนั้นค่อยเอาไปเขียนที่ cell ของ AccessToken

src/index.js

import ThailandPost from './service';

const onOpen = () => {
  const ui = SpreadsheetApp.getUi();

  //สร้าง Menu ชื่อ Thailand Post Tracker บน Sheet
  //มี Submenu ชื่อ Renew AccessToken เมื่อคลิกแล้วจะ cell getNewAccessToken()
  ui.createMenu('Thailand Post Tracker')
    .addItem('Renew AccessToken', 'getNewAccessToken')
    .addToUi();
};

const getNewAccessToken = () => {
  const activeSheet = SpreadsheetApp.getActiveSpreadsheet();

  //อ้างอิงถึง cell Static Token และ Access Token ใน Sheet
  const staticTokenCell = activeSheet.getRangeByName('StaticToken');
  const accessTokenCell = activeSheet.getRangeByName('AccessToken');

  //ดึงเอา static token ออกมาจาก cell
  const staticToken = staticTokenCell.getValue();

  //ทำการ request ขอ getToken จาก API
  const accessToken = ThailandPost.getToken(staticToken);

  //บันทึก accessToken ที่ได้ลงบน cell เพื่อใช้ต่อในอนาคต (ก็มันใช้ได้ตั้งเดือนนึง)
  accessTokenCell.setValue(accessToken);
};

//อย่าลืมประกาศเป็น global ด้วย
global.onOpen = onOpen;
global.getNewAccessToken = getNewAccessToken;

เรียบร้อยแล้วก็ save หลังจากนั้นก็

npm run deploy

ลอง Refresh หน้า Sheet แล้วจะมีเมนูใหม่ชื่อ "Thailand Post Tracker" โผล่ขึ้นมา ลองคลิก "Renew AccessToken" เราจะเห็นว่ามี AccessToken ถูกเขียนลงไปในช่องว่าง

ทดลองใช้ getToken

สร้าง Formula สำหรับ GetItems

ในที่สุดก็มาถึงส่วนที่เรารอคอยที่ไฟล์ /src/service/index.js ผมเพิ่ม getItems() เข้ามาโดย payload ผมจะตั้งตายตัวเป็นภาษาไทยและดึงเอาข้อมูลมาทั้งหมดครับ

src/service/index.js

  getItems(accessToken, barcode) {
    const options = {
      //กำหนดข้อมูลตาม API Documentations
      method: 'POST',
      contentType: 'application/json',
      headers: {
        Authorization: `Token ${accessToken}`,
        'Content-Type': 'application/json'
      },
      validateHttpsCertificates: false,
      //payload คือ body ตาม document นะครับ
      //อย่าลืมเปลี่ยนเป็น json string ด้วยล่ะ
      payload: JSON.stringify({
        status: 'all',
        language: 'TH',
        barcode: [barcode]
      })
    };
    const data = UrlFetchApp.fetch(`${url}/track`, options);
    const { response } = JSON.parse(data);

    //return กลับไปเฉพาะ items เพราะเราอยากได้แค่สถานะ
    return response.items[barcode];
  }

เสร็จแล้วเราก็มาเขียน Formula สำหรับ Sheet ที่ src/index.js ครับ ผมก็เพิ่ม ThailandPostTrack() คำสั่งนี้จะรับเอา barcode และ type ในการแสดงข้อมูลดังนี้ครับ

  • recent แสดงข้อมูลพัสดุล่าสุด
  • first แสดงข้อมูลชุดแรกของพัสดุ
  • all แสดงข้อมูลทั้งหมดตั้งแต่เริ่มส่งจนถึงปลายทาง
const ThailandPostTrack = (barcode, type) => {
  //อ้างอิงถึง spreadsheet และ accessToken ที่เก็บไว้
  const activeSheet = SpreadsheetApp.getActiveSpreadsheet();
  const accessToken = activeSheet.getRangeByName('AccessToken').getValue();
  //getItems ด้วย accessToken และ barcode ที่ได้มาจาก input parameter
  const items = ThailandPost.getItems(accessToken, barcode);

  //เปลี่ยน items ให้เหลือแค่ค่าที่เราอย่างได้ ผมอยากได้แค่ สถานะ, วันเวลา และสถานที่
  //ที่ใส่ Array เพราะผมต้องการแสดงผลหลาย cells
  const results = items.map(item => [item.status_description, item.status_date, item.location]);

  if (results.length === 0) {
      return 'ไม่พบข้อมูล';
  }

  //กำหนดการ return จากรูปแบบการแสดงผล
  //ผมใช่้ Array ครอบตลอดเพราะต้องการให้แสดงผลแนวนอนไม่ใช่แนวตั้ง
  switch (type) {
    case 'recent':
      return [results[results.length - 1]];
    case 'first':
      return [results[0]];
    case 'all':
      return results;
    default:
      return [results[results.length - 1]];
  }
};

//เหมือนเดิมเลย อย่าลิมประกาศเป็น global ด้วยนะครับ
global.ThailandPostTrack = ThailandPostTrack;

จากนั้นก็ save แล้ว

npm run deploy

เรียกสูตร =ThailandPostTrack() จริงๆบ้าง

ถ้า sheet Token มีข้อมูลครบอยู่แล้ว เราก็จะได้ accessToken มาใช้เรียก GetItems ได้แล้ว

=ThailandPostTrack("หมายเลขพัสดุ", "รูปแบบการแสดงผล")
//type: recent, first, all

เรียกสูตร =ThailandPostTrack()

Source Code

thailand-post-tracker-gas

เรียบร้อยแล้วครับ หวังว่าบทความนี้จะทำให้หลายๆคนเห็นภาพการเขียน Google Apps Script เพื่อใช้ call API แล้วจัดการ response ที่ได้มาเพื่อเอามาแสดงบน sheet ของเราเอง จริงๆส่วนนี้เราสามารถต่อยอดเป็นการทำ Library หรือทำเป็น Add-On ก็ได้นะครับ แต่เพื่อไม่ให้ยาวเกินไปผมจะขอยกไปเขียนในบทความต่อๆไปแทนนะครับ เหมือนเดิมครับท่านใดมีคำถามสามารถสอบถามเข้ามาที่ Inbox ของ Facebook Page ได้เลยนะครับ

บทความใกล้เคียง