Skip to content

Instantly share code, notes, and snippets.

@clowestab
Created November 10, 2018 16:50
Show Gist options
  • Save clowestab/7d5bcbdf1369d2e41b1facb97343e967 to your computer and use it in GitHub Desktop.
Save clowestab/7d5bcbdf1369d2e41b1facb97343e967 to your computer and use it in GitHub Desktop.
A script for synchronising your Instagram posts with your Ghost blog as discussed on my blog (https://thomasclowes.com)
const Parser = require('rss-parser');
const request = require('request');
const fs = require('fs');
const mime = require('mime-types');
const slugify = require('slugify');
//Enter your ghost credentials here
const clientId = "ghost-frontend";
const clientSecret = "enter-your-secret";
const username = "enter-your-email";
const password = "enter-your-password";
//Function to query the ghost API for an access token with which to sign requests
const getAccessToken = function() {
console.log("Get Access Token Called");
const tokenResponse = request
.post('https://thomasclowes.com/ghost/api/v0.1/authentication/token', function (e, r, body) {
console.log(e);
const parsed = JSON.parse(body);
const accessToken = parsed.access_token;
console.log(accessToken);
//Once we have an access token we can fetch our Instagram posts
getInstagramPosts(accessToken);
})
.form({
'username': username,
'password': password,
'grant_type': 'password',
'client_id': clientId,
'client_secret':clientSecret
});
}
//We will hold our post objects fetched and parsed from our feed globally
//So we dont have to pass them around functions. This is lame.
let itemsArray;
//Function for pulling Instagram posts from my personal QueryFeed RSS Feed
const getInstagramPosts = function(accessToken) {
(async () => {
const feedUrl = 'https://queryfeed.net/instagram?q=thomasclowes';
const feed = await parser.parseURL(feedUrl);
console.log("Feed Title: " + feed.title);
console.log("Looping through parsed Instagram posts");
//Set our posts object in our global var
itemsArray = feed.items;
//Fetch the first image
getAndGo(accessToken);
return true;
})();
}
//Function that pulls the image file for the next Instagram post
//And calls to upload it to my server
const getAndGo = function(accessToken) {
const item = itemsArray.shift();
const imageUrl = item.enclosure.url;
const instagramLink = item.link;
const publishDate = item.pubDate; //Format Tue, 30 Oct 2018 15:59:02 +0900
const description = item.contentSnippet;
//Upload the image
uploadImage(accessToken, imageUrl, publishDate, description, instagramLink);
}
//Function which handles uploading an image to the server
const uploadImage = function(accessToken, imageUrl, publishDate, description) {
console.log("Upload image called");
const filename = imageUrl.substr(imageUrl.lastIndexOf('/') + 1);
const fullFilename = "/home/scripts/images/" + filename;
//Check if that file already exists
if (fs.existsSync(fullFilename)) {
console.log("File (" + fullFilename + ") already exists");
console.log("This post has likely already been posted");
//Get the next image
getAndGo(accessToken);
return false;
//File doesnt already exist
} else {
//Get the image and write it to the file system
request(imageUrl)
.pipe(fs.createWriteStream(fullFilename))
.on('error', function (err) {
console.log("Error saving image");
console.log(err);
})
.on('close', function(){
console.log('Done saving image');
const type = mime.lookup(fullFilename);
var uploadOptions = {
url: 'https://thomasclowes.com/ghost/api/v0.1/uploads',
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'image/jpeg'
},
formData: {
uploadimage: fs.createReadStream(fullFilename),
}
};
//Upload the image through the Ghost API
//This essentially makes a copy of the image
const postResponse = request.post(uploadOptions, function (error, r, imageUrl) {
if (error) {
console.log("Error uploading image");
console.log(error);
}
//URL returned from Ghost API is enclosed in ". Remove these.
const processedImageUrl = imageUrl.replace(/"/g,'');
console.log(processedImageUrl);
console.log("Uploaded Image");
//Create a Ghost post using the details we have
submitPost(accessToken, processedImageUrl, publishDate, description);
return true;
});
});
}
}
//Function that creates a Ghost post through the Ghost public API
const submitPost = function(accessToken, imageUrl, publishDate, description) {
const mobileDoc = {
version: "0.3.1",
atoms: [],
cards: [
["image", {"caption": description, "src": imageUrl}],
["markdown",{"markdown":" This post was originally posted on [my Instagram](https://instagram.com/thomasclowes)."}]
],
markups: [],
sections: [[10,0],[10,1],[1,"p",[]]]
}
//We will make post titles the first sentence of description
const postTitle = description.substr(0, description.indexOf('.') !== -1 ? description.indexOf('.') : description.length);
const postSlug = slugify("Instagram " + postTitle);
const postTags = [];
//We will tag all posts with 'Instagram'
postTags.push({"name": "instagram"});
//Filter out any hash tags and set them as tags on the post
const hashTags = description.split(' ').filter(v => v.startsWith('#'));
hashTags.forEach(function(hashTag) {
postTags.push({"name": hashTag.replace("#", "")});
});
//Post data object in a format accepted by the Ghost API
const postData = {
"posts": [{
author: "1",
//featured: false,
feature_image: imageUrl,
language: "en_GB",
mobiledoc: JSON.stringify(mobileDoc),
meta_description: description,
custom_excerpt: description,
meta_title: postTitle,
//page: false,
published_by: null,
slug: postSlug,
status: "published",
tags: postTags,
title: postTitle,
published_at: publishDate
}]
};
var options = {
url: 'https://thomasclowes.com/ghost/api/v0.1/posts',
headers: {
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
}
};
//Submit the post to the API
const postResponse = request.post(options, function (e, r, body) {
console.log(body);
console.log("Posted");
//If there are still posts to process
//Lets process the next one
if (itemsArray.length != 0) {
console.log("Getting the next item. " +itemsArray.length + " left.");
getAndGo(accessToken);
}
return true;
}).form(postData);
}
//Get the Access Token/Begin
getAccessToken();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment