[w-steemjs] #002 텔레그램으로 스팀잇 댓글 받기/보내기 !!

in #kr6 years ago (edited)

안녕하세요 @wonsama 입니다.

이번시간에 따라 만들어 볼 것은,

간편하게 로그인 없이 스팀잇 댓글을 텔레그램으로 확인 및 답글 전송까지 할 수 있는 프로그램 입니다.

(관련 글을 보고 싶으시면 ... #w-steemjs 을 눌러 확인하실 수 있습니다.)



111.jpg

[그림1] 텔레그램에서 댓글을 받고 보내기


222.png

[그림2] 스팀잇에서 본 댓글(잘 나오네요 ㅋ)

선행 학습

  • nodejs 설치
  • botfather를 통해 bot 추가

주의사항

  • 마지막 댓글에만 답글을 달 수 있음
    ( 이것도 개선하면 id 값 받고 전송할 수 있지만 불편해서 제거 )
  • 텔레그램에서는 텍스만(이모지) 전송 가능함. (파일등 전송 불가)

동작원리

  1. steemit : steem.api.getAccountHistoryAsync 신규 댓글 확인
  2. (메시지 수신 시 최종 수신한 id 값 기록)
  3. telegram : 확인 된 신규 댓글을 사용자에게 전송
  4. telegram : (신규 댓글에 대한)답글 작성
  5. telegram : client.getUpdates 를 통해 댓글 확인
  6. telegram : client.sendMessage 를 통해 댓글 전송

동작 원리만 이해해도 굳 ~ nodejs 가 아니더라도 python도 동일하게 구성 가능

소스코드 : 설정

const CHAT_ID = '000000000'; /* (수정필요) 텔레그램의 나의 CHAT ID, 확인방법 https://telegram.me/userinfobot 로 접속하여 메시지 보내면 확인 가능 */
const BOT_TOKEN = '5xxxxxxxx:Axxxxxx-uxxxxxxxxxxxxxxxxxxxxxxxx_XXX'; /* (수정필요) botfather를 통해 생성된 bot의 TOKEN 정보 */
const PRIVATE_KEY = 'P5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; /* (수정필요) 스팀잇 나의 PRIVATE KEY */
const AUTHOR_ID = 'xxxxxxxxxx'; /* (수정필요) 나의 ID @빼고 입력 */

소스코드

주석참조/별도 설명은 없습니다.

const CHAT_ID = '000000000'; /* (수정필요) 텔레그램의 나의 CHAT ID, 확인방법 https://telegram.me/userinfobot 로 접속하여 메시지 보내면 확인 가능 */
const { TelegramClient } = require('messaging-api-telegram');
const BOT_TOKEN = '5xxxxxxxx:Axxxxxx-uxxxxxxxxxxxxxxxxxxxxxxxx_XXX'; /* (수정필요) botfather를 통해 생성된 bot의 TOKEN 정보 */
const client = TelegramClient.connect(BOT_TOKEN); 

const fs = require('fs');
const dateFormat = require('dateformat');
const steem = require('steem');

const PRIVATE_KEY = 'P5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; /* (수정필요) 스팀잇 나의 PRIVATE KEY */
const AUTHOR_ID = 'xxxxxxxxxx'; /* (수정필요) 나의 ID @빼고 입력 */

const WIF = steem.auth.toWif(AUTHOR_ID, PRIVATE_KEY, 'posting'); // prvate key 정보 기준으로 posting key 생성

const NUM_START_FROM_END = 99999999;
const NUM_START_AT = 1;
const NUM_GET_COUNT = 100 - NUM_START_AT;

const STEEM_LAST_FILE = "./steem_last.json"; /* 파일-스팀잇 : 최종으로 받은 메시지 정보 : {"id":24859,"tag":"kr-diary","msg":"에고 많이 놀라셨겠어요. \n아이가 많이 아프지 않고 빨리 회복되길!","author":"gyedo","permlink":"re-wonsama-18-05-11-5-20180511t215027415z","parent_author":"wonsama","parent_permlink":"18-05-11-5"} */
const TELEGRAM_LAST_FILE = "./telegram_last.json"; /* 파일-텔레그램 : 최종으로 받은 메시지 정보 : {"id":135442399} */

let STEEM_TRX = JSON.parse(fs.readFileSync(STEEM_LAST_FILE, 'utf-8') || {});
let TELEGRAM_TRX = JSON.parse(fs.readFileSync(TELEGRAM_LAST_FILE, 'utf-8') || {});

// API 접속 RPC 서버를 설정한다
// steem.api.setOptions({ url: 'https://gtg.steem.house:8090' });
steem.api.setOptions({ url: 'https://api.steemit.com' });


let isSendReply = false;
let getTeleReply = () => {

    // 메시지가 전송 진행 상태 중 인경우에는 대기한다 
    if (isSendReply) {
        console.log('sending...')
        return;
    }

    client
        .getUpdates({
            // 최근 변경된 1개의 정보를 가져온다
            // offset: 0,
            offset: TELEGRAM_TRX.id,
            limit: 1
        })
        .then(res => {
            // console.log(TELEGRAM_TRX.id , JSON.stringify(res));
            if (res.length > 0 && TELEGRAM_TRX.id <= res[0].update_id) {

                // 내꺼 답글만 답변함
                if(res[0].message.chat.id!=CHAT_ID){
                    // 정보 업데이트
                    TELEGRAM_TRX.id = res[0].update_id + 1;

                    // 파일에 변경된 정보를 기록
                    let saveMessage = JSON.stringify(TELEGRAM_TRX);
                    fs.writeFileSync(TELEGRAM_LAST_FILE, saveMessage, 'utf-8');

                    isSendReply = false;
                    return;
                }

                // 댓글을 작성한다, 작성하는 글의 permlink가 동일함(new/update)
                let jsonMetadata = { tags: [STEEM_TRX.tag], app: 'telereply/wonsama' };
                isSendReply = true;
                steem.broadcast.comment(WIF, STEEM_TRX.author, STEEM_TRX.permlink, AUTHOR_ID, `${STEEM_TRX.permlink}-telereply`, '', res[0].message.text, jsonMetadata, function(err, result) {


                    if (result) {
                        let link = `https://steemit.com/${STEEM_TRX.tag}/@${STEEM_TRX.author}/${STEEM_TRX.permlink}`;
                        let sendMessage = `${STEEM_TRX.author}님에게 댓글 전송이 완료 되었습니다.\n\n${link}`;

                        client.sendMessage(CHAT_ID, sendMessage, {
                            disable_web_page_preview: false,
                            disable_notification: false,
                        }).then(console.log).catch(console.log);
                    } else {
                        let sendMessage = `${STEEM_TRX.author}님에게 댓글 전송이 실패 했습니다.`;
                        client.sendMessage(CHAT_ID, sendMessage, {
                            disable_web_page_preview: false,
                            disable_notification: false,
                        }).then(console.log).catch(console.log);

                        // 오류 내역을 출력
                        console.log(err);
                    }

                    // 정보 업데이트
                    TELEGRAM_TRX.id = res[0].update_id + 1;

                    // 파일에 변경된 정보를 기록
                    let saveMessage = JSON.stringify(TELEGRAM_TRX);
                    fs.writeFileSync(TELEGRAM_LAST_FILE, saveMessage, 'utf-8');

                    isSendReply = false;
                });
            }

        })
        .catch(e => {
            console.log(e)
        })
}

let getSteemReply = () => {
    // 선택한 계정(AUTHOR_ID)의 뒤부터(최대치를 넘어가면 자동 최대치로 설정됨)(NUM_START_FROM_END)에서 일정 수 만큼의(NUM_GET_COUNT) 정보를 가져온다
    steem.api.getAccountHistoryAsync(AUTHOR_ID, NUM_START_FROM_END, NUM_GET_COUNT).then(res => {

        if (STEEM_TRX.id != res[res.length - 1][0]) {
            // if(true){
            let sendMessage = [];

            for (let act of res.filter(filterComment)) {
                let trx_id = Number(act[0]);
                let op = act[1].op;
                let msg = op[1].body;
                let tag = "";
                let author = op[1].author;
                let permlink = op[1].permlink;
                let parent_author = op[1].parent_author;
                let parent_permlink = op[1].parent_permlink;

                try {
                    tag = JSON.parse(op[1].json_metadata).tags[0];
                } catch (e) {
                    // 봇 같은 경우 json에서 tag가 없이 작성하는 경우가 존재 
                }

                let p_url = `https://steemit.com/${tag}/@wonsama/${op[1].parent_permlink}`;

                if (trx_id > STEEM_TRX.id) {
                    // @@ 수정된 댓글 제외
                    // 태그 없는 경우 (봇댓글) 제외
                    if (tag != "") {

                        // 댓글 수정인 경우임, 메시지를 디코딩 해야 됨
                        if (msg.indexOf("@@") >= 0) {
                            msg = decodeURIComponent(msg);
                        }

                        sendMessage.push(`FROM : ${author}`);
                        sendMessage.push(`LINK : ${p_url}`);
                        sendMessage.push(`MSG : ${msg}`);
                        sendMessage.push('');

                        // 마지막 메시지 정보를 기록한다
                        STEEM_TRX.id = trx_id;
                        STEEM_TRX.tag = tag;
                        STEEM_TRX.msg = msg;
                        STEEM_TRX.author = author;
                        STEEM_TRX.permlink = permlink;
                        STEEM_TRX.parent_author = parent_author;
                        STEEM_TRX.parent_permlink = parent_permlink;
                    }
                }
            }

            // 추가적으로 보낼 메시지가 있는 경우에만 텔레그램으로 전송한다
            if (sendMessage.length != 0) {
                client.sendMessage(CHAT_ID, sendMessage.join('\n\n'), {
                    disable_web_page_preview: false,
                    disable_notification: false,
                }).then(console.log).catch(console.log)
            }

            // console.log(`LAUNCHED - ${sendMessage.length}`, dateFormat(new Date(), 'yy-mm-dd HH:MM:ss'), 'LAST_TRX_ID', STEEM_TRX.id);

            // 파일에 변경된 정보를 기록
            let saveMessage = JSON.stringify(STEEM_TRX);
            fs.writeFileSync(STEEM_LAST_FILE, saveMessage, 'utf-8');
        } else {
            // console.log('LAUNCHED - NONE', dateFormat(new Date(), 'yy-mm-dd HH:MM:ss'), 'LAST_TRX_ID', STEEM_TRX.id);
        }
    });

    // 댓글을 작성
    getTeleReply();
}

// 20초 단위로 호출
setInterval(() => {
    getSteemReply();
}, 1000 * 20);
getSteemReply();

// 댓글 정보(comment)만 추출
let filterComment = (item) => {
    // item[0] : tran_id, item[1] : tran_info
    let op = item[1].op;
    let type = op[0];
    let info = op[1];
    if (type == 'comment' && info.author != AUTHOR_ID && info.parent_author == AUTHOR_ID) {
        return true;
    }
    return false;
}

결론

  • 텔레그램을 통해 손쉽게 메시지를 주고 받는 것이 가능
  • 프로그램 설정이 의외로 복잡함.(아직 버그도 있음 ... 나중에 천천히 고치는 걸로 함)
  • 이처럼 steemit의 api를 잘활용하면 다양한 서비스에 접목할 수 있을거 같습니다.
Sort:  

pairplay 가 kr-dev 컨텐츠를 응원합니다! :)

우오.. 이런게 되는군요!!
저는 댓글 확인정도만 하고있었는데
댓글 달기까지 되는지는 처음 알았네요+_+

steem api와 접목하면 손쉽게? 댓글 읽기 / 답글 모두 가능 하죠 ^^

안녕하세요~~~ 인사 드리러 왔는데 엄청 어려워 보이네요 ^^

네, 프로그램 모르시는 분들은 어려울 지도 ^^; 하지만 알고보면 그리 어렵지만은 않아요 ~

대단하시네요. 컴퓨터 언어를 얼마나 공부해야 이런 프로그램을 만들 수 있는거에요? 학교에서 하는 printf로는 아무리 생각해도 뭘 할수 없는것같아서요..

저도 뭐 hello, world 부터 시작했는데요 뭘...

@wonsama 님 능력자시네요

오 신기방기하네요!!

^^ 잼있긴하죠 프로그램이 ㅎㅎ

유용한 정보 감사합니다. 시간이 걸려도 한번 시도 해 보겠습니다.
좋은 주말 보내세요!!! 리스팀 하고 갑니다

네, 시간 되실 때 위 소스 참조하셔서 천천히 해보세요 ^^

엄청난 능력자시군요! 텔레그램 연동되면 빨리 확인하고 답글을 달 수 있어 편하겠네요ㅎㅎ 팔로우, 리스팀하고 갑니다~!

엄청난 까진 아니고... -__-; 응원 감사합니다.

앗, 제 오타가 이렇게 캡쳐가 되다니요 ㅎㅎ

이런... 그러네요... 아궁

괜찮아요 ^^

@tipu upvote this post with 5 sbd

Sorry, @tipU needs to recover voting power - will be back in 9 hours and 35 minutes. Please try then!

Coin Marketplace

STEEM 0.14
TRX 0.12
JST 0.025
BTC 53375.38
ETH 2393.56
USDT 1.00
SBD 2.15