Login in webchat

Hi, it’s me again :smiley:

Another subject for my pretty fun bot :
How to log in to access at private functionalities.

I seen the documentation about this (https://botpress.io/docs/latest/recipes/login/) and I implemented the interaction with database with success. But for the moment, my user give his login and his password in the bot box, and it’s realy not securised :

What is your login ?

test

What is your password ?

azerty

Welcome test

Awful, isn’t it? :stuck_out_tongue: So I would like to do better.

  1. My bot is in a website, where user can log in. Is it possible to give informations from website to chatbot ?

I know that node js hasn’t acces on the DOM.
So I tried this :

  • In my website (in twig), I have :
window.botpressWebChat.sendEvent({
    type: 'proactive-trigger',
    platform: 'web',
    text: '{{ app.session.get('login') ? app.session.get('login') : false }}'
})
  • And in my bot :
bp.hear({ type: /proactive-trigger/i }, async ({ user, text }, next) => {
    console.log(text)
    bp.renderers.sendToUser(user, '#builtin_text', { text: 'Bonjour, en quoi puis-je vous aider?', typing: true })
    next()
  })

But in bot, how can I store the text content inside a user variable?
In src/index.js, I don’t have access on state or event variable.

  1. Another solution may be to have a login form inside the bot. Is it possible?

I really don’t understand how to use the renderers

Thanks in advance :woman_technologist:

@Delphine, you have access to event within bp.hear function. You are destructuring it in your example into { user, text }, but you could have something like bp.hear({ type: /proactive-trigger/i }, async (event, next) => {...}.

As for renderer, webchat supports login_prompt renderer type, which renders login form with password that isn’t showing up. Did you try adding #login-form renderer from the example in docs? You’d be able to send it via action by calling event.reply('#login-form').

1 Like

Really nice :slight_smile:

Here is my final function (if someone else needs to do the same) and it works very well :

  bp.hear({ type: /proactive-trigger/i }, async (event, next) => {
    // event.text = login or false
    if(event.text){
      await event.bp.kvs.set('storage/users/'+ event.user.id+'/login', {"value":event.text,"expiry":"never"})
      await event.bp.kvs.set('storage/users/'+ event.user.id+'/isLogin', {"value":true,"expiry":"never"})
    } else {
      await event.bp.kvs.set('storage/users/'+ event.user.id+'/isLogin', {"value":false,"expiry":"never"})
    }
    bp.renderers.sendToUser(event.user, '#builtin_text', { text: 'Bonjour, en quoi puis-je vous aider?', typing: true })
    next()
  })

I’m sorry but I really don’t understand how to use the renderers

I added '#login-form': data => [{ typing: true, type: 'login_prompt' }], in renderers.js
I added login_prompt in bp.hear, in index.js
But I still don’t understand how to invoke it.

I suppose that I can’t invoke it directly in the flow
So I make a new action who use event.reply('#login-form')
My action is called by a node on OnReceive with the argument {{event.text}}
But I have :

11:45:49 - warn: Outgoing queue failed to process job: Expected event to contain (type: string), (platform: string), (text: string), (raw: any)
11:45:49 - warn: Outgoing queue failed to process job: Expected event to contain (type: string), (platform: string), (text: string), (raw: any)
11:45:49 - error: Retrying job within Outgoing queue failed 2 times. Abandoning the job.

It’s really not clear for me.
When to call these action in the flow ? With which arguments ?
Does this action have to do more than event.reply('#login-form') ?

@Delphine, you should add text key to the object that renderer returns.
You can pass params to it by calling it like event.reply('#login-form', { param: 'something' }) and it becomes available under data argument.

I don’t understand the interest as the message does not appear in the window of conversation, but OK, it works, I have a form.

In renderers.js :

'#login-form': data => [{ typing: true, type: 'login_prompt', text:data.param }],

In action.js :

async function loginForm(state, event) {
    event.reply('#login-form', {param: 'something'})

    return {
        ...state
    }
}

Now how I can retrieve the elements given by the user in the form to perform the verification ?

11:16:26 - info: [NLU Extraction] Provided login information
{ text: ‘Provided login information’,
type: ‘login_prompt’,
data: { username: ‘AAA’, password: ‘BBB’ },
conversationId: 1 }

I have nothing in state, user tag, user/conversaiton/global variable, nlu.
I tried a await event.reply('#login-form', {param: 'something'}) .then(function(username, password) { console.log(username) }) but the console.log is made before the user completes the form.
So where it is ?

And minor question : the form is full in english, is it possible to modify it to french? (username/password/submit)

Sorry again for all my questions, but I did not find explanation in documentation

Found it!
event.raw.data.username and event.raw.data.password

Can you share your final solution all together please?

All the elements are above :slight_smile: But I can put them together.
How I create a form to login into the chat box:

In renderers.js :

'#login-form': data => [{ typing: true, type: 'login_prompt', text:data.param }],

In index.js :

bp.hear ({ type: /text|message|login_prompt/i }, (event, next) => {

In action.js :

/**
 * Login into the chat box with a form
 */
async function loginForm(state, event) {
    await event.reply('#login-form', {param: 'something'})

    return { ...state }
}

/**
 * Login into the chat box with a mysql/mariadb database
 */
async function loginChat(state, event) {
    const isLogin = await event.bp.mySqlDB
        .select('password')
        .from('account')
        .where('login', event.raw.data.username)
        .then(function(rows) {
            if(!rows[0]){ return false }
            if (rows[0].password == event.raw.data.password){ return true } 
            else { return false }
        })

    if(isLogin){
        await event.bp.kvs.set('storage/users/'+ event.user.id+'/login', {"value":event.raw.data.username,"expiry":"never"})
        await event.bp.kvs.set('storage/users/'+ event.user.id+'/isLogin', {"value":"true","expiry":"never"})
        return {
            ...state,
            isLogin : "true"
        }
    } else {
        return {
            ...state,
            isLogin : "false"
        }
    }
}

In the flow you call loginForm(), then loginChat(). So the state.isLogin === “true” or “false”. And informations are also in getUserVariable(“name”:“isLogin”) and getUserVariable(“name”:“login”)

1 Like

fantastic! Thanks for sharing :slight_smile: Happy Bot-ing!

I followed the recipe but getting this error:
The styleprop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.

image

This error msg shows up and the chat-box disappear.

@Delphine can you share the full syntax for how you registered the bp.hear in index.js and render.js file please? Hey @alex and @dmytropanontko can I get some help here please?

Hi, here’s an example of my hear function:

  bp.hear(/.+/i, async (event, next) => {
    const stateId = event.sessionId || event.user.id
    bp.dialogEngine.processMessage(stateId, event).then()
  })

Can you also share an example of login form content type?

The renderer from the recipe should work:

'#login-form': data => [{ typing: true, type: 'login_prompt' }]

But it appeared there was an issue rendering login_prompt, which I’ve just fixed within https://github.com/botpress/botpress/pull/943

1 Like

Ok sorry, to open again this topic but I have a question about the login. I’ve done what is written in the documentation, and I can fetch the username/password in my bp.hear function. What I try to do is : launch a action function from here, to authenticate and then change the state and continue to a differente node.
Is that possible ?


  bp.hear ({ type: /text|message|login_prompt/i },  async (event, next) => {
    console.log('HERE AFTER LOGIN BUTTON')
    const userId = event.user.id

    console.log('------------------->', event.raw.data)

    // CALL AN ACTION FUNCTION FOR AUTHENTIFCATION AND GO TO NEXT NODE + CHANGE STATE
    
      // actions.authentificate(event.raw.data).then((result) => {
     // set the state : state.authenticate = result
    //})

  })  

}

Sure! This can be done via event.bp.dialogEngine.jumpTo method.

1 Like

@alex ! You are my hero ! I hadn’t seen this function. Thank you

1 Like

I have followed all the steps as you have mentioned, I am using sqlite3 as the db. I have created a table like below in the db

image

now how would I connect with the db and what query should I apply here. My db is in …/botpress/.data

I am using 10.50 of botpress

please help and thanks in advance for your help.

Hey @alex and @dmytropanontko can I get some help here please?

Hello all,
I am using Botpress 12.1.6 and I am trying to do the exact same type of login (login in the Webchat) as @Delphine.
But I cannot access my index.js, renderer.js or my action.js file. And also I think the “bp.hear” call is deprecated?
Can someone please help me to implement this type of login with Botpress 12.1.6?
I am not sure whether the instructions above are still applicable for the latest versions of Botpress.
Thank you very much.