[steemit] 스팀잇에서 md 파일을 읽어들여 글쓰기/수정 : 7일 지난 글도 수정 가능요 ㅎㅎ

in #kr-dev6 years ago (edited)

0. 사전준비

  • nodejs 설치
  • nodejs 에 대한 기본 이해 ㅜㅜ : 나중에 설명 또는 인터넷 검색을 ...
  • 의존성 설치 (2개) : npm install steem dotenv
  • 어려울 수도 ... 하지만 알고나면 쉽습니다.
  • 특징 : 정말 손쉽게 글을 수정할 수 있습니다. 이제 7일 지나도 글 수정 되는 거 아시죠 ? ㅋㅋㅋㅋ

1. 설정 (.env) 필수 정보

333.PNG

  • 글쓴이(AUTHOR), 포스팅키(AUTHOR_POSTING_KEY) 설정이 .env 또는 환경변수에 사전 지정이 되어야 됨
  • AUTHOR=wonsama
  • AUTHOR_POSTING_KEY=5KS................

2. 글쓰기

111.PNG

  • 바탕화면 - steemit - 폴더아래 파일이 존재 해야 됨
  • wlib.write('마크다운 파일 명')
/*
* 필요한 process.env 설정 값
*
* AUTHOR : 계정명 
* AUTHOR_POSTING_KEY : 포스팅키 
* FILE_NAME : 파일명
*
* 파일에 필요한 값 정보
* line 1: 제목(title)
* line 2: 카테고리(parent_permlink) / 첫번째 들어가는 태그, 한번 정하면 수정 불가임에 유의
* line 3: 태그(tags) ,로 구분(카테고리에 해당하는 태그는 넣어도 않넣어도 무관함)
* line 4~: 본문(body)
*/
let wlib = require('./writemdlib');

// 글쓰기용 
wlib.write('write-with-md.md');

3. 글 다운로드

222.PNG

  • wlib.download('스팀잇 글 주소').then(console.log).catch(console.log);
  • 바탕화면 - steemit - 폴더에 파일을 생성함
  • 글 다운로드는 아무사람거나 됨 / 나중에 글쓰기에서는 자신의 글만 됨에 유의 (설정 값에 AUTHOR를 지정하기 때문)
let wlib = require('./writemdlib');

// 글 다운로드용
wlib.download('https://steemit.com/dclick/@wonsama/dev-windows10-64bit-cassandra--1539238365600').then(console.log).catch(console.log);

4. writemdlib 소스

/*
* 필요한 process.env 설정 값
*
* AUTHOR : 계정명 
* AUTHOR_POSTING_KEY : 포스팅키 
* FILE_NAME : 파일명
*
* 파일에 필요한 값 정보
* line 1: permlink
* line 2: 제목(title)
* line 3: 태그(tags) ,로 구분
* line 4~: 본문(body)
*/

// 기본 라이브러리 로딩
const steem = require('steem');
const fs = require('fs');
const readline = require('readline');

// 설정 정보를 읽어들인다 
require('dotenv').config();
const AUTHOR = process.env.AUTHOR;
const AUTHOR_POSTING_KEY = process.env.AUTHOR_POSTING_KEY;
const FILE_NAME = process.env.FILE_NAME;

// 상수값 설정
const SEP = require('path').sep;
const HOME_DIR = require('os').homedir();
const DESKTOP = `${HOME_DIR}${SEP}Desktop${SEP}`;
const DEFAULT_FOLDER = `${DESKTOP}steemit${SEP}`;
const FILE_CHARSET = 'utf8';
const SPLITER = require('os').platform()=='win32'?'\r\n':'\r';
const MD_EXT = '.md';

/*
* 텍스트 파일 정보를 읽어들인다(UTF8)
* 실패 시 null 반환
* @param path 파일경로
* @return 읽어들인 파일 정보
*/
function readFile (path) {
    try{
        return fs.readFileSync(path, FILE_CHARSET); 
    }catch(e){
        console.error(e);
        return null;
    }
}

/*
* 폴더 기준 파일 목록정보 읽어들이기
* fname : 전체경로, name : 파일명, ctimeMs : 생성시간 밀리초, mtimeMs : 수정시간 밀리초
* @param dir 폴더경로
* @return 폴더 내부에 존재하는 파일 목록 정보
*/
function getFolerFiles (dir, files_=[]){
    let files = fs.readdirSync(dir);
    for (let i in files){
        let name = dir + files[i];
        let stat = fs.statSync(name);
        if (stat.isDirectory()){
            getFolerFiles(name, files_);
        } else {
            files_.push({fname:name, name : name.split(SEP).pop(), ctimeMs:stat.ctimeMs, mtimeMs:stat.mtimeMs });
        }
    }
    // 최근 수정시간 역순 정렬 반환
    files_.sort((a,b)=>b.mtimeMs-a.mtimeMs);
    return files_;
}

/*
* Promise 개체에서 err정보와 처리(data)정보를 동시에 읽어 들인다
* @param promise Promise 개체
* @return [err,data] 형태의 정보
*/
function asyncTo (promise) {
  return promise
  .then(data=>[null,data])
  .catch(err=>[err]);
}

/*
* 주소 정보에서 스팀잇에 사용되는 주소 정보를 분할 반환
* @param 주소창의 주소
* @return 정제된 주소 정보
*/
function getInfoFromLink(link) {

    // https:// 부분은 cut
  // 이후 구성 [ 도메인 - 태그 - 저자 - 펌링크 ]
  let infos = link.substr(8).split('/');

  if(!infos || infos.length!=4){

    let msg = [];
    msg.push(`입력받은 ${link} 는 올바른 주소 형식이 아닙니다.`);
    msg.push('sample link : https://steemit.com/kr/@wonsama/kr-dev-krob');

    return {
        data:{
            domain: '',
            category: '',
            author: '',
            permlink: ''
        },
        ok:false,
        cd:999,
        msg:msg.join('\n')
      }
  }

  return {
    data:{
        domain: infos[0],
        category: infos[1],
        author: infos[2].substr(1),
        permlink: infos[3]
    },
    ok:true,
    cd:0, /* 0 : 정상, 양수 : 비정상, 추후 코드별 분기(로컬라이징, 코드메시지) 필요 */
      msg:'success'
  }
}

/*
* 폴더가 없는 경우 폴더를 생성
* @param path 폴더경로
*/
function makeFolder (path) {
    const folders = path.split(SEP);
    // check path is exist
    if(!fs.existsSync(path)){
        let paths = [];
        try{
            // make folder with recursivly
            for(let f of folders){
                paths.push(f);
                let p = paths.join(SEP);
                if(p!='' && !fs.existsSync(p)){
                    fs.mkdirSync(p);
                }
            }
        }catch(e){
            console.error(e);
        }
    }
}

/*
* 유니크한 대상을 반환하는 필터
* @param value 값
* @param index 인덱스
* @param self 자기자신
*/
function onlyUnique(value, index, self) { 
    return self.indexOf(value) === index;
}

/*
* 질문을 수행한다 
* @parma msg 질문
* @return 답
*/
async function question (msg){
  return new Promise((resolve, reject)=>{
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout
    });

    try{
      rl.question(msg, answer=>{
          rl.close();
          resolve(answer);
      });
    }catch(e){
      reject(e);
    }
  });
}



let fn = {};

fn.DESKTOP = DESKTOP;
fn.DEFAULT_FOLDER = DEFAULT_FOLDER;

fn.test = async (path) =>{
    return getFolerFiles(path);
}

/*
* 해당 URL의 MARKDOWN 파일을 다운로드 받는다
*/
fn.download = async (url, filefolder=DEFAULT_FOLDER) =>{
    let info = getInfoFromLink(url);
    if(!info.ok){
        return Promise.reject(info.msg);
    }

    let cont = await steem.api.getContentAsync(info.data.author, info.data.permlink);
    let title = cont.title;
    let body = cont.body;
    let category = cont.parent_permlink;
    let json = cont.json_metadata?JSON.parse(cont.json_metadata):'';
    let tags = json.tags;
    tags = tags.filter(x=>x!=category);
    let author = cont.author;
    let permlink = cont.permlink;

    // 확인용 컨텐츠 정보 출력
    // console.log('title', title);
    // console.log('body', body.substr(0,100));
    // console.log('category', category);
    // console.log('tags', tags);
    // console.log('author', author);
    // console.log('permlink', permlink);

    // 정보 생성
    let msg = [];
    msg.push(title);            // 제목
    msg.push(category);         // 카테고리(분류 parent_permlink)
    msg.push(tags.join(','));   // 태그목록 / 첫번째 태그는 변경 불가함. 카테고리
    msg.push(body);             // 내용 

    // 파일 쓰기
    makeFolder(filefolder);
    fs.writeFileSync( filefolder+permlink+MD_EXT, msg.join(SPLITER), FILE_CHARSET);
    return Promise.reject(true);
}

/*
* 파일 읽어들여 글쓰기를 수행한다
* @permlink_md_file 마크다운 파일명 (생략 시 filefolder 에서 최신 수정된 정보를 기반으로 처리한다 )
* @filefolder 폴더 경로 
*/
fn.write = async (permlink_md_file='', filefolder=DEFAULT_FOLDER)=>{

    // permlink가 '' 일 경우 filefolder 에서 최신 수정된 파일 정보를 가져와 해당 정보를 확인한다
    if(permlink_md_file==''){
        let files = getFolerFiles(filefolder);
        if(files.length>0){
            // permlink는 대쉬 숫자 영소문자로만 구성됨
            permlink_md_file = files[0].name;
        }else{
            return Promise.reject(`${filefolder} has no files.`);
        }
    }

    // 읽어들일 파일은 markdown 형식으로 작성 하며 (steemit 기준)
    // 1째줄 : 타이틀
    // 2째줄 : 태그목록 , 구분
    // 3째줄 : 내용
    let data = readFile(filefolder+permlink_md_file);
    if(data==null){
        return Promise.reject(`${filefolder+permlink_md_file} file not found.`);
    }

    // 기본 정보 확인
    let splits = data.split(SPLITER);
    let permlink = permlink_md_file.split('.')[0];
    let title = splits[0];
    let category = splits[1];   // 글 수정 시 parent_permlink 첫번째 태그(카테고리)는 변경 할 수 없다
    let tags = splits[2].split(',').map(x=>x.replace(/ /gi, ''));
    tags.splice(0,0,category);
    tags = tags.filter(onlyUnique);
    let body = splits.splice(3).join(SPLITER);
    let jsonMetadata = {"tags":tags,"app":"steemit/0.1","format":"markdown"};
    
    // 확인용 컨텐츠 정보 출력
    console.log('\ntitle ::::\n', title);
    console.log('\ntags ::::\n', tags);
    console.log('\npermlink ::::\n', permlink);
    console.log('\ncategory ::::\n', category);
    console.log('\njsonMetadata ::::\n', jsonMetadata);
    console.log('\nbody ::::\n', body.substr(0,100)+"...");

    // 확인 및 작업진행
    let q = await question('\n글 쓰기/수정 작업을 진행하겠습니까 (y/n) ? ');
    if(q && q.toLowerCase()=='y'){
        // 글쓰기 
        return steem.broadcast.commentAsync(
            AUTHOR_POSTING_KEY, // wif
            '', // parentAuthor
            category, // parentPermlink
            AUTHOR, // author
            permlink, // permlink
            title, // title
            body, // body
            jsonMetadata // jsonMetadata
            );
    }
    return Promise.reject(`Entered [ ${q} ] : canceled action !`);
    
}


/*
* @param filepath md 파일 경로 또는 수정할 URL 주소
*/
module.exports = fn;

5. 맺음말

  • 사전에 nodejs에 지식이 없으면 약간 힘들거 같네요 ㅜㅜ
  • 소스는 복붙급으로 최대한 손쉽게 사용하실 수 있게 작성요(npm에 올리려다 넘 기능이 없어서 그냥 말았음)
  • 기타 문의사항은 댓글로 부탁 드립니다.
  • @anpigon 님 께서 요청해서 작성 ㅋ

Sponsored ( Powered by dclick )
DCLICK: An Incentivized Ad platform by Proof of Click - 스팀 기반 애드센스를 소개합니다.

안녕하세요 스티미언 여러분. 오늘 여러분께 스팀 블록체인 기반 광고 플랫폼 DCLICK을 소개...

logo

이 글은 스팀 기반 광고 플랫폼
dclick 에 의해 작성 되었습니다.

Sort:  

md 파일 읽어서 왜 글을 써야하는지 이해를 옷했습니다!

제가 대신 답변드리자면 마크다운 에디터로 작성한 글을 올릴때 사용하면 편할것 같습니다.
참고로 저는 "타이포라"라는 에디터를 사용하고 있어요.

에디터로 글을 작성하는 방법이군요. 이해했습니다. 감사합니다.

저는 서브라임 텍스트 플러그인으로 ㅎㅎ

서브라임 텍스트 플러그인을 사용하시는 군요.ㅋ 찾아봐야지 ㅎㅎㅎ
저는 타이포라 이전에는 아톰에 플러그인을 설치해서 사용했는데 너무 버벅거려서 ㅋㅋ

곰돌이가 @anpigon님의 소중한 댓글에 $0.006을 보팅해서 $0.018을 살려드리고 가요. 곰돌이가 지금까지 총 838번 $12.581을 보팅해서 $11.321을 구했습니다. @gomdory 곰도뤼~

Congratulations @wonsama ! you got 0.230 SBD 3rd prize of holdem round 27.see more info at https://steemit.com/@wcasino.holdem

JOIN HOLDEM ( needs 0.100 SBD )

늅늅이라 뭔 말인지는 잘 모르지만 응원합니다 ㅎㅎ

응원 감사요 :))))

거.... 소스가 너무 긴거 아닙니까 ㅋㅋㅋ

소스는 역시 스파게티 !! ㅋ

스파게티 소스는 뽀모도로 소스~!

Posted using Partiko Android

역시 코딩천재 원사마님^^
node js는 자바스크립트를 가지고 서버에다 일 시킬 수 있는 것이군요...

7일이 지난 포스팅에 수정 버튼이 있으면 좋겠지만 steemit.com이나 busy.org 등 아무리 찾아봐도 없어서 궁금해하고 있었는데, node js를 통해 자바스크립트로 포스팅 수정하라고 시키는 것인가요?

글쎄요 거기까진 저도잘 ㅎㅎ

위 소스를 가지고 작업 하면 7일 이상 지난 글 수정은 하실 수 있습니다 :)

아.... 코드 길이가... 복잡하네요.ㅎㅎ
원사마님 감사합니다. 집에가서 공부해야겠어요.ㅋ

파이썬으로 간결하게 컨버팅 해주세요 ㅋ

제가 아직 파이썬 초보라 이 정도까지 구현하려면 공부해야할게 많네요.ㅋ

야근 후는 무리고..
주말에 맑은 정신에 따라 해봐야겠네요. ㅋㅋ
늘 친절한 주석과 코드 감사합니다. 👍

네 :) 그래도 주말은 쉬어야죠 ㅋ

질문이 있습니다~
조사빼고 나머지는 모두 설명이 필요합니다^^;;;;;,
글수정하는데 많은 지식이 필요하군요. 전 7일내에 수정해야겠습니다^^

언젠가 스팀잇에 다시 수정 버튼이 나오길 바라면서... 으윽

저야 수정안하면 그만인데. 혹시 이런방식으로 DClick광고도 7일후 수정가능한건가요? 그걸 고민하시는것 같던데^^

모든글 수정 가능 합니다 :)

혹시 마크다운 에디터를 쓸 때도 이미지를 올리기 편한 방법이 있나요?

꼼수쓰면 가능요 이미지는 jsteem (자바 이미지 업로드 라이브러리) 써서 md에 로컬 이미지 경로를 포함하면 내부적으로 검사 후 aws로 이미지 업로드 되게 구현 가능요

그리고 업로드 완료되면 리턴받은 이미지 경로를 해당 md의 주소 정보를 업로드된 이미지 url 로 replace 하면 끝 ㅋ

이게 발췌소스라 그런데 노드로 비슷하게 구현해서 일렉트론으로 만들어서 개인적으로 사용 중여

아직 스킬이 낮아 공개급 까진 아니고 대충 만들어 쓰네요 ㅜㅜ

오 구미가 당기네요. 혹시 드래그앤드랍으로 md 에디터에 올리는 방식은 없겠죠? 클립보드에 있는 이미지를 올린다던지. 사실 아직도 글 작성을 비지나 스팀잇을 쓸수밖에 없는이유가. ...

표준 마크다운뷰어랑 좀 달라보이는 부분들도 많아서....

이미지 올리기도 많이 편하고... ㅎㅎㅎ

이미지 올리는게 혹시 드래그나 클립보드 둘중 하나만 되더라도 확 당길것 같긴하네요~!

nodejs 에서 electron으로 개발하면 데스크톱 앱을 만들 수 있어서요 드레그앤 드랍 되요 ㅋ

모바일은 flutter로 개발 데스크톱 electron + react 형태로 만들어서 사용중요 ㅋ 정말 개인용 날코딩에 ㅋ

비지 않가고 json_meta 에 커뮤니티랑 app 을 비지로만 설정해서 글쓰면 그곳은 이미 비지 ㅋㅋ
첫번째 태그는 물론 비지로설정하고여 ㅋ

그래서 탬플릿 내부에 만들어서 데탑에서 글쓰면 정말 편해여 ㅋㅋ 최고임

모바일은 또 무슨 용도로 개발하신거에요? 정말 못말리겠네 못만드는게 없으셔 정말 ㅋㅋㅋ

구글이 키워준 플러터 함 테스트중이져 ㅋ

그냥 습작수준이죠 뭐 다 ㅜㅜ

원사마님이 만드신 플루터앱과 일렉트론앱이 너무너무 갖고 싶습니다.~~~~~~
날코딩으로 금방 앱을 뚝딱 만드시는 원사마님 능력이 부럽네요.ㅋ

Posted using Partiko Android

클립보드 이미지도 가능요 ㅋ 클립보드 카피 후 메모리에 드로잉 후 이미지 file export 후 파일 업로드 처리로여

저도 마크다운 에디터에서 이미지 편하게 올리는거 있으면 좋겠어요.ㅋ

Posted using Partiko Android

이것이 인싸언어인 nodejs 이군요! 아직도 jquery 끄적거리고 있는 코더 1인 구경하고 갑니다 ㅠ

복붙 한방으로 아싸도 인싸 가능요 ㅋ

Coin Marketplace

STEEM 0.29
TRX 0.11
JST 0.033
BTC 63945.57
ETH 3135.76
USDT 1.00
SBD 4.00