DevTools Google Chrome:
On this site (https://booyah.live/users/41874362/followers), to load the complete list of followers it is necessary to keep scrolling down the page to reload more profiles, but there comes a time when the page weighs so much that the browser crashes and it ends up needing to be closed.
Is there any way to be able to collect the follow buttons without this happening?
The current script I use is:
setInterval(function(){
document.getElementById("layout-content").scrollTo(0, 50000000000000000000000000000000000000);
document.querySelectorAll('.components-button.components-button-size-mini.components-button-type-orange.desktop.components-button-inline').forEach(btn => btn.click());
}, 10)
I use setInterval to create a loop of:
1 - Scrolling the page
2 - Loading more profiles
3 - Clicking the follow buttons
My need:
For the study I'm doing for learning, the idea is that my profile follows all profiles followers of a single most famous profile in order to analyze how many people follow back on this social media.
Additional:
In this answer provided by Leftium, it is possible to follow only one profile:
https://stackoverflow.com/a/67882688/11462274
In this answer given by KCGD, it is possible to collect the entire list of followers but during this collection the profiles are not followed, it is possible to create a list and save the data, but not follow the profiles:
https://stackoverflow.com/a/67865968/11462274
I tried to contact them both, but they haven't returned yet. It was a good way but I couldn't combine the two answers so I can follow all the profiles, I thought about the possibility according to which I would collect the profiles of the KCGD response, I would follow the profiles too, but not only the first one but also the answer of the Leftium.
Would it be possible to take advantage of the loop created by the response from KCGD and from each response, already follow all profiles instead of just the first one as in Leftium's response?
I tried to create but was unsuccessful.
The browser crashes because too much memory is used. As you scroll down the page, the HTML DOM tree is extended and more avatar images are downloaded. These HTML and image resources are not necessary for your goal.
It is possible to avoid crashing by calling the (internal) Booyah API directly. This will be much faster and consume less resources since only the text is transferred. There are two API endpoints of interest:
GET /api/v3/users/[USERID]/followers?cursor=0&count=100
Gets list of followers following a certain user:
[USERID] is the ID of the user being studied (WEEDZAO's id).
cursor is where in the list of followers to start listing. When the page first loads, this is 0. As you scroll down, the following API calls increment this (101, 201, 301...)
count is how many results to return.
Since this is a GET call, you can open this URL in your browser.
POST /api/v3/users/[USERID]/followings
Follows a user (same as clicking their 'Follow' button).
Here [USERID] is ID of the user whose follower list will be updated (your own ID).
A payload must be sent that looks like this: {followee_uid: ID, source: 43}. I'm not sure what source is.
Also a CSRF header must be included.
Because this is a POST type call, it is not possible to open this type of URL directly in your browser.
DELETE /api/v3/users/[USERID]/followings
There is also an API to unfollow a user. (Just for reference).
If you call these API's from outside the browser, you probably need to send session cookies.
This script will list WEEDZAO's first 10 followers, then follow the first one from the list:
You must replace USERID and CSRF_TOKEN with your own values.
You can copy/paste this code into the browser dev console.
Alternatively, you can use this code from a web scraping framework like Puppeteer.
// Find these values in dev console "Network" tab:
var CSRF_TOKEN, USERID, USERID_TARGET, main;
USERID_TARGET = '41874362';
USERID = '12345678';
CSRF_TOKEN = 'MTYy...p8wg';
main = async function() {
var body, followers, json, options, payload, response, url;
// Get list of first 10 followers
console.log(url = `/api/v3/users/${USERID_TARGET}/followers?cursor=0&count=10`);
response = (await fetch(url));
json = (await response.json());
followers = json.follower_list;
console.table(followers);
// Follow first member from list above
console.log(url = `/api/v3/users/${USERID}/followings`);
payload = JSON.stringify({
followee_uid: followers[0].uid,
source: 43
});
response = (await fetch(url, options = {
method: 'POST',
body: payload,
headers: {
'X-CSRF-Token': CSRF_TOKEN
}
}));
body = (await response.text());
return console.log(body);
};
main();
It crashes because the interval is too fast
setInterval(function(){}, 10)
you are trying to call a scroll and click function every 10 milliseconds (that's 100 function call every 1 second). Which also interferes with the server as they fetch new users while scrolling.
Your script could work if you will adjust the interval to atleast 1000 milliseconds (1 second). Of course, it may take a while, but it will work. You should also expect that the page may become laggy specially when the page already loaded tons of users because Virtual Scrolling is not implemented in this page.
Even with slowing down the rate of the scrolling it still really bogs down the browser, the solution to this may be in the API the page contacts. To get the user's followers it contacts the site's V3 API
https://booyah.live/api/v3/users/41874362/followers?cursor=[LAST USER IN API RETURN]&count=100
to get all the users that would show up in the page. I wrote a script that can contact the api over and over again to get all the follower data, just run it in the page's console and use print() when you want to export the data
and copy/paste it into a .json file
//WARNING: THIS SCRIPT USES RECURSION, i have no clue how long the followers list goes so use at your own risk
var followers = []; //data collected from api
function getFollowers(cursor){
httpGet(`https://booyah.live/api/v3/users/41874362/followers?cursor=${cursor}&count=100`, function (data) { //returns data from API for given cursor (user at the end of last follower chunk)
console.log("got cursor: "+cursor);
var _followChunk = JSON.parse(String(data));
console.log(_followChunk)
followers.push(_followChunk.follower_list); //saves followers from chunk
var last_user = _followChunk.follower_list[_followChunk.follower_list.length - 1]; //gets last user of chunk (cursor for the next chunk)
setTimeout(function(){ //1 second timeout so that the API doesnt return "too many requests", not nessicary but you should probably leave this on
getFollowers(last_user.uid); //get next chunk
},1000)
})
}
var print = function(){console.log(JSON.stringify(followers))};
getFollowers(0); //get initial set of followers (cursor 0)
function httpGet(theUrl, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", theUrl, false); // false for synchronous request
xmlHttp.setRequestHeader("Cache-Control", "no-store");
xmlHttp.send(null);
callback(xmlHttp.responseText);
};
if you really only need the button elements then the only way is to scroll all the way down for each time it loads new followers, as the page creates the elements as you scroll down
This is a fully working solution that I have tested in my own Chrome browser with a fresh account, successfully following all the follower accounts of the account you are targeting.
UPDATE (2021-06-18)
I've updated my solution to a drastically improved and faster function, rewritten with async/await. This new function reduces the estimated runtime from ~45min to ~10min. 10min is still a long while, but that's to be expected considering the large number of followers the user you are targeting has.
After a few iterations, the latest function not only improves speed, performance, and error reporting, but it also extends what is possible with the function. I provide several example below my solutions of how to use the function completely.
For the sake of de-cluttering my answer, I am removing my older function from this solution altogether, but you can still reference it in my solution's edit history if you like.
TL;DR
Here is the final, fastest, working solution. Make sure to replace PUT_YOUR_CSRF_TOKEN_HERE with your own CSRF token value. Detailed instructions on how to find your CSRF token are below.
You must run this in your console on the Booyah website in order to avoid CORS issues.
const csrf = 'PUT_YOUR_CSRF_TOKEN_HERE';
async function booyahGetAccounts(uid, type = 'followers', follow = 1) {
if (typeof uid !== 'undefined' && !isNaN(uid)) {
const loggedInUserID = window.localStorage?.loggedUID;
if (uid === 0) uid = loggedInUserID;
const unfollow = follow === -1;
if (unfollow) follow = 1;
if (loggedInUserID) {
if (csrf) {
async function getUserData(uid) {
const response = await fetch(`https://booyah.live/api/v3/users/${uid}`),
data = await response.json();
return data.user;
}
const loggedInUserData = await getUserData(loggedInUserID),
targetUserData = await getUserData(uid),
followUser = uid => fetch(`https://booyah.live/api/v3/users/${loggedInUserID}/followings`, { method: (unfollow ? 'DELETE' : 'POST'), headers: { 'X-CSRF-Token': csrf }, body: JSON.stringify({ followee_uid: uid, source: 43 }) }),
logSep = (data = '', usePad = 0) => typeof data === 'string' && usePad ? console.log((data ? data + ' ' : '').padEnd(50, '━')) : console.log('━'.repeat(50),data,'━'.repeat(50));
async function getList(uid, type, follow) {
const isLoggedInUser = uid === loggedInUserID;
if (isLoggedInUser && follow && !unfollow && type === 'followings') {
follow = 0;
console.warn('You alredy follow your followings. `follow` mode switched to `false`. Followings will be retrieved instead of followed.');
}
const userData = await getUserData(uid),
totalCount = userData[type.slice(0,-1)+'_count'] || 0,
totalCountStrLength = totalCount.toString().length;
if (totalCount) {
let userIDsLength = 0;
const userIDs = [],
nickname = userData.nickname,
nicknameStr = `${nickname ? ` of ${nickname}'s ${type}` : ''}`,
alreadyFollowedStr = uid => `User ID ${uid} already followed by ${loggedInUserData.nickname} (Account #${loggedInUserID})`;
async function followerFetch(cursor = 0) {
const fetched = [];
await fetch(`https://booyah.live/api/v3/users/${uid}/${type}?cursor=${cursor}&count=100`).then(res => res.json()).then(data => {
const list = data[type.slice(0,-1)+'_list'];
if (list?.length) fetched.push(...list.map(e => e.uid));
if (fetched.length) {
userIDs.push(...fetched);
userIDsLength += fetched.length;
if (follow) followUser(uid);
console.log(`${userIDsLength.toString().padStart(totalCountStrLength)} (${(userIDsLength / totalCount * 100).toFixed(4)}%)${nicknameStr} ${follow ? 'followed' : 'retrieved'}`);
if (fetched.length === 100) {
followerFetch(data.cursor);
} else {
console.log(`END REACHED. ${userIDsLength} accounts ${follow ? 'followed' : 'retrieved'}.`);
if (!follow) logSep(targetList);
}
}
});
}
await followerFetch();
return userIDs;
} else {
console.log(`This account has no ${type}.`);
}
}
logSep(`${follow ? 'Following' : 'Retrieving'} ${targetUserData.nickname}'s ${type}`, 1);
const targetList = await getList(uid, type, follow);
} else {
console.error('Missing CSRF token. Retrieve your CSRF token from the Network tab in your inspector by clicking into the Network tab item named "bug-report-claims" and then scrolling down in the associated details window to where you see "x-csrf-token". Copy its value and store it into a variable named "csrf" which this function will reference when you execute it.');
}
} else {
console.error('You do not appear to be logged in. Please log in and try again.');
}
} else {
console.error('UID not passed. Pass the UID of the profile you are targeting to this function.');
}
}
booyahGetAccounts(41874362);
Detailed explanation of the process
As the function runs, it logs the progress to the console, both how many users have been followed so far, and how much progress has been made percentage-wise, based on the total number of followers the profile you are targeting has.
Retrieving your CSRF token
The only manual portion of this process is retrieving your CSRF token. This is rather simple though. Once you log into Booyah, navigate to the Network tab of your Chrome console and click on the item named bug-report-claims, then scroll all the way down the details window which appears on the right. There should see x-csrf-token. Store this value as a string variable in your console as csrf, which my function will reference when it runs. This is necessary in order to use the POST method to follow users.
Here is what it will look like:
The solution
The function will loop through all users the account you are targeting follows in batches of 100 (the max amount allowed per GET request) and follow them all. When the end of each batch is met, the next batch is automatically triggered recursively.
🚀 Version 3 (Fastest and most flexible, using async/await and fetch())
My previous two solution versions (🐇 …🐢) can be referenced in this answer's edit history.
Make sure to replace PUT_YOUR_CSRF_TOKEN_HERE with your own CSRF token value. Detailed instructions on how to find your CSRF token are below.
You must run this in your console on the Booyah website in order to avoid CORS issues.
const csrf = 'PUT_YOUR_CSRF_TOKEN_HERE';
async function booyahGetAccounts(uid, type = 'followers', follow = 1) {
if (typeof uid !== 'undefined' && !isNaN(uid)) {
const loggedInUserID = window.localStorage?.loggedUID;
if (uid === 0) uid = loggedInUserID;
const unfollow = follow === -1;
if (unfollow) follow = 1;
if (loggedInUserID) {
if (csrf) {
async function getUserData(uid) {
const response = await fetch(`https://booyah.live/api/v3/users/${uid}`),
data = await response.json();
return data.user;
}
const loggedInUserData = await getUserData(loggedInUserID),
targetUserData = await getUserData(uid),
followUser = uid => fetch(`https://booyah.live/api/v3/users/${loggedInUserID}/followings`, { method: (unfollow ? 'DELETE' : 'POST'), headers: { 'X-CSRF-Token': csrf }, body: JSON.stringify({ followee_uid: uid, source: 43 }) }),
logSep = (data = '', usePad = 0) => typeof data === 'string' && usePad ? console.log((data ? data + ' ' : '').padEnd(50, '━')) : console.log('━'.repeat(50),data,'━'.repeat(50));
async function getList(uid, type, follow) {
const isLoggedInUser = uid === loggedInUserID;
if (isLoggedInUser && follow && !unfollow && type === 'followings') {
follow = 0;
console.warn('You alredy follow your followings. `follow` mode switched to `false`. Followings will be retrieved instead of followed.');
}
const userData = await getUserData(uid),
totalCount = userData[type.slice(0,-1)+'_count'] || 0,
totalCountStrLength = totalCount.toString().length;
if (totalCount) {
let userIDsLength = 0;
const userIDs = [],
nickname = userData.nickname,
nicknameStr = `${nickname ? ` of ${nickname}'s ${type}` : ''}`,
alreadyFollowedStr = uid => `User ID ${uid} already followed by ${loggedInUserData.nickname} (Account #${loggedInUserID})`;
async function followerFetch(cursor = 0) {
const fetched = [];
await fetch(`https://booyah.live/api/v3/users/${uid}/${type}?cursor=${cursor}&count=100`).then(res => res.json()).then(data => {
const list = data[type.slice(0,-1)+'_list'];
if (list?.length) fetched.push(...list.map(e => e.uid));
if (fetched.length) {
userIDs.push(...fetched);
userIDsLength += fetched.length;
if (follow) followUser(uid);
console.log(`${userIDsLength.toString().padStart(totalCountStrLength)} (${(userIDsLength / totalCount * 100).toFixed(4)}%)${nicknameStr} ${follow ? 'followed' : 'retrieved'}`);
if (fetched.length === 100) {
followerFetch(data.cursor);
} else {
console.log(`END REACHED. ${userIDsLength} accounts ${follow ? 'followed' : 'retrieved'}.`);
if (!follow) logSep(targetList);
}
}
});
}
await followerFetch();
return userIDs;
} else {
console.log(`This account has no ${type}.`);
}
}
logSep(`${follow ? 'Following' : 'Retrieving'} ${targetUserData.nickname}'s ${type}`, 1);
const targetList = await getList(uid, type, follow);
} else {
console.error('Missing CSRF token. Retrieve your CSRF token from the Network tab in your inspector by clicking into the Network tab item named "bug-report-claims" and then scrolling down in the associated details window to where you see "x-csrf-token". Copy its value and store it into a variable named "csrf" which this function will reference when you execute it.');
}
} else {
console.error('You do not appear to be logged in. Please log in and try again.');
}
} else {
console.error('UID not passed. Pass the UID of the profile you are targeting to this function.');
}
}
Usage
To run the function (for either of the above solutions), just call the function name with the desired User ID name as an argument, in your example case, 41874362. The function call would look like this:
booyahGetAccounts(41874362);
The function is quite flexible in its abilities though. booyahGetAccounts() accepts three parameters, but only the first is required.
booyahGetAccounts(
uid, // required, no default
type = 'followers', // optional, must be 'followers' or 'followings' -> default: 'followers'
follow = 1 // optional, must be 0, 1, or -1, -> default: 1 (boolean true)
)
The second parameter, type, allows you to choose whether you would like to process the targeted user's followers or followings (the users which that user follows).
The third parameter allows you to choose whether you would like to follow/unfollow the returned users or only retrieve their User IDs. This defaults to 1 (boolean true) which will follow the users returned, but if you only want to test the function and not actually follow the returned users, set this to a falsy value such as 0 or false. Using -1 will unfollow the users returned.
This function intelligently retrieves your own User ID for you from the window.localStorage object, so you don't need to retrieve that yourself. If you would like to process your own followers or followings, simply pass 0 as the main uid parameter value, and the function will default the uid to your own User ID.
Because you can't re-follow users you already follow, if you try to follow your followings, the function will produce the warning You already follow your followings. 'follow' mode switched to 'false'. Followings will be retrieved instead of followed. and instead return them as if you had set the follow parameter to false.
However, it can be very useful to process your own list. For example, if you want to follow all of your own followers back, you could do so like this:
booyahGetAccounts(0); // `type` and `follow` parameters already default to the correct values here
On the other hand, if you were strategically using a follow/unfollow technique in order to increase your number of followers and needed to unfollow all of your followers, you could do so like this:
booyahGetAccounts(0, 'followers', -1);
By setting the follow parameter value to -1, you instruct the function to run its followUser function on all returned User IDs using the DELETE method instead of the POST method, thereby unfollowing those users returned instead of following them.
Desired outcome
Function call
Follow all your own followers
booyahGetAccounts(0, 'followers');
Unfollow all your own followers
booyahGetAccounts(0, 'followers', -1);
Unfollow all your own followings
booyahGetAccounts(0, 'followings', -1);
Follow users that follow User ID #12345
booyahGetAccounts(12345, 'followers');
Follow users followed by User ID #12345
booyahGetAccounts(12345, 'followings');
Retrieve User IDs of accounts following User ID #12345
booyahGetAccounts(12345, 'followers', 0);
Retrieve User IDs of accounts followed by User ID #12345
booyahGetAccounts(12345, 'followings', 0);
Other notes
To improve the performance of this function, as it's very heavy, I've replaced all calls to userIDs.length with a dedicated userIDsLength variable which I add to using += with each iteration rather than calling length each time. Similarly, I store the length of the stringified followerCount in the variable followerCountStrLength rather than calling followerCount.toString().length with each iteration. Because this is a rather heavy function, it is possible for your browser window to crash. However, it should eventually complete.
If the page appears to crash by flickering and auto-closing the console, FIRST try to re-open the console without refreshing the page at all. In my case, the inspector occasionally closed on its own, likely due to the exhaustion from the function, but when I opened the inspector's console again, the function was still running.
I am new to Node.js and Express. I have a PHP background on server side, just for you to understand how I understand the backend side.
I will explain in words what I want to achieve and after that I will paste a minimal example of code from my project, the part which does not work as expected.
Description:
I have a table called room which has an id, room_name and player_id. This table is already populated with 1 row using: INSERT INTO room (id, room_name, player_id) VALUES (1, "room1", ""). In other words I have one room which has some missing information regarding the player which joined that room.
I have a function called addPlayerToRoom(id_player, room_name) which is executed whenever I want to add a player to a room. This function can return 3 type of responses :
Room does not exist (if the room_name is not found in the table)
Room is full (if the player_id is already populated in the db on that specific room row)
Player added to the room (when that room exists and no players were previously added to that room)
I want to return one of these 3 statuses and console.log them but everything I do, I get undefined even though the code works for each case. If I console log the response before the return I get the good response in the console. Also everything works in the db also, meaning that the player is added if the room is empty.
Code:
const response = addPlayerToRoom(7, "room1"); // 7 is the player_id which is any random number
console.log(response) // => undefined (why I get undefined ???)
function addPlayerToRoom(id_player, room_name) {
let sql1 = "SELECT * FROM room WHERE room_name = ?";
let pre1 = [room_name];
sql1 = mysql.format(sql1, pre1);
// fetch the room in the db
connection.query(sql1, function (err1, rows1) {
// check if room exists
if (rows1.length > 0) {
// check if the room is full
if (row1[0].player_id !== '') {
return "Room is full !"
}
else {
let sql2 = 'UPDATE room SET player_id = ? WHERE name = ?';
let pre2 = [id_player, room_name];
sql2 = mysql.format(sql2, pre2);
connection.query(sql2, function (err2, rows2) {
return "Player added to room !"
});
}
}
else {
return "Room does not exist !"
}
}
}
What I believe at this point is that the console.log triggers before the function is executed because if I add any random console.log in the function it gets pasted in the console after I get the undefined pasted...which means to me that it does not wait for the function execution.
This is somehow strange for me when I think at PHP where I did not experienced this type of issues. Does this means that everytime when I do a mysql query I should somehow use a promise or something? I tried something but without success.
Any ideea how to solve this ? Thanks
Building a Twitter bot to:
search for tweets with keywords like "Net Neutrality"
Return the tweet IDs and usernames for those tweets
Publish a tweet in response to that user (via in_reply_to_status_id, as described in Twitter Docs)
Here is my current code:
const Twitter = new twit(config);
let tweet = function() {
let params = {
q: '#netneutrality, #savethenet, Net Neutrality',
result_type: 'mixed',
lang: 'en'
}
// search through all tweets using our params and execute a function:
Twitter.get('search/tweets', params, function(err, data) {
// if there is no error
if (!err) {
// loop through the first 4 returned tweets
for (let i = 0; i < 4; i++) {
// iterate through those first four defining a rtId that is equal to the value of each of those tweets' ids
let rtId = data.statuses[i].id_str;
let username = data.statuses[i].username;
// the post action
Twitter.post('statuses/update', {
// setting the id equal to the rtId variable
in_reply_to_status_id: rtId
, status: `#${username} Send single-click #SaveTheNet tweets to key politicians at fliplist.app. Take 60 seconds to protect our Internet, once and for all.`
// log response and log error
}, function(err, response) {
if (response) {
console.log('Successfully tweeted');
}
if (err) {
console.log(err);
}
});
}
}
else {
// catch all log if the search could not be executed
console.log('Could not search tweets.');
}
});
}
tweet();
setInterval(tweet, 600000);
When I ran it, there was a successful output ("Succesfully tweeted" 4 times in terminal), and there were indeed 4 new tweets published from my account.
However, the username in all of those tweets was #undefined.
So I imagine I'm either failing to collect those usernames from the relevant tweets or failing to add them to the status string appropriately.
Any suggestions for how I can fix this?
For reference, here is a link to one of the #undefined tweets: https://twitter.com/ProoveTweets/status/1075855455958765569
And a screenshot is attached here
Two things to note. Firstly, Twitter's usernames are called "Screen Names". So you code should probably read:
let username = data.statuses[i].user.screen_name;
Take a look at the search documentation for an example of the JSON being returned. https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets.html
Secondly - please don't do this! Automatically replying to keywords is against the developer terms of service and is really annoying to users.
https://help.twitter.com/en/rules-and-policies/twitter-automation
I have a chat app that I am building in ionic and firebase. I have a simple join channel function that takes the channel id, and adds the current user id to the list of member in the database. According to everything I am seeing it is working correctly, however whenever I go to the firebase console it is not showing the new item in the database. Here is the code I am using to add the item to the database when a user joins a chat channel...
joinChannel(type, user_id) {
return new Promise((resolve, reject) => {
if(this.availableChannels[type] !== undefined) {
console.log("joining channel: " + type);
console.log('for user: ' + user_id);
let update = {};
update['/chat/members/' + type + '/' + user_id] = {active: true, joinedAt: firebase.database.ServerValue.TIMESTAMP};
this.afDB.database.ref().update(update).then(() => {
console.log("updated /chat/members/" + type + "/" + user_id + " to");
console.log({active: true, joinedAt: firebase.database.ServerValue.TIMESTAMP});
this.activeChannel = '/channels/' + type;
resolve(true);
})
} else {
reject("channel_not_found");
}
});
}
Now, if I open up the developer console in chrome and click to join the channel I get this in the console...
joining channel: news
chat.ts:210 for user: [USERIDHERE]
chat.ts:214 updated /chat/members/news/[USERIDHERE] to
chat.ts:215 Object
active: true
joinedAt: Object
.sv: "timestamp"
__proto__: Object
__proto__: Object
So the console shows that the database ran the update, however if I then log into my firebase console, there is no "news" item in the "chat/members" database. I have tried it with different channels, and always I get the same exact response from firebase, saying it updated the database, but in the firebase console that item remains blank.
Just in case it is needed the this.afDB variable is
public afDB: AngularFireDatabase
in the constructor. I know the database is working because I can update things, send messages in chat channels and they appear in the firebase console, but for whatever reason, when a user joins a channel, I get the successful console log, but the database is not updating with the new information. I am really lost on this one and could really use some help.
it seems to me you want to use a set instead of update
this.afDB.database.ref('/chat/members/' + type + '/' + user_id)
.set({active: true, joinedAt: firebase.database.ServerValue.TIMESTAMP});
This question already has answers here:
Create an empty child record in Firebase
(2 answers)
Closed 6 years ago.
I'm working on a chrome extension and pushing data from a Google-authenticated user to Firebase.
I'm listening for a message coming from another JS file and when it comes in, I want to take the "user profile" object (request) and tack on a property to it, called "visitedLinks". visitedLinks should be set to an empty object.
I do 3 console.logs throughout the code, and in all three cases, the console.logs show "visitedLinks" set to an empty object, yet when I push to Firebase, "visitedLinks" isn't a property.
//Relevant 3 console statements are the following
//console.log('request.accountData = ', request.accountData)
//console.log('userObject test #1 = ', userObject)
//console.log('userObject = ', userObject)
var rootRef = new Firebase("https://search-feed-35574.firebaseio.com/");
if (localStorage.userIsAuthenticated) {
console.log('user is authenticaled')
chrome.runtime.onMessage.addListener(
//listen for messages
function(request, sender, sendResponse) {
//url is coming in from a content script, use localStorage.uid to make database call
if (request.url) {
console.log('message coming from content script')
var uid = localStorage.uid;
var url = request.url;
var userRef = rootRef.child(uid);
newLinkRef = userRef.push(url);
//otherwise, we're getting a message from popup.js, meaning they clicked it again, or they've signed in for the first time
} else {
console.log('message coming from popup')
//here we're passing in all the data from the person's Google user account
var googleUID = request.accountData.uid
//then we make a new object with the key as their google UID and the value all the account data
request.accountData.visitedLinks = {}
console.log('request.accountData = ', request.accountData)
var userObject = {};
userObject[googleUID] = request.accountData;
console.log('userObject test #1 = ', userObject)
//here were checking to see if the UID is already a key in the database
//basically, if they've ever logged in
rootRef.once('value', function(snapshot) {
if (snapshot.hasChild(googleUID)) {
//user has authenticated before, they just happened to click the popup again
console.log('already authenticated, you just clicked the popup again')
} else {
console.log('users first DB entry');
//if they're not in the database yet, we need to push userObject to the DB
//and push their current url to the publicLinks array
rootRef.set(userObject, function(error) {
console.log('error = ', error);
console.log('userObject after DB insert = ', userObject)
});
}
})
}
//figure out if this user has entries in the DB already
//just push the link information onto the "links" node of the db object
//if not, push a ref (to the right place)
// console.log(sender)
});
} else {
console.log('user isnt authenticated')
}
So it turns out that you can't insert empty objects into the database. Similar question answered here: Create an empty child record in Firebase