Skip to main content

ทดลองเขียน Edge/Chrome extension สำหรับอ่าน comment ใน Facebook live แบบไม่ใช้ API

·3 mins

พอดีวันนี้เป็นวันหยุดชดเชย ผมก็ไถ facebook feed ไปเรื่อยๆก็สังเกตได้ว่าบ้านเรานี่ live ขายของกันเยอะจริงๆ แถมหาวิธีสื่อสารในการขายกันได้สมบูรณ์ด้วยทั้งๆที่ facebook เองก็ไม่ได้ให้เครื่องมืออะไรนอกจากช่อง comment แต่แทบทุกคนที่ขายจะต้องมีทีมงานหลังบ้านคอยนับจำนวนคน cf หรือคนตอบลูกค้าอยู่ ผมเลยนึกเล่นๆว่ามันจะมีวิธีทำให้พ่อค้าแม่ค้าสามารถปิดการขายใน live ด้วยตัวคนเดียวได้ไหม

ปัญหา #

พ่อค้าแม่ค้าจะ live ถ้ามีลูกค้าจำนวนมากจะเกิดปัญหาอ่าน comment ไม่ทัน จดชื่อลูกค้าไม่ทัน อยากขายไปด้วยเก็บข้่อมูลไปด้วยคนเดียวดูไม่มีทางเป็นไปได้แน่นอน จำเป็นต้องหาคนหรือจ้างพนักงานมาช่วย

แนวทางแก้ปัญหา #

เขียน extension เพื่อช่วยรับ comment ใหม่ๆเข้ามาแล้ว พิจารณาด้วย regular expression ว่า message ที่ถูกพิมพ์ส่งมาตรงกับเงื่อนไขของพ่อค้าแม่ค้าไหม

สร้าง Extension กัน #

อ่านคู่มือก่อนทำอย่างอื่น!! #

ก่อนจะไปถึงจุดอื่นๆในบทความนี้ผมอยากให้ทุกๆท่านอ่านพื้นฐานการสร้าง extension สำหรับ browser กันก่อนนะครับจะได้เห็นภาพคร่าวๆกันก่อน

เริ่มต้นจากความว่างเปล่า #

  1. สร้าง index.js แล้วเขียน function ง่ายๆทิ้งไว้ก่อน
const hello = () => {
    console.log('Hello Edge');
}

hello();
  1. สร้าง manifest.json เพื่อกำหนดค่าพื้นฐานของ extension ตั้งชื่อ กำหนด match url ด้วยนะ
{
    "name": "Facebook Live Helper",
    "version": "0.1",
    "description": "Check customer name and price",
    "content_scripts": [
        {
            "matches": [
                "https://www.facebook.com/*/videos/*"
            ],
            "js": [
                "index.js"
            ]
        }
    ],
    "manifest_version": 2
}
create-facebook-live-extension-for-edge-or-chrome

ติดตั้ง Extension ใน browser แบบไม่ต้องผ่าน store #

  1. ไป settings / extension
create-facebook-live-extension-for-edge-or-chrome
  1. เปิด developer
create-facebook-live-extension-for-edge-or-chrome
  1. load unpacked เลือก folder ของ extension เราซะ
create-facebook-live-extension-for-edge-or-chrome
create-facebook-live-extension-for-edge-or-chrome
  1. ทดลองเข้า url เปิด live สักคลิปที่มี url match ของ manifest.json แบบนี้ "https://www.facebook.com/*/videos/*" แล้วดูว่าใน console มี Hello Edge อยู่ไหม
create-facebook-live-extension-for-edge-or-chrome

พิจารณาหน้า page ของ Facebook Live เพื่อหาว่าข้อมูลเรามาจากตรงไหน #

create-facebook-live-extension-for-edge-or-chrome
  1. ส่วนที่เราสนใจจะอยู่แค่ในกรอบสีแดง(ภาพด้านบน) เพราะนั่นคือส่วนของ comment ที่มีการ update ตลอดเวลา
  2. ในกรอบแดงจะประกอบด้วย <div> ที่มี class ชื่อว่า UFICommentActorAndBodySpacing ซึ่งจะครอบชื่อและข้อความของ comment เอาไว้
  3. ชื่อของผู้คอมเมนต์จะเป็น <a> ที่มี class ชื่อว่า UFICommentActorName
  4. comment จะอยู่ใน <span> ที่มี class ชื่อว่า UFICommentBody
  5. ส่วน pinned comment ให้สังเกตว่าจะมี <span> ที่มี class ชื่อว่า UFILivePinnedCommentLabel แปะอยู่ด้านบน
  6. วิธีดึงข้อมูลของเราก็คือจะวน loop ดึงเอาข้อมูลจาก .UFICommentActorAndBodySpacing ทุกๆ 0.5 วินาที โดยเราจะเลือกเอาตัวที่อยู่ล่างสุดหรือตัวสุดท้ายใน NodeList เพราะมันคือข้อความล่าสุด แต่หากมี pinned comment เราจะเลือกตัวรองสุดท้ายแทน
create-facebook-live-extension-for-edge-or-chrome

ได้เวลาลงไม้ลงมือกันล่ะครับ #

  1. เนื่องจาก facebook ใช้ javascript render ตัว element ทำให้ element บางตัวจะถูกแสดงขึ้นมาหลังจาก load เสร็จดังนั้นเราต้องเขียน function ที่จะรอทำงานก็ต่อเมื่อมี element ที่เราต้องการปรากฏขึ้นมาก่อน เพื่อไม่ให้เสียเวลาผมก็จะขอหยิบเอา function elementReady ของคุณ Joshua Wilson มาใช้เลยละกัน
Gist ของ function elementReady #

jwilson8767/es6-element-ready.js

เมื่อเข้าใจที่มาที่ไปแล้วเราก็หยิบเอา function แปะลงไปที่ index.js เลย

const elementReady = (selector) => {
    return new Promise((resolve, reject) => {
      let el = document.querySelector(selector);
      if (el) {resolve(el);}
      new MutationObserver((mutationRecords, observer) => {
        // Query for elements matching the specified selector
        Array.from(document.querySelectorAll(selector)).forEach((element) => {
          resolve(element);
          //Once we have resolved we don't need the observer anymore.
          observer.disconnect();
        });
      })
        .observe(document.documentElement, {
          childList: true,
          subtree: true
        });
    });
}
  1. เขียน function ไล่จับ comment โดยผมก็จะไล่อ่านไปตาม node ที่มีชื่อ class ตามที่ผมพูดถึงในหัวข้อก่อนหน้า ส่วนนี้ผมจะขอเขียนเนื้อหาใน comment ของ code แทนนะครับ
let commentSet = new Set();
let temporarySize = 0;

const commentReader = () => {
    //Pinned comment node
    const pinnedNodes = document.querySelectorAll('.UFILivePinnedCommentLabel');

    //Comment nodes
    const commentNodes = document.querySelectorAll('.UFICommentActorAndBodySpacing');

    //ถ้ามี pinned ให้ถอยมาพิจารณา comment รองสุดท้าย แต่ถ้าไม่มีก็เอาอันสุดท้ายเลย
    const index = (pinnedNodes.length > 0 ? commentNodes.length - 2 : commentNodes.length - 1);

    //ถ้า index แล้วก็เรียก comment node ออกมา
    const commentNode = commentNodes[index];

    //ดึง node ของชื่อและข้อความใน comment
    const customerNameNode = commentNode.querySelector('.UFICommentActorName');
    const commentMessageNode = commentNode.querySelector('.UFICommentBody');

    if (customerNameNode) {
        //ดึงข้อมูลออกมาด้วย innerText และ href ในกรณีที่เป็น facebook profile url
        const name = customerNameNode.innerText;
        const url = customerNameNode.href;
        const message = commentMessageNode.innerText;

        //เก็บไว้ใน comment object
        const comment = { name, message, url }

        //พิจารณาว่า message มันมีตัวเลขอยู่ไหม ถ้ามีถือว่าลูกค้าสนใจ
        //ตรงนี้แก้ไขไปตามเงื่อนไขที่ต้องการได้เลย
        if (/\d+/.test(message.toLowerCase())) {

            //บันทึก comment object ลงใน set เพราะมันจัดการข้อมูลซ้ำให้ได้เองในตัว
            //แต่ต้องเปลี่ยนมันเป็น string กันก่อน ตอนนี้ก็ถือว่าเป็น Set ของ string แล้ว
            commentSet.add(JSON.stringify(comment));

            //add ใส่ set ไปแล้วจำนวนมันมากขึ้นจากเดิมไหม ถ้ามากขึ้นแปลว่ามี comment ใหม่เข้ามา
            if (commentSet.size > temporarySize) {

                //ปรับจำนวนของ size ว่าเปลี่ยนไปแล้วนะ จะได้เอาไว้เทียบกับอีก 500ms ต่อไป
                temporarySize = commentSet.size;
                
                //เรียบร้อยแล้วก็ log ออกมาดูหน่อยว่าได้ผลไหม แต่อันนี้ขอ censor หน่อยเผื่อตอนทำ screenshot จะได้ไม่โดนฟ้อง :3
                console.log({
                    name: `${comment.name.substring(0,4)}...(censor)`,
                    profileUrl: `${comment.url.substring(0,44)}...(censor)`,
                    message: comment.message,
                })
            }
        }
    }

    // วนใหม่อีกใน 0.5 วินาที
    setTimeout(commentReader, 500);
}

สรุปผลการทดลอง #

create-facebook-live-extension-for-edge-or-chrome
  1. ใน console ก็จะแสดงรายชื่อ profile url พร้อมกับ message ที่เรา filter แล้วอย่างสวยงาม
  2. ถึงแม้จะเห็นผลดี แต่ยังไม่เคยลองกับ page ใหญ่ๆที่มี comment เข้ามารัวๆตรงนี้ก็ยังการันตีไม่ได้ชัดเจนนักว่าจะตกหล่นไหม อาจจะปรับรอบการวนให้ถี่ขึ้นได้ แต่ก็ได้ข้อความตามที่แสดงขึ้นมาใหม่ในหน้า web เท่านั้น ไม่รู้ว่า facebook เองจะส่งมาให้เราทั้งหมดหรือเปล่า
  3. ต้องเปิดตั้งแต่เริ่ม live เพราะถ้าเปิดทีหลัง คนก่อนหน้าก็จะตกหล่น
  4. ถ้าเน็ตหลุดหรือเผลอ refresh ตัวนี้จะนับใหม่เลย ข้อมูลก็หายไป
  5. ไม่สามารถดึงข้อมูลก่อนหน้ามาได้

แนวทางการพัฒนาต่อ #

  1. เขียน function ดึง comment บนๆมาด้วยตอนที่โหลดครั้งแรก
  2. save เป็นไฟล์ออกมาก่อนได้ ป้องกันปัญหาเวลาเน็ตหลุดหรือเผลอปิด
  3. เพิ่ม field สำหรับกำหนด pattern ของ message เพราะการขายแต่ละอย่างจะเปลี่ยนตามราคาหรือชื่อไปเรื่ือยๆ
  4. ถ้ามี stock ก็ใส่จำนวนแล้วค่อยๆหัก stock ไปก็ได้
  5. เปลี่ยนไปใช้ Facebook Live Comments API

ตัวอย่าง code #

Github: facebook-live-helper-for-edge

จบไปแล้วครับสำหรับการทดลองง่ายๆในวันนี้ เชื่อว่าหลายๆท่านน่าจะเห็นช่องทางในการพัฒนาต่อได้นะครับ สำหรับท่านไหนที่มีข้อเสนอแนะสามารถบอกผมได้เลยครับ ผมยินดีมากที่จะได้ปรับปรุงครับ สำหรับท่านใดที่มีคำถามสามารถสอบถามเข้ามาที่ Inbox ของ Facebook Page ได้เลยนะครับ