유명현

Merge branch 'feature/line-bot-refactor' into 'main'

Feature/line bot refactor

minor 기능 구현 (UX) 및 코드 수정

See merge request !23
const setKeywordsFlexMessage = require("../message/setKeywordsFlexMessage")
const setKeywordsFlexMessage = require("../message/setKeywordsFlexMessage");
const db = require("../../apis/database");
const checkKeywords = (client, event) => {
db.getKeywordsByUserId(event.source.userId).then((keywords) => {
flexMessage = setKeywordsFlexMessage(keywords);
client.replyMessage(event.replyToken, flexMessage)
})
db.getKeywordsByUserId(event.source.userId).then((keywords) => {
flexMessage = setKeywordsFlexMessage(keywords);
client.pushMessage(event.source.userId, flexMessage);
});
};
module.exports = { checkKeywords };
\ No newline at end of file
module.exports = { checkKeywords };
......
const { marketMultiSearch } = require("../search/marketSearch");
const setCarouselMessage = require("../message/setCarouselMessage");
// Database APIs
const db = require("../../apis/database");
// API List
// database.addKeyword = async function(keyword, userId)
// database.deleteKeyword = async function(userId, keyword)
// database.getKeywordsByUserId = async function(userId)
// database.getUsersByKeyword = async function(keyword)
// database.getAllUsers = async function()
// database.getAllKeywords = async function()
const multiCheckMamul = (client) => {
db.getAllKeywords().then((keywords) => {
for (let i = 0, pending = Promise.resolve(); i < keywords.length; i++) {
pending = db.getUsersByKeyword(keywords[i]).then((userIds) => {
marketMultiSearch(keywords[i]).then((res) => {
client.multicast(userIds, [setCarouselMessage(res)]);
client.multicast(userIds, [setCarouselMessage(res, keywords[i])]);
});
});
}
......@@ -24,11 +18,11 @@ const multiCheckMamul = (client) => {
const checkMamul = (client, userId) => {
db.getKeywordsByUserId(userId).then((keywords) => {
for (let i = 0, pending = Promise.resolve(); i< keywords.length; i++) {
for (let i = 0, pending = Promise.resolve(); i < keywords.length; i++) {
pending = marketMultiSearch(keywords[i]).then((res) => {
client.pushMessage(userId, setCarouselMessage(res));
client.pushMessage(userId, setCarouselMessage(res, keywords[i]));
});
};
}
});
};
......
// Line chatbot + Message generate functions
const line = require("@line/bot-sdk");
const setFlexMessage = require("./message/setFlexMessage");
const setCarouselMessage = require("./message/setCarouselMessage");
const setKeywordsFlexMessage = require("./message/setKeywordsFlexMessage")
// Market Search
const { daangnSingleSearch } = require("./search/daangnSearch");
const { daangnMultiSearch } = require("./search/daangnSearch");
const { joongnaSingleSearch } = require("./search/joongnaSearch");
const { joongnaMultiSearch } = require("./search/joongnaSearch");
const { bunjangSingleSearch } = require("./search/bunjangSearch");
const { bunjangMultiSearch } = require("./search/bunjangSearch");
const { marketMultiSearch } = require("./search/marketSearch");
// File search - Will be deleted (Unused)
// File search
const fs = require("fs");
// Cron for Mamul Notification
const schedule = require("node-schedule");
const job = schedule.scheduleJob("0 */1 * * *", () => {
multiCheckMamul(client);
multiCheckMamul(client);
});
// Database APIs
const db = require("../apis/database");
// API List
// database.addKeyword = async function(keyword, userId)
// database.deleteKeyword = async function(userId, keyword)
// database.getKeywordsByUserId = async function(userId)
// database.getUsersByKeyword = async function(keyword)
// database.getAllUsers = async function()
// database.getAllKeywords = async function()
// Import credentials for Line chatbot
require("dotenv").config({ path: __dirname + "/../config/.env" });
......@@ -41,8 +26,7 @@ const config = {
// Cron for Mamul Notification
const { multiCheckMamul, checkMamul } = require("./check/checkMamul");
const { checkKeywords } = require("./check/checkKeywords")
const { checkKeywords } = require("./check/checkKeywords");
// Line chat bot client & event
const client = new line.Client(config);
......@@ -74,25 +58,21 @@ function handleEvent(event) {
);
}
} else if (event.postback.data == "checkItems") {
return Promise.resolve(
checkMamul(client, event.source.userId),
);
return Promise.resolve(checkMamul(client, event.source.userId));
} else if (event.postback.data == "deleteKeyword") {
var foundDelete = waitDeleteMamulList.indexOf(event.source.userId);
if (foundDelete == -1) {
waitDeleteMamulList.push(event.source.userId);
console.log(`waitDeleteMamulList Changed : ${waitDeleteMamulList}`);
return Promise.resolve(
client.replyMessage(event.replyToken, {
type: "text",
text: "삭제할 매물 키워드를 알려주세요!",
})
);
}
} else if (event.postback.data == "checkKeywords") {
var foundDelete = waitDeleteMamulList.indexOf(event.source.userId);
if (foundDelete == -1) {
waitDeleteMamulList.push(event.source.userId);
console.log(`waitDeleteMamulList Changed : ${waitDeleteMamulList}`);
return Promise.resolve(
checkKeywords(client, event)
)
client.replyMessage(event.replyToken, {
type: "text",
text: "삭제할 매물 키워드를 알려주세요!",
})
);
}
} else if (event.postback.data == "checkKeywords") {
return Promise.resolve(checkKeywords(client, event));
}
}
return Promise.resolve(null);
......@@ -110,22 +90,29 @@ function handleEvent(event) {
text: `매물이 등록되었습니다!\n등록된 매물: ${event.message.text}`,
}),
marketMultiSearch(event.message.text).then((res) => {
client.pushMessage(event.source.userId, setCarouselMessage(res));
client.pushMessage(
event.source.userId,
setCarouselMessage(res, event.message.text)
);
})
);
}
var foundDelete = waitDeleteMamulList.indexOf(event.source.userId);
if (foundDelete != -1) {
waitDeleteMamulList.splice(foundDelete, 1);
console.log(waitDeleteMamulList[foundDelete]);
return Promise.resolve(
db.deleteKeyword(event.source.userId, event.message.text),
client.replyMessage(event.replyToken, {
type: "text",
text: `매물이 삭제되었습니다!\n삭제된 매물: ${event.message.text}`,
})
)
waitDeleteMamulList.splice(foundDelete, 1);
console.log(waitDeleteMamulList[foundDelete]);
return Promise.resolve(
db.deleteKeyword(event.source.userId, event.message.text),
client
.replyMessage(event.replyToken, {
type: "text",
text: `매물이 삭제되었습니다!\n삭제된 매물: ${event.message.text}`,
})
.then(() => {
checkKeywords(client, event);
})
);
}
}
}
......@@ -181,83 +168,82 @@ module.exports = { handleEvent, config };
/*리치메뉴 설정*/
// let richMenu = {
// size: {
// width: 2006,
// height: 827,
// size: {
// width: 2006,
// height: 827,
// },
// selected: false,
// name: "Real richMenu",
// chatBarText: "메뉴 열기",
// areas: [
// {
// bounds: {
// x: 0,
// y: 0,
// width: 1003,
// height: 413,
// },
// action: {
// type: "postback",
// label: "newKeyword",
// data: "newKeyword",
// displayText: "키워드 추가",
// inputOption: "openKeyboard",
// fillInText: "",
// },
// },
// selected: false,
// name: "Real richMenu",
// chatBarText: "메뉴 열기",
// areas: [
// {
// bounds: {
// x: 0,
// y: 0,
// width: 1003,
// height: 413,
// },
// action: {
// type: "postback",
// label: "newKeyword",
// data: "newKeyword",
// displayText: "키워드 추가",
// inputOption: "openKeyboard",
// fillInText: "",
// },
// },
// {
// bounds: {
// x: 1003,
// y: 0,
// width: 1003,
// height: 413,
// },
// action: {
// type: "postback",
// label: "deleteKeyword",
// data: "deleteKeyword",
// displayText: "키워드 삭제",
// inputOption: "openKeyboard",
// fillInText: "",
// },
// },
// {
// bounds: {
// x: 0,
// y: 413,
// width: 1003,
// height: 414,
// },
// action: {
// type: "postback",
// label: "checkKeywords",
// data: "checkKeywords",
// displayText: "키워드 확인",
// },
// },
// {
// bounds: {
// x: 1003,
// y: 413,
// width: 1003,
// height: 414,
// },
// action: {
// type: "postback",
// label: "checkItems",
// data: "checkItems",
// displayText: "매물 즉시 검색",
// },
// },
// ],
// {
// bounds: {
// x: 1003,
// y: 0,
// width: 1003,
// height: 413,
// },
// action: {
// type: "postback",
// label: "deleteKeyword",
// data: "deleteKeyword",
// displayText: "키워드 삭제",
// inputOption: "openKeyboard",
// fillInText: "",
// },
// },
// {
// bounds: {
// x: 0,
// y: 413,
// width: 1003,
// height: 414,
// },
// action: {
// type: "postback",
// label: "checkKeywords",
// data: "checkKeywords",
// displayText: "키워드 확인",
// },
// },
// {
// bounds: {
// x: 1003,
// y: 413,
// width: 1003,
// height: 414,
// },
// action: {
// type: "postback",
// label: "checkItems",
// data: "checkItems",
// displayText: "매물 즉시 검색",
// },
// },
// ],
// };
// 등록
// client.createRichMenu(richMenu).then((richMenuId) => {
// console.log(richMenuId)
// console.log(richMenuId);
// });
// client.setRichMenuImage(
// "richmenu-ab4bba1c3c9235be50e3e8924fabd940",
// fs.createReadStream("./static/image/richMenu.png")
// );
// client.setDefaultRichMenu("richmenu-ab4bba1c3c9235be50e3e8924fabd940");
//
// "richmenu-de8d05638cd98d81e765576986376314",
// fs.createReadStream("./static/image/richMenu.png")
// );
// client.setDefaultRichMenu("richmenu-de8d05638cd98d81e765576986376314");
......
const setFlexMessage = require("./setFlexMessage");
function setCarouselMessage(mamuls) {
function setCarouselMessage(mamuls, keyword) {
let flexMessages = [];
let flexMessage = {};
if (
mamuls[0] == undefined &&
mamuls[1] == undefined &&
mamuls[2] == undefined
) {
if (mamuls[0] == undefined) {
let nonMamulMessage = {
type: "flex",
altText: "매물 검색 에러",
altText: `${keyword} 매물은 아직 없어요!`,
contents: setFlexMessage(
"-",
"매물이 없습니다!",
......@@ -20,6 +16,20 @@ function setCarouselMessage(mamuls) {
"-"
),
};
nonMamulMessage["contents"]["header"] = {
type: "box",
layout: "horizontal",
contents: [
{ type: "text", text: "매무리 봇", size: "sm", color: "#1DB446" },
{
type: "text",
text: `키워드: ${keyword}`,
align: "end",
color: "#1DB446",
weight: "bold",
},
],
};
return nonMamulMessage;
}
for (i = 0; i < mamuls.length; i++) {
......@@ -51,9 +61,48 @@ function setCarouselMessage(mamuls) {
if (mamuls[i]["extraInfo"] == undefined || mamuls[i]["extraInfo"] == "") {
mamuls[i]["extraInfo"] = "없음";
} else if (mamuls[i]["extraInfo"].length > 150) {
mamuls[i]["extraInfo"] = mamuls[i]["extraInfo"].slice(0, 150) + "\n...";
} else {
if (
mamuls[i]["platform"] === "joongna" ||
mamuls[i]["platform"] === "중고나라"
) {
let searchDot = mamuls[i]["extraInfo"].indexOf("...");
if (searchDot !== -1) {
mamuls[i]["extraInfo"] = mamuls[i]["extraInfo"].slice(0, searchDot);
}
}
console.log(`unparsed extraInfo : \n${mamuls[i]["extraInfo"]}`);
let searchValue = "\n";
let pos = 0;
let foundPos = 0;
for (let j = 0; j < 4 && foundPos !== -1; j++) {
foundPos = mamuls[i]["extraInfo"].indexOf(searchValue, pos);
pos = foundPos + 1;
}
console.log(`pos: ${pos}`);
if (foundPos !== -1) {
mamuls[i]["extraInfo"] =
mamuls[i]["extraInfo"].slice(0, foundPos) + "\n...";
console.log(`parsed extraInfo : \n${mamuls[i]["extraInfo"]}`);
}
if (mamuls[i]["extraInfo"].length > 40) {
mamuls[i]["extraInfo"] =
mamuls[i]["extraInfo"].slice(0, 40) + "\n...";
console.log(`parsed extraInfo : \n${mamuls[i]["extraInfo"]}`);
}
}
// } else if (mamuls[i]["extraInfo"].length > 70) {
// mamuls[i]["extraInfo"] = mamuls[i]["extraInfo"].slice(0, 70) + "\n...";
// } else {
// }
// else if (mamuls[i]["extraInfo"].indexOf("\n") !== -1) {
// console.log(mamuls[i]["extraInfo"].indexOf("\n", 4));
// let slicePoint = mamuls[i]["extraInfo"].indexOf("\n", 4);
// mamuls[i]["extraInfo"] =
// mamuls[i]["extraInfo"].slice(0, slicePoint) + "\n...";
// }
flexMessage = setFlexMessage(
mamuls[i]["platform"],
......@@ -69,10 +118,23 @@ function setCarouselMessage(mamuls) {
continue;
}
}
flexMessages[0]["header"] = {
type: "box",
layout: "horizontal",
contents: [
{ type: "text", text: "매무리 봇", size: "sm", color: "#1DB446" },
{
type: "text",
text: `키워드: ${keyword}`,
align: "end",
color: "#1DB446",
weight: "bold",
},
],
};
let carouselMessage = {
type: "flex",
altText: "Carousel mamul message",
altText: `${keyword} 매무리가 도착했어요!`,
contents: {
type: "carousel",
contents: flexMessages,
......
......@@ -44,46 +44,6 @@ function setFlexMessage(
weight: "bold",
size: "xl",
},
// {
// type: "box",
// layout: "baseline",
// margin: "md",
// contents: [
// {
// type: "icon",
// size: "sm",
// url: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
// },
// {
// type: "icon",
// size: "sm",
// url: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
// },
// {
// type: "icon",
// size: "sm",
// url: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
// },
// {
// type: "icon",
// size: "sm",
// url: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
// },
// {
// type: "icon",
// size: "sm",
// url: "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png",
// },
// {
// type: "text",
// text: "4.0",
// size: "sm",
// color: "#999999",
// margin: "md",
// flex: 0,
// },
// ],
// },
{
type: "box",
layout: "vertical",
......
function setKeywordsFlexMessage(keywords) {
let flexMessage = {
type: "bubble",
body: {
type: "box",
layout: "vertical",
contents: [
{
type: "text",
text: "매무리 봇",
weight: "bold",
color: "#1DB446",
size: "sm",
},
{
type: "text",
text: "등록된 키워드",
weight: "bold",
size: "xxl",
margin: "md"
},
{
type: "separator",
margin: "xxl"
},
{
type: "box",
layout: "vertical",
contents: [],
margin: "md"
}
]
let flexMessage = {
type: "bubble",
body: {
type: "box",
layout: "vertical",
contents: [
{
type: "text",
text: "매무리 봇",
weight: "bold",
color: "#1DB446",
size: "sm",
},
};
{
type: "text",
text: "등록된 키워드",
weight: "bold",
size: "xxl",
margin: "md",
},
{
type: "separator",
margin: "xxl",
},
{
type: "box",
layout: "vertical",
contents: [],
margin: "md",
},
],
},
};
for (let i = 0; i < keywords.length; i++) {
const textbox = createKeywordTextBox(keywords[i]);
flexMessage.body.contents[3].contents.push(textbox);
}
for (let i = 0; i < keywords.length; i++) {
const textbox = createKeywordTextBox(keywords[i]);
flexMessage.body.contents[3].contents.push(textbox);
}
return {
type: "flex",
altText: "키워드 조회 오류",
contents: flexMessage
}
return {
type: "flex",
altText: "매무리 키워드 확인",
contents: flexMessage,
};
}
function createKeywordTextBox(keyword) {
return {
type: "text",
text: keyword,
size: "lg",
align: "center",
margin: "md"
}
return {
type: "text",
text: keyword,
size: "lg",
align: "center",
margin: "md",
};
}
module.exports = setKeywordsFlexMessage;
\ No newline at end of file
module.exports = setKeywordsFlexMessage;
......
const { daangnSingleSearch } = require("./daangnSearch");
const { bunjangSingleSearch } = require("./bunjangSearch");
const { joongnaSingleSearch } = require("./joongnaSearch");
const setCarouselMessage = require("../message/setCarouselMessage");
const { daangnMultiSearch } = require("./daangnSearch");
const { bunjangMultiSearch } = require("./bunjangSearch");
const { joongnaMultiSearch } = require("./joongnaSearch");
const marketMultiSearch = (keyword) => {
const result = [];
return new Promise((resolve, reject) => {
daangnSingleSearch(keyword).then((res) => {
result.push(res);
bunjangSingleSearch(keyword).then((res) => {
result.push(res);
joongnaSingleSearch(keyword).then((res) => {
result.push(res);
daangnMultiSearch(keyword).then((res) => {
console.log(`daangn: ${res}`);
if (res !== undefined && res !== null) {
for (let i = 0; i < res.length && i < 4; i++) {
result.push(res[i]);
}
}
bunjangMultiSearch(keyword).then((res) => {
console.log(`bunjang: ${res}`);
if (res !== undefined && res !== null) {
for (let i = 0; i < res.length && i < 4; i++) {
result.push(res[i]);
}
}
joongnaMultiSearch(keyword).then((res) => {
console.log(`joongna: ${res}`);
if (res !== undefined && res !== null) {
for (let i = 0; i < res.length && i < 4; i++) {
result.push(res[i]);
}
}
resolve(result);
});
});
......