Бот для пасивного заробітку в SteemitsteemCreated with Sketch.

in hive-145157 •  2 days ago 

До написання цього допису мене мотивувало прочитання про пасивний дохід у системі Steemit. Мене дуже зацікавив сервіс botsteem, який виконує за користувача деякі дії з курації, і це було б дуже зручно, оскільки в більшості випадків у мене немає часу вчасно читати та ставити апвоути на дописи тих користувачів, на яких я підписаний.

Я пробував запустити механізм курації дописів на Steemit через botsteem.com, але в мене чомусь нічого не вийшло. Після налаштування акаунта не відбувалося жодних дій. Після цього я трохи пошукав інформацію в інтернеті про інші способи ставити автоматичні апвоути на Steemit і прийшов до висновку, що найдієвіший спосіб — це створити бота на JavaScript, який працює з ПК в середовищі Node.js і виконує певні дії.

Node.js дозволяє запустити виконання будь-якого JavaScript на комп’ютері поза вебсервером. Це простими словами. Якщо у вас є більше запитань на цю тему, ви можете запитати в ChatGPT або пошукати в Google. Встановити Node.js дуже просто. Завантажте інсталяційний файл із офіційного сайту, запустіть його та дотримуйтеся інструкцій програми. Під час встановлення не вибирайте нічого зайвого, лише базовий набір.

Після встановлення відкрийте діалогове вікно Windows, натиснувши клавіші Win+R, і введіть команду cmd, щоб відкрити командний рядок.

Перш за все перевірте встановлену версію Node.js командою:

node -v

А також перевірте версію менеджера пакетів командою:

npm -v

Перед початком роботи з ботом потрібно встановити бібліотеку dsteem, яка використовуватиметься для роботи з блокчейном Steem. Команда:

npm install dsteem

Усе має запрацювати з першої спроби. Якщо ви бачите повідомлення в командному рядку про необхідність оновити версію npm, просто дотримуйтеся інструкцій що у повідомленні. Ну а якщо щось піде не так або ви заплутаєтесь, просто запитайте в ChatGPT — він чудово вміє вирішувати такі проблеми, зокрема проблеми з кодом і командами.

Приступаємо до написання самого бота, який ставитиме апвоути на нові дописи. І тут у нас два варіанти: ставити апвоути на дописи користувачів, на яких ви підписані, або стежити за новими дописами заздалегідь підготовленого списку користувачів.

Розглянемо покроково перший приклад, який голосує за нові дописи користувачів, на яких ви підписані.

Підключаємо бібліотеку dsteem для роботи з блокчейном Steem. Клієнт dsteem.Client підключається до ноди https://api.steemit.com з таймаутом 8 секунд і порогом відмови у 3 спроби:

const dsteem = require('dsteem');

const client = new dsteem.Client('https://api.steemit.com', {
    timeout: 8000, 
    failoverThreshold: 3 
});

Позначаємо змінні: ім'я облікового запису та приватний ключ.

const username = '';
const postingKey = '';

Отримання та оновлення списку підписок:

async function updateFollowingList() {
    try {
        console.log('Оновлюємо список підписок...');
        followingList = await getFollowing(username);
        console.log('Список підписок оновлено:', followingList.length, 'акаунтів');
    } catch (error) {
        console.error('Помилка оновлення списку підписок:', error);
    }
}

Надсилаємо запит на Steemit API щоб отримати список підписок облікового запису. Збираємо всі підписки до масиву following по 100 користувачів, а якщо останній отриманий результат менше 100 - завершуємо завантаження.

async function getFollowing(account) {
    let following = [];
    let start = '';
    let batchSize = 100;

    while (true) {
        const result = await client.database.call('get_following', [account, start, 'blog', batchSize]);
        if (result.length === 0) break;

        following = following.concat(result.map(user => user.following));
        start = result[result.length - 1].following;

        if (result.length < batchSize) break;
    }

    return following;
}

Функція запуску бота: виводимо повідомлення про запуск, оновлюємо список підписок перед запуском. Запускається таймер, який оновлює список підписок кожні 120 хвилин.

async function startBot() {
    console.log('Бот запущено і стежить за новими дописами...');
    await updateFollowingList(); // Оновити список підписок перед стартом

    setInterval(updateFollowingList, 100 * 60 * 1000); // Оновлювати список кожні 120 хвилин

Запускаємо цикл моніторингу операцій блокчейну. Коли приходить нова операція перевіряємо чи це допис, а не коментар.
Перевіряємо автора допису у підписках. Якщо бот ще не відправляв апвоут на цей допис викликаємо upvotePost, інакше пропускаємо.

    while (true) {
        try {
            const stream = client.blockchain.getOperationsStream();

            stream.on('data', async (operation) => {
                try {
                    if (operation.op[0] === 'comment') {
                        const op = operation.op[1];

                        if (op.parent_author === '' && followingList.includes(op.author)) {
                            console.log('Новий допис від @' + op.author + ': ' + op.permlink);

                            const voted = await hasVoted(op.author, op.permlink, username);
                            if (!voted) {
                                await upvotePost(op.author, op.permlink);
                            } else {
                                console.log('Вже голосували за @' + op.author + '/' + op.permlink + ', пропускаємо...');
                            }
                        }
                    }
                } catch (err) {
                    console.error('Помилка при обробці даних із потоку:', err);
                }
            });

Якщо в потоці даних відбувається помилка – перепідключається.

            stream.on('error', (err) => {
                console.error('Помилка потоку операцій:', err);
                reconnect();
            });

Функція перепідключення чекає 10 секунд та перезапускає бот.

async function reconnect() {
    console.log('Перепідключення через 10 секунд...');
    await sleep(10000);
    startBot();
}

Перевірка - чи голосував бот за допис.

async function hasVoted(author, permlink, voter) {
    try {
        const content = await client.database.call('get_content', [author, permlink]);
        return content.active_votes.some(vote => vote.voter === voter);
    } catch (error) {
        console.error('Помилка під час перевірки апвоутів:', error);
        return false;
    }
}

Функція голосування. Якщо апвоут успішно надіслано, виводить повідомлення до консолю.

async function upvotePost(author, permlink, weight = 10000) {
    try {
        const vote = {
            voter: username,
            author: author,
            permlink: permlink,
            weight: weight
        };

        await client.broadcast.vote(vote, dsteem.PrivateKey.fromString(postingKey));
        console.log('Апвоут для @' + author + '/' + permlink + ' успішно відправлено');
    } catch (error) {
        console.error('Помилка під час голосування:', error);
    }
}

Функція очікування.

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

Запуск бота

startBot();

Весь код:

const dsteem = require('dsteem');

const client = new dsteem.Client('https://api.steemit.com', {
    timeout: 8000, 
    failoverThreshold: 3 
});

const username = '';
const postingKey = '';

let followingList = [];

async function updateFollowingList() {
    try {
        console.log('Оновлюємо список підписок...');
        followingList = await getFollowing(username);
        console.log('Список підписок оновлено:', followingList.length, 'аккаунтов');
    } catch (error) {
        console.error('Помилка оновлення списку підписок:', error);
    }
}

async function getFollowing(account) {
    let following = [];
    let start = '';
    let batchSize = 100;

    while (true) {
        const result = await client.database.call('get_following', [account, start, 'blog', batchSize]);
        if (result.length === 0) break;

        following = following.concat(result.map(user => user.following));
        start = result[result.length - 1].following;

        if (result.length < batchSize) break;
    }

    return following;
}

async function startBot() {
    console.log('Бот запущено і стежить за новими дописами...');
    await updateFollowingList(); // Оновити список підписок перед стартом

    setInterval(updateFollowingList, 100 * 60 * 1000); // Оновлювати список кожні 120 хвилин

    while (true) {
        try {
            const stream = client.blockchain.getOperationsStream();

            stream.on('data', async (operation) => {
                try {
                    if (operation.op[0] === 'comment') {
                        const op = operation.op[1];

                        if (op.parent_author === '' && followingList.includes(op.author)) {
                            console.log('Новий допис від @' + op.author + ': ' + op.permlink);

                            const voted = await hasVoted(op.author, op.permlink, username);
                            if (!voted) {
                                await upvotePost(op.author, op.permlink);
                            } else {
                                console.log('Вже голосували за @' + op.author + '/' + op.permlink + ', пропускаємо...');
                            }
                        }
                    }
                } catch (err) {
                    console.error('Помилка при обробці даних із потоку:', err);
                }
            });

            stream.on('error', (err) => {
                console.error('Помилка потоку операцій:', err);
                reconnect();
            });

            return;
        } catch (err) {
            console.error('Помилка під час запуску потоку, перезапуск через 10 секунд...', err);
            await sleep(10000);
        }
    }
}

async function reconnect() {
    console.log('Перепідключення через 10 секунд...');
    await sleep(10000);
    startBot();
}

async function hasVoted(author, permlink, voter) {
    try {
        const content = await client.database.call('get_content', [author, permlink]);
        return content.active_votes.some(vote => vote.voter === voter);
    } catch (error) {
        console.error('Помилка під час перевірки апвоутів:', error);
        return false;
    }
}

async function upvotePost(author, permlink, weight = 10000) { // weight = 5000 = 50%
    try {
        const vote = {
            voter: username,
            author: author,
            permlink: permlink,
            weight: weight
        };

        await client.broadcast.vote(vote, dsteem.PrivateKey.fromString(postingKey));
        console.log('Апвоут для @' + author + '/' + permlink + ' успішно відправлено');
    } catch (error) {
        console.error('Помилка під час голосування:', error);
    }
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

startBot();

Код бота необхідно зберегти на вашому комп'ютері у файлі з розширенням .js в окремій папці.

Запускаємо бот через командний рядок. Нагадаю як: відкриваємо діалогове вікно Windows комбінацією клавіш Win+R і вводимо команду cmd.

cmd

Якщо ви перейменували папку з ботом у "bot", то в командному рядку виконуємо таку команду:

cd bot

або

cd C:\папка

Команда cd використовується в командному рядку для переходу між папками.

Тепер нам потрібно запустити JavaScript-файл за допомогою Node.js. Для цього виконайте команду:

node ваш_файл_бота.js

Якщо ви хочете пропустити команду cd, то можете запустити бот без переходу в його директорію. Команда:

node C:\шлях\до\script.js

Наведений вище приклад бота в мене працює добре. У процесі роботи з’являються деякі повідомлення про помилки та ще системні сповіщення, але вони не заважають роботі бота, ви можете їх просто проігнорувати. Не забудьте вказати у змінних ім’я користувача та posting key.

Другий приклад бота, який я хочу продемонструвати, ставить апвоути на нові дописи за списком користувачів, який ви готуєте заздалегідь. Я не розбиратиму покроково всі функції, а лише виділю основний масив, до якого потрібно вписати список користувачів: const whitelistUsers. Але його помітно й неозброєним оком новачка, тому в налаштуванні бота все буде зрозуміло:

const dsteem = require('dsteem');

const client = new dsteem.Client('https://api.steemit.com', {
    timeout: 8000, 
    failoverThreshold: 3 
});

const username = '';
const postingKey = '';

// Список користувачів, за яких бот буде голосувати
const whitelistUsers = [
    'username_1',
    'username_2',
    'username_3',
    'username_4',
    'username_5',
    'username_6',
    'username_7',
    'username_8',
    'username_9',
    'username_10'
];

async function startBot() {
    console.log('Бот працює і стежить за новими дописами...');

    while (true) {
        try {
            const stream = client.blockchain.getOperationsStream();

            stream.on('data', async (operation) => {
                try {
                    if (operation.op[0] === 'comment') {
                        const op = operation.op[1];

                        if (op.parent_author === '' && whitelistUsers.includes(op.author)) {
                            console.log('Новий допис від @' + op.author + ': ' + op.permlink);

                            const voted = await hasVoted(op.author, op.permlink, username);
                            if (!voted) {
                                await upvotePost(op.author, op.permlink);
                            } else {
                                console.log('Вже голосували за @' + op.author + '/' + op.permlink + ', пропускаємо...');
                            }
                        }
                    }
                } catch (err) {
                    console.error('Помилка при обробці даних із потоку:', err);
                }
            });

            stream.on('error', (err) => {
                console.error('Помилка потоку операцій:', err);
                reconnect();
            });

            return;
        } catch (err) {
            console.error('Помилка під час запуску потоку, перезапуск через 10 секунд...', err);
            await sleep(10000);
        }
    }
}

async function reconnect() {
    console.log('Перепідключення через 10 секунд...');
    await sleep(10000);
    startBot();
}

async function hasVoted(author, permlink, voter) {
    try {
        const content = await client.database.call('get_content', [author, permlink]);
        return content.active_votes.some(vote => vote.voter === voter);
    } catch (error) {
        console.error('Помилка під час перевірки апвоутів:', error);
        return false;
    }
}

async function upvotePost(author, permlink, weight = 10000) { // weight = 10000 = 100%
    try {
        const vote = {
            voter: username,
            author: author,
            permlink: permlink,
            weight: weight
        };

        await client.broadcast.vote(vote, dsteem.PrivateKey.fromString(postingKey));
        console.log('Апвоут для @' + author + '/' + permlink + ' успішно відправлено');
    } catch (error) {
        console.error('Помилка під час голосування:', error);
    }
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

startBot();

Перш ніж ви почнете активно використовувати ботів для апвоутів (і якщо почнете), ви повинні розуміти, що система голосування передбачає голосування людьми за дописи, які дійсно корисні та містять якусь мінімально цінну інформацію. Якщо цього не відбуватиметься, то втрачається весь сенс ведення блогів на Steemit. Уявіть, якщо замість кожного учасника голосуватиме бот.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

https://steemways.com

Ось цей сервіс ніби працює. Можна підписатися на когось і ставити слідом за цим аккаунтом апп.

Дякую, буду знати 👍🏻
Забув додати, що для роботи бота треба тримати ввімкнутим ПК 😁

Коли не ввімкнутий голосів не буде ?

Звичайно, він же повинен працювати 🙂

а як же те, що не можна використовувати ші тут? хіба бот то не те й саме?
Може дурні запитання, бо я не тямлю в багатьох речах, а ще й багатьох просто боюся)
А ще як бот контролює рівень "батарейки"?

Бот витрачає батарейку так само як і людина коли ставить апвоути.
Про ШІ - то якщо є особисті питання, ШІ профі в технічних проблемах 🙂