I'm wanting to allow users of my site that use Discord to be able to "automatically" join my guild.
I have everything done except I always get a 401: Unauthorized from Discord's API using the following;
router.get("/cb", passport.authenticate("discord", { failureRedirect: "/" }), async function(req, res) {
const data = { access_token: req.user.accessToken };
axios.put(`https://discordapp.com/api/v8/guilds/${config.CyberCDN.server_id}/members/${req.user.id}`, {
headers: {
"Content-Type": "application/json",
"Authorization": `Bot ${config.CyberCDN.bot_token}`
},
body: JSON.stringify(data)
}).then((success) => {
console.log(`[DASHBOARD] ${req.user.username}#${req.user.discriminator} - Logging in...`);
console.log(success.config.data)
console.log(success.response.status)
return res.status(200).redirect("/");
}).catch((error) => {
console.log(`[DASHBOARD] ${req.user.username}#${req.user.discriminator} - Failed Logging in...`);
console.log(error.config.data.replace(config.CyberCDN.bot_token,"TOKEN"))
console.log(error.response.status)
return res.status(403).redirect("/");
});
});
I don't understand how when everything I have done is correct;
I have even asked in the Discord-API server regarding this matter with the same issue,
I did however have it working ONE TIME and now it's broke again, I have 0 clue how it broke.
My scopes are as follow "oauth_scopes": ["guilds.join"]
I found a better solution to this problem I had:
const DiscordOauth2 = require("discord-oauth2");
const discord = new DiscordOauth2();
/**
* Other required stuff for express.js goes here...
*/
router.get("/login", passport.authenticate("discord"));
router.get("/cb", passport.authenticate("discord", { failureRedirect: "/forbidden" }), async function(req, res) {
req.session.user = req.user;
res.redirect('/');
});
router.get("/support", authOnly, async function(req, res) {
discord.addMember({
accessToken: req.session.user.accessToken,
botToken: config.CyberCDN.bot_token,
guildId: config.CyberCDN.server_id,
userId: req.session.user.id,
roles: [config.CyberCDN.site_role]
}).then((r) => {
if(r) {
let date = new Date(r.joined_at);
res.status(200).json({ status: "Joined Server" });
const embed = new Embed()
.title(`New User Joined Via Site\n${r.user.username}#${r.user.discriminator}`)
.colour(16763904)
.thumbnail(`https://cdn.discordapp.com/avatars/${r.user.id}/${r.user.avatar}.webp?size=128`)
.footer(`User joined at: ${date.toLocaleDateString()}`)
.timestamp();
dhooker.send(embed);
console.log(r)
}else{
res.status(401).json({ status: "Already In There?" });
}
});
});
Basically through browsing my initial 401: Unauthorized error stumbled across a nice little OAuth2 NPM For Discord called discord-oauth2 developed by reboxer and numerous other people, which can be found here.
The helpful part was documented further down the README.md of that repo, in relation to my problem. Relation found here
I have also contributed that they add the removeMember feature also.
I've created a Azure Web App Bot and added a OAuth Connection Setting which takes the user to Salesforce. Everything works well, I'm able to authenticate the user through my bot and also, I can get the access token from Salesforce.
Problem
Can someone help me to get the user information from Salesforce? Because, I am able to get the access token alone and not sure, how to get the user id from Salesforce.
I've written the below code,
var salesforce = {};
salesforce.signin = (connector, session, callback) => {
builder.OAuthCard.create(connector,
session,
connectionName,
"Sign in to your Salesforce account",
"Sign in",
(createSignInErr, createSignInRes) => {
if (createSignInErr) {
callback({
status: 'failure',
data: createSignInErr.message
});
return;
}
callback({
status: 'success',
data: createSignInRes
});
});
};
salesforce.getUserToken = (connector, session, callback) => {
connector.getUserToken(session.message.address,
connectionName,
undefined,
(userTokenErr, userTokenResponse) => {
if (userTokenErr) {
callback({
status: 'failure',
data: userTokenErr.message
});
return;
}
callback({
status: 'success',
data: userTokenResponse
});
});
};
salesforce.accessToken = (connector, session, callback) => {
salesforce.getUserToken(connector, session, (userTokenResponse) => {
if (userTokenResponse.status == 'failure') {
// If the user token is failed, then trigger the sign in card to the user.
salesforce.signin(connector, session, (signinResponse) => {
// If the sign in is failed, then let the user know about it.
if (signinResponse.status == 'failure') {
session.send('Something went wrong, ', signinResponse.message);
return;
}
// If the sign in is success then get the user token and send it to the user.
salesforce.getUserToken(connector, session, (newUserTokenResponse) => {
if (newUserTokenResponse.status == 'failure') {
session.send('Something went wrong, ', newUserTokenResponse.message);
return;
}
callback(newUserTokenResponse);
return;
});
});
}
callback(userTokenResponse);
});
};
I can get the userTokenResponse here. But I need Salesforce user id so that I can start interacting with Salesforce behalf of the user.
If you have only OAuth access token you may query details about the user by invoking http GET against:
https://login.salesforce.com/services/oauth2/userinfo for PROD or
https://test.salesforce.com/services/oauth2/userinfo for sandbox
Add only Authorization: Bearer Y0UR0AUTHTOKEN to the header of the http GET request.
Based on my recent test the result returned from the server looks like:
{
"sub": "https://test.salesforce.com/id/[organizationid]/[userId]",
"user_id": "000",
"organization_id": "000",
"preferred_username": "me#mycompany.com",
"nickname": "myNick",
"name": "name lastname",
"urls": {
...
},
"active": true,
"user_type": "STANDARD",
...
}
You don't need a userId to get the user information where an accessToken is enough. I've installed jsforce and used the below code to get the identity information.
Solved by doing,
const jsforce = require('jsforce');
var connection = new jsforce.Connection({
instanceUrl: instanceUrl,
sessionId: accessToken
});
connection.identity((error, response) => {
if(error) {
callback({
status: 'failure',
message: error.message
});
return;
}
callback({
staus: 'success',
data: response
});
});
When a user visits my chat for the first time they are greeted with welcome message and immediately asked to provide their first name. As soon as the user inputs their first name the welcome message is and text prompt for their first name is displayed once again. Only after they input their first name for the second time, the bot moves on to the next question about their last name.
Additionally when user finally enters their first and last name in first chat and they come back again to the same chat, welcome message & first name prompt is displayed, only when user provides some input bot sends welcome back message.
This is minimal code required to reproduce this problem.
let restify = require('restify');
let builder = require('botbuilder');
// Setup Restify Server
let server = restify.createServer();
let connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
server.post('/api/messages', connector.listen());
let bot = new builder.UniversalBot(connector);
// Define default Dialog
bot.dialog('/', [
function (session) {
if (!session.userData.firstName) {
// firstName used as a flag to assess whether the user is coming back
// or new user - we can use it because the very first dialog is asking them
// for their first name.
session.send("Welcome!");
} else {
session.send("Welcome back %s!", session.userData.firstName);
}
session.beginDialog('mainConversationFlow');
}
])
bot.dialog('mainConversationFlow', [
function(session, args, next) {
if (!session.userData.firstName)
session.beginDialog('getFirstName');
else
next();
},
function(session, args, next) {
if(!session.userData.lastName)
session.beginDialog('getLastName');
else
next();
},
function(session, args, next) {
session.endConversation('The End');
}
])
bot.dialog('getFirstName', [
function(session, args) {
let msg = "What's your first name?";
builder.Prompts.text(session, msg);
},
function(session, results) {
session.userData.firstName = results.response.trim();
session.endDialog();
}
])
bot.dialog('getLastName', [
function(session, args) {
let msg = builder.Message.composePrompt(session,
["Hi %s, what's your last name?"], session.userData.firstName);
builder.Prompts.text(session, msg);
},
function(session, results) {
session.userData.lastName = results.response.trim();
session.endDialog();
}
])
bot.on('conversationUpdate', function (message) {
if (message.membersAdded) {
message.membersAdded.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
bot.beginDialog(message.address, '/');
}
});
}
})
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
})
User on the first run of the app should be shown welcome message and asked for their first name. As soon as they input their first name, the bot should move right away to the next question about their last name and after the user answers that bot should end conversation.
When user entered their first & last names and they come back, the bot should only display welcome back message and end conversation.
This is snippet of my client code using BotFramework-WebChat:
let directLineSecret = 'mysecret';
let directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
BotChat.App({
directLine: {secret: directLineSecret},
user: { id: localStorage.getItem('email')},
bot: { id: 'testbot' },
resize: 'detect'
}, this.botChatContainer.nativeElement);
let directLineClient = rp(directLineSpecUrl)
.then(function(spec) {
return new Swagger({
spec: JSON.parse(spec.trim()),
usePromise: true
});
})
.then(function(client) {
return rp({
url: 'https://directline.botframework.com/v3/directline/tokens/generate',
method: 'POST',
headers: {
'Authorization': 'Bearer ' + directLineSecret
},
json: true
}).then(function(response) {
let token = response.token;
client.clientAuthorizations.add('AuthorizationBotConnector', new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + token, 'header'));
return client;
});
})
.catch(function(err) {
console.error('Error initializing DirectLine client', err);
throw err;
});
These screenshots were taken in dev.botframework.com test window. However the same behaviour applies in my web app which uses WebChat.
Can you please help me to solve this issue?
Update
Logs:
2018-01-13 19:29:46.876 INFO - Container logs 2018-01-13T19:29:45.006255595Z
UserConversation message: , user: undefined 2018-01-13T19:29:45.006543896Z {"typ
e":"conversationUpdate","timestamp":"2018-01-13T19:29:44.4543348Z","membersAdded
":[{"id":"mybot#j4OUxKYkEpQ","name":"MyBot"}],"text":"","attachments":[],"entiti
es":[],"address":{"id":"C27bFaQ1Ohr","channelId":"webchat","user":{"id":"8f8399d
115774c86b83634bf7086f354"},"conversation":{"id":"8f8399d115774c86b83634bf7086f3
54"},"bot":{"id":"mybot#j4OUxKYkEpQ","name":"MyBot"},"serviceUrl":"https://webch
at.botframework.com/"},"source":"webchat","agent":"botbuilder","user":{"id":"8f8
399d115774c86b83634bf7086f354"}}
2018-01-13T19:29:45.006562196Z ----------------------------
2018-01-13T19:29:45.937402126Z Incoming message:
2018-01-13T19:29:45.937559026Z ----------------------------
2018-01-13T19:29:46.291227879Z Outgoing message: Welcome!
2018-01-13T19:29:46.291465679Z {"type":"message","agent":"botbuilder","source":"
webchat","address":{"id":"C27bFaQ1Ohr","channelId":"webchat","user":{"id":"8f839
9d115774c86b83634bf7086f354"},"conversation":{"id":"8f8399d115774c86b83634bf7086
f354"},"bot":{"id":"mybot#j4OUxKYkEpQ","name":"MyBot"},"serviceUrl":"https://web
chat.botframework.com/"},"text":"Welcome!"}
2018-01-13T19:29:46.291479179Z ----------------------------
2018-01-13T19:29:46.291708779Z Outgoing message:
What's your first name? 2018-01-13T19:29:46.291740980Z {"text":"What's your
first name?","inputHint":"expectingInput","type":"message","address":{"id":"C27b
FaQ1Ohr","channelId":"webchat","user":{"id":"8f8399d115774c86b83634bf7086f354"},
"conversation":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"mybot#j4OU
xKYkEpQ","name":"MyBot"},"serviceUrl":"https://webchat.botframework.com/"}}
2018-01-13T19:29:46.291759880Z ----------------------------
2018-01-13 19:29:56.876 INFO - Container logs 2018-01-13T19:29:53.471348251Z
UserConversation message: , user: undefined 2018-01-13T19:29:53.471657052Z {"typ
e":"conversationUpdate","timestamp":"2018-01-13T19:29:53.3233269Z","membersAdded
":[{"id":"AvfenKwcS1o","name":"You"}],"text":"","attachments":[],"entities":[],"
address":{"id":"DbpPwxf2m7T","channelId":"webchat","user":{"id":"8f8399d115774c8
6b83634bf7086f354"},"conversation":{"id":"8f8399d115774c86b83634bf7086f354"},"bo
t":{"id":"mybot#j4OUxKYkEpQ","name":"MyBot"},"serviceUrl":"https://webchat.botfr
amework.com/"},"source":"webchat","agent":"botbuilder","user":{"id":"8f8399d1157
74c86b83634bf7086f354"}}
2018-01-13T19:29:53.471672552Z ----------------------------
2018-01-13T19:29:53.515781796Z UserConversation
message: John, user: You 2018-01-13T19:29:53.515792596Z {"type":"message","times
tamp":"2018-01-13T19:29:53.1827153Z","textFormat":"plain","text":"John","entitie
s":[{"type":"ClientCapabilities","requiresBotState":true,"supportsTts":true,"sup
portsListening":true}],"textLocale":"en","sourceEvent":{"clientActivityId":"1515
871784086.6213104132628995.0"},"attachments":[],"address":{"id":"8f8399d115774c8
6b83634bf7086f354|0000002","channelId":"webchat","user":{"id":"AvfenKwcS1o","nam
e":"You"},"conversation":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"
mybot#j4OUxKYkEpQ","name":"MyBot"},"serviceUrl":"https://webchat.botframework.co
m/"},"source":"webchat","agent":"botbuilder","user":{"id":"AvfenKwcS1o","name":"
You"}}
2018-01-13T19:29:53.515801796Z ----------------------------
2018-01-13T19:29:53.545361425Z Incoming message: John
2018-01-13T19:29:53.545373525Z ----------------------------
2018-01-13T19:29:53.802571982Z Outgoing message: Welcome!
2018-01-13T19:29:53.802593382Z {"type":"message","agent":"botbuilder","source":"
webchat","textLocale":"en","address":{"id":"8f8399d115774c86b83634bf7086f354|000
0002","channelId":"webchat","user":{"id":"AvfenKwcS1o","name":"You"},"conversati
on":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"mybot#j4OUxKYkEpQ","n
ame":"MyBot"},"serviceUrl":"https://webchat.botframework.com/"},"text":"Welcome!
"}
2018-01-13T19:29:53.802600382Z ----------------------------
2018-01-13T19:29:53.802602782Z Outgoing message: What's your first name?
2018-01-13T19:29:53.802604982Z {"text":"What's your first name?","inputHint":"ex
pectingInput","type":"message","address":{"id":"8f8399d115774c86b83634bf7086f354
|0000002","channelId":"webchat","user":{"id":"AvfenKwcS1o","name":"You"},"conver
sation":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"mybot#j4OUxKYkEpQ
","name":"MyBot"},"serviceUrl":"https://webchat.botframework.com/"},"textLocale"
:"en"}
2018-01-13T19:29:53.802610082Z ----------------------------
2018-01-13 19:30:01.878 INFO - Container logs 2018-01-13T19:29:57.806548081Z
UserConversation message: John, user: You 2018-01-13T19:29:57.809735285Z {"type"
:"message","timestamp":"2018-01-13T19:29:57.6990081Z","textFormat":"plain","text
":"John","textLocale":"en","sourceEvent":{"clientActivityId":"1515871784086.6213
104132628995.2"},"attachments":[],"entities":[],"address":{"id":"8f8399d115774c8
6b83634bf7086f354|0000005","channelId":"webchat","user":{"id":"AvfenKwcS1o","nam
e":"You"},"conversation":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"
mybot#j4OUxKYkEpQ","name":"MyBot"},"serviceUrl":"https://webchat.botframework.co
m/"},"source":"webchat","agent":"botbuilder","user":{"id":"AvfenKwcS1o","name":"
You"}}
2018-01-13T19:29:57.809755085Z ----------------------------
2018-01-13T19:29:57.828015903Z Incoming message: John
2018-01-13T19:29:57.828028303Z ----------------------------
2018-01-13T19:29:58.122706697Z Outgoing message: Got response as: John
2018-01-13T19:29:58.122972998Z {"type":"message","agent":"botbuilder","source":"
webchat","textLocale":"en","address":{"id":"8f8399d115774c86b83634bf7086f354|000
0005","channelId":"webchat","user":{"id":"AvfenKwcS1o","name":"You"},"conversati
on":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"mybot#j4OUxKYkEpQ","n
ame":"MyBot"},"serviceUrl":"https://webchat.botframework.com/"},"text":"Got
response as: John"}
2018-01-13T19:29:58.122997998Z ----------------------------
2018-01-13T19:29:58.123366398Z Outgoing message: Hello John! What is your last
name? 2018-01-13T19:29:58.123377798Z {"text":"Hello John! What is your last name
?","inputHint":"expectingInput","type":"message","address":{"id":"8f8399d115774c
86b83634bf7086f354|0000005","channelId":"webchat","user":{"id":"AvfenKwcS1o","na
me":"You"},"conversation":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":
"mybot#j4OUxKYkEpQ","name":"MyBot"},"serviceUrl":"https://webchat.botframework.c
om/"},"textLocale":"en"}
2018-01-13T19:29:58.123395698Z ----------------------------
2018-01-13T19:30:00.551811524Z UserConversation
message: Doe, user: You 2018-01-13T19:30:00.552098924Z {"type":"message","timest
amp":"2018-01-13T19:30:00.4252782Z","textFormat":"plain","text":"Doe","textLocal
e":"en","sourceEvent":{"clientActivityId":"1515871784086.6213104132628995.4"},"a
ttachments":[],"entities":[],"address":{"id":"8f8399d115774c86b83634bf7086f354|0
000008","channelId":"webchat","user":{"id":"AvfenKwcS1o","name":"You"},"conversa
tion":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"mybot#j4OUxKYkEpQ",
"name":"MyBot"},"serviceUrl":"https://webchat.botframework.com/"},"source":"webc
hat","agent":"botbuilder","user":{"id":"AvfenKwcS1o","name":"You"}}
2018-01-13T19:30:00.552114924Z ----------------------------
2018-01-13T19:30:00.590356662Z Incoming message: Doe
2018-01-13T19:30:00.590371762Z ----------------------------
2018-01-13T19:30:00.857187129Z Outgoing message: Got last name as: Doe
2018-01-13T19:30:00.857206229Z {"type":"message","agent":"botbuilder","source":"
webchat","textLocale":"en","address":{"id":"8f8399d115774c86b83634bf7086f354|000
0008","channelId":"webchat","user":{"id":"AvfenKwcS1o","name":"You"},"conversati
on":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"mybot#j4OUxKYkEpQ","n
ame":"MyBot"},"serviceUrl":"https://webchat.botframework.com/"},"text":"Got last
name as: Doe"}
2018-01-13T19:30:00.857220329Z ----------------------------
2018-01-13T19:30:00.857222929Z Outgoing message: End of "mainConversationFlow"
dialog. 2018-01-13T19:30:00.857225229Z {"type":"message","agent":"botbuilder","s
ource":"webchat","textLocale":"en","address":{"id":"8f8399d115774c86b83634bf7086
f354|0000008","channelId":"webchat","user":{"id":"AvfenKwcS1o","name":"You"},"co
nversation":{"id":"8f8399d115774c86b83634bf7086f354"},"bot":{"id":"mybot#j4OUxKY
kEpQ","name":"MyBot"},"serviceUrl":"https://webchat.botframework.com/"},"text":"
End of \"mainConversationFlow\" dialog."}
2018-01-13T19:30:00.857230729Z ----------------------------
Code I have used for logs:
const logUserConversation = (event) => {
console.log('UserConversation message: ' + event.text + ', user: ' + event.address.user.name);
console.log(JSON.stringify(event));
console.log('----------------------------');
};
const logIncomingMessage = function (session) {
console.log('Incoming message: ' + session.message.text);
console.log(JSON.stringify(session.user));
console.log('----------------------------');
};
const logOutgoingMessage = function (event) {
console.log('Outgoing message: ' + event.text);
console.log(JSON.stringify(event));
console.log('----------------------------');
};
bot.use({
receive: function (event, next) {
logUserConversation(event);
next();
},
botbuilder: function (session, next) {
logIncomingMessage(session);
next();
},
send: function (event, next) {
logOutgoingMessage(event);
next();
}
})
Actually, when the bot connecter first connect to bot server, the bot firstly joins in the conversation, so conversationUpdate event will be triggerred for your bot, which do not contain the session.userData object.
And once the user inputs someting in the bot webchat, when there will be a second conversationUpdate for the user. At this time, bot beginDialog '\' inside the conversationUpdate event with the session contains session.userData object.
you can add the following middleware to detect this issue:
bot.use({
receive: function (event, next) {
console.log(event)
next();
},
send: function (event, next) {
console.log(event)
next();
}
});
Unfortunately, I cannot find a method to let bot triggers conversationUpdate for user when the webchat init.
update
You can leverage webchat js sdk on website and the backchannel mechanism to achieve your requirement.
website client:
//define a user
const user = {id:'userid',name:'username'};
const botConnection = new BotChat.DirectLine({
domain: params['domain'],
secret: '<secrect>',
webSocket: params['webSocket'] && params['webSocket'] === 'true' // defaults to true
});
botConnection .postActivity({ type: "event", from: user, name: "ConversationUpdate", value: "" }) .subscribe(id => console.log("Conversation updated"));
BotChat.App({
botConnection: botConnection,
bot: bot,
user: user,
resize: 'detect'
}, document.getElementById("BotChatGoesHere"));
bot server:
bot.on('event',(event)=>{
console.log(event)
if(event.name==='ConversationUpdate'){
bot.beginDialog(event.address, '/');
}
})
To the people who still get the issue
When the first conversation is established between the web channel and the bot, the ConversationUpdate activity is raised twice. One by the user and another by the channel, therefore we get a welcome message twice. We need to make sure that we send the welcome message for the activity raised by the user. Find the working code here