/* tslint:disable:no-var-requires max-line-length no-console */ const envPath = process.env.NODE_ENV ? `.env.${process.env.NODE_ENV}` : '.env.development'; require('dotenv').config({ path: envPath }); import fetch from 'node-fetch'; import moment from 'moment'; import axios from 'axios'; import gql from 'graphql-tag'; import plugins from '../../plugins/datocms-access/'; import { TYPE } from '../../plugins/datocms-access/constants'; import { Builder, Message } from '@floom/xbot'; const {api} = require('../../plugins/datocms-access').default; const PAGE_SIZE = 100; const INSTAGRAM_URL = 'https://www.instagram.com/'; const QUERY = gql` query Instagram($pageSize: IntType, $beginningNode: IntType) { allInstagramPosts(first:$pageSize, skip: $beginningNode) { posted mediaId id merchant { id } } } `; const loadInstagramItemsFromDato = async (beginningNode): Promise => { return api.query( { query: QUERY, variables: {pageSize: PAGE_SIZE, beginningNode}, fetchPolicy: 'no-cache' } ); }; const getAllInstagramPosts = async (): Promise => { let datoItems = await loadInstagramItemsFromDato(0) .then(response => response.data.allInstagramPosts); if (datoItems.length === PAGE_SIZE) { let beginningNode = PAGE_SIZE; let done = false; while (!done) { const extraItems = await loadInstagramItemsFromDato(beginningNode) .then(response => response.data.allInstagramPosts); datoItems = datoItems.concat(extraItems); beginningNode += PAGE_SIZE; if (extraItems.length !== PAGE_SIZE) { done = true; } } } return datoItems; }; async function fetchMedias(instagramHandle: string): Promise { const res = await fetch(`${INSTAGRAM_URL}${instagramHandle}/?__a=1`); const body = await res.json(); const media = body.graphql; return media; } const buildInstagramData = async (siteConfig: any, merchantProjectId: string, datoItems: any): Promise => { const medias = await fetchMedias(siteConfig.instagramHandle); if (!medias) { console.log(`no medias found for ${siteConfig.instagramHandle}`); return; } let images = []; try { images = medias.user.edge_owner_to_timeline_media.edges.map(x => x.node); } catch (e) { console.log(`no images found for ${siteConfig.instagramHandle}`); return; } const filteredDatoItems = datoItems.filter(x => x.merchant ? x.merchant.id === siteConfig.merchant : false); const datoItemsToRemove = filteredDatoItems .filter((datoItem: any) => { return !images.some((media: any) => { return media.id === datoItem.mediaId; }); }); console.log(`${datoItemsToRemove.length} to remove`); const removed = await Promise.all(datoItemsToRemove .map((datoItem: any) => { return plugins.client.items.destroy(datoItem.id) .catch((error: any) => { console.error(error); }); })); console.log(`${removed.length} removed for ${siteConfig.instagramHandle}`); const newImages = images.filter((media: any) => { return !filteredDatoItems.some((datoItem: any) => { return datoItem.mediaId === media.id; }); }); console.log(`${newImages} new posts`); console.log(`${siteConfig.instagramHandle} merchant: ${merchantProjectId}`); const insertPromises = newImages.map((media: any) => { const imageUrl = media.display_url; const link = `https://www.instagram.com/p/${media.shortcode}/`; return plugins.client.uploadImage(imageUrl) .then((image: any) => { return plugins.client.items.create({ itemType: TYPE.instagramPost, image, mediaId: media.id, posted: moment.unix(media.taken_at_timestamp).format('YYYY-MM-DDTHH:mm:ss.SSS'), merchant: siteConfig.merchant, link }); }).then(() => { const message = Message.builder() .addHeader('Uploaded Instagram post') .section(section => { section.addTitledTextField('Project ID', merchantProjectId); section.addTitledTextField('Link', link); section.withImage(imageUrl); }) .build(); return post(message); }); }); const inserted = await Promise.all(insertPromises); console.log(`Inserted ${inserted.length} for ${siteConfig.instagramHandle}`); if (!!newImages.length || !!datoItemsToRemove.remove) { console.log(`initiated deployment for ${siteConfig.instagramHandle} :::: https://${merchantProjectId}.netlify.com`); const message = new Builder() .addHeader('Updating Instagram posts') .section(section => { section.addTitledTextField('Project ID', merchantProjectId); section.addTitledLinkField( 'Handle', `@${siteConfig.instagramHandle}`, `https://instagram.com/${siteConfig.instagramHandle}` ); }) .build(); await post(message); await triggerBuild(merchantProjectId); } }; async function triggerBuild(projectId: string): Promise { try { // tslint:disable-next-line:max-line-length await axios.get(`https://${projectId}.netlify.com/.netlify/functions/build-site?build-hook-key=${process.env.BUILD_HOOK_KEY}&source=Instagram-Sync`); } catch (e) { console.log(`cannot build site: https://${projectId}.netlify.com`); console.error(e.message); } } async function post(message: Message): Promise { return message.post(process.env.SLACK_BOT_KEY, 'x-sync'); } async function execute(): Promise { let siteConfigs = await plugins.client.items.all({ 'filter[type]': TYPE.siteConfig }); siteConfigs = siteConfigs.filter(x => !!x.instagramHandle); const merchants = await plugins.client.items.all({ 'filter[type]': TYPE.merchant }); const instagramPosts = await getAllInstagramPosts(); for (const siteConfig of siteConfigs) { const merchant = merchants.find(x => x.id === siteConfig.merchant); if (!merchant) { console.log(`No merchant found for ${siteConfig.instagramHandle}`); } else { await buildInstagramData(siteConfig, merchant.projectId, instagramPosts); } } } execute() .then(result => { console.log(result || 'Finished'); process.exit(0); }) .catch(error => { console.error(error); process.exit(1); });