Created
January 23, 2017 12:50
-
-
Save worst-developer/37f5b2c791d34802be92f6f28f0cfad1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Promise = require('bluebird'); | |
const ObjectId = require('sails-mongo/node_modules/mongodb').ObjectID; | |
/** | |
* Advertises a video. | |
* Creates adset, adcreatives, ads and links it to existing compaign and account | |
* | |
* @param {string} postMessage in the ad post | |
* @param {object} place {lat, lng, radius} | |
* @param {object} time {startTime, endTime} like 2015-12-16 22:59:59 UTC or Unix time | |
* @param {json} boostingParams object: | |
* @param {integer} boostingParams.budget amount in cents | |
* @param {string} boostingParams.callToActionUrl | |
* @param {string} boostingParams.gender | |
* @param {array} boostingParams.age Like [minAge, maxAge], | |
* @param {string} videoId id of video | |
* @param {string} published flag indicates post visibility | |
* @param {string} facebookPageId id of facebook page | |
* @param {string} pageToken fb page token | |
* @param {string} userId id of the user | |
*/ | |
module.exports.advertise = function (postMessage, place, time, boostingParams, videoId, published, facebookPageId, pageToken, userId) { | |
return new Promise(function (resolve, reject) { | |
let fbAdId; | |
let video; | |
let user; | |
const dataToSave = { | |
postMessage: postMessage, | |
budget: parseInt(boostingParams.budget), | |
currency: boostingParams.currency, | |
callToAction: boostingParams.callToActionUrl, | |
gender: boostingParams.gender, | |
age: boostingParams.age, | |
place: [ | |
{ | |
'latitude': place.lat, | |
'longitude': place.lng, | |
'radius': parseInt(place.radius), | |
'distance_unit': 'kilometer' | |
} | |
], | |
startTime: time.startTime, | |
endTime: time.endTime, | |
published: published, | |
pageToken: pageToken, | |
pageId: facebookPageId, | |
userId: new ObjectId(userId), | |
videoId: new ObjectId(videoId) | |
}; | |
const FBService = FacebookService( | |
sails.config.shuuttv.facebook.userToken, | |
pageToken | |
); | |
FacebookAd.create(dataToSave).then(function (fbAd) { | |
fbAdId = fbAd.id; | |
// in order to not wait we just return id | |
// that can be used to obtain boosting status | |
resolve({ fbAdId: fbAdId }); | |
return getUserAndVideo(videoId); | |
}).then((res) => { | |
user = res[0]; | |
dataToSave.videoOwnerId = user.id; | |
video = res[1]; | |
dataToSave.chargeBudget = video.budget; | |
}).then(() => { | |
return maybeCreateAdAccount(user); | |
}).then((facebookAdAccountId) => { | |
dataToSave.accountId = facebookAdAccountId; | |
// return FBService.createCampaign( | |
// postMessage.substr(0, 20) + ", fbAdId " + fbAdId, | |
// dataToSave.accountId, | |
// "VIDEO_VIEWS", | |
// process.env.NODE_ENV === 'production' ? "ACTIVE" : "PAUSED" | |
// ); | |
}).then((res) => { | |
// dataToSave.compaignId = res.id; | |
dataToSave.compaignId = 6035078288133; | |
console.log( | |
"Choosing payment method (amoutOfFreeBoostings, userId):", | |
user.amoutOfFreeBoostings, | |
video.userId | |
); | |
if (user.amoutOfFreeBoostings > 0) { | |
console.log("Decrease amoutOfFreeBoostings"); | |
dataToSave.isFreeBoosting = true; | |
return UserService.decreaseAmountOfFreeBoostings(user.id); | |
} else { | |
return PaymentService.auth(dataToSave.chargeBudget, user.id, dataToSave.currency, fbAdId).then(function (payment) { | |
dataToSave.paymentId = new ObjectId(payment.id); | |
}); | |
} | |
}).then(function () { | |
return FBService.createFacebookAdPost( | |
userId, | |
published, | |
dataToSave.pageId, | |
postMessage, | |
dataToSave.callToAction, | |
RouteService.url('cloudfront', { name: video.path }), | |
video.thumbnail | |
); | |
}).then(function (fbAdPost) { | |
dataToSave.postId = fbAdPost.id; | |
return tryToCreateAdset( | |
parseInt(process.env.AMOUNT_OF_BOOSTING_TRIES), | |
fbAdId, | |
dataToSave.place[0].radius, | |
dataToSave, | |
FBService | |
); | |
}).then(function (adSetRes) { | |
dataToSave.adsetId = adSetRes.id; | |
return FBService.createAdcreatives( | |
'Creative ' + postMessage, | |
dataToSave.accountId, | |
dataToSave.pageId, | |
dataToSave.postId, | |
dataToSave.callToAction, | |
RouteService.url('cloudfront', { name: video.thumbnail }), | |
postMessage | |
); | |
}).then(function (adCreativeRes) { | |
dataToSave.adcreativeId = adCreativeRes.id; | |
return FBService.createAds( | |
'Ad ' + postMessage, | |
dataToSave.accountId, | |
dataToSave.adsetId, | |
dataToSave.adcreativeId | |
); | |
}).then(function (adsRes) { | |
dataToSave.adId = adsRes.id; | |
dataToSave.status = "sent"; | |
delete dataToSave.userId; | |
delete dataToSave.videoId; | |
}).then(function () { | |
return handleSuccessfulBoosting(fbAdId, dataToSave, videoId); | |
}).catch(function (err) { | |
dataToSave.originalError = err; | |
dataToSave.error = [{ message: UtilsService.fetchError(err) }]; | |
return handleFailedBoosting( | |
fbAdId, | |
dataToSave, | |
user.apnsToken, | |
videoId, | |
user.lang, | |
err | |
); | |
}); | |
}); | |
}; | |
function getUserAndVideo(videoId) { | |
return Promise.all([ | |
VideoV2Service.getUserByVideoId(videoId), | |
Video.findOne({ id: videoId }) | |
]); | |
}; | |
/* | |
* Creates an ad account if a user does not have it yet | |
* Assigns sysuser to just created an ad account | |
* Assigns ad account to instagram account | |
*/ | |
function maybeCreateAdAccount(user) { | |
return "act_1027924497228108"; | |
if (!user.facebookAdAccountId) { | |
const FBService = FacebookService( | |
sails.config.shuuttv.facebook.userToken | |
); | |
let facebookAdAccountId; | |
console.log("Creating an ad account", user.id); | |
return FBService.createAdAccount( | |
sails.config.shuuttv.facebook.businessId, | |
user.email + ", " + user.id, | |
sails.config.shuuttv.facebook.appID, | |
sails.config.shuuttv.facebook.fundingId | |
).then((res) => { | |
return Users.update(user.id, {facebookAdAccountId: res.id}); | |
}).then((users) => { | |
facebookAdAccountId = users[0].facebookAdAccountId; | |
return FBService.updateUserPermissions( | |
facebookAdAccountId, | |
sails.config.shuuttv.facebook.userId, | |
sails.config.shuuttv.facebook.businessId, | |
"ADMIN" | |
); | |
}).then(()=>{ | |
return FBService.assignAdAccountToInstagramAccount( | |
sails.config.shuuttv.facebook.instagramId, | |
facebookAdAccountId.replace("act_", ""), | |
sails.config.shuuttv.facebook.businessId | |
); | |
}).then(()=>{ | |
return facebookAdAccountId; | |
}) | |
} | |
return user.facebookAdAccountId; | |
}; | |
function tryToCreateAdset(triesLeft, fbAdId, radius, dataToSave, FacebookService) { | |
dataToSave.triesToBoost = process.env.AMOUNT_OF_BOOSTING_TRIES - triesLeft; | |
dataToSave.place[0].radius = parseInt(radius); | |
return FacebookService.createAdsets( | |
'Budget ' + (dataToSave.budget / 100) + ", Radius " + (radius), | |
dataToSave.accountId, | |
dataToSave.compaignId, | |
{ | |
budget: dataToSave.budget, | |
place: dataToSave.place, | |
gender: dataToSave.gender, | |
age: dataToSave.age, | |
place: dataToSave.place, | |
}, | |
{ | |
startTime: dataToSave.startTime, | |
endTime: dataToSave.endTime, | |
}, | |
'VIDEO_VIEWS', | |
'ACTIVE', | |
'VIDEO_VIEWS' | |
).catch(function (err) { | |
if (err.error_user_title === "Radius too small" && triesLeft > 0) { | |
var newRadius = parseInt(dataToSave.place[0].radius) + 4; | |
console.log("Trying again with", newRadius); | |
console.log("Tries left", triesLeft); | |
return tryToCreateAdset(triesLeft - 1, fbAdId, newRadius, dataToSave, FacebookService); | |
}; | |
return MarketingService.cleanUp(dataToSave.postId, dataToSave.pageToken).then(function (status) { | |
console.log("Cleaned UP", status); | |
}).then(function () { | |
return Promise.reject(err); | |
}); | |
}); | |
} | |
function handleSuccessfulBoosting(fbAdId, dataToSave, videoId) { | |
return Promise.all([ | |
FacebookAd.updateNative({ _id: ObjectId(fbAdId) }, dataToSave), | |
VideoV2Service.updateBoostStatus(videoId, 'SCHEDULED_FOR_REVIEW') | |
]).then(() => { | |
console.log('Boosting Succeeded'); | |
console.log('Boost status changed to SCHEDULED_FOR_REVIEW'); | |
}); | |
}; | |
function handleFailedBoosting(fbAdId, dataToSave, apns, videoId, lang, err) { | |
let handle = [ | |
FacebookAd.updateNative({ _id: ObjectId(fbAdId) }, dataToSave) | |
]; | |
if (dataToSave.isFreeBoosting) { | |
console.log("Restore free boosting amount"); | |
handle.push( | |
UserService.increaseAmountOfFreeBoostings(dataToSave.videoOwnerId) | |
); | |
} | |
return Promise.all(handle).then((result) => { | |
sails.log.error(err); | |
console.log(result[0].result.nModified ? "FacebookAd error Saved" : "FacebookAd error not Saved"); | |
}); | |
}; | |
/** | |
* Revert changes | |
* @param {string} videoId id of video | |
*/ | |
module.exports.cleanUp = function (videoId, pageToken) { | |
return FacebookService( | |
sails.config.shuuttv.facebook.userToken, | |
pageToken | |
).removeVideoPost(videoId); | |
}; | |
/** | |
* Status of boosted ad | |
* @param {string} videoId id of video | |
*/ | |
module.exports.status = function (id) { | |
return FacebookAd.findNative({ _id: new ObjectId(id) }).then((r) => { | |
return r[0]; | |
}); | |
}; | |
/** | |
* Handle getting permissions to the page | |
* It will either: | |
* try to claim access to the Page if our System User does not have role yet | |
* Or try to assign permissions to our System User if user has already approved claimed access | |
* @param {string} pageId id of page | |
*/ | |
module.exports.getAccessToPage = function (pageId, pageName, userId) { | |
let res = { | |
hasAccessToThePage: false, | |
isClaimAccessToPagePending: false, | |
changingPageRoleUrl: "https://business.facebook.com/"+pageId+"/settings/?tab=admin_roles" | |
}; | |
let FBService = FacebookService(sails.config.shuuttv.facebook.userToken); | |
let savePageId = (userId, pageName, pageId) => { | |
return Users.update({id: userId}, {facebookPageName: pageName, facebookPageId: pageId}); | |
}; | |
let maybeAssignSystemUserToPage = (pageId) => { | |
return this.checkPermissionsToPage(pageId) | |
.then((hasRoleOnPage) => { | |
if (!hasRoleOnPage) { | |
console.log("Trying to assing permission to page", pageId); | |
return FBService.updateUserPermissions( | |
pageId, | |
sails.config.shuuttv.facebook.userId, | |
sails.config.shuuttv.facebook.businessId | |
); | |
} | |
return hasRoleOnPage; | |
}).then(()=>{ | |
return savePageId(userId, pageName, pageId); | |
}).then(()=>{ | |
return true; | |
}); | |
}; | |
if (pageId == sails.config.shuuttv.facebook.pageId) { | |
res.hasAccessToThePage = true; | |
console.log("User choose to use our fb page", userId); | |
return savePageId(userId, pageName, pageId).then((result)=>{ | |
return res; | |
}); | |
} | |
return FBService.getPageAgencies(pageId) | |
.then((agencies) => { | |
return agencies.data.filter((agency) => { | |
return agency.id === sails.config.shuuttv.facebook.businessId; | |
})[0]; | |
}) | |
.then((agency) => { | |
if (!agency) { | |
console.log("Will claim access"); | |
res.isClaimAccessToPagePending = true; | |
return this.claimAccessToPage(pageId); | |
} else if(agency.access_status === 'CONFIRMED') { | |
console.log("Access already confirmed"); | |
return maybeAssignSystemUserToPage(pageId).then((result) => { | |
res.hasAccessToThePage = result; | |
return; | |
}); | |
} else { | |
console.log("Claim access is pending"); | |
res.isClaimAccessToPagePending = true; | |
return; | |
} | |
}) | |
.then(() => { | |
return res; | |
}); | |
}; | |
/** | |
* Check if we have permissions to a page | |
* @param {string} pageId id of page | |
*/ | |
module.exports.checkPermissionsToPage = function (pageId) { | |
return FacebookService(sails.config.shuuttv.facebook.userToken) | |
.getUserPermissions(pageId) | |
.then((results) => { | |
return results.data.some((item) => { | |
return item.user.id === sails.config.shuuttv.facebook.userId && | |
item.role === "MANAGER"; | |
}); | |
}); | |
}; | |
/** | |
* Claims access to the page | |
* @param {string} pageId id of page | |
*/ | |
module.exports.claimAccessToPage = function (pageId) { | |
return FacebookService(sails.config.shuuttv.facebook.userToken) | |
.claimAccessToPage(sails.config.shuuttv.facebook.businessId, pageId) | |
.then((result) => { | |
if (result.access_status === 'PENDING') { | |
return {succeeded: true}; | |
} else { | |
console.log("Claiming access to a page is failed", result); | |
return {succeeded: false}; | |
} | |
}); | |
}; | |
module.exports.getFacebookPagesWithTokens = function (videoId) { | |
let pages = {facebookPages: JSON.parse(process.env.FACEBOOK_ALL_PAGES)}; | |
return Video.findOne(videoId).then((video)=>{ | |
return Users.findOne(video.userId.toHexString()); | |
}).then((user) => { | |
if (!user.facebookPageId || sails.config.shuuttv.facebook.pageId === user.facebookPageId) { | |
return pages; | |
} | |
return FacebookService(sails.config.shuuttv.facebook.userToken) | |
.getPage(user.facebookPageId) | |
.then((result)=>{ | |
pages.facebookPages = [{ | |
id: user.facebookPageId, | |
token: result.access_token, | |
name: user.facebookPageName | |
}]; | |
return pages; | |
}); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment