안녕하세요 @wonsama 입니다.
이번시간에 따라 만들어 볼 것은,
간편하게 로그인 없이 스팀잇 댓글을 텔레그램으로 확인 및 답글 전송까지 할 수 있는 프로그램 입니다.
(관련 글을 보고 싶으시면 ... #w-steemjs 을 눌러 확인하실 수 있습니다.)
선행 학습
- nodejs 설치
- botfather를 통해 bot 추가
주의사항
- 마지막 댓글에만 답글을 달 수 있음
( 이것도 개선하면 id 값 받고 전송할 수 있지만 불편해서 제거 ) - 텔레그램에서는 텍스만(이모지) 전송 가능함. (파일등 전송 불가)
동작원리
- steemit :
steem.api.getAccountHistoryAsync
신규 댓글 확인 - (메시지 수신 시 최종 수신한 id 값 기록)
- telegram : 확인 된 신규 댓글을 사용자에게 전송
- telegram : (신규 댓글에 대한)답글 작성
- telegram :
client.getUpdates
를 통해 댓글 확인 - 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를 잘활용하면 다양한 서비스에 접목할 수 있을거 같습니다.
pairplay 가 kr-dev 컨텐츠를 응원합니다! :)
오. 짱입니다
빠르게 확인.답글이 되서 좋아요 😄
앗, 제 오타가 이렇게 캡쳐가 되다니요 ㅎㅎ
이런... 그러네요... 아궁
괜찮아요 ^^
안녕하세요~~~ 인사 드리러 왔는데 엄청 어려워 보이네요 ^^
네, 프로그램 모르시는 분들은 어려울 지도 ^^; 하지만 알고보면 그리 어렵지만은 않아요 ~
대단하시네요. 컴퓨터 언어를 얼마나 공부해야 이런 프로그램을 만들 수 있는거에요? 학교에서 하는 printf로는 아무리 생각해도 뭘 할수 없는것같아서요..
저도 뭐 hello, world 부터 시작했는데요 뭘...
@wonsama 님 능력자시네요
오 신기방기하네요!!
^^ 잼있긴하죠 프로그램이 ㅎㅎ
유용한 정보 감사합니다. 시간이 걸려도 한번 시도 해 보겠습니다.
좋은 주말 보내세요!!! 리스팀 하고 갑니다
네, 시간 되실 때 위 소스 참조하셔서 천천히 해보세요 ^^
엄청난 능력자시군요! 텔레그램 연동되면 빨리 확인하고 답글을 달 수 있어 편하겠네요ㅎㅎ 팔로우, 리스팀하고 갑니다~!
엄청난 까진 아니고... -__-; 응원 감사합니다.
우오.. 이런게 되는군요!!
저는 댓글 확인정도만 하고있었는데
댓글 달기까지 되는지는 처음 알았네요+_+
steem api와 접목하면 손쉽게? 댓글 읽기 / 답글 모두 가능 하죠 ^^
@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!
텔르그램으로도 이용할 수 있느 기능이군요. 대단하네요.
텔레그램이 기능 접목하기 좋은 플렛폼이죠 ^^
우와...능력자시구나..!!
텔레그램으로 댓글 다는거 진짜 편리해 보여요..! ㅎㅎ
@tipu upvote this post with 5 sbd
Sorry, @tipU needs to recover voting power - will be back in 19 hours and 2 minutes. Please try then!
오. 이거 좋네요! 굳
대단한 기능이네요. 프로그래밍을 몰라서 써먹진 못하지만요 ㅎ
오 싱기방기! 별게 다 있네요!