การสร้าง Tinder บอทด้วย AI

เนื่องจากช่วงนี้ผมโดนทักเรื่องที่ผมโสดจากพี่จากที่ทำงาน
ตอนแรกก็คิดว่าไม่เป็นอะไร เพราะไม่เคยรู้สึกว่าชีวิตตัวเองขาดอะไรไปเลย
แต่พอโดนทักบ่อย ๆ เข้าก็เลยคิดว่ามันคงเป็นปัญหาจริง ๆ ละ
แต่จะหาคนคุยสักคนมันก็ไม่ใช่เรื่องง่าย
เพราะว่าผมเองไม่ใช้เฟสบุค ไม่อยากให้มันใช้ข้อมูลตัวเราไปขาย
วันหยุดก็อ่านหนังสือ ค้นคว้าหาความรู้ใส่ตัว ออกไปปั่นจักรยานบ้างให้หุ่นยังฟิต
นอนหลับไปพร้อม ๆ ดงหนังสือข้างเตียง
ไม่มีโอกาสได้พบเจอคนใหม่ ๆ ที่จะพัฒนาความสัมพันธ์ต่อได้
ผมควรต้องทำอะไรสักอย่างเพื่อแก้ปัญหานี้
แอพที่ช่วยให้เจออื่นได้ที่นิยมมาก ๆ ก็คือแอพ Tinder นั่นเอง
แต่การที่ต้องปัดไปเรื่อย ๆ เพื่อหาคนแมตช์ มันค่อนข้างใช้เวลา
ผมเองอยากใช้เวลาไปทำอย่างอื่นมากกว่า
ถ้าจะปัดขวาให้หมดแล้วไม่ได้คุยก็แอพเห็นใจคนที่ไลค์เราอย่างตั้งใจ
ก็เลยคิดว่าน่าจะเป็นไปที่จะใช้ปัญญาประดิษฐ์ (AI) เข้ามาช่วยปัดขวาให้คนที่เราน่าจะชอบ

การเลือก AI ให้เหมาะกับงาน
เลือกผู้หญิงว่ายากแล้วเลือก AI นั้นยากกว่า
เพราะว่า AI มันมีหลากหลายแขนง การนำมาประยุกต์ใช้นั้นต้องอาศัยความเข้าใจ
ในเรื่องงานนั้น ๆ วิเคราะห์ความผิดพลาดที่เรายอมรับได้
ความยากง่ายในการพัฒนาก็เป็นด้วย
โดยส่วนตัวผมชอบ Reinforcement-learning (RL) เพราะว่าให้มันค่อยๆ เรียนรู้ของมันเองไปเรื่อย ๆ คล้ายเล่นเกมโดยไม่ต้องบอกกฎกติกา ทำถูกก็ได้แต้มเยอะ
ให้มันลองเล่นไปเรื่อย ๆ ไม่ต้องเตรียมข้อมูลจำนวนมาก
ว่าแบบไหนที่ผมชอบ แบบไหนที่ผมไม่ชอบ
แต่ปัญหาของ RL ก็คือจะให้คะแนนเป็นอะไรดี เพราะ Tinder ไม่ใช่เกม
ถ้าจะคิดวิธีให้คะแนนเองผมก็ต้องเป็นคนให้คนแนนว่าคนที่แมตช์มาตรงใจแค่ไหน
ซึ่งตัวผมเองก็จะเป็นคอขวดในการพัฒนาบอทนี้ และการปล่อยให้มันเรียนรู้เองก็ใช้เวลา ต้องสร้างสภาพแวดล้อมเสมือนให้มันลองด้วย ไม่งั้นปล่อยมันไปลองของจริงเลยก็จะมั่วไปหมด RL จึงไม่ค่อยเหมาะสักเท่าไหร่กับงานนี้
ถ้าเป็น Convolutional neural network (CNN) ก็จะเหมาะกับงานนี้มาก ๆ เพราะคนใช้ Tinder ใส่แค่รูปภาพก็พอ ไม่ต้องใส่ข้อมูลอื่นก็ได้ แต่ก็มีข้อเสียคือต้องเตรียมข้อมูลดี ๆ ในที่นี้ก็จะเป็นรูปสาว ๆ ทั้งที่ผมชอบและไม่ชอบจำนวนมาก
ซึ่งก็ต้องใช้เวลา แต่ก็น่าจะพอทำได้
ถ้าจะใช้เทคโนโลยีสำเร็จรูปเลยอย่างเช่น Google Vision API ก็จะง่ายมาก ๆ แต่อาจจะไม่ได้ผลดีเท่าที่ควรเท่าจากผมตัดสินใจเอง แต่ข้อมูล Label พวกความสวย รอยยิ้ม ทรงผม สกัดจากภาพก็เอามาช่วยตัดสินใจได้เยอะ ใช้เวลาในการพัฒนาน้อยมาก ๆ ราคาก็ไม่แพงมากแค่ $1.5 ต่อ 1,000 รูป ใช้ฟรี 1,000 รูปต่อเดือน
ในเบื้องต้นผมคิดว่าจะใช้วิธีนี้ไปก่อน
เพราะต้องการทำ MVP ให้ออกมาลองใช้ได้ก่อน ตามแบบฉบับ Lean Startup
แล้วถ้าอยากได้ดีกว่านี้ค่อยเทรนเอง

ขั้นตอนในการพัฒนา
บอทที่ผมพัฒนาจะออกมาอยู่ในรูปแบบ Chrome Extension เพราะพัฒนาใช้กับเว็บที่เดียวไม่ต้องไปทำแอพแยกบนทั้ง Android และ iOS โดยจะตั้งชื่อบอทว่า Swipe Right ว่ามีการพัฒนาดังนี้

  • สมัครบัญชี Google Cloud และเปิดใช้งาน Vision API ที่นี่ https://console.cloud.google.com/apis/library/vision.googleapis.com/
  • สร้าง API Key สำหรับให้ Extension เรียกใช้งาน API ได้ที่ https://console.cloud.google.com/apis/credentials และก๊อปเก็บเอาไว้
  • สร้างโฟลเดอร์ของโปรเจคชื่อ swipe-right-bot
  • สร้างไฟล์ manifest.json เพื่อบอกว่า extension รายละเอียดเกี่ยวกับบอทเรา เช่นชื่ออะไร เริ่มทำงานเมื่อเข้าไปอยู่บนหน้า https://tinder.com/app/recs เท่านั้น เข้าไปเรียกข้อมูลจาก Vision API ได้ที่ https://vision.googleapis.com/* โดยใส่ข้อมูลดังนี้
{
"manifest_version": 2,
"name": "Swipe Right - Auto like for Tinder",
"description": "Auto like for Tinder using AI",
"version": "1.0.0",
"icons": {
"128": "icon.png"
},
"permissions": [
"storage",
"https://tinder.com/*",
"https://vision.googleapis.com/*"
],
"content_scripts": [
{
"matches": [
"https://tinder.com/app/recs"
],
"js": [
"content.js"
]
}
]
}
  • สร้างไฟล์ content.js เพื่อให้มันทำงานเมื่อเราเข้าเว็บ Tinder โดยจะโหลด API Key จากที่ แล้วให้มันวนลูปอ่านข้อมูลรูปโปรไฟล์ของคนที่เราจะต้องปัดจากบนหน้าเว็บทุกๆ 5 วินาที จากการ Inspect บน Chrome จะพบว่ารูปโปรไฟล์จะมีกล่องที่มี CSS class ชื่อ StretchedBox::a อยู่ เราก็ตั้งหารูปโปรไฟล์จากตรงนี้ แต่มันจะมีสองรูปคือคนที่อยู่ข้างบนกับคนที่อยู่ด้านล่าง เราใช้รูปคนที่อยู่ด้านบนจะใช้ Index ที่ 1
let apiKey = 'AIzaSnbso13jdbm19drnhdi-888888888888';
function mainLoop() {
setTimeout(function () {
let imageNode = document.querySelectorAll('.StretchedBox\\:\\:a')[1];
if (imageNode) {
let imgUrl = imageNode.style.backgroundImage;
imgUrl = imgUrl.substring(
imgUrl.indexOf("\"") + 1,
imgUrl.lastIndexOf("\"")
);
processImage(apiKey, imgUrl);
} else {
console.log('Not found profile image, please refresh the page.');
}
mainLoop();
}, 5000)
}
mainLoop();
  • เพิ่ม function ปัดซ้ายกับ ปัดขวา ที่ content.js เพื่อให้โปรแกรมหลักเรียก โดยจะกดที่ปุ่มที่มี aria-label เป็น Nope กับ Like ดังนี้
function swipeLeft() {
let bnt = document.querySelector("button[aria-label='Nope']");
if (bnt) {
bnt.click();
}
}
function swipeRight() {
let bnt = document.querySelector("button[aria-label='Like']");
if (bnt) {
bnt.click();
}
}
  • เพิ่ม function ที่ใช้ในการประมวลผลรูปภาพ โดยจะส่ง URL ของที่ได้ไปให้ Google API โดยจะใช้ LABEL_DETECTION เพื่อดึงของมูล label ต่างๆ ที่พบบนรูปภาพ ในที่นี้จะได้ API อันเดียวเพราะใช้เยอะมันคิดตังค์เยอะ ดังนี้
function processImage(apiKey, profileImageURL) {
let data = {
requests: [
{
image: {
source: {
imageUri: profileImageURL
}
},
features: [
{
type: "LABEL_DETECTION"
}
]
}
]
};
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
// TODO Add like or nope logic
} else if (this.readyState === 4 && this.status !== 200) {
console.log(this.status);
console.log(this.responseText);
swipeLeft();
}
});
xhr.open("POST", "https://vision.googleapis.com/v1/images:annotate?key=" + apiKey);
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.setRequestHeader("cache-control", "no-cache");
xhr.send(JSON.stringify(data));
}
  • ใส่ตรรกะในการเลือกว่าจะปักซ้ายหรือขวาตามที่ตัวเองต้องการ โดยทดลองอัพรูปของเราชอบหรือไม่ชอบไปที่ https://cloud.google.com/vision/ ยกตัวอย่างผมเองก็จะเลือกก่อนว่าเป็นผู้หญิง สวย ยิ้มสวย ทรงผมดี หรือเลือกปัดซ้ายถ้ามีอะไรที่เราไม่ชอบเช่น selfie duck face ชุดชั้นใน ไอดอลญี่ปุ่น เพราะส่วนมากไม่ไช่รูปตัวเอง ค่าความมั่นใจของ Vision API ของแต่ละ label จะอยู่ที่ 0 ถึง 1 เช่น 0.9 หมายถึงมั่นใจมากๆ ใช้ในการตัดสินใจที่ซับซ้อนได้ เพิ่มการปัดขวาให้คนที่รูปไม่ผ่านอยู่เล็กน้อยที่ 3.14159 % ให้โอกาสพายลิขิตทำนายกัน
ตัวอย่างการเลือก label บน https://cloud.google.com/vision/
  • จะได้โค้ดทั้งหมดที่ใช้ในการประมวลผลรูปภาพดังนี้
function processImage(apiKey, profileImageURL) {
let data = {
requests: [
{
image: {
source: {
imageUri: profileImageURL
}
},
features: [
{
type: "LABEL_DETECTION"
}
]
}
]
};
let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4 && this.status === 200) {
let res = JSON.parse(this.responseText);
let labels = res["responses"][0]["labelAnnotations"];
if (labels === undefined) {
swipeLeft();
return;
}
let labelMap = {};
for (let i = 0; i < labels.length; i++) {
labelMap[labels[i]["description"]] = labels[i]["score"];
}
console.log(labelMap);
if ("girl" in labelMap && labelMap["girl"] >= 0.6) {
if ("gravure idol" in labelMap || "japanese idol" in labelMap) {
// I don't like it.
swipeLeft();
} else if ("selfie" in labelMap) {
// I don't like it.
swipeLeft();
} else if ("brassiere" in labelMap || "lingerie" in labelMap || "undergarment" in labelMap) {
// I don't like it.
swipeLeft();
} else if ("beauty" in labelMap && labelMap["beauty"] >= 0.9) {
// Definitely swipe right. Never miss this.
swipeRight();
} else if ("beauty" in labelMap && labelMap["beauty"] >= 0.8 &&
"smile" in labelMap && labelMap["smile"] >= 0.8) {
// smile!
swipeRight();
} else if ("beauty" in labelMap && labelMap["beauty"] >= 0.8 &&
"hairstyle" in labelMap && labelMap["hairstyle"] >= 0.8) {
// hairstyle!
swipeRight();
} else if ("lady" in labelMap && labelMap["lady"] >= 0.8) {
// lady!
swipeRight();
} else {
if (Math.random * 100 <= Math.PI) {
swipeRight();
} else {
swipeLeft();
}
}
} else {
swipeLeft();
}
} else if (this.readyState === 4 && this.status !== 200) {
console.log(this.status);
console.log(this.responseText);
swipeLeft();
}
});
xhr.open("POST", "https://vision.googleapis.com/v1/images:annotate?key=" + apiKey);
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.setRequestHeader("cache-control", "no-cache");
xhr.send(JSON.stringify(data));}let apiKey = 'AIzaSnbso13jdbm19drnhdi-888888888888';
function mainLoop() {
setTimeout(function () {
let imageNode = document.querySelectorAll('.StretchedBox\\:\\:a')[1];
if (imageNode) {
let imgUrl = imageNode.style.backgroundImage;
imgUrl = imgUrl.substring(
imgUrl.indexOf("\"") + 1,
imgUrl.lastIndexOf("\"")
);
processImage(apiKey, imgUrl);
} else {
console.log('Not found profile image, please refresh the page.');
}
mainLoop();
}, 5000)
}
mainLoop();
  • หาไอคอนมาใส่ตั้งชื่อไฟล์ว่า icon.png
  • เข้า Google Chrome chrome://extensions/ เปิด Developer mode แล้วคลิ๊ก Load unpacked
  • เข้า Tinder แล้วไปที่ https://tinder.com/app/recs ถ้ามันใช้งานได้จะเป็นดังนี้

ผลลัพธ์ที่ได้ก็ค่อนข้างเป็นที่น่าพอใจ ไม่ได้ปัดขวาให้พร่ำเพรื่อ น่าจะสามารถเปลี่ยนเป็นโมเดลที่ผมเทรนเองในอนาคตเพื่อให้ผลลัพธ์ตรงใจมากที่สุด น่าจะเป็นไอเดียให้ใครหลาย ๆ คนเข้ามาลองศึกษา AI นำมาประยุกต์ใช้ในชีวิตประจำวันได้ และหวังว่าผมคงได้ใช้ Tinder ได้เร็วนี้เช่นกัน 55+

สำหรับโค้ดทั้งหมดจะอยู่บน Github ที่นี่
https://github.com/artiya4u/swipe-right-bot

ตอนนี้ดาวนโหลดได้แล้วที่ Chrome Web Store
https://chrome.google.com/webstore/detail/swipe-right-auto-like-for/kieidchojiibkccheopboengbgnapmfm

--

--

Programmer • Innovator • Photographer https://www.buymeacoffee.com/artiya4u

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store