Source

plugins/lifehacks.js

// Modules to install separately
const ConvertBase64 = require('../lib/convertBase64');
const convert64 = new ConvertBase64();
const base = "https://www.instagram.com/";
const gqlUri = "https://www.instagram.com/graphql/query";
const cheerio = require('cheerio');
const rp = require('request-promise');
const profiles = {
    "en": "5.min.crafts",
    "es": "ideas.en.5.minutos"
}
var profilePosts = {
    "en": [],
    "es": []
}

getLastPosts = async (username) => {
    return new Promise(async (resolve, reject) => {
        let profile = await getProfile(base + username);
        if (profile !== false) {
            let mediaLite = [];
            let media = profile.graphql.user.edge_owner_to_timeline_media.edges;
            for (let i = 0; i < media.length; i++) {
                mediaLite.push(media[i].node)
            }
            resolve(mediaLite);
        } else {
            reject("User not found!");
        }
    });
}

getProfile = async (url) => {
    try {
        let html = await rp(url);
        let $ = cheerio.load(html);
        let data;
        for (let i = 0; i < $('script').length; i++) {
            if ($('script')[i].children.length > 0) {
                if ($('script')[i].children[0].data.startsWith("window._sharedData =")) {
                    data = $('script')[i].children[0].data;
                }
            }
        }
        if (data) {
            data = data.replace("window._sharedData = ", "").slice(0, -1);
            data = JSON.parse(data);
            let profile = data.entry_data.ProfilePage[0]
            return profile;
        } else {
            return false;
        }
    } catch (error) {
        return false;
    }
}

getProfileByLogged = async params => {
    try {
        const profileleUri = `https://www.instagram.com/${params.username}/?__a=1`
        let options = {
            uri: profileleUri,
            headers: {
                'user_agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.3",
                'cookie': params.cookie
            },
            json: true
        };
        let profile = await rp(options);
        if (profile.graphql.user.is_private) {
            throw new Error("Profile is private!")
        } else {
            return profile;
        }

    } catch (error) {
        if (error.statusCode === 404) {
            throw new Error("User not found!");
        } else throw error;
    }
}

getAllPosts = async params => {
    try {

        let profile = await getProfileByLogged(params);
        let options = {
            uri: gqlUri,
            qs: {
                query_hash: params.query_hash,
                first: params.media_count,
                id: profile.graphql.user.id
            },
            headers: {
                'user_agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.3",
                'cookie': params.cookie
            },
            json: true
        };

        let endCursor = profile.graphql.user.edge_owner_to_timeline_media.page_info.end_cursor;
        let hasNextPage = profile.graphql.user.edge_owner_to_timeline_media.page_info.has_next_page;

        if ((params.media_count <= 50 && params.media_count > -1) || hasNextPage === false) {
            options.qs.first = 50;
            let media = await rp(options);
            let m = [];
            let edges = media.data.user.edge_owner_to_timeline_media.edges;
            for (let i = 0; i < edges.length; i++) {
                m.push(edges[i].node)
            }
            return m;
        } else {

            let rParams = {
                endCursor, hasNextPage
            }
            let media = await pullAllMedia(rParams, options);
            let m = [];
            let edges = media.data.user.edge_owner_to_timeline_media.edges;
            for (let i = 0; i < edges.length; i++) {
                m.push(edges[i].node)
            }
            return m;
        }

    } catch (error) {
        throw error;
    }
}


pullAllMedia = async (params, options) => {
    try {
        let mediaLimit = options.qs.first;
        let endCursor = params.endCursor;
        let hasNextPage = params.hasNextPage;
        options.qs.first = 50;
        let results = [];
        let r;
        while (hasNextPage) {
            let result = await rp(options);
            const pageInfo = result.data.user.edge_owner_to_timeline_media.page_info;
            hasNextPage = pageInfo.has_next_page
            endCursor = pageInfo.end_cursor;
            options.qs.after = endCursor;
            let edges = result.data.user.edge_owner_to_timeline_media.edges;
            results.push.apply(results, edges);
            if (hasNextPage === false) {
                r = result;
                break;
            } else {
                if (mediaLimit !== -1 && results.length >= mediaLimit) {
                    r = result;
                    break;
                }
            }
        }

        if (results.length > mediaLimit && mediaLimit !== -1) {
            results = results.slice(0, mediaLimit);
        }
        r.data.user.edge_owner_to_timeline_media.edges = results;
        return r;
    } catch (error) {
        throw error;
    }
}

loadCachePosts = async (language, params) => {
    getAllPosts({
        username: profiles[language],
        query_hash: params.query_hash,
        media_count: params.media_count || -1,
        cookie: params.cookie
    })
    .then(posts => {
        let mediaPosts = posts.filter(el => el.is_video);
        profilePosts[language] = mediaPosts;
    })
    .catch(err => {
        console.error('Error loading instagram posts', err);
    })
}

const defaultConfig = {
    idChat: '',
    language: 'en',
    messageError: '*Ooops, an error occurred while trying to get the video, try again later*',
}
/**
 * Plugin to get videos from life hacks
 * @function lifehacks
 * @memberof Plugins
 * @param {string} idChat - Chat id to send the new image to
 * @param {string} language - Language to search
 * @param {string} messageError - Message to send in case of error
 * @param {string} messageBadLanguage - Message to send when the indicated language is not supported
 */
module.exports = {
    /**
    * Id - Name of the plugin to use
    * @property {string}  id - Name of the plugin to use
    */
    id: 'lifehacks',
    /**
    * Initial setting function
    * @param {object} data - Initial information for the plugin
    * @param {string} data.query_hash 
    * @param {string} data.cookie
    * @param {number} data.media_count
    * @param {number} data.timeRefresh
    */
    setup(data) {
        if (
            data.query_hash && data.query_hash !== ''
            && data.cookie && data.cookie !== ''
        ) {
            let timeRefresh = data.timeRefresh || 120;
            for (var key in profiles) {
                loadCachePosts(key, {
                    query_hash: data.query_hash,
                    media_count: data.media_count || 1001,
                    cookie: data.cookie
                });
            }
            setInterval(() => {
                for (var key in profiles) {
                    loadCachePosts(key, {
                        query_hash: data.query_hash,
                        media_count: data.media_count || 1001,
                        cookie: data.cookie
                    });
                }
            }, 60000 * timeRefresh);
        }
    },
    plugin(_args) {
        const _this = this;
        const args = _this.mergeOpts(defaultConfig, _args);
        if (args.idChat !== '') {
            let profile = profiles[args.language];
            if (typeof profile !== 'undefined' && profile !== '') {
                if (profilePosts[args.language].length === 0) {
                    getLastPosts(profile)
                    .then(data => {
                        let mediaPosts = data.filter(el => el.is_video);
                        let index = Math.floor(Math.random() * mediaPosts.length);

                        convert64.convert(mediaPosts[index].video_url)
                        .then(async (res) => {
                            let base64Video = res.split(';base64,').pop();
                            _this.sendVideo({
                                "idChat": args.idChat, 
                                "caption": "",
                                "file": base64Video
                            })
                        })
                        .catch(err => {
                            _this.sendMessage({
                                "idChat": args.idChat, 
                                "message": args.messageError
                            });
                        })
                    })
                    .catch(err => {
                        _this.sendMessage({
                            "idChat": args.idChat, 
                            "message": args.messageError
                        });
                    })
                } else {
                    let index = Math.floor(Math.random() * profilePosts[args.language].length);

                    convert64.convert(profilePosts[args.language][index].video_url)
                    .then(async (res) => {
                        let base64Video = res.split(';base64,').pop();
                        _this.sendVideo({
                            "idChat": args.idChat, 
                            "caption": "",
                            "file": base64Video
                        })
                    })
                    .catch(err => {
                        _this.sendMessage({
                            "idChat": args.idChat, 
                            "message": args.messageError
                        });
                    })
                }
                
            } else {
                _this.sendMessage({
                    "idChat": args.idChat, 
                    "message": args.messageBadLanguage
                });
            }
        }
    }
};