twitterbot is repeating itself - javascript

For a bit of fun I decided to put a twitter bot together using a youtube based tutorial, and while this was relatively easy to execute I am having an issue with the bot repeating the same tweet a few times before it tweets something new.
At this stage the bot is only set up to post tweets that are put together at random from three seperate arrays
Array.prototype.pick = function() {
return this[Math.floor(Math.random()*this.length)];
};
console.log("The bot is starting");
var Twit = require('twit');
var T = new Twit(require('./config.js'));
var partOne = require('./content/partone.js');
var partTwo = require('./content/parttwo.js');
var emoji = require('./content/emojis.js');
var statusOne = partOne.pick();
var statusEmoji = emoji.pick();
var statusTwo = partTwo.pick();
function tweetIt() {
var tweet = {
status: statusOne + statusTwo + ' ' + statusEmoji
};
T.post('statuses/update', tweet, tweeted);
function tweeted(err, data, response) {
if (err) {
console.log("Something went wwrong!");
} else {
console.log("It worked!");
}
}
}
//tweetIt();
setInterval(tweetIt, 60 * 1000);
I have looked over this but cannot seem to figure out what would cause the bot to repeat itself a few times instead of creating a brand new tweet,
Any help with this would be much appreciated

You're picking your tweet parts outside of your interval. Try putting this:
var statusOne = partOne.pick();
var statusEmoji = emoji.pick();
var statusTwo = partTwo.pick();
inside your tweetIt function.

Related

JavaScript random item from an imported array behaving strangely

The following code seems to be behaving strangely. Basically, I'm importing a list of line-separated sentences from a text file, then making an array out of them. But when I try to choose a random sentence from the array, it doesn't work because sentenceString becomes undefined.
However, when I run
Math.floor(Math.random() * (sentenceArr.length) + 1);
I get a nice random number as expected.
And when I run sentenceArr.length
I get the number 12, which is indeed the length.
What am I missing?
var sentenceArr = [];
$.get('sentences.txt', function(data){
sentenceArr = data.split('\n');
});
var rand = Math.floor(Math.random() * (sentenceArr.length) + 1);
var sentenceString = sentenceArr[rand];
var sentence = sentenceString.split(' ');
Update:
I tried making a Promise as suggested below, but it still doesn't seem to be working. My new code with the Promise looks like this:
var sentenceArr = [];
var done = false;
function loadSentences() {
var rand = Math.floor(Math.random() * (sentenceArr.length) + 1);
var sentenceString = sentenceArr[rand];
var sentence = sentenceString.split(' ');
};
$.get('/sentences.txt', function(data){
sentenceArr = data.split('\n');
done = true;
});
var isItDone = new Promise(function(resolve) {
if(done) {
resolve('it worked');
}
});
//consume the promise:
var checkIfDone = function() {
isItDone
.then(function (fulfilled) {
loadSentences();
})
.catch(function (error) {
console.log('oops, it failed');
});
};
checkIfDone();
This seems to always return "oops, it failed", as if the promise is never fulfilled. However, when I check the value of "done", it is "true", meaning the Ajax request was completed before moving on to the next steps. Could anyone enlighten me? I've read three tutorials on promises already, and can't seem to figure out my error in applying the concept to my own code. Thank you.
The problem is you are trying to manipulate the file content before the response of server be complete.
Take a look at promises to understand more https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
And the way to solve your question using jquery ajax api.
var sentenceArr = [];
var file = 'path/to/file';
$.get(file)
.done(function (data) {
sentenceArr = data.split('\n');
var rand = Math.floor(Math.random() * (sentenceArr.length) + 1);
var sentenceString = sentenceArr[rand];
var sentence = sentenceString.split(' ');
console.log(sentenceArr)
console.log(rand)
console.log(sentenceString)
console.log(sentence)
});
Thanks, I solved the issue by first wrapping everything in a function, and using .then() to run everything only after the Ajax "get" was completed:
var sentenceArr = [];
const getData = () =>
$.get('http://eslquiz.net/wordmix/sentences.txt', function(data){
})
.then(data => { // use this format to run function only after json get is done (since it's async)
// "data" is the contents of the text file
sentenceArr = data.split('\n');
console.log('Made sentence array');
loadSentences();
});

JavaScript Class Objects not returning value

I've been working with the Microsoft Bot Framework to create a bot that can interface between MS Teams and AWS. I've been trying to write some JS functions but have been unsuccessful in getting them to operate how I want them to.
Here is what I am currently working on and am stuck on:
I am creating a 'ping' like functionality so a bot user can ping an instance in AWS and receive its status whether its running and has passed the system checks or not. My code is currently able to take the user request for the ping, retrieve the information from AWS, and can even print that info to the console. However, when I am trying to retrieve that information back out of the object that I set it to and print it to MS Teams, it says my variable is undefined.
Some code snippets are below:
class aws_Link {
constructor (mT, ping_1, i_state, i_status) {
this.myTag = mT;
this.ping = ping_1;
this.instance_state = i_state; // I declare this here, but should I?
this.instance_status = i_status; // I declare this here, but should I?
}
//i_state and i_status are just passed NULL when the object is initialized
//so they would be holding some value, not sure if I have to do this
api_link () {
var mainLink = API_LINK_TAKEN_OUT_FOR_OBVIOUS_REASONS;
var myTagFill = "myTag=";
var ampersand = "&";
var pingFill = "ping=";
var completeLink = String(mainLink + myTagFill + this.myTag + ampersand + pingFill + this.ping);
var finalLink = completeLink;
finalLink = finalLink.split(' ').join('');
//set up API-key authenticication
var options = {
url: finalLink,
headers: {
'x-api-key': 'AWS-PRIVATE-TOKEN'
}
};
if(this.ping == "TRUE") { // if the user wants to use /ping
var res = request(options, function(error, response, body) {
console.log("PING REQUEST"); //debug
body = JSON.parse(body);
var h_state = body['instanceState'];
var h_status = body['instanceStatus'];
this.instance_state = h_state;
this.instance_status = h_status;
console.log("STATE: " + h_state); //debug
console.log("STATUS: " + h_status); //debug
});
}
}
pingFunction () {
var tmp = "Instance State: " + this.instance_state + " Instance Status: " + this.instance_status;
return tmp;
}
}
And here is where I call the api_link() function and pingFunction():
var apiLink1 = new aws_Link("MY_TAG_VALUE", "TRUE", "NULL", "NULL");
var completeAPILink = apiLink1.api_link();
session.send('Request complete.');
session.send("PING: " + apiLink1.pingFunction());
So essentially the user enters in some info which gets passed to where I create the "new aws_Link" which then a my understanding is, creates an object called apiLink1. From there, it makes the request to AWS in my api_link() function, which retrieves the info I want. I thought I was then saving this info when I do the: this.instance_state = h_state; & this.instance_status = h_status;. So then when I call pingFunction() again on apiLink1, I thought I would be able to retrieve the information back out using this.instance_state and this.instance_status, but all it prints out is undefined. Any clarification on why my current code isn't working and any changes or improvements I can make would be greatly appreciated.
Thanks!

javascript "don't make function in a loop"

How can I refractor my code to get rid of this error from JSLinter?
I tried moving the entire function out to a var but the code wasn't able to run after that.
for (i = 0; i < timeDifference; i++) {
timestamp ++;
console.log(timestamp);
energyDatum.find({timestamp: timestamp}).toArray(function(err, result) {
var data = {};
result.forEach(function(element) {
data[element.deviceId] = element;
});
var roomRawData = [];
mappings.forEach(function(room) {
var hash = {};
hash.floor = room.floor;
hash.name = room.name;
hash.room_type = room.room_type;
hash.energy_ac = sumApplianceEnergy('energy_ac', room, data);
hash.energy_light = sumApplianceEnergy('energy_light', room, data);
hash.energy_socket_1 = sumApplianceEnergy('energy_socket_1', room, data);
hash.energy_socket_2 = sumApplianceEnergy('energy_socket_2', room, data);
hash.energy_socket_3 = sumApplianceEnergy('energy_socket_3', room, data);
hash.energy_total = hash.energy_ac + hash.energy_light + hash.energy_socket_1 + hash.energy_socket_2 + hash.energy_socket_3;
hash.timestamp = timestamp;
roomRawData.push(hash);
});
roomRaw.insert(roomRawData, {w:1}, function(err, result) { console.log('done'); });
});
lastTimestamp.update({_id: timestampId}, {timestamp: timestamp});
}
JSLinter shows this message because your code has potential errors.
Take a look at this line:
energyDatum.find({timestamp: timestamp}).toArray(...);
This method is async, right? It means that the callback of toArray method
is called after the for loop finishes its iterations, and therefore timestamp
variable (when you use it inside this callback) doesn't have a value of current iteration,
but instead it has value incremented for timeDifference times.
To solve this problem you could move this callback to another function:
var getIterationFunc = function(timestamp) {
return function(err, result) {
var data = {};
// rest of function ...
}
}
and then use it:
energyDatum.find({timestamp: timestamp}).toArray(getIterationFunc(timestamp));
I believe this error should be fixed now. Hope this helps.
P.S. sorry for my English

cloud code in parse.com doesn't work to all data

I just start learning how to modify my database in Parse.com. I'm using cloud code in my mac.
In my database, I got around 150 data, with class openhour and closehour. Unfortunately, it was written in string. It's structure is like this 12:30.
I want to convert all of it from string to number, and store it in class openHour and closeHour. For an example, I will change 12:30 as string, to 12.5 as number.
I've wrote this code, deploy it, and execute it using terminal in mac. It seems succeeded since I can see some of my data in class openHour and closeHour are being filled.
My problem is, there are several data that are still empty. Can someone show me why this is happening? did I miss something?
var _ = require('underscore.js');
Parse.Cloud.define("openclose", function(request, response) {
Parse.Cloud.useMasterKey();
var query = new Parse.Query("Places");
query.limit = 1000;
query.find().then(function(results) {
_.each(results, function(result) {
var openString = result.get("openhour");
var openHourString = openString.slice(0,2);
var openHour = Number(openHourString);
var openMinuteString = openString.slice(3);
var openMinute = Number(openMinuteString);
result.set("openHour", openMinute/60 + openHour);
var closeString = result.get("closehour");
var closeHourString = closeString.slice(0,2);
var closeHour = Number(closeHourString);
var closeMinuteString = closeString.slice(3);
var closeMinute = Number(closeMinuteString);
result.set("closeHour", closeMinute/60 + closeHour);
});
return Parse.Object.saveAll(results);
}).then(function(results) {
response.success(results);
}, function(error) {
response.error(error);
});
});
Try adding a "return" before before the query.find()
return query.find().then(function(results) {
Also - I assume you have < 1,000 rows of data? Otherwise, you will need to make this recursive because of the max query limit.

javascript - get items with query and match items to a user who has favorited item

I am writing a script on parse.com's javascript cloud code SDK. Here is the information I have saved in my parse.com account and what I am trying to do with it.
I have a bunch of items saved in a parse class called TestItem, theses items have an objectId, item name, meal time (lunch, dinner) and a location for there columns. I also have a class called UserFavourites. In this class the objects have an objectId, item name and a pointer to the user who saved the item as a favourite.
And with this information I am trying to write a cloud code script in javascript. That will match the an item(s) to the item(s) that a user has favourited and send them a push notification saying where and what the item is and the location of the item. I have some code that will do that but this code will send a different notification for each item which could get annoying for the user here is that code.
Parse.Cloud.define("push", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var query = new Parse.Query(TestItem);
query.limit(1000);
query.equalTo('school', 'Union College (NY)');
query.find({
success: function(resultsItem) {
//console.log("Successfully retrieved " + resultsItem.length + " :1111.");
for (var i = 0; i < resultsItem.length; i++) {
var object = resultsItem[i];
var item = object.get('item');
var school = object.get('school');
var meal = object.get('meal');
var meal = meal.toLowerCase();
var diningLocation = object.get('schoolMenu');
//var itemArray = [];
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryFavourite = new Parse.Query(UserFavourite);
queryFavourite.limit(1000);
queryFavourite.equalTo("item", item)
queryFavourite.equalTo("school", school)
queryFavourite.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var objectFav = results[i];
var user = objectFav.get('user');
var userID = user.id;
var realItem = objectFav.get('item');
console.log(objectFav.get('user'));
console.log(objectFav.get('item'));
var UserClass = Parse.Object.extend("User");
var queryUser = new Parse.Query(UserClass);
queryUser.get(userID, {
success: function(userResult) {
console.log(userResult.get('school'));
console.log('install:' + userResult.get('installation').id);
var userInstallationId = userResult.get('installation').id;
var queryInstallation = new Parse.Query(Parse.Installation);
queryInstallation.equalTo('objectId', userInstallationId);
queryInstallation.find({
success: function(results) {
console.log('number' + results.length);
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: realItem + " is being served at " + diningLocation + " for " + meal
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
},
error: function(error) {
console.log('error');
}
});
},
error: function(error) {
console.log('error');
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
});
As you can see it is quite long and not very nice looking, I tried to save items to an array so to avoid sending two or more notifications but couldn't get that to work.
So I started writing another script that uses promises which looks much nicer but haven't gotten it all the way right now, it can match the items to users that have an item favourited and put the objectId's of those users in an array. Here is that code.
Parse.Cloud.define("test", function(request, response) {
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryFavourite = new Parse.Query(UserFavourite);
var userArray = [];
var TestItem = Parse.Object.extend("TestItem");
var query = new Parse.Query(TestItem);
query.limit(1000);
query.equalTo('school', 'Union College (NY)');
query.find().then(function(results) {
return results;
}).then(function(results) {
var promises = [];
for (var i = 0; i < results.length; i++) {
var object = results[i];
var item = object.get('item');
var school = object.get('school');
var meal = object.get('meal');
var UserFavourite = Parse.Object.extend("UserFavourite");
var queryUser = new Parse.Query(UserFavourite);
queryUser.equalTo("item", item);
queryUser.equalTo("school", school);
var prom = queryUser.find().then(function(users) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
var userID = user.get('user').id;
if (userArray.indexOf(userID) === -1) {
userArray.push(userID);
}
}
return userArray;
});
promises.push(prom);
}
return Parse.Promise.when.apply(Parse.Promise, promises);
}).then(function(results) {
console.log(userArray);
});
});
But now with this code I don't know where to go, I think using promises and such is the right way to go but I am now confused as once I have all the users that have an item favourited what to do, I then need to get there items that are favourited and are available in the TestItem class, this is where I am struggling.
Here is a pic of my UserFavourite class it has a pointer to the user who favorited the item as you can see, and also a user has more than one favorite.
Thanks a bunch for the help in advance.
Here is your code, and I changed a couple things.
Parse.Cloud.define("getAllFavoriteItems", function (request, response) {
var TestItems = Parse.Object.extend("TestItems");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItems);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', 'Union College (NY)');
userFavoritesQuery.include('testItems'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.limit(1000); //limit results to 1000
userFavoritesQuery.ascending('userId'); //group the user id's together in your array
userFavoritesQuery.find({
success:function(results) {
var pushNotificationMessage = "";
var userId = "";
for (var i=0; i <results.length; i++) {
if (results[i].get('userId') != userId) {
if (results[i].get('userId') != "") {
//TODO send push notification
}
userId = results[i].get('userId');
pushNotificationMessage = ""; //start a new push notification
}
pushNotificationMessage += results[i].get('item').get('name') + ": " + results[i].get('item').get('location') + "\n";
//SOMEWHERE BEFORE HERE I NEED THE INSTALLATION ID OF THE USER
//TO SEND THE PUSH TO THAT USER
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: pushNotificationMessage
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
}
response.success(true);
},
error:function(error) {
response.error();
}
})
});
Some code that might create push per user, rough outline though
if (i > 0) {
if (results[i].get('user') === results[i-1].get('user')) {
userItems.push(results[i]);
}
else {
userItems.length = 0;
}
}
else {
userItems.push(results[i]);
}
Not sure let me know if you understand what I'm trying to do...
So it a user has two items favourited I want it to group that into one, phrase that says what and where both items are being served
And here is code to send push
Parse.Push.send({
// deviceType: [ "ios" ],
where: queryInstallation,
data: {
alert: pushNotificationMessage
}
},
{
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
It can also be done with then/ promises,
I agree with #Maxwell that your UserFavorite should have links to both User and TestItem. This makes it possible to make your cloud-function as simple as:
Parse.Cloud.define("getAllFavoriteItems", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItem);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', request.params.school);
userFavoritesQuery.include('testItem');
userFavoritesQuery.include('user');
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.find().then(function(results) {
var alerts = {};
for(var i =0 ; i<results.length; i++ ){
var user = results[i].get('user');
var testItem = results[i].get('testItem');
if(user && testItem){
var instId = user.get('installationId');
if(!alerts[instId]) {
alerts[instId] = [];
}
var m = results[i].get('item') + " is being served at {{diningLocation}} for " + testItem.get('meal');
alerts[instId].push(m);
}
}
response.success(alerts);
}, function(error) {
response.error();
});
});
This is working code that you can also find in my github repo.
You can also see the working demo here
The idea is the same as in Maxwell's answer: to have link in UserFavorites class to both User (where installationId is located) and TestItem entities. I've just made it working by including user and testItems properties in query, so when the result is returned filtered by school name I already have a list of installationIds.
Here is my schema:
User
TestItem
UserFavorites
Update:
In this code I added push notifications:
Parse.Cloud.define("getAllFavoriteItems", function(request, response) {
var TestItem = Parse.Object.extend("TestItem");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItem);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', request.params.school);
function SendPush(installationId, msg) {
var query = new Parse.Query(Parse.Installation);
query.equalTo('objectId', installationId);
Parse.Push.send({
where: query,
data: {alert: msg}
});
}
userFavoritesQuery.include('testItem');
userFavoritesQuery.include('user');
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.find().then(function(results) {
var groupedAlerts = {};
// manually iterating though results to get alert strings ang group by user in groupedAlerts[installationId]
for(var i =0 ; i<results.length; i++ ){
var user = results[i].get('user');
var testItem = results[i].get('testItem');
if(user && testItem){
var instId = user.get('installationId');
if(!groupedAlerts[instId]) {
groupedAlerts[instId] = [];
}
var m = results[i].get('item') + " is being served at {{dining Location}} for " + testItem.get('meal');
groupedAlerts[instId].push(m);
}
}
// reformat to array and send push notifications
var alerts = [];
for(var key in groupedAlerts) {
alerts.push({
installationId: key,
alerts: groupedAlerts[key],
});
// Send push notifications
SendPush(key, groupedAlerts[key].join());
}
response.success(alerts);
}, function(error) {
response.error();
});
});
I've also updated test data in live demo (just press Get Alerts) or feel free to play around with test data hot it changes cloud code response. gitnub repo is also up to up to date.
This is based on what I understand as the problem you're trying to solve. If it's not addressing the right issue, let me know and I'll see what I can do.
Looking first at your database model, we can simplify this a bit by modifying the UserFavorites table. Starting with the initial two classes, you have a table of items and a table of users. Since a user can favorite many items and an item can be favorited by many users, we have a many-to-many relationship that exists. When this happens, we need to make a third class that points to each of the other two classes. This is where the UserFavorites table comes into play. In Parse terms, the UserFavorites table needs to have two pointers in it: one for the user and one for the item.
Once the UserFavorite table exists with it's two pointers, we can do a few things fairly easily. In your case, we have a few searching criteria:
each item must be at a given school
you want to limit your responses to the first 1000
To accomplish this you can combine two queries into one by calling matchesQuery.
Parse.Cloud.define("getAllFavoriteItems", function (request, response) {
var TestItems = Parse.Object.extend("TestItems");
var UserFavorites = Parse.Object.extend("UserFavorites");
var testItemsQuery = new Parse.Query(TestItems);
var userQuery = new Parse.Query(Parse.User);
var userFavoritesQuery = new Parse.Query(UserFavorites);
testItemsQuery.equalTo('school', 'Union College (NY)');
userQuery.include('Installation');
userFavoritesQuery.include('testItems'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.include('User'); //This makes sure to pull all of the favorite item data instead of just the pointer object
userFavoritesQuery.matchesQuery('testItem', testItemsQuery); //This will run this second query against the TestItems
userFavoritesQuery.matchesQuery('user', userQuery); //This will run the third query against Users, bringing the installation data along with it
userFavoritesQuery.limit(1000); //limit results to 1000
userFavoritesQuery.ascending('userId'); //group the user id's together in your array
userFavoritesQuery.find({
success:function(results) {
...
},
error:function(error) {
response.error();
}
})
})
Once we get that far, then compiling the push message for each user should be a matter of straight-forward string parsing logic. For example, in the success function, one way we can extract the data we is this:
success:function(results) {
var pushNotificationMessage = "";
var userId = "";
for (var i=0; i <results.length; i++) {
if (results[i].get('userId') != userId) {
if (results[i].get('userId') != "") {
//TODO send push notification
}
userId = results[i].get('userId');
pushNotificationMessage = ""; //start a new push notification
}
pushNotificationMessage += results[i].get('item').get('name') + ": " + results[i].get('item').get('location') + "\n";
}
response.success(true);
}
I haven't tested these examples to see if they'll work, but I hope this gives you an idea of how to simplify your queries into something a little more manageable.

Categories