
AUTOVOT์ ํน์ ๋ฃฐ ๊ธฐ๋ฐ์ผ๋ก ์๋์ผ๋ก ๋ณดํ
์ ํด์ฃผ๋ ํ๋ก๊ทธ๋จ ์
๋๋ค.
๋ฌผ๋ก ๋ค ํ๋ถํ๋ถ ์ฐพ์๊ฐ๋ฉฐ ๊ธ์ ์ฝ์ ํ ๋ณดํ
ํ๋ฉด ์ข๊ฒ ์ง๋ง ์ค์ง์ ์ผ๋ก ํ๋ค๊ธดํ์ง๋ผ...
(์๊ฐ๋ ์๊ณ , ๋จ๋ ๋ณดํ
ํ์๋ ์๊น๊ธฐ๋ ํ๊ณ )
๊ณ ๋ ๋ถ๋ค์ด kr์ ๋ ๋ง์ด ๋ณดํ
ํ์ ํ๋ก ์จ๊ฐ์๋ฉด์ ๋งค์ผ ๋ณดํ
์ ํด์ฃผ์๋ฉด ์ข์๊ฒ ๊ฐ์๋ฐ ... ๋ ์ด๋ทฐ์ง์ด๋ ๋ญ๋๋ก ๋ชฐ๋ฆฌ๋ฉด ๋ ํ ...
์ด์ ๊ธ : ์๋ก์ด ์คํ : ์๋๋ณดํ ์์ ๋ฒ์ ์ ํ์์ต๋๋ค.
๋ณดํ
์์ : ํน์ ์กฐ๊ฑด ํด๋น ๊ธ์ ๋ฐฐ์ ํ๊ณ ๋ณดํ
์ ์ํ
๋ฑ์ ๋ฃฐ์ ์ ์ฉํ์ฌ ์ค์ง์ ์ผ๋ก ๋ ์๋ ค์ง(๋ณด์์ด ์๋) ๊ธ๋ค์ ๋ณดํ ์ ํ๋๋ก ํ์์ต๋๋ค.
ํน์๋ ํ์ํ์ ๋ถ์ ์์ค์ฝ๋ ์ฐธ์กฐ ๋ฐ๋๋๋ค.
(๋ฌผ๋ก ๋ฃฐ์ ์ค์ ๊ฐ์ ํตํด ์ ์ ํ๊ฒ ๋ณ๊ฒฝ ๊ฐ๋ฅ ํฉ๋๋ค. )
[์๋ฆผ] ์ผ๋ถ ๊ธ์ ์ด๋ทฐ์ ธ๋ก ๋ถ๋ฅ? ๋๋ ๊ธ์ด ํฌํจ๋ ์ ์์ต๋๋ค.
ํด๋น ์ฌํญ์ ๋๊ธ ์ฃผ์๋ฉด ๋ณดํ ์ ์ธ ๋์ ์ถ๊ฐ ๋ฐ ๋ณดํ ํ์๋ฅผ ํ๊ฒ ์ต๋๋ค.
ํ๋ก๊ทธ๋จ ์์ฑ๋๊ฐ 100% ๊ฐ ์๋์ง๋ผ ์ง์์ ์ผ๋ก ๋ณด์๋๋ก ํ๊ฒ ์ต๋๋ค.
/*
autovot
์๋ ๋ณดํ
๋ฃฐ
15๋ถ ๋จ์๋ก ์ํ
kr ํ๊ทธ ์ฌ์ฉ์ ์ค ์ต์ ๊ธ ๊ธฐ์ค 100๋ฒ์งธ ์ด์ ๊ธ ๋ถํฐ ์กฐํ
์ ๋ชฉ์์ ํ๊ธ์ด ํฌํจ๋ ๋จ์ด๋ง
์ ๋ชฉ์์ ํน์ ๋จ์ด ํํฐ๋ง ( ๋ณดํ
, ์ด๋ฒคํธ, ๊ฐ์ํํ ๋ฑ )
ํน์ ์ฌ์ฉ์ ์ ์ธ ( kr-guide ์ ๊ณ ๋นํ ๊ธ ๋ฑ )
๋ณดํ
ํ์๊ฐ 1~5ํ ( 0ํ๋ ๋๋ฌด ์ด์ํ ๊ธ์ด ๋ง์ด ํ์ง๋จ์ ์ ์ธ, 5ํ ์ด์์ ์ด๋ฏธ ์ ๋ช
๊ธ )
๋ณด์๊ธ์ก ์ฝ 0.1$ ์ดํ ( ๋ณด์๊ธ์ก ๊ณ์ฐ๋ถ๋ถ์ ์ค๋ฌ ์์ธ์ ์ฐ๋ ๋ ์์ด์ ์ฝ๊ฐ ์ฐจ์ด๋ ์ ์์ )
ํ๋ก์ 1000๋ช
์ดํ : ํ๋ก์ ๋ง์ผ์ ๋ถ๋ค์ ๊ธ๋ฐฉ ๋ณดํ
์์น๊ฐ ์ฌ๋ผ๊ฐ
๋ณด์๊ฑฐ์ ๊ธ์ ๋ณดํ
์ ์ธ
์ฐธ์กฐ
๊ธฐ์กด์ ๋ณดํ
๋ชฉ๋ก์ ์๋๋ผ๋ ํ๋จ์ ์ ๊ฐ ์ ํ ๋ฃฐ์ ๋ฒ์ด๋๋ ๊ธ์ ์์๋ก ๋ณดํ
์ ํ์ํ๋ ์ ์ ์๋ ค ๋๋ฆฝ๋๋ค.
์ ๊ฐ ์ ํ ๋ฃฐ์ ๋ฒ์ด๋๋ ๋ค์ด๋ณดํ
์ ์์ผ๋ฉฐ, ๋ณดํ
์ ํ์ ํ ๋ฟ ์
๋๋ค. ^^
๋ฃฐ์ ์์๋ก ์ถ๊ฐ๋๊ฑฐ๋ ์ญ์ ๋ ์ ์์ต๋๋ค.
๋ณดํ
ํ์ ๋์
์ ์๊ถ ์นจํด ๊ด๋ จ ๊ธ
kr-guide ์ ๊ณ ๊ฑด
์ด๋ฏธ์ง ๋ธ๋์ ๋ณต๋ถ ๊ธ
*/
////////////////////////////////////////////////
//
// require
//
const steem = require('steem');
const f = require('util').format;
const fs = require('fs');
const schedule = require('node-schedule');
const dateFormat = require('dateformat');
////////////////////////////////////////////////
//
// settings
//
// API ์ ์ RPC ์๋ฒ๋ฅผ ์ค์ ํ๋ค
steem.api.setOptions({ url: 'https://api.steemit.com' });
// ์ค์ ์ ๋ณด
const VCFG = {
CREATOR : "๊ณ์ ๋ช
", /* ๊ธ์ด์ด */
PRIVATE_KEY : "์ํธํค", /* PRIVATE ์ํธํค */
BODY_MIN : 1000, /* ๋ด์ฉ ์ต์๊ธธ์ด */
VOTE_MIN : 0, /* ์ต์ ํฌํ ์ */
VOTE_MAX : 5, /* ์ต๋ ํฌํ ์ */
VOTE_SAVE_MAX : 72, /* ๋ณดํ
๋ฐ์ ์์ด๋ ์ ์ฅ ๊ฐฏ์ - ์ค๋ณต๋ณดํ
๋ฐฉ์ง์ฉ */
VOTE_RATE : 100 * 10, /* ๋ณดํ
ํผ์ผํธ 10000 = 100% */
VOTE_DOLLOR_DIV : 330125227228, /* ์์ต์ vote_rshares๋ก ๋๋ ์ ํด๋น ๊ฐ, ๊ฐ๋ณ์ */
VOTE_DOLLOR_LIMIT : 0.1, /* ์์ต์ด 0.5๋ฌ๋ฌ ๋ฏธ๋ง */
QUERY_COUNT : 100, /* ๊ธ ์กฐํ ๊ฐฏ์ (์ต๋ 100๊ฐ ์) */
FOLLOWER_COUNT : 1000
};
// ํ์ผ ์ค์ ์ ๋ณด
const FCFG = {
LAST_VOTE :'./lastvote.json', /* ์ต๊ทผ ๋ณดํ
ํ author ์ ๋ณด */
BAN_TITLE :'./bantitle.json', /* ๊ธ์ง ์ ๋ชฉ ์ ๋ณด */
BAN_ID :'./banauthor.json', /* ์ ํ๋ author ์ ๋ณด */
PRE_DATA_VOTE : './data/votebot_', /* ์ผ์๋ณ ๋ณดํ
์ ๋ณด */
EXT_TXT : ".txt"
};
////////////////////////////////////////////////
//
// define function
//
// ์๊ฐ์ ์ฐ์ฐํ๋ค
// h : ์๊ฐ
Date.prototype.addHours = function(h) {
this.setTime(this.getTime() + (h * 60 * 60 * 1000));
return this;
}
// created ์ ๋ณด๋ฅผ Date๋ก ๋ณํ
// created : ์์ฑ์๊ฐ
let getLocalTime = (created)=>{
created = created.replace("T", " ")
var t = new Date(created).addHours(9);
return t;
}
// ์ถ๋ ฅ์ฉ ์๊ฐ ์ ๋ณด์ฒ๋ฆฌ
// t : ์๊ฐ์ ๋ณด
let getFormadate = (t)=>{
return t.toLocaleDateString('ko-KR').substr(2).replace(/-/gi, "/") + " " + t.toLocaleTimeString('en-US', { hour12: false }).substr(0, 8);
}
// ํ์ผ์ ์ฝ์ด ๋ฐฐ์ด(json) ํํ๋ก ๋ฐํํ๋ค
// path ์ฝ์ด๋ค์ผ ํ์ผ ๊ฒฝ๋ก
let fileToArray = (path)=>{
try{
return JSON.parse(fs.readFileSync( path, 'utf-8'));
}catch(e){
// ๋ณดํต ํ์ผ์ด ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ์
// console.log(e);
return new Array();
}
}
// ํ๊ธ ์ฌ๋ถ๋ฅผ ํ๋จํ๋ค
// s : ์
๋ ฅ ๋ฌธ์์ด
let isHangul = (s)=>{
const pattern = /[\u3131-\u314e|\u314f-\u3163|\uac00-\ud7a3]/g;
return pattern.test(s);
}
// ๊ธ๋ชฉ๋ก ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค
let getDiscussionsByCreated = ()=>{
return new Promise( (resolve, reject) => {
steem.api.getDiscussionsByCreated( {limit: VCFG.QUERY_COUNT,tag: 'kr'}, function(err, result) {
if(err!=undefined){
reject(err);
}
else if(result.length == 0){
reject("getAccounts : [ " + name + " ] is not found.");
}
else{
resolve(result);
}
});
});
}
// ๊ณ์ ์ ํ๋ก์ ํ๋ก์ ์นด์ดํธ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค
// name : ๊ณ์ ๋ช
let getFollowCount = (name)=>{
return new Promise( (resolve, reject) => {
steem.api.getFollowCount( name, function(err, result) {
if(err!=undefined){
// ์ฌ๊ธฐ ๊ฑธ๋ฆด ์ผ์ ์์๋ฏ
// name ์ ์๋ชป ์
๋ ฅํ ๊ฒฝ์ฐ์๋ count๊ฐ ๋๋ค 0์ผ๋ก ๋ณด์ฌ์ง๋ค.
reject(err);
}else{
resolve(result);
}
});
});
}
// ์กฐํ ๋ item ๊ธฐ์ค ํด๋น ์ฌ๋ฌ ์กฐ๊ฑด์ ๋ง์กฑํ๋์ง ์ฌ๋ถ๋ฅผ ํ๋จํ๋ค
let isMatch = (item)=>{
return new Promise( (resolve, reject) => {
const ROOT_TITLE = item.root_title.toUpperCase(); // ์ ๋ชฉ
const ITEM_URL = "https://steemit.com" + item.url;
// ์ ๋ชฉ ๊ฒ์ฆ : ํ๊ธ ๋ฏธํฌํจ์ฌ๋ถ
if( !isHangul(ROOT_TITLE) ){
reject( f("url : [ %s ]\ntitle [ %s ] is not include hangul.\n",ITEM_URL ,ROOT_TITLE ) );
}
// ์ ๋ชฉ ๊ฒ์ฆ : ๊ธ์ง ๋จ์ด ํฌํจ
for(var i=0;i<BAN_TITLE.length;i++){
if (ROOT_TITLE.indexOf(BAN_TITLE[i]) >= 0) {
// console.log("title banned");
reject( f("url : [ %s ]\ntitle [ %s ] is banned. ( %s )\n",ITEM_URL ,ROOT_TITLE, BAN_TITLE[i] ) );
// break;
}
}
// ์๊ฐ ๊ฒ์ฆ : ์ ์ธ๋์ ์๊ฐ ์ฌ๋ถ
for(var i=0;i<BAN_ID.length;i++){
if (item.author == BAN_ID[i].id) {
reject( f("url : [ %s ]\nauthor [ %s ] is banned.\n",ITEM_URL ,item.author ) );
break;
}
}
// ์ต๊ทผ ํฌํ ๋์ ๊ฒ์ฆ
for(var i=0;i<lastvote.length;i++){
if (item.author == lastvote[i]) {
reject( f("url : [ %s ]\nauthor [ %s ] is voted in %d.\n",ITEM_URL ,ROOT_TITLE, VCFG.VOTE_SAVE_MAX ) );
break;
}
}
// ๋ณด์๊ฑฐ์
if (!item.allow_votes){
reject( f("url : [ %s ]\nauthor [ %s ] allow_votes is reward reject.\n",ITEM_URL ,item.author ) );
}
// ๋ณดํ
ํ์ ๊ฒ์ฆ
const vcnt = item.active_votes.length;
if (vcnt<VCFG.VOTE_MIN || vcnt>VCFG.VOTE_MAX){
reject( f("url : [ %s ]\nauthor [ %s ] vote count (%d) not in [ %d ~ %d ]\n",ITEM_URL ,item.author, vcnt, VCFG.VOTE_MIN, VCFG.VOTE_MAX) );
}
// ๋ณดํ
๊ธ์ก ์ ํ
const _vdol = isNaN(item.vote_rshares) ? 1 : Number(item.vote_rshares);
const vdol = Math.round((_vdol / VCFG.VOTE_DOLLOR_DIV) * 100) / 100;
if (vdol > VCFG.VOTE_DOLLOR_LIMIT){
reject( f("url : [ %s ]\nauthor [ %s ] vote dollor (%d) is exeed [ %d ] dollor.\n",ITEM_URL,item.author, vdol, VCFG.VOTE_DOLLOR_LIMIT) );
}
// ๋ณธ๋ฌธ๊ธธ์ด ์ ํ
if (item.body_length < VCFG.BODY_MIN){
reject( f("url : [ %s ]\nauthor [ %s ] body_length ( %d ) is too short. min (%d)\n",ITEM_URL ,item.author, item.body_length, VCFG.BODY_MIN) );
}
// ์ ์ ์ฒ๋ฆฌ
resolve(item);
});
}
// ํํฐ๋ ๊ฒฐ๊ณผ์ ๊ฐ์ผ๋ก ํฌํ๋ฅผ ์ํํ๋ค.
let doVote = (item)=>{
return new Promise( (resolve, reject) => {
const OUT_TEMPLATE = "| title | %s | url | https://steemit.com%s | voting | %d | reward | %d | created | %s | gap | %d min |\n";
// ์ ๋ณด๊ฐ ์๋์ง ์ฌ๋ถ๋ฅผ ํ๋จํ๋ค
if(item==null || !item){
reject("item is empty");
}
const created = getLocalTime(item.created);
const created_fmt = getFormadate(created);
const gap = Math.floor(((new Date() - created) / 1000) / 60); // min
const _vdol = isNaN(item.vote_rshares) ? 1 : Number(item.vote_rshares);
const vdol = Math.round((_vdol / VCFG.VOTE_DOLLOR_DIV) * 100) / 100;
const OUT_RESULT = f(OUT_TEMPLATE, item.title, item.url, item.active_votes.length, vdol, created_fmt, gap);
const wif = steem.auth.toWif(VCFG.CREATOR, VCFG.PRIVATE_KEY, 'posting');
try{
steem.broadcast.vote(wif, VCFG.CREATOR, item.author, item.permlink, VCFG.VOTE_RATE, function(e1, r) {
// ์ต๊ทผ ํฌํํ ์์ด๋ ์ ๋ณด ์
๋ฐ์ดํธ
lastvote.push(item.author);
if(lastvote.length>=VCFG.VOTE_SAVE_MAX){
lastvote.shift();
}
fs.writeFileSync( FCFG.LAST_VOTE, JSON.stringify(lastvote), 'utf-8');
// ๋ ์ง๋ณ ํ์ผ์ ๋ณดํ
์ ๋ณด ๊ธฐ๋ก
fs.appendFile( FCFG.PRE_DATA_VOTE + dateFormat(new Date(), "yymmdd") + FCFG.EXT_TXT, OUT_RESULT, function(e2) {
// console.log(item);
});
// ์์
์๋ฃ ์ฌ๋ถ๋ฅผ ์๋ฆผ
resolve(OUT_RESULT);
});
}catch(e3){
reject(e3);
}
});
};
// ํฌํ๋ฅผ ์ํํ๋ค
async function vote(){
// START
console.log("start", getFormadate(new Date()) , "\n" );
// ๋ชฉ๋ก ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค
const discussions = await getDiscussionsByCreated().catch(e=>{
// console.log("cause", e);
return new Array();
});
var findIdx = 1;
var match = false;
while(true){
match = await isMatch(discussions[discussions.length - findIdx]).catch(e=>{
// console.log("skip cause", e);
return false;
});
// ํ๋ก์ ์ ์ ํ
if(match || findIdx>=discussions.length){
const mc = await getFollowCount(match.author);
if(mc.follower_count<VCFG.FOLLOWER_COUNT){
// ์ฐพ๊ธฐ ์ฑ๊ณต !!
break;
}else{
// console.log( f(" %s follower_count is %d.",match.author, mc.follower_count));
match = false;
}
}
findIdx++;
}
// ๋ณดํ
์ ์ํํ๋ค
const voteResult = await doVote(match).catch(e=>{
console.log("cause", e);
return false;
});
console.log("vote result : " + voteResult);
// END
console.log("end", getFormadate(new Date()) );
}
////////////////////////////////////////////////
//
// logic
//
// ๊ฐ์ฅ ์ต๊ทผ์ ๋ณดํ
ํ ์์ด๋ - ๋์ผํ ๊ฒฝ์ฐ ์ ์ธ ๋ค์ ๊ธ๋ก ...
var lastvote = fileToArray( FCFG.LAST_VOTE );
// ์ ์ธ ๋์ ์ ๋ชฉ
const BAN_TITLE = fileToArray( FCFG.BAN_TITLE );
// ์ ์ธ ๋์ ์์ด๋
const BAN_ID = fileToArray( FCFG.BAN_ID );
// ์๊ฐ๋น 4ํ ๋ณดํ
์ํ
var j = schedule.scheduleJob('1,15,30,45 * * * *', function() {
vote();
});