Click here to Skip to main content
15,031,495 members
Articles / Desktop Programming / Win32
Article
Posted 26 Feb 2021

Tagged as

Stats

1.9K views
4 bookmarked

Fetching Most Recent Minds.com Posts

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
26 Feb 2021CPOL1 min read
Minds.com is a blockchain-based social network where users can earn money or cryptocurrency for using it.
Minds, and tokens can be used to boost their posts or crowdfund other users. Minds has been described as more privacy-focused than mainstream social media networks. I looked for a way to get the most recent posts programmatically.

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).

Image 1

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:

C++
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:

C++
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:

C++
SG_ReadMindsFeed("ottman");

I created a class and the SG_ReadMindsFeed goes like this:

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

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

Then the read function is called:

C++
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:

C++
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.

C++
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Michael Haephrati
CEO Secured Globe, Inc.
United States United States
Michael Haephrati, Musician and CEO and co-founder of Secured Globe, Inc. Worked on many ventures starting from HarmonySoft, designing Rashumon, the first Graphical Multi-lingual word processor for Amiga computer. During 1995-1996 he worked as a Contractor with Apple at Cupertino.

You can also Buy me coffee.





Comments and Discussions

 
-- There are no messages in this forum --