How to unregister middleware in Telegraf? - telegram-bot

When I add bot.hears(...), it registers middleware for handling matching text messages. But now it will handle those messages even if they are sent any time, even if not expected.
So if I am creating a stateful service, I would like to listen to particular messages only at appropriate time.
How can I unregister middleware, so that it does not hear any more previously handled messages?

I turned out I was looking for Scenes. How to use them is described on Github.
I'll just post a slightly modified code from the links above:
const { Telegraf, Scenes, session } = require('telegraf')
const contactDataWizard = new Scenes.WizardScene(
'CONTACT_DATA_WIZARD_SCENE_ID', // first argument is Scene_ID, same as for BaseScene
(ctx) => {
ctx.reply('Please enter guest\'s first name', Markup.removeKeyboard());
ctx.wizard.state.contactData = {};
return ctx.wizard.next();
},
(ctx) => {
// validation example
if (ctx.message.text.length < 2) {
ctx.reply('Please enter real name');
return;
}
ctx.wizard.state.contactData.firstName = ctx.message.text;
ctx.reply('And last name...');
return ctx.wizard.next();
},
);
const stage = new Scenes.Stage();
stage.register(contactDataWizard);
bot.use(session());
bot.use(stage.middleware());
But I still don't know how to generally implement it, so I need to find it out in the Scenes code of Telegraf.

Related

How to securely store phone numbers in Twilio Functions?

I am using Twilio Functions. I wonder if the phone numbers stored in the Function's code are secure?
I am using code similar to that found here: https://support.twilio.com/hc/en-us/articles/223180548-How-Can-I-Stop-Receiving-or-Block-Incoming-Phone-Calls-#blacklistNumbers
Basically, it rejects numbers that on a blacklist of your choosing. The blacklist is in the Function's code itself.
Perhaps this is already secure. Forgive my misunderstanding.
The aforementioned code:
exports.handler = function(context, event, callback) {
// Listing all the blocked phone numbers, at the moment "+1(212)555-1234" and "+1(702)555-6789"
let blacklist = event.blacklist || [ "+12125551234", "+17025556789" ];
let twiml = new Twilio.twiml.VoiceResponse();
let blocked = true;
if (blacklist.length > 0) {
if (blacklist.indexOf(event.From) === -1) {
blocked = false;
}
}
if (blocked) {
twiml.reject();
}
else {
// if the caller's number is not blocked, redirecting to another TwiML which includes instructions for what to do
twiml.redirect("https://demo.twilio.com/docs/voice.xml");
}
callback(null, twiml);
};
Heyooo. Twilio Developer Evangelist here. 👋
I don't think you have to worry about the security of these numbers. But what you could do it to add the blacklist to the function configuration instead. This way you wouldn't have to change the function code whenever you want to add a new number to the blacklist.
These values below will be available in the context object that is passed into your function.
exports.handler = async function(context, event, callback) {
console.log(context.BLACKLIST); // 1212...
}

Auth0 hooks post-user-registration edit user_metadata

I created a post-user-registration hook, in which i would like to save some information to user_metadata. However, I don't see the data being saved
/*
#param {object} user.user_metadata - user metadata
*/
module.exports = function (user, context, cb) {
// Perform any asynchronous actions, e.g. send notification to Slack.
user.user_metadata = {
"someinfo": "abcd"
}
cb();
};
Something like:
module.exports = function (user, context, cb) {
var response = {};
user.user_metadata.foo = 'bar';
response.user = user;
return cb(null, response);
};
worked fine for me.
For rules the docs say that you can't directly update the user_metadata. As described on the link you have to use the updateUserMetadata function after you set the new values. I am not sure if this applies to hooks too (probably not, since the auth0 object is not defined on hooks).
p.s. Keep in mind that hooks only run for Database Connections, as outlined in the docs. Is there a chance you used an account based on social login?

RxJs: How to conditionally chain observable of BehaviorSubject?

I've got an observable data service (UserService) that returns the currently logged in user. I followed this tutorial - https://coryrylan.com/blog/angular-observable-data-services, which describes using a BehaviorSubject to return a default currentUser immediately, then emit the real currentUser once it's loaded or altered. The service is basically like this...
private _currentUser: BehaviorSubject<User> = new BehaviorSubject(new User());
public currentUser: Observable<User> = this._currentUser.asObservable();
constructor(private http: Http) {}
loadUser() { // app.component onInit and login component call this
return this.http.get('someapi.com/getcurrentuser')
.map(response => <User>this.extractData(response))
.do(
(user) => {
this.dataStore.currentUser = user;
this._currentUser.next(Object.assign(new User(), this.dataStore).currentUser);
},
(error) => this.handleError(error)
)
.catch(error -> this.handleError(error));
}
I'm having problems whenever a user hits F5 to reload the entire spa. When a consuming component subscribes to the currentUser on the UserService, it immediately receives a default user while the UserService waits for an api call to receive the actual user. The moment that api call finishes, the real user is emitted by UserService and all the subscribers get the real user. The first value emitted by the BehaviorSubject, however, is the default value and it always has an id of "undefined", so we can't make our next api call yet. In fact, when the real user comes through and I CAN make a valid call using the user.id, the chained subscription never happens and I don't get the values out of the response.
I know I'm doing something stupid, but I haven't figured out exactly what yet. I just stumbled across concatMap, but I'm not sure how to use it. While I pursue that, I'd like to know why the below code doesn't work. I particularly want to know why the subscribe never fires, even after the real user comes in, just to help my newbie understanding of Observables.
this.userService.currentUser
.flatMap((user) => {
this.user = user;
// Need to NOT call this if the user does not have an id!!!
this.someOtherService.getSomethingElse(user.id); // user.id is always undefined the first time
})
.subscribe((somethingElse) => {
// This never gets called, even after the real user is emitted by the UserService
// and I see the getSomethingElse() call above get executed with a valid user.id
this.somethingElse = somethingElse;
});
If you want to ignore user instances that do not have an id, use the filter operator:
import 'rxjs/add/operator/filter';
this.userService.currentUser
.filter((user) => Boolean(user.id))
.flatMap((user) => {
this.user = user;
this.someOtherService.getSomethingElse(user.id);
})
.subscribe((somethingElse) => {
this.somethingElse = somethingElse;
});
Regarding "why the subscribe never fires", it's likely due to an error arising from the undefined id. You only pass a next function to subscribe, so any errors will be unhandled. And if an error occurs, the observable will terminate and will unsubscribe any subscribers - as that is how observables behave - so any subsequent users with defined id properties will not be received.

Prevent duplicate routes in express.js

Is there a nice way to prevent duplicate routes from being registered in express? I have a pretty large application with hundreds of routes across different files, and it gets difficult to know if I've already registered a certain route when I go to add a new one. For example, I'd like to throw an error when express gets to routes487.js:
File: routes1.js
var ctrl = require('../controllers/testctrl');
var auth = require('../libs/authentication');
module.exports = function (app) {
app.get('/hi', auth.getToken, ctrl.hi);
app.get('/there', auth.getToken, ctrl.there);
};
File: routes487.js
var ctrl = require('../controllers/testctrl487');
var auth = require('../libs/authentication');
module.exports = function (app) {
app.get('/hi', auth.getToken, ctrl.hi487);
};
You could try a custom solution by wrapping express methods with the validation. Consider the following modification to your express app:
// route-validation.js
module.exports = function (app) {
var existingRoutes = {}
, originalMethods = [];
// Returns true if the route is already registered.
function routeExists(verb, path) {
return existingRoutes[verb] &&
existingRoutes[verb].indexOf(path) > -1;
}
function registerRoute(verb, path) {
if (!existingRoutes[verb]) existingRoutes[verb] = [];
existingRoutes[verb].push(path);
}
// Return a new app method that will check repeated routes.
function validatedMethod(verb) {
return function() {
// If the route exists, app.VERB will throw.
if (routeExists(verb, arguments[0]) {
throw new Error("Can't register duplicate handler for path", arguments[0]);
}
// Otherwise, the route is saved and the original express method is called.
registerRoute(verb, arguments[0]);
originalMethods[verb].apply(app, arguments);
}
}
['get', 'post', 'put', 'delete', 'all'].forEach(function (verb) {
// Save original methods for internal use.
originalMethods[verb] = app[verb];
// Replace by our own route-validator methods.
app[verb] = validatedMethod(verb);
});
};
You just need to pass your app to this function after creation and duplicate route checking will be implemented. Note that you might need other "verbs" (OPTIONS, HEAD).
If you don't want to mess with express' methods (we don't know whether or how express itself or middleware modules will use them), you can use an intermediate layer (i.e., you actually wrap your app object instead of modifying its methods). I actually feel that would be a better solution, but I feel lazy to type it right now :)

Nested WLJSONStore Calls Not Executing in Expected Sequence and Not Adding Items to the Collection

This is Worklight 6.1 code with dojo, testing with Chrome and the std dev server Liberty. What I want this code to do is to query a collection, which should have 0 or 1 entries, and either retrieve the one entry if it exists or create an entry with a supplied set of values. What I'm trying to do is store a url, id, and password for a service. If this is the first time the app has run after installation I want to prompt the user for this info and store it. The code to prompt the user will be added later. If it is not the first run of the app then the values should be stored in the collection and be retrieved. I'll add code later to allow the user to change and update the values.
What is happening now is that the .add never seems to be executed, and also the execution sequence I'm seeing thru the breakpoints I've set seems weird.
Here is the setup code
// set up the jsonStore
var collectionName = 'servers';
var collections = {};
collections[collectionName] = {};
// initialize the default jsonStore Monitor credentials
var jsonURL = 'http://myserver.com:9082';
var jsonUser = 'keyser';
var jsonPassword = 'soze';
And here is the problem code
// Initialize the JSONStore
WL.JSONStore.init(collections)
.then(function() {
console.log("store initialized");
// query the store
var query = {_id: 0};
WL.JSONStore.get(collectionName)
.find(query)
.then(function(arrayResults) {
console.log("credentials retrieved " + arrayResults.length);
if (arrayResults.length > 0) {
// retrieve the credentials from the json object
console.log("password retrieved " + arrayResults[0].json.password);
jsonURL = arrayResults[0].json.url;
jsonUser = arrayResults[0].json.user;
jsonPassword = arrayResults[0].json.password;
} else {
// load the default credentials into jsonStore
var credentials = {url: jsonURL, user: jsonUser, password: jsonPassword};
WL.JSONStore.get(collectionName)
.add(credentials)
.then(function() {
console.log("credentials loaded " + credentials.url);
})
.fail(function(errorObject) {
console.log("credential load failed");
});
} // end of else
// Query the model list
queryModels();
}) // end of get(collectionName) then
.fail(function(errorObject) {
console.log("credentials not retrived");
}); // end of get(collectionName) fail
}) // end of init(collections) then
.fail(function(errorObject) {
console.log("store init failed" + errorObject);
}); // end of init(collections) fail
}); // end of ready
When I step thru it flows in this sequence.
init(collections)
Then it jumps immediately to the "end of ready". Seems weird but I'm a rookie so maybe it's OK?
Back to the get(collectionName)
to the .then and logs "credentials retrieved" with and array length of 0
To the else clause of the statement
And it breaks on the get(collectionName) in the else clause. So far so good
From here it jumps to queryModels(), skipping over the .add (far as I can tell)
Then it returns to the .then under the 2nd get and logs "credentials loaded"
At this point execution ends "normally" except,
The item never gets added to the collection, and
The queryModels runs before I expect it to, I want it to run after the item is added.
By now it's probably obvious that I'm a rookie, so I'm probably making the rookie mistake. I know
I'm dealing with deferreds here with the .then and .fails, and I'm nesting them, which seems to be
an accepted technique, but I'm not getting the execution sequence I want.
I've tried this code commenting out the 2nd get(collections) in a couple of formats and it barfs both ways.
// WL.JSONStore.get(collectionName)
.add(credentials)
and
// WL.JSONStore.get(collectionName)
servers.add(credentials)
Any help greatly appreciated. Thanks!
Here's my "answer" below based on what I learned from the other answers below.
Bluewing and cnandrue's answers were both very helpful, and I got it working. The main issues I had turned out to be.
I had failed to grasp that slot 0 in a collection equates to a document _id key of 1. I was trying to query _id = 0, and never getting a hit. The add to the collection was working all along, I was just not reading it correctly.
Moving the queryModels into the if/else clauses (bluewing's suggestion) worked, and reading the material cnandreu referenced (very worthwhile to read) explained why it worked. Thanks!
The tip about the "weird" execution sequence being an artifact of the breakpoints was also very useful, I quit chasing that red herring.
Here is a working draft of the code after fixing these issues. I did not implement all of the suggestions yet, but probably will as I polish this up. Thanks again.
// Initialize the JSONStore - you have to .init to start the collection before you can read it.
WL.JSONStore.init(collections)
.then(function() {
console.log("store initialized");
// query the store
var query = {_id: 1};
WL.JSONStore.get(collectionName) // get 1
.find(query)
.then(function(arrayResults) {
console.log("credentials retrieved " + arrayResults.length);
if (arrayResults.length > 0) {
// retrieve the credentials from the json object
console.log("password retrieved " + arrayResults[0].json.password);
jsonURL = arrayResults[0].json.url;
jsonUser = arrayResults[0].json.user;
jsonPassword = arrayResults[0].json.password;
queryModels();
} else {
// load the default credentials into jsonStore
var credentials = {url: jsonURL, user: jsonUser, password: jsonPassword};
WL.JSONStore.get(collectionName) // get 2
.add(credentials)
.then(function(numberOfDocumentsAdded) {
console.log("Number of Docs Added" + numberOfDocumentsAdded);
queryModels();
}); // end of .add then
} // end of else
}); // end of get(collectionName) 1 then
}) // end of init(collections) then
.fail(function(errorObject) {
console.log("something failed" + errorObject);
}); // end of init(collections) fail
All the JSON store calls ( like add , init etc) are asynchronous. So only you are getting that weird flows when you are checking with Breakpoints.
To get you execution sequence try to move the queryModels(); once the credentials are loaded.
WL.JSONStore.get(collectionName)
.add(credentials)
.then(function() {
console.log("credentials loaded " + credentials.url);
queryModels();
})
My suggestion is the same as Bluewings', but I wanted to share some pseudocode:
function handleCredentials (arrayResults, callback) {
if (arrayResults.length > 0) {
//.... synchronous code here.
setTimeout(function () {
callback();
}, 0);
} else {
WL.JSONStore.get(collectionName)
.add({url: jsonURL, user: jsonUser, password: jsonPassword})
.then(function() {
callback();
});
}
}
WL.JSONStore.init(collections)
.then(function() {
WL.JSONStore.get(collectionName)
.find({_id: 1})
.then(function (arrayResults) {
handleCredentials(arrayResults, function () {
queryModels();
});
});
});
Notice I created a function for handleCredentials, that function will either do a synchronous operation (setting some variables with the result from the find call) or an asynchronous operation (calling add to add credentials). A setTimeout with 0 is called to preserve async behavior, this is explained in detail here. After the handleCredentials function has finished, you call the queryModels function via the callback pattern.
As an aside, I recommended reading this blog post: What’s so great about JavaScript Promises?. Especially the "Error Handling" section. You don't need to add a .fail to every promise, you can get away with less failure functions and the error object should provide enough details into what went wrong. JSONStore error objects are documented here, notice they contain the source of the failure (e.g. src: 'find').