app.js 7.71 KB
const express = require('express');
const app = express();
const mysql = require('mysql2/promise');
const axios = require('axios').default;
const bodyParser = require('body-parser');

app.use(bodyParser.json());
app.use('/images', express.static('images'));

const channelToken = 'Bearer ZVI571ynUAVT0U44734ziyjVtStYiUi2UA4txCgVUvugv5YQwToMDgE991Sd4UbsZtJpBhXEtQKX/YkvQVx0PgH9F7qy75eUR0iKMwz33RXD5BG8JuFZ67FqR/GXZQNSk+kXHMBVnCQPeeNqTNn5MgdB04t89/1O/w1cDnyilFU=';

const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: '0712',
    database: 'tft'
});

app.post('/messages', function(req, res) {
    for(let event of req.body.events) {
        if(event.type == 'message')
            onMessage(event.message, event.replyToken);
    }

    res.sendStatus(200);
});

function onMessage(message, replyToken) {
    if(message.type != 'text')
        return;
    
    if(message.text.indexOf('아이템') >= 0) {
        recommendItem(message.text.split(' ')[0], replyToken);
        return;
    }
    
    let keywords = [
        '추천덱',
        '추천메타',
        '덱추천',
        '메타추천',
        '메타덱',
        '추천시너지',
        '메타시너지',
        '시너지추천'
    ];

    let isMeta = false;
    let textOnly = message.text.replace(' ', '');
    for(let k of keywords) {
        if(textOnly.indexOf(k) >= 0)
            isMeta = true;
    }
    if(isMeta) {
        recommendMeta(replyToken);
        return;
    }

    if(message.text.indexOf('확률') >= 0) {
        sendProbabilities(replyToken);
        return;
    }

    replyMessage(replyToken, [{type: 'text', text: '수행할 수 없는 명령입니다.'}]);
}

function recommendItem(charAlias, replyToken) {
    let sql = 'SELECT n.`alias`, i.`id`, i.`name` FROM `character_aliases` a JOIN `character_items` c ON a.`character_id`=c.`character_id` JOIN `character_aliases` n ON c.`character_id`=n.`character_id` JOIN `items` i ON c.`item`=i.`id` WHERE a.`alias`=? AND n.`is_name`=1 ORDER BY c.`count` DESC LIMIT 0,5';
    pool.query(sql, [charAlias]).then(function([results]) {
        let reply;
        if(results.length == 0)
            reply = {type: 'text', text: '데이터가 없습니다.'};
        else
            reply = {
                type: 'flex',
                altText: `${results[0].alias}의 추천 아이템은 ${results.map(e => e.name).join(', ')} 입니다.`,
                contents: {
                    type: 'bubble',
                    size: 'kilo',
                    body: {
                        type: 'box',
                        layout: 'vertical',
                        alignItems: 'flex-start',
                        contents: [
                            {type: 'text', text: `${results[0].alias}의 추천 아이템`}
                        ].concat(results.map(e => ({
                            type: 'box',
                            layout: 'horizontal',
                            alignItems: 'center',
                            justifyContent: 'flex-start',
                            spacing: 'md',
                            contents: [
                                {
                                    type: 'image',
                                    url: `https://jongro3.ga/images/items/${e.id.toString().padStart(2, '0')}.png`,
                                    size: 'xxs',
                                    flex: 0
                                },
                                {
                                    type: 'text',
                                    text: e.name,
                                    flex: 0
                                }
                            ]
                        })))
                    }
                }
            };

        replyMessage(replyToken, [reply]);
    });
}

async function recommendMeta(replyToken) {
    let sql = 'SELECT * FROM decks ORDER BY `count` DESC LIMIT 0,3';
    let [results] = await pool.query(sql);
    let messages = [];

    for(let row of results) {
        let chars = [];
        sql = 'SELECT `alias` FROM character_aliases WHERE character_id=? AND is_name=1';
        for(let i = 1; i <= 8; i++) {
            [results] = await pool.query(sql, [row[`character_${i}`]]);
            chars.push({id: row[`character_${i}`], name: results[0].alias});
        }
    
        let traitCnt = new Map();
        traitCnt.set(row.chosen, 1);

        let checked = new Set();
    
        sql = 'SELECT trait FROM character_traits WHERE character_id=?';
        for(let i = 0; i <= 8; i++) {
            if(checked.has(row[`character_${i}`]))
                continue;

            [results] = await pool.query(sql, [row[`character_${i}`]]);
    
            for(let t of results) {
                if(traitCnt.has(t.trait))
                    traitCnt.set(t.trait, traitCnt.get(t.trait) + 1);
                else
                    traitCnt.set(t.trait, 1);
            }

            checked.add(row[`character_${i}`]);
        }
    
        let traits = [];
        sql = 'SELECT t.name, l.count FROM traits t JOIN trait_levels l ON t.id=l.trait_id WHERE t.id=? AND l.count <= ? ORDER BY l.count DESC LIMIT 1';
        for(let [key, value] of traitCnt.entries()) {
            [results] = await pool.query(sql, [key, value]);
            if(results.length == 0)
                continue;

            traits.push(`${results[0].count}${results[0].name}`);
        };

        [results] = await pool.query('SELECT `name` FROM traits WHERE `id`=?', [row.chosen]);
        messages.push({
            type: 'flex',
            altText: traits.join(', ') + '\n' + chars.join(', ') + '\n' + '선택받은 자: ' + results[0].name,
            contents: {
                type: 'bubble',
                size: 'giga',
                body: {
                    type: 'box',
                    layout: 'vertical',
                    contents: [
                        {type: 'text', text: '시너지'},
                        {
                            type: 'text',
                            text: traits.join(', '),
                            wrap: true
                            /*layout: 'horizontal',
                            contents: traits.map(e => ({
                                type: 'text',
                                text: e
                            }))*/
                        },
                        {type: 'text', text: '캐릭터'},
                        {
                            type: 'box',
                            layout: 'horizontal',
                            contents: chars.map(e => ({
                                type: 'image',
                                url: `https://jongro3.ga/images/champions/${e.id}.png`,
                                size: 'xxs'
                            }))
                        },
                        {type: 'text', text: chars.map(e => e.name).join(', '), wrap: true},
                    ]
                }
            }
        });
    }

    replyMessage(replyToken, messages);
}

function sendProbabilities(replyToken) {
    let probs = [
        [100, 0, 0, 0, 0],
        [100, 0, 0, 0, 0],
        [75, 25, 0, 0, 0],
        [55, 30, 15, 0, 0],
        [45, 33, 20, 2, 0],
        [35, 35, 25, 5, 0],
        [22, 35, 30, 12, 1],
        [15, 25, 35, 20, 5],
        [10, 15, 30, 30, 15]
    ];

    replyMessage(replyToken, [{
        type: 'text',
        text: probs.map((arr, idx) => `Lv.${idx + 1}: ${arr.map(e => `${e}%`).join(' ')}`).join('\n')
    }])
}

function replyMessage(replyToken, messages) {
    axios.post('https://api.line.me/v2/bot/message/reply', {
        replyToken: replyToken,
        messages: messages
    }, {
        headers: {'Authorization': channelToken}
    });
}

app.listen(3000, () => console.log('TFT Chatbot started'));