Tutorial SeosprintPlus - Chiến Seosprint bằng tool miễn phí (Minigame)

C
Created at
Index progress
Incomplete

Một loạt các series tools, apps, scripts cũng như những bài viết mổ xẻ, hướng dẫn code 1001 thứ khác nhau cùng với khanhne :D
CodeBanana Tutorials tập 4

khanhne

Senior
Joined
Nov 18, 2019
Messages
496
Reactions
435
MR
36.746
Phone Number
Call me! Call me!
Telegram
Chat with me via Yahoo Messenger
Facebook
Follow me on Facebook
cover.png

Minigame ở cuối bài nha, phần thưởng siêu hấp dẫn!


Chuyện là như này​


Khá lâu về trước mình cũng như nhiều anh em newbie khác, tìm kiếm con đường kiếm tiền không vốn trên mạng. Sau một hồi lăn lộn nhiều site khác nhau, mình chạm mặt Seosprint. Cày cuốc đã đời thì mình thấy mức kiếm cũng k nhiều lắm, nản nên mình bỏ. Dạo gần đây thấy forum nổi lên vài bài về Seosprint và mọi người rủ nhau lập hội nhóm cày Seosprint, chia sẻ tasks,... làm mình rạo rực theo :D nên mình quyết định làm con tool này để giúp sức cho anh em MMO4Me chiến Seosprint ngon lành hơn.

Hôm nay mạn phép release bản đầu tiên của tool với tính năng đầu tiên:
Yêu employer ngay từ cái nhìn đầu tiên

Giao diện tasks (https://seosprint.net/earn-task/) sẽ được thêm phần hiển thị thông tin về tasks của employer cũng như thông tin cơ bản của employer. Demo như sau

1628958213376.png


Link tải (và cài đặt) https://openuserjs.org/scripts/KhanhhNe/SeosprintPlus

Video demo và hướng dẫn cài đặt

Chúc bạn kiếm tiền vui vẻ! Bái baiiii
.
.
.
Vậy là chỉ còn anh em coder mình thôi hehehehe. Chiến thôi!


HTML của Seosprint trông như nào​


Vài bước F12 cơ bản là xong. Kết quả như sau

1. Lấy cấu trúc HTML để hiển thị thông tin​


1628958620279.png

1628958723807.png


Cấu trúc HTML cơ bản nó sẽ là thế này

HTML:
<div class="adv-line">
    <div class="adv-line-cell-1">...</div> <!-- Đây là nơi ta dùng appendChild để thêm thông tin tasks -->
    <div class="adv-line-cell-2">
        <span title="Site ID">...</span> <!-- Và dùng prepend vào <span> này để thêm thông tin employer -->
        ...
    </div>
</div>

2. Tìm cách crawl thông tin employer và tasks​


1628959133523.png
1628959227865.png


Về số tasks active, chỉ cần đếm số lượng .adv-line là được. Còn lại dùng querySelector với selector trong ảnh là ra thông tin thôi. Thế là xong!


Inspect xong đến code chứ gì nữa​


Với thông tin đã inspect được ở trên, ta sẽ lần lượt code từng phần, sau đó ghép nối chúng lại với nhau, up lên OpenUserJS cho mọi người dùng, viết bài đăng MMO4Me câu like là xong!

1. Crawl thông tin employer​


JavaScript:
/*
* Lấy thông tin employer từ seosprint (/member, /employer-tasks)
*/
async function getEmployerInfo(employerId) {
    // Lấy tổng số tasks, thông tin member của employer
    const getMemberInfo = new Promise(async function (resolve, reject) {
        const response = await fetch(`/member/${employerId}`)
        const body = await response.text()
        const doc = (new DOMParser()).parseFromString(body, 'text/html')
        resolve({
            name: doc.querySelector('.mem-title').textContent,
            id: doc.querySelector('.nick').textContent,
            total_tasks: parseInt(doc.querySelector('a.mem-box').textContent) || 0
        })
    })
    // Lấy số tasks đang active
    const getActiveTasksCount = new Promise(async function (resolve, reject) {
        const response = await fetch(`/employer-tasks/${employerId}`)
        const body = await response.text()
        const doc = (new DOMParser()).parseFromString(body, 'text/html')
        resolve({
            active_tasks: doc.querySelectorAll('.adv-line').length
        })
    })
    // Dùng Promise.all để chạy cả 2 task cùng lúc
    return (await Promise.all([getActiveTasksCount, getMemberInfo])).reduce((a, b) => Object.assign(a, b), {})
}

Để lấy thông tin employer, ta sử dụng Fetch API để request HTML từ link /member/<id>/employer-tasks/<id>. Tiếp đó, ta dùng DOMParser.parseFromString() để parse từ HTML text đó sang dạng DOM tree, rồi dùng querySelector để chọn element và trích dữ liệu ra là xong!

Trong đoạn trên, mình viết theo dạng Promise và dùng Promise.all() để chạy cùng lúc 2 Promise đó. Mục đích của việc này là để tránh phải chạy lần lượt từng Promise đợi nhau, làm tăng (gấp đôi) tốc độ crawl dữ liệu.

2. Tối ưu việc crawl dữ liệu​


Dù đã sử dụng Promise.all() rồi nhưng tốc độ vẫn khá chậm, cần đợi vài giây sau khi load trang để load đầy đủ thông tin employer, với người vội vàng như mình thì điều này siêu siêu khó chịu. Vì thế mình "phát minh" ra phương án caching sau để tối ưu việc crawl này.

JavaScript:
/*
* Wrapper để cache dữ liệu employer và quản lí việc gọi hàm getEmployerInfo tối ưu thời gian nhất
*/
async function getEmployerCached(employerId) {
    if (!data[employerId]) {
        // Lấy dữ liệu về employer nếu không có sẵn trong data
        data[employerId] = {
            ...await getEmployerInfo(employerId)
        }
        console.log(`Got ${employerId}`)
    } else {
        // Trả về dữ liệu đã lưu trước đó trong data
        console.log(`Cached ${employerId}`)
        getEmployerInfo(employerId).then(employerData => {
            // Chạy task update employerData trong background
            data[employerId] = {
                ...employerData
            }
            console.log(`Updated ${employerId}`)
        })
    }
    return data[employerId]
}

Đoạn code này hoạt động như sau:
  1. Nếu thông tin employer chưa crawl lần nào thì crawl, lưu lại rồi trả về
  2. Nếu thông tin employer đã có từ trướcthì
    1. Lên lịch crawl lại thông tin employer để update thông tin cũ
    2. Trả về thông tin đã crawl từ trước để đảm bảo user không phải đợi lâu
Sử dụng cơ chế đó, user sẽ chỉ cần đợi thông tin mỗi employer 1 lần duy nhất và không bao giờ phải đợi nữa, trong khi vẫn đảm bảo thông tin employer luôn được cập nhật thường xuyên. Chỉ thế thôi :D

3. Hiển thị ra màn hình​


Code "backend" thế là xong! Giờ ta cần hiển thị nó ra cho gọn gàng đẹp mắt để tiện dùng.

JavaScript:
(function() {
    'use strict';
    const init = {}

    for (const adv of document.querySelectorAll('.adv-line')) {
        // Lấy ID employer
        const employerId = adv.querySelector('a.ref-block-av').href.match(/\d+/g)[0]
        const employerClass = `employer-${employerId}-tasks`

        // Thêm element hiển thị thông tin tasks của employer
        const template = document.createElement('template')
        template.innerHTML = `<span style="white-space: nowrap" translate="no" class="${employerClass}">
            <b class="active-tasks" title="Active tasks" style="color: green; font-size: 1.25rem"></b>
            <span>/</span>
            <b class="total-tasks" title="Total tasks"></b>
        </span>`
        adv.querySelector('.adv-line-cell-1').append(template.content.firstChild)

        // Thêm element hiển thị thông tin member của employer
        template.innerHTML = `<span style="margin-right: 14px" translate="no" class="${employerClass}">
            <span class="member-info"></span>
        </span>`
        adv.querySelector('.advmoder > div > span:first-child').prepend(template.content.firstChild)

        if (!init[employerId]) {
            // Chỉ chạy task getEmployerCached nếu employerId chưa khởi tạo (chỉ chạy 1 lần duy nhất)
            getEmployerCached(employerId).then(employerData => {
                // Update thông tin trong web bằng dữ liệu lấy được
                for (const elem of document.querySelectorAll(`.${employerClass} .active-tasks`)) {
                    elem.innerHTML = employerData.active_tasks
                }
                for (const elem of document.querySelectorAll(`.${employerClass} .total-tasks`)) {
                    elem.innerHTML = employerData.total_tasks
                }
                for (const elem of document.querySelectorAll(`.${employerClass} .member-info`)) {
                    elem.innerHTML = `${employerData.name} ${employerData.id}`
                }
            })
            init[employerId] = true
        }
    }
})();

Đoạn này hoá ra lại dài dòng và nhiều trò mới nhất :D Chúng ta cùng nhau điểm qua từng thứ một nhé.

Đầu tiên để ý đoạn const init = {}. Biến init này có vai trò lưu lại thông tin rằng employer với ID đó đã được khởi tạo (lên lịch task crawl employer data) hay chưa. Nhờ vậy với mỗi employer trong page, ta chỉ chạy code crawl của employer đó đúng 1 lần duy nhất. Nếu không nhiều task getEmployerCached sẽ được gọi cùng lúc, dẫn đến việc crawl thông tin employer này bị lặp lại và làm chậm đi việc crawl thông tin các employer khác.

Tiếp theo, để ý việc sử dụng <template> nhé. Sở dĩ dùng cách này vì lí do: ngắn gọn. Cách này ngắn gọn và nhìn đẹp hơn cách sử dụng document.createElement() nhiều, cũng như code dễ hiểu, tường minh hơn hẳn.

Cuối cùng là attribute translate="no". Attribute này giúp cho browser nhận ra thông điệp ý nghĩa: "Đừng biến số '1' của t thành 'one', làm zdậy chi?!?"

Và một lưu ý nho nhỏ nữa là việc gói gọn chúng vào (function() {...})();. Câu lệnh này đảm bảo rằng phần code bên trong chỉ chạy khi browser đã load xong page hiện tại, tránh các lỗi không đáng có do page chưa load xong hoàn toàn, thiếu element, thiếu nội dung backend,...

4. Đem đống code này ném lên OpenUserJS (hoặc cài vào TamperMonkey)​


Để làm việc này, thay vì sử dụng template gốc của TamperMonkey, ta sẽ sử dụng cấu trúc của OpenUserJS. Đoạn code hoàn chỉnh sẽ trông như sau

JavaScript:
// ==UserScript==
// @namespace    https://github.com/KhanhhNe
// @name         SeosprintPlus
// @description  Chiến Seosprint siêu đỉnh cùng KhanhhNe và MMO4Me
// @icon         https://github.com/KhanhhNe/CodeBanana-Tutorials/raw/main/seosprintplus/logo.png
// @copyright    2021, KhanhhNe (https://github.com/KhanhhNe)
// @license      CC-BY-SA-3.0; http://creativecommons.org/licenses/by-sa/3.0/
// @license      MIT
// @version      1.0.0
// @include      https://seosprint.net/earn-task/*
// @grant        none
// ==/UserScript==

// ==OpenUserJS==
// @author       KhanhhNe
// ==/OpenUserJS==


/*jshint esversion: 9 */
/*jshint asi: true */


let data;
// Lấy dữ liệu từ localStorage
try {
    data = JSON.parse(localStorage.getItem('employersData')) || {}
} catch (e) {
    data = {}
}

// Update liên tục dữ liệu data lên localStorage
const int = setInterval(() => localStorage.setItem('employersData', JSON.stringify(data)), 1000)
// Ngưng update dữ liệu sau 20s vì không còn dữ liệu mới nữa (đã crawl đầy đủ)
setTimeout(() => clearInterval(int), 20000)
...

Thế là xong! Bạn có thể tìm và cài đặt bản code full qua link sau: https://openuserjs.org/scripts/KhanhhNe/SeosprintPlus


Minigame​


Phần hướng dẫn hôm nay thế là xong. SeosprintPlus là một dự án tool mình sẽ duy trì lâu dài. Tuy nhiên mình lại không phải "chiên da" trong lĩnh vực cày bừa này, vì vậy mạn phép dùng ít quà mọn nhờ mọi người giúp đỡ :D Chi tiết như sau

Mọi người hãy comment một tính năng/đề xuất mới, comment đạt được lược react nhiều nhất hoặc hay nhất sẽ được hiện thực hoá trong phiên bản tiếp theo. Chủ nhân của comment may mắn cũng nhận được phần quà trị giá lên đến $1,000 cụ thể là 0.5MR, hy vọng sẽ là một món quà cho người comment cũng như động lực nho nhỏ cho các bạn để nghĩ ra những ý tưởng thật hay ho vì không chỉ nhận được quà, bạn còn được bộ tool miễn phí, mất gì đâu nào!


Lời kết​


Bài viết hướng dẫn cho hôm nay chỉ đến đây thôi. Hy vọng mọi người biết thêm một chút kiến thức hữu ích cho bản thân. Nếu các bạn thấy hay (hoặc thấy thích việc đọc những bài như thế này dù không hiểu lắm/chả bao giờ làm theo) thì hãy nhấn Like (thả tim cũng được) và comment bên dưới để mình biết còn có người thích những gì mình viết ra nhé ❤️ Và đừng quên tham gia minigame để nhận phần quà siêu hấp dẫn, siêu to khổng lồ nha.

Chúc mọi người một ngày vui vẻ và kiếm được thật nhiều tiền!!!

Một vài chiếc link hữu ích​



footer.png
 

Attachments

  • cover.png
    cover.png
    283.7 KB · Views: 226
  • 1628958715691.png
    1628958715691.png
    32.5 KB · Views: 92
Last edited:
hèn j bữa ni nó nát thế , và đang dần chặn ip VN
 
Fencs có thể đóng gói lại file .exe cho tiện những ng ngu học như mình chỉ cần tải về click 1 phát là chạy từ a-z được không?
Mình nghĩ tính năng đó rất hữu ít :popo_ah:
 
Fencs có thể đóng gói lại file .exe cho tiện những ng ngu học như mình chỉ cần tải về click 1 phát là chạy từ a-z được không?
Mình nghĩ tính năng đó rất hữu ít :popo_ah:
Bạn cài tiện ích mở rộng TamperMonkey trên trình duyệt (có sẵn trên store của chrome và firefox) rồi vào link download này bấm install là nó cài xong. Vì chạy trên browser nên khó chuyển thành exe được bạn
Thế là xong! Bạn có thể tìm và cài đặt bản code full qua link sau: https://openuserjs.org/scripts/KhanhhNe/SeosprintPlus
 
View attachment 181219
Minigame ở cuối bài nha, phần thưởng siêu hấp dẫn!


Chuyện là như này​


Khá lâu về trước mình cũng như nhiều anh em newbie khác, tìm kiếm con đường kiếm tiền không vốn trên mạng. Sau một hồi lăn lộn nhiều site khác nhau, mình chạm mặt Seosprint. Cày cuốc đã đời thì mình thấy mức kiếm cũng k nhiều lắm, nản nên mình bỏ. Dạo gần đây thấy forum nổi lên vài bài về Seosprint và mọi người rủ nhau lập hội nhóm cày Seosprint, chia sẻ tasks,... làm mình rạo rực theo :D nên mình quyết định làm con tool này để giúp sức cho anh em MMO4Me chiến Seosprint ngon lành hơn.

Hôm nay mạn phép release bản đầu tiên của tool với tính năng đầu tiên:
Yêu employer ngay từ cái nhìn đầu tiên

Giao diện tasks (https://seosprint.net/earn-task/) sẽ được thêm phần hiển thị thông tin về tasks của employer cũng như thông tin cơ bản của employer. Demo như sau

View attachment 181210

Link tải (và cài đặt) https://openuserjs.org/scripts/KhanhhNe/SeosprintPlus
Chúc bạn kiếm tiền vui vẻ! Bái baiiii
.
.
.
Vậy là chỉ còn anh em coder mình thôi hehehehe. Chiến thôi!


HTML của Seosprint trông như nào​


Vài bước F12 cơ bản là xong. Kết quả như sau

1. Lấy cấu trúc HTML để hiển thị thông tin​


View attachment 181211
View attachment 181213

Cấu trúc HTML cơ bản nó sẽ là thế này

HTML:
<div class="adv-line">
    <div class="adv-line-cell-1">...</div> <!-- Đây là nơi ta dùng appendChild để thêm thông tin tasks -->
    <div class="adv-line-cell-2">
        <span title="Site ID">...</span> <!-- Và dùng prepend vào <span> này để thêm thông tin employer -->
        ...
    </div>
</div>

2. Tìm cách crawl thông tin employer và tasks​


View attachment 181216View attachment 181217

Về số tasks active, chỉ cần đếm số lượng .adv-line là được. Còn lại dùng querySelector với selector trong ảnh là ra thông tin thôi. Thế là xong!


Inspect xong đến code chứ gì nữa​


Với thông tin đã inspect được ở trên, ta sẽ lần lượt code từng phần, sau đó ghép nối chúng lại với nhau, up lên OpenUserJS cho mọi người dùng, viết bài đăng MMO4Me câu like là xong!

1. Crawl thông tin employer​


JavaScript:
/*
* Lấy thông tin employer từ seosprint (/member, /employer-tasks)
*/
async function getEmployerInfo(employerId) {
    // Lấy tổng số tasks, thông tin member của employer
    const getMemberInfo = new Promise(async function (resolve, reject) {
        const response = await fetch(`/member/${employerId}`)
        const body = await response.text()
        const doc = (new DOMParser()).parseFromString(body, 'text/html')
        resolve({
            name: doc.querySelector('.mem-title').textContent,
            id: doc.querySelector('.nick').textContent,
            total_tasks: parseInt(doc.querySelector('a.mem-box').textContent) || 0
        })
    })
    // Lấy số tasks đang active
    const getActiveTasksCount = new Promise(async function (resolve, reject) {
        const response = await fetch(`/employer-tasks/${employerId}`)
        const body = await response.text()
        const doc = (new DOMParser()).parseFromString(body, 'text/html')
        resolve({
            active_tasks: doc.querySelectorAll('.adv-line').length
        })
    })
    // Dùng Promise.all để chạy cả 2 task cùng lúc
    return (await Promise.all([getActiveTasksCount, getMemberInfo])).reduce((a, b) => Object.assign(a, b), {})
}

Để lấy thông tin employer, ta sử dụng Fetch API để request HTML từ link /member/<id>/employer-tasks/<id>. Tiếp đó, ta dùng DOMParser.parseFromString() để parse từ HTML text đó sang dạng DOM tree, rồi dùng querySelector để chọn element và trích dữ liệu ra là xong!

Trong đoạn trên, mình viết theo dạng Promise và dùng Promise.all() để chạy cùng lúc 2 Promise đó. Mục đích của việc này là để tránh phải chạy lần lượt từng Promise đợi nhau, làm tăng (gấp đôi) tốc độ crawl dữ liệu.

2. Tối ưu việc crawl dữ liệu​


Dù đã sử dụng Promise.all() rồi nhưng tốc độ vẫn khá chậm, cần đợi vài giây sau khi load trang để load đầy đủ thông tin employer, với người vội vàng như mình thì điều này siêu siêu khó chịu. Vì thế mình "phát minh" ra phương án caching sau để tối ưu việc crawl này.

JavaScript:
/*
* Wrapper để cache dữ liệu employer và quản lí việc gọi hàm getEmployerInfo tối ưu thời gian nhất
*/
async function getEmployerCached(employerId) {
    if (!data[employerId]) {
        // Lấy dữ liệu về employer nếu không có sẵn trong data
        data[employerId] = {
            ...await getEmployerInfo(employerId)
        }
        console.log(`Got ${employerId}`)
    } else {
        // Trả về dữ liệu đã lưu trước đó trong data
        console.log(`Cached ${employerId}`)
        getEmployerInfo(employerId).then(employerData => {
            // Chạy task update employerData trong background
            data[employerId] = {
                ...employerData
            }
            console.log(`Updated ${employerId}`)
        })
    }
    return data[employerId]
}

Đoạn code này hoạt động như sau:
  1. Nếu thông tin employer chưa crawl lần nào thì crawl, lưu lại rồi trả về
  2. Nếu thông tin employer đã có từ trướcthì
    1. Lên lịch crawl lại thông tin employer để update thông tin cũ
    2. Trả về thông tin đã crawl từ trước để đảm bảo user không phải đợi lâu
Sử dụng cơ chế đó, user sẽ chỉ cần đợi thông tin mỗi employer 1 lần duy nhất và không bao giờ phải đợi nữa, trong khi vẫn đảm bảo thông tin employer luôn được cập nhật thường xuyên. Chỉ thế thôi :D

3. Hiển thị ra màn hình​


Code "backend" thế là xong! Giờ ta cần hiển thị nó ra cho gọn gàng đẹp mắt để tiện dùng.

JavaScript:
(function() {
    'use strict';
    const init = {}

    for (const adv of document.querySelectorAll('.adv-line')) {
        // Lấy ID employer
        const employerId = adv.querySelector('a.ref-block-av').href.match(/\d+/g)[0]
        const employerClass = `employer-${employerId}-tasks`

        // Thêm element hiển thị thông tin tasks của employer
        const template = document.createElement('template')
        template.innerHTML = `<span style="white-space: nowrap" translate="no" class="${employerClass}">
            <b class="active-tasks" title="Active tasks" style="color: green; font-size: 1.25rem"></b>
            <span>/</span>
            <b class="total-tasks" title="Total tasks"></b>
        </span>`
        adv.querySelector('.adv-line-cell-1').append(template.content.firstChild)

        // Thêm element hiển thị thông tin member của employer
        template.innerHTML = `<span style="margin-right: 14px" translate="no" class="${employerClass}">
            <span class="member-info"></span>
        </span>`
        adv.querySelector('.advmoder > div > span:first-child').prepend(template.content.firstChild)

        if (!init[employerId]) {
            // Chỉ chạy task getEmployerCached nếu employerId chưa khởi tạo (chỉ chạy 1 lần duy nhất)
            getEmployerCached(employerId).then(employerData => {
                // Update thông tin trong web bằng dữ liệu lấy được
                for (const elem of document.querySelectorAll(`.${employerClass} .active-tasks`)) {
                    elem.innerHTML = employerData.active_tasks
                }
                for (const elem of document.querySelectorAll(`.${employerClass} .total-tasks`)) {
                    elem.innerHTML = employerData.total_tasks
                }
                for (const elem of document.querySelectorAll(`.${employerClass} .member-info`)) {
                    elem.innerHTML = `${employerData.name} ${employerData.id}`
                }
            })
            init[employerId] = true
        }
    }
})();

Đoạn này hoá ra lại dài dòng và nhiều trò mới nhất :D Chúng ta cùng nhau điểm qua từng thứ một nhé.

Đầu tiên để ý đoạn const init = {}. Biến init này có vai trò lưu lại thông tin rằng employer với ID đó đã được khởi tạo (lên lịch task crawl employer data) hay chưa. Nhờ vậy với mỗi employer trong page, ta chỉ chạy code crawl của employer đó đúng 1 lần duy nhất. Nếu không nhiều task getEmployerCached sẽ được gọi cùng lúc, dẫn đến việc crawl thông tin employer này bị lặp lại và làm chậm đi việc crawl thông tin các employer khác.

Tiếp theo, để ý việc sử dụng <template> nhé. Sở dĩ dùng cách này vì lí do: ngắn gọn. Cách này ngắn gọn và nhìn đẹp hơn cách sử dụng document.createElement() nhiều, cũng như code dễ hiểu, tường minh hơn hẳn.

Cuối cùng là attribute translate="no". Attribute này giúp cho browser nhận ra thông điệp ý nghĩa: "Đừng biến số '1' của t thành 'one', làm zdậy chi?!?"

Và một lưu ý nho nhỏ nữa là việc gói gọn chúng vào (function() {...})();. Câu lệnh này đảm bảo rằng phần code bên trong chỉ chạy khi browser đã load xong page hiện tại, tránh các lỗi không đáng có do page chưa load xong hoàn toàn, thiếu element, thiếu nội dung backend,...

4. Đem đống code này ném lên OpenUserJS (hoặc cài vào TamperMonkey)​


Để làm việc này, thay vì sử dụng template gốc của TamperMonkey, ta sẽ sử dụng cấu trúc của OpenUserJS. Đoạn code hoàn chỉnh sẽ trông như sau

JavaScript:
// ==UserScript==
// @namespace    https://github.com/KhanhhNe
// @name         SeosprintPlus
// @description  Chiến Seosprint siêu đỉnh cùng KhanhhNe và MMO4Me
// @icon         https://github.com/KhanhhNe/CodeBanana-Tutorials/raw/main/seosprintplus/logo.png
// @copyright    2021, KhanhhNe (https://github.com/KhanhhNe)
// @license      CC-BY-SA-3.0; http://creativecommons.org/licenses/by-sa/3.0/
// @license      MIT
// @version      1.0.0
// @include      https://seosprint.net/earn-task/*
// @grant        none
// ==/UserScript==

// ==OpenUserJS==
// @author       KhanhhNe
// ==/OpenUserJS==


/*jshint esversion: 9 */
/*jshint asi: true */


let data;
// Lấy dữ liệu từ localStorage
try {
    data = JSON.parse(localStorage.getItem('employersData')) || {}
} catch (e) {
    data = {}
}

// Update liên tục dữ liệu data lên localStorage
const int = setInterval(() => localStorage.setItem('employersData', JSON.stringify(data)), 1000)
// Ngưng update dữ liệu sau 20s vì không còn dữ liệu mới nữa (đã crawl đầy đủ)
setTimeout(() => clearInterval(int), 20000)
...

Thế là xong! Bạn có thể tìm và cài đặt bản code full qua link sau: https://openuserjs.org/scripts/KhanhhNe/SeosprintPlus


Minigame​


Phần hướng dẫn hôm nay thế là xong. SeosprintPlus là một dự án tool mình sẽ duy trì lâu dài. Tuy nhiên mình lại không phải "chiên da" trong lĩnh vực cày bừa này, vì vậy mạn phép dùng ít quà mọn nhờ mọi người giúp đỡ :D Chi tiết như sau

Mọi người hãy comment một tính năng/đề xuất mới, comment đạt được lược react nhiều nhất hoặc hay nhất sẽ được hiện thực hoá trong phiên bản tiếp theo. Chủ nhân của comment may mắn cũng nhận được phần quà trị giá lên đến $1,000 cụ thể là 0.5MR, hy vọng sẽ là một món quà cho người comment cũng như động lực nho nhỏ cho các bạn để nghĩ ra những ý tưởng thật hay ho vì không chỉ nhận được quà, bạn còn được bộ tool miễn phí, mất gì đâu nào!


Lời kết​


Bài viết hướng dẫn cho hôm nay chỉ đến đây thôi. Hy vọng mọi người biết thêm một chút kiến thức hữu ích cho bản thân. Nếu các bạn thấy hay (hoặc thấy thích việc đọc những bài như thế này dù không hiểu lắm/chả bao giờ làm theo) thì hãy nhấn Like (thả tim cũng được) và comment bên dưới để mình biết còn có người thích những gì mình viết ra nhé ❤️ Và đừng quên tham gia minigame để nhận phần quà siêu hấp dẫn, siêu to khổng lồ nha.

Chúc mọi người một ngày vui vẻ và kiếm được thật nhiều tiền!!!

Một vài chiếc link hữu ích​



View attachment 181218
1629087653646.png
sao mình cài rồi mà mở nó không chạy nhỉ? Mình thao tác saiởđoạn nào hả b?
 

Update 16/8/2021​

Mình đã test lại và phát hiện ra lỗi tool không bắt được toàn bộ URL liên quan đến list tasks. Mình đã update lại trên Github và OpenUserJS, mọi người có thể update bằng link cũ nhé.

1629095792206.png
 

Update lần 2 16/8/2021​

Mình thấy nhiều bạn thắc mắc cách sử dụng nên mình làm 1 video ngắn hướng dẫn cài đặt và sử dụng tool. Chúc mọi người kiếm tiền vui vẻ :D

 

Update lần 2 16/8/2021​

Mình thấy nhiều bạn thắc mắc cách sử dụng nên mình làm 1 video ngắn hướng dẫn cài đặt và sử dụng tool. Chúc mọi người kiếm tiền vui vẻ :D

làm vid như không :V
chưa thấy đoạn tool nó chạy luôn
 
Làm sao nữa để nó hoạt động vậy b
làm vid như không :V
chưa thấy đoạn tool nó chạy luôn
Sau khi cài đặt xong vào lại seosprint là bạn sẽ thấy sự thay đổi trong giao diện, không cần làm thêm bước gì nữa cả, chỉ cần reload lại trang thôi. Trường hợp bạn vào vẫn không thấy khác biệt gì thì gửi screenshot cho mình bao gồm URL, tab Console của DevTools (F12 -> Console), hình ảnh hiển thị khi bạn click vào nút TamperMonkey trên trình duyệt, mình sẽ check cho bạn xem tại sao.

Hình ảnh tool chạy từ giây 0:13 của video.

Tab console của DevTools
1629100185873.png


Menu hiện ra khi click vào nút TamperMonkey
1629100222581.png
 
Sau khi cài đặt xong vào lại seosprint là bạn sẽ thấy sự thay đổi trong giao diện, không cần làm thêm bước gì nữa cả, chỉ cần reload lại trang thôi. Trường hợp bạn vào vẫn không thấy khác biệt gì thì gửi screenshot cho mình bao gồm URL, tab Console của DevTools (F12 -> Console), hình ảnh hiển thị khi bạn click vào nút TamperMonkey trên trình duyệt, mình sẽ check cho bạn xem tại sao.
Mình làm y chang video nhưng không có thay đổi gì hết bạn
 

Attachments

  • 1.png
    1.png
    291.5 KB · Views: 84

Announcements

Today's birthdays

Forum statistics

Threads
418,229
Messages
7,068,835
Members
170,372
Latest member
0388811472
Back
Top Bottom