Products like Loyalty lion offer "reward points" when a user follows your Instagram account. I am trying to add the same functionality to my website but cannot for the life of me figure out how they do it. In Loyalty Lion you can click a "follow us on Instagram" button on your website, a small window opens, asks you to sign in to Instagram if you are not already, then imitates the follow automatically and closes the window. While this behavior is ideal, I thought I would try a simpler approach... but none of them seem to work.
My first thought was to ask a user to follow my account then click a verify button to receive points. This would trigger a function that checks if that specific user has my account in their list of follows. According to this answer however, this solution is no longer viable (as of 2018).
My second through was to just record the follower count at the beginning of the process and check if it had increased when the user clicks confirms. I realize this is not very secure but it seemed like the most basic (and therefore most likely to be supported) method. I have tried three different implementations now to get my follower count and while some of them work on on my local machine, none work once deployed:
Option 1: Using axios. worked the very first time I ran it but seems to be blocked on all subsequent attempts. If I simply paste the link into the browser I get readable JSON but the response to my HTTP request is gibberish.
const axios = require("axios").default;
exports.getInstagramFollowerCount = async (req, res) => {
axios
.get("https://www.instagram.com/username/?__a=1")
.then((resp) => {
return res.status(200).json({ count: resp.data.graphql.user.edge_followed_by.count });
})
.catch((err) => {
return res.status(500).json({ msg: JSON.stringify(err) });
});
};
Option 2: using the instagram-followers package. This works locally but not when deployed to a firesbase server
const followers = require("instagram-followers");
exports.getInstagramFollowerCount = async (req, res) => {
followers(username)
.then((no) => {
return res.status(200).json({ count: Number(no) });
})
.catch((err) => {
return res.status(500).json({ msg: err });
});
};
Option 3: using the instagram-basic-data-scraper-with-username package. This works locally but not when deployed to a firesbase server
const instaObj = require("instagram-basic-data-scraper-with-username");
exports.getInstagramFollowerCount = async (req, res) => {
instaObj
.specificField(username, "followers")
.then(resp => {
return res.status(200).json({ count: Number(resp.data) });
})
.catch(err => {
return res.status(500).json({ msg: err });
});
};
Why is this incredibly rudimentary implementation not working when deployed? Does anybody know of a better way to confirm that a user has followed your account?
I have a problem with fetch data. My friend creates Rest Api.
There is my fun:
const AnyCors = `https://cors-anywhere.herokuapp.com/`;
const urlAllBus = `http://207.185.72.111:15430/stops?size=15`;
fetchBusStop = () => {
return new Promise((resolve, rejects) => {
fetch(AnyCors+urlAllBus)
.then((result) => {
if (!result.ok) throw result.json();
return result.json();
})
.then((result) => {
console.log(result);
resolve(result);
})
.catch((error) =>
error.then((body) => {
console.log('Bad', body);
rejects(body);
}),
);
});
};
I create an app with react-native. When I use only urlAllBus my virtual machine work fine. The problem is with a physical machine. When I try using my fun with urlAllbus in chrome I get a problem with CORS so I used AnyCors+urlAllBus and everything works fine. But in the virtual and physical machine there solutions not work. I don't know what I should do
You friend's API should accept CORS by adding a Access-Control-Allow-Origin: * header to its responses to allow any website to access it. They can also limit access to a specific site by setting the header to the base URL of such site, like Access-Control-Allow-Origin: http://example.com.
If the API is express-based, I hightly recommend the cors package for the job, as it makes it a single-line change.
Otherwise, tell them to give this MDN page a read for more information about CORS :)
I know that Background Sync API is not supported in the apple ecosystem, so how would you get around it and make a solution that would work in the apple ecosystem and other platforms as well, now i have a solution that uses Background Sync API and for some reason it literally does not do anything on IOS, it just saves the failed requests, and then never sync-s, could i just access the sync queue somehow, with a indexedDB wrapper and then sync at an arbitrary time?
I tried it once and it broke everything, do you guys have an idea how?
const bgSyncPlugin = new workbox.backgroundSync.Plugin('uploadQueue', {
maxRetentionTime: 60 * 24 * 60,
onSync: async ({ queue }) => {
return getAccessToken().then((token) => {
replayQueue(queue, token).then(() => {
return showNotification();
});
});
},
});
This is the code i have, they all. have a purpose, since my token has a timeout i have to check if the token is expired or not and proceed after that and replace the token in the headers if it is expired, and i have to change data as well when i sync in the request bodies, but it all works good on anything other than apple devices. Apple devices never trigger the onsync, i tried to do listen to fetch events and trigger onsync with:
self.registration.sync.register('uploadQueue');
But to no awail, i tried to register sync on servvice worker registration, nothing seems to help.
If the sync registration is not viable on ios, then can i access the upload queue table somehow?
P.S.: I`m using dexie.js as a indexedDB wrapper, it is a vue.js app, with laravel api, and the sync process is quite complex, but it is working, just have to figure out how to do it on IOS!
I have found an answer to this after like 2 weeks of it being on my mind and on my to do list.
Now get some popcorn and strap yourself the heck in, because this is quite a chonker.
In my case the sync process was pretty complex as my users could be away from any connection for such a long time that my accessTokens would expire so i had to do a check for the access token expiration as well and reFetch it.
Furthermore my users could add new people to the database of people, which all had their on unique server side id-s, so i had to order my requests in a way that the person registrations are sent first then the tasks and campaigns that were completed for them, so i can receive the respective ids from the API.
Now for the fun part:
Firstly you cant use a bgSyncPlugin, because you cant access the replayQueue, you have to use a normal queue, like this:
var bgSyncQueue = new workbox.backgroundSync.Queue('uploadQueue', {
maxRetentionTime: 60 * 24 * 60,
onSync: () => syncData(),
});
And push the failed requests to the queue inside the fetch listener:
this.onfetch = (event) => {
let requestClone = event.request.clone();
if (requestClone.method === 'POST' && 'condition to match the requests you need to replay') {
event.respondWith(
(() => {
const promiseChain = fetch(requestClone).catch(() => {
return bgSyncQueue.pushRequest(event);
});
event.waitUntil(promiseChain);
return promiseChain;
})()
);
} else {
event.respondWith(fetch(event.request));
}
};
When user has connection we trigger the "syncData()" function, on ios this is a bit complicated(more on this later), on android it happens automatically, as the service worker sees it has connection, now lets just check out what syncData does:
async function syncData() {
if (bgSyncQueue) //is there data to sync?
return getAccessToken() //then get the access token, if expired refresh it
.then((token) => replayQueue(bgSyncQueue, token).then(() => showNotification({ body: 'Succsesful sync', title: 'Data synced to server' })))
.catch(() => showNotification({ title: 'Sync unsuccessful', body: 'Please find and area with better coverage' })); //replay the requests and show a notification
return Promise.resolve('empty');//if no requests to replay return with empty
}
For the android/desktop side of thing we are finished you can be happy with your modified data being synced, now on iOS we cant just have the users data be uploaded only when they restart the PWA, thats bad user experience, but we are playing with javascript everything is possible in a way or another.
There is a message event that can be fired every time that the client code sees that it has internet, which looks like this:
if (this.$online && this.isIOSDevice) {
if (window.MessageChannel) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = (event) => {
this.onMessageSuccess(event);
};
} else {
navigator.serviceWorker.onmessage = (event) => {
this.onMessageSuccess(event);
};
}
navigator.serviceWorker.ready.then((reg) => {
try {
reg.active.postMessage(
{
text: 'sync',
port: messageChannel && messageChannel.port2,
},
[messageChannel && messageChannel.port2]
);
} catch (e) {
//firefox support
reg.active.postMessage({
text: 'sync',
});
}
});
}
this is inside a Vue.js watch function, which watches whether we have connection or not, if we have connection it also checks if this is a device from the apple ecosystem, like so:
isIosDevice() {
return !!navigator.platform && /iPad|iPhone|MacIntel|iPod/.test(navigator.platform) && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
And so it tells the service worker that it has internet and it has to sync, in that case this bit of code gets activated:
this.onmessage = (event) => {
if (event.data.text === 'sync') {
event.waitUntil(
syncData().then((res) => {
if (res !== 'empty') {
if (event.source) {
event.source.postMessage('doNotification');//this is telling the client code to show a notification (i have a built in notification system into the app, that does not use push notification, just shows a little pill on the bottom of the app with the message)
} else if (event.data.port) {
event.data.port.postMessage('doNotification'); //same thing
}
return res;
}
})
);
}
};
Now the most useful part in my opinion, the replay queue function, this guy gets the queue and the token from getAccessToken, and then it does its thing like clockwork:
const replayQueue = async (queue, token) => {
let entry;
while ((entry = await queue.shiftRequest())) {//while we have requests to replay
let data = await entry.request.clone().json();
try {
//replay the person registrations first and store them into indexed db
if (isPersonRequest) {
//if new person
await fetchPerson(entry, data, token);
//then replay the campaign and task submissions
} else if (isTaskOrCampaignRequest) {
//if task
await fetchCampaigns(entry, data, token);
}
} catch (error) {
showNotification({ title: 'no success', body: 'go for better internet plox' });
await queue.unshiftRequest(entry); //put failed request back into queue, and try again later
}
}
return Promise.resolve();
};
Now this is the big picture as how to use this guy on iOS devices and make Apple mad as heck :) I am open to any questions that are related, in this time i think i have become pretty good with service worker related stuff as this was not the only difficult part of this project but i digress, thats a story for another day.
(you may see that error handling is not perfect and maybe this thing is not he most secure of them all, but this project has a prettty small amount of users, with a fixed number which know how to use it and what it does, so im not really afraid of security in this case, but you may want to improve on things if you use in in a more serious project)
Hope i could help and all of you have a grea day.
So, this is the first time I'm trying to take a large js app written in one file and modularize it into separate files. The goal is to create a more organized base of files rather than one big one.
There are a lot of api calls and a lot of shared information. I'm making use of module.exports but I'm not sure that it's the best way to go about it. I'd like some advice on how to do it more correctly or maybe I should use some other method? I'm using module.exports to pass back specific data rather than functions.
For example, here's the authentication function which was in the larger file and now in authenticate.js (some irrelevant parts were taken out):
module.exports.authenticate = (logger) => {
return new Promise((resolve, reject) => {
const authentication = new logger("Authentication Service");
fs.createReadStream('auth.json').pipe(request.post(('https://example.com/auth'), function (error, response, body) {
authentication.log('Authenicating API access');
body = JSON.parse(body);
token = body.response.token
if (typeof(token) === 'undefined' || token === '') {
reject('No Token Available');
}
authentication.log('Successfully logged in.');
module.exports.token = token;
resolve();
}));
})
}
So specifically, i'm using 'module.exports.token = token;' to pass back the token info that was just retrieved from the api call, I'm doing this in quite a few modules though for different pieces of information.
Is this proper and good practice?
Thanks!
Background: I am working with the Shopify ScriptTag which allows me to add a JavaScript file on the storefront. All I have is that script file.
Current Behaviour: There is an option, "Buy It Now", which allow customers to checkout directly by skipping Add To Cart. When they click on Buy It Now, Shopify sends a fetch() POST request to checkouts.json to create the checkout.
Problem: I need to detect that this "fetch request happened" in my own JavaScript file.
self.addEventListener('fetch', event => {
console.log("event happened");
});
I have tried Fetch Event API, but it seems to be only working in Service Worker scope.
Is there a possibility to detect this?
Like we can detect XMLHttpRequest by overriding its open method using prototypal inheritance.
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.initiatorType === "fetch") {
console.log('Fetch request detected to', entry.name);
}
}
});
observer.observe({
entryTypes: ["resource"]
});
fetch('https://cors-anywhere.herokuapp.com/')
.then(res => res.text())
.then(text => console.log(text.split('\n')[0]));
Using Performance Observer. Thanks to #guest271314.
Yes, you can overwrite window.fetch with your own function that calls the original window.fetch after (or before) running your own code:
const nativeFetch = window.fetch;
window.fetch = function(...args) {
console.log('detected fetch call');
return nativeFetch.apply(window, args);
}
fetch('https://cors-anywhere.herokuapp.com/')
.then(res => res.text())
.then(text => console.log(text.split('\n')[0]));