UNPKG

steemkr

Version:

스팀잇 기반 CLI 툴 : CLI(Command Line Interface) Tool for steemit.

333 lines (284 loc) 10.5 kB
const help = require('./help'); const {removeSpace2} = require('../util/wstring'); const {to} = require('../util/wutil'); const {sleep} = require('../util/wutil'); const {getHostAddr} = require('../util/wstring'); const steem = require('steem'); const ora = require('ora'); const axios = require('axios'); const cheerio = require('cheerio'); const dateFormat = require('dateformat'); // 파서 const padefault = require('./parser/padefault'); const pazdnet = require('./parser/pazdnet'); const painfoq = require('./parser/painfoq'); // 일단 기본 파서로 동작하지 않는 것만 처리하는 방향으로 ^^ // 본문 전체를 추출하는 것이 아니기 때문인지라 ... const IS_PARSE_TEST = false; const PARSERS = [ { links:['zdnet.co.kr/news'], parser:pazdnet }, { links:['infoq.com/news','infoq.com/minibooks','infoq.com/presentations'], parser:painfoq }, ]; // 기본값 const DEFAULT_TAG = 'kr-scrap'; // 1개만 설정 가능 - category로도 사용되기 때문 const DEFAULT_BODY_LEN = 200; const DEFAULT_UNIQUE_LEN = 15; const STEEM_AUTHOR = process.env.STEEM_AUTHOR; const STEEM_SCRAP_AUTHOR = process.env.STEEM_SCRAP_AUTHOR; const STEEM_SCRAP_KEY_POSTING = process.env.STEEM_SCRAP_KEY_POSTING; /* * 파라미터 정보를 초기화 해준다 * @param args 외부로부터 입력받은 파라미터 */ function initParams(args) { // 초기화 args = args?args:[]; // new 처리 하므로 return 처리 해야 됨에 유의 // 1번째 : 링크 - 필수입력 // 2번째 : 스크랩 작가 if(args.length==1){ if(STEEM_SCRAP_AUTHOR){ args.push(STEEM_SCRAP_AUTHOR); } } // 3번째 : 스크랩 작가 포스팅 키 if(args.length==2){ if(STEEM_SCRAP_KEY_POSTING){ args.push(STEEM_SCRAP_KEY_POSTING); } } return args; } /* * frameset을 사용하여 link 정보를 변경할 필요가 있는 경우 처리 * @param link 호출 링크 * @return 변경된 링크정보 */ function updateLink(link){ // https://blog.naver.com/etripr/221329850354 // https://blog.naver.com/PostView.nhn?blogId=etripr&logNo=221329850354 let item = link.split(/\//).filter(x=>x!=''); if(link.indexOf('blog.naver.com')>=0 && link.indexOf('PostView.nhn')<0 && item.length==4){ return `https://blog.naver.com/PostView.nhn?blogId=${item[2]}&logNo=${item[3]}`; } return link; } /* * 비동기 작업을 수행한다 * @param link 보팅할 주소 * @param account 나의 계정명 * @param wif 나의 포스팅키 */ let spinner; async function processAsyc(link, account, wif){ let err; // medium 사이트는 accept 가 반드시 존재해야 됨에 유의 const AXIOS_CONFIG = { headers: { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36', 'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7' } }; let linkInfo; let $; spinner = ora().start('loading link context'); // 파싱이 가능한지 여부 파악 let parser = padefault; for(let par of PARSERS){ let arr = par.links.filter(lk=>link.indexOf(lk)>=0); if(arr.length==1){ parser = par.parser; break; } } // frameset을 사용하여 link 정보를 변경할 필요가 있는 경우 처리 link = updateLink(link); // 파싱 수행 // permlink에는 .이 포함되면 안됨에 유의하기 바랍니다. let context; if(!err){ [err, linkInfo] = await to(axios.request(link, AXIOS_CONFIG)); if(!err){ // console.log(linkInfo.data); $ = cheerio.load(linkInfo.data); spinner.succeed(); context = parser($, link, DEFAULT_TAG, DEFAULT_BODY_LEN, DEFAULT_UNIQUE_LEN); // console.log('context', context); } } // 파싱된 정보 확인 // 기본 파서로 파싱, 오류발생 시 추후 해당 도메인에 매칭되는 파서 제작 유도 let isContextEmpty = false; if(context){ for(let con of Object.entries(context)){ if(!con[1] || con[1]==''){ isContextEmpty = true; break; } } } if(!context || isContextEmpty){ err = `check context has empty value !` } // 파서 테스트를 하는 경우 파싱만 확인한다 if(IS_PARSE_TEST){ console.log(`____________________________________________________________`); console.log(`is test mode : ${IS_PARSE_TEST}`); if(err){ spinner.fail(); console.log(err); } return Promise.resolve(context); } // 글쓰기 - 하루에 하나만 쓰도록 PERMLINK를 고정 let today = dateFormat(new Date(),'yyyy-mm-dd'); // 글쓰기 이력 정보 확인 let cont; if(!err){ spinner = ora().start('check content is exist'); let permlink = `${account}-${today}`; permlink = permlink.replace(/\./gi, '-'); // permlink에는 .이 포함되면 안됨에 유의 permlink = permlink.replace(/\_/gi, '-'); // permlink에는 _이 포함되면 안됨에 유의 context.parentLink = `https://steemit.com/${DEFAULT_TAG}/@${account}/${permlink}`; [err, cont] = await to(steem.api.getContentAsync(account, permlink)); if(!err){ spinner.succeed(); } } // 글쓰기 if(!err && cont && cont.id==0){ // 비지로 작성하고 싶은 경우는 아래 구문의 주석을 해제하면 된다. // let jsonMetadata = { // community:"busy", // app:"busy/2.5.4", // format:"markdown", // tags:['kr-scrap','busy'] // }; let jsonMetadata = { app:"steemit/0.1", format:"markdown", tags:[ DEFAULT_TAG ] }; spinner = ora().start('write content'); let title = `${account}${today} 이것저것 스크랩`; let body = `### ${today} 기준 스크랩 된 목록을 공유합니다.\n\n자세한 내용은 댓글을 참조 바랍니다.`; let permlink = `${account}-${today}`; permlink = permlink.replace(/\./gi, '-'); // permlink에는 .이 포함되면 안됨에 유의 permlink = permlink.replace(/\_/gi, '-'); // permlink에는 _이 포함되면 안됨에 유의 permlink = permlink.replace(/\+/gi, '-'); permlink = permlink.replace(/\#/gi, '-'); permlink = permlink.replace(/\=/gi, '-'); permlink = permlink.replace(/\&/gi, '-'); // 설정 값에 STEEM_AUTHOR 가 존재하면 해당 계정으로 베니피셔리(수익자)를 설정한다. 수익은 스팀파워로 수령하게 됨 if(STEEM_AUTHOR){ let beneficiaries = [ /* weight : 10000 => 100% */ { account: STEEM_AUTHOR, weight: 10000 } ]; const operations = [ ['comment', { parent_author: '', parent_permlink: DEFAULT_TAG, author: account, permlink:permlink, title: title, body: body, json_metadata: JSON.stringify(jsonMetadata) } ], ['comment_options', { author: account, permlink:permlink, max_accepted_payout: '1000000.000 SBD', percent_steem_dollars: 10000, allow_votes: true, allow_curation_rewards: true, extensions: [ [0, { beneficiaries: beneficiaries }] ] }] ]; [err, cont] = await to(steem.broadcast.sendAsync({ operations, extensions: [] }, { posting: wif })); }else{ [err, cont] = await to(steem.broadcast.commentAsync(wif, '', DEFAULT_TAG, account, permlink, title, body, JSON.stringify(jsonMetadata) )); } if(!err){ // 20초간 대기를 수행한다. 글 쓴 이후 댓글을 20초 이후 작성 가능 await sleep(20000); spinner.succeed(); } } // 댓글쓰기 if(!err){ let image = `https://steemitimages.com/300x0/${context.image}`; let jsonMetadata = { app:"steemit/0.1", format:"markdown", tags:context.tags, image:[image], links:[context.url] }; let body = []; body.push(`<img src='${image}' />`); body.push(`#### [${context.title}](${context.url})`); body.push(`> <i>${context.body}</i>`); body.push(`<i>from : ${getHostAddr(link)}</i>`); spinner = ora().start('write reply'); let parentPermlink = `${account}-${today}`; parentPermlink = parentPermlink.replace(/\./gi, '-'); // permlink에는 .이 포함되면 안됨에 유의 // permlink에는 대문자가 포함되면 안됨에 유의 let permlink = `${account}-${today}-${context.unique}`; permlink = permlink.replace(/\./gi, '-').toLowerCase(); permlink = permlink.replace(/\_/gi, '-').toLowerCase(); permlink = permlink.replace(/\+/gi, '-').toLowerCase(); permlink = permlink.replace(/\#/gi, '-').toLowerCase(); permlink = permlink.replace(/\=/gi, '-').toLowerCase(); permlink = permlink.replace(/\&/gi, '-').toLowerCase(); [err, cont] = await to(steem.broadcast.commentAsync(wif, account, parentPermlink, account, permlink, '', body.join('\n\n'), JSON.stringify(jsonMetadata) )); if(!err){ spinner.succeed(); } } // 글쓰기 if(!err){ return Promise.resolve(context); } // 오류처리 if(err){ spinner.fail(); return Promise.reject(err.toString()); } } module.exports = (args)=>{ // 파라미터 초기화 args = initParams(args); // 입력 파라미터 유효성 검증 if(args.length<1){ console.error('\n [경고] 파라미터 오류 : 아래 메뉴얼을 참조 바랍니다'); help('scrappost'); return; } let link = args[0]; // 스크랩 할 포스팅 let account = args[1]; // 계정명 let wif = args[2]; // 포스팅키 processAsyc(link, account, wif) .then(res=>{ // 처리 정보를 화면에 출력한다 console.log(`____________________________________________________________`); console.log(res); console.log(`____________________________________________________________`); }) .catch(e=>{ console.log(`____________________________________________________________`); console.error(e); console.log(`____________________________________________________________`); console.error('오류가 발생 했습니다.위쪽 라인을 참조 바랍니다.'); console.log(`____________________________________________________________`); }); };