65.9K
CodeProject is changing. Read more.
Home

Fetching Most Recent Minds.com Posts

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Feb 26, 2021

CPOL

1 min read

viewsIcon

3680

Minds.com is a blockchain-based social network where users can earn money or cryptocurrency for using it.

Introduction

I needed to fetch the most recent posts from a given Minds.com user ID. To do so, I wanted to use the Minds.com API.

Building Blocks

One of the key building blocks to such project, would be libCurl. I used it as a static library. The .lib file is included in the article's source code, however you can read about using libCurl as a static library here.

Note: WriteLogFile() is one of my old logging functions described in this article.

Getting the User Unique Number

Usually, we will need to supply our function a user ID, such as "minds". You can also check my own profile "haephrati" (https://www.minds.com/haephrati).

How Minds.com Requests API Works

Minds.com provides an API call which looks like that:

?sync=1&limit=2&as_activities=0&export_user_counts=0&from_timestamp=

If we break this into its ingredients, we would get the following attributes, and here they are, along with a suggested value:

    int sync                      = 1;
    int limit                     = 150;
    int as_activities             = 0;
    int export_user_counts        = 0;
    string from_timestamp         = "";

Initiating libCurl

First, we initiate the Curl object:

curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init();

Reading Posts

I wanted a simple function which I can use to read all posts of a given user. So if the user is ottman, I will call:

SG_ReadMindsFeed("ottman");

I created a class and the SG_ReadMindsFeed goes like this:

bool SG_ReadMindsFeed(string userID)
{
    int count = 100;

    MindsFeedReader reader(count);
    return reader.read(userID);
}

Then the read function is called:

bool MindsFeedReader::read(string userID)
{
    userid = userID;
    
    if (readGUID() == false)
    {
        return false;
    }

    total_read_count = 0;
    while (total_read_count < count)
    {
        if (readNext() == false)
        {
            return false;
        }

        if (read_count <= 0)
        {
            break;
        }

        total_read_count += read_count;
    }
    return true;
}

The GUID

The actual part that differs in each user in the feed is a long number called the GUID. It can be composed as follows:

bool MindsFeedReader::readGUID()
{
    string url = CHANNEL + userid;

    // Initialize the CURL request.
    CURL* curl = curl_easy_init();
    if (curl == NULL)
    {
        WriteLogFile(L"Cannot intialize the CURL request");
        return false;
    }

    CURLcode result;
    string response;

    // Perform the CURL request.
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_string);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
    result = curl_easy_perform(curl);
    curl_easy_cleanup(curl);

    // Check the response success.
    if (result != CURLE_OK)
    {
        WriteLogFile(L"Fail to perform the CURL request");
        return false;
    }

    // Parse the JSON response.
    JSONValue* jsonRoot = JSON::Parse(response.c_str());
    if (response == "")
    {
        WriteLogFile(L"Response empty. CURL request failed");
        return false;

    }
    JSONObject jsonRootObject = jsonRoot->AsObject();

    // Read the post details.
    JSONValue* jsonChannel = jsonRootObject[L"channel"];
    JSONObject jsonChannelObject = jsonChannel->AsObject();

    // Read GUID.
    JSONValue* jsonGUID = jsonChannelObject[L"guid"];
    if (jsonGUID == NULL || !jsonGUID->IsString())
    {
        WriteLogFile(L"Invalid response. CURL request failed");
        return false;
    }

    wstring wguid = jsonGUID->AsString();
    guid = string(wguid.begin(), wguid.end());

    return true;
}

Reading Chunks of Data

As you can see in the source code below, here is how we read a batch of posts (up to 10 in this case), then move to the next batch.

bool MindsFeedReader::readNext()
{
    // Build the query string.
    // ex. ?sync=1&limit=2&as_activities=0&export_user_counts=0&from_timestamp=
    string url_with_params = FEED + guid + ACTIVITIES;
    url_with_params += string("?") + PARAM_SYNC + "=" + to_string(sync);
    url_with_params += string("&") + PARAM_LIMIT + "=" + to_string(limit);
    url_with_params += string("&") + PARAM_AS_ACTIVITIES + "=" + to_string(as_activities);
    url_with_params += string("&") + PARAM_EXPORT_USER_COUNTS + 
                    "=" + to_string(export_user_counts);
    url_with_params += string("&") + PARAM_FROM_TIMESTAMP + "=" + from_timestamp;

    // Initialize the CURL request.
    CURL* curl = curl_easy_init();
    if (curl == NULL)
    {
        WriteLogFile(L"Cannot intialize the CURL request");
        return false;
    }

    CURLcode result;
    string response;

    // Perform the CURL request.
    curl_easy_setopt(curl, CURLOPT_URL, url_with_params.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_string);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
    result = curl_easy_perform(curl);
    curl_easy_cleanup(curl);

    // Check the response success.
    if (result != CURLE_OK)
    {
        WriteLogFile(L"Fail to perform the CURL request");
        return false;
    }

    // Parse the JSON response.
    JSONValue* jsonRoot = JSON::Parse(response.c_str());
    if (response == "")
    {
        WriteLogFile(L"Response empty. CURL request failed");
        return false;
    }
    
    JSONObject jsonRootObject = jsonRoot->AsObject();

    // Update the next timestamp.
    JSONValue* jsonLoadNext = jsonRootObject[L"load-next"];
    wstring loadNext = jsonLoadNext->AsString();
    from_timestamp = string(loadNext.begin(), loadNext.end());

    // Read the post details.
    JSONValue* jsonEntities = jsonRootObject[L"entities"];
    JSONArray jsonEntitiesArray = jsonEntities->AsArray();

    read_count = 0;
    for (JSONValue* jsonEntitiesItem : jsonEntitiesArray)
    {
        ++read_count;
        WriteLogFile(L"Showing post %d", total_read_count + read_count);

        JSONObject jsonEntitiesItemObject = jsonEntitiesItem->AsObject();

        JSONValue* jsonEntity = jsonEntitiesItemObject[L"entity"];
        if (jsonEntity->IsObject())
        {
            JSONObject jsonEntityObject = jsonEntity->AsObject();

            JSONValue* jsonMessage = jsonEntityObject[L"message"];
            if (jsonMessage != NULL && jsonMessage->IsString())
            {
                wstring message = jsonMessage->AsString();
                WriteLogFile(L"\n\nMessage:\n%s\n\n", message.c_str());
            }

            JSONValue* jsonThumbnailSrc = jsonEntityObject[L"thumbnail_src"];
            if (jsonThumbnailSrc != NULL && jsonThumbnailSrc->IsString())
            {
                wstring thumbnailSrc = jsonThumbnailSrc->AsString();
                WriteLogFile(L"\n\nThumbnail Src:\n%s\n\n", thumbnailSrc.c_str());
            }

            JSONValue* jsonTimeCreated = jsonEntityObject[L"time_created"];
            if (jsonTimeCreated != NULL && jsonTimeCreated->IsString())
            {
                wchar_t buffer[TIMESTAMP_BUFFER_SIZE];
                wstring wstrTimeCreated = jsonTimeCreated->AsString();
                string strTimeCreated = string(wstrTimeCreated.begin(), wstrTimeCreated.end());
                long timestamp = atol(strTimeCreated.c_str());
                
                time_t rawtime = (const time_t)timestamp;
                struct tm* timeinfo;
                timeinfo = localtime(&rawtime);
                
                wcsftime(buffer, TIMESTAMP_BUFFER_SIZE, L"%Y-%m-%dT%H:%M:%S.%z%Z", timeinfo);
                WriteLogFile(L"\n\nTime Created:\n%s\n\n", buffer);
            }

            JSONValue* jsonTimeUpdated = jsonEntityObject[L"time_updated"];
            if (jsonTimeUpdated != NULL && jsonTimeUpdated->IsString())
            {
                wchar_t buffer[TIMESTAMP_BUFFER_SIZE];
                wstring wstrTimeUpdated = jsonTimeUpdated->AsString();
                string strTimeUpdated = string(wstrTimeUpdated.begin(), wstrTimeUpdated.end());
                long timestamp = atol(strTimeUpdated.c_str());

                time_t rawtime = (const time_t)timestamp;
                struct tm* timeinfo;
                timeinfo = localtime(&rawtime);

                wcsftime(buffer, TIMESTAMP_BUFFER_SIZE, L"%Y-%m-%dT%H:%M:%S.%z%Z", timeinfo);
                WriteLogFile(L"\n\nTime Updated:\n%s\n\n", buffer);
            }
        }
        else
            break;
    }
    return true;
}

History

  • 26th February, 2021: Initial version