(For reference I have admin_consent for the organization with a auth scope of offline_access User.ReadWrite.All Group.ReadWrite.All AppCatalog.ReadWrite.All for my token that I use to interact with the Teams instance.)
After installing the app via POST /teams/{id}/installedApps it sends an conversationUpdate event that I respond to and save the entire ConversationReference object. It has a lot of stuff I don't need but I'm not sure what is necessary. The immediate response goes to the General channel of the specified Team.
Now I want to use that ConversationReference to post proactive notification messages to a channel that the user has designated outside of Teams. So the user has not interacted with the bot in this channel, but I can list the channel and have its ID.
I can post the message into the General channel utilizing the entire ConversationReference I captured, or message the user directly in chat via ommiting the channel speicifc fields, but I can't seem to get the message sent to a specific channel if I specify it as the channelId
const msBotAdapter = new BotFrameworkAdapter({
appId: TEAMS_CLIENT_ID,
appPassword: TEAMS_CLIENT_SECRET,
});
//Paired down the saved reference to look like this
const conversationReference = {
"user" : {
"id" : "9:1rafmaopfopakdfafMzCYlCtg",
"aadObjectId" : "fffffff-ffff-ffff-ffff-ffffffff"
},
"bot" : {
"id" : "8:sdfsfsdf-dddd-ddddd-aaaaa-vvvvvvv",
"name" : "Bot Name"
},
"conversation" : {
"isGroup" : true,
"conversationType" : "channel",
"tenantId" : "ffffffff-ssssss-ssssss-ss-ssssss"
},
"channelId" : "msteams",
"serviceUrl" : "https://smba.trafficmanager.net/amer/"
}
const heroCard = CardFactory.heroCard(label, text, undefined, undefined, {
subtitle: fromUser?.name ? `From: ${fromUser.name}` : undefined,
});
const channelId = {...retrieve channel Id}
const activity = {
recipient: {
id: channelId,
name: 'Test channel 2',
},
type: ActivityTypes.Message,
timestamp: new Date(),
localTimezone: 'America/New_York',
callerId: TEAMS_CLIENT_ID,
serviceUrl: conversationReference.serviceUrl!,
channelId,
from: conversationReference.bot as { id: string; name: string },
valueType: 'text',
attachments: [heroCard],
};
await msBotAdapter.createConversation(
conversationReference,
async turnContext => {
await turnContext.sendActivity(activity);
}
);
SUCCESS! Turns out directing the message to another channel requires manipulating the ConversationReference not (as I thought) specifying it in the Activity being sent. I'm showing this by removing the Activity I created in the original question and just sending plain text via await turnContext.sendActivity('Test Message');
const channelId = //retrieve desitnation channelId I use the graph api `/teams/${teamId}/channels`
const msBotAdapter = new BotFrameworkAdapter({
appId: TEAMS_CLIENT_ID,
appPassword: TEAMS_CLIENT_SECRET,
});
//Paired down the initial conversation reference to bare necessities, the important part is setting the `conversationReference.conversation.id` to the `channelId` that you wish the message to go to.
const conversationReference = {
"bot" : {
"id" : "8:sdfsfsdf-dddd-ddddd-aaaaa-vvvvvvv",
},
"conversation" : {
//This is where you dictate where the message goes
id: channelId
},
"serviceUrl" : "https://smba.trafficmanager.net/amer/"
}
await msBotAdapter.createConversation(
conversationReference,
async turnContext => {
await turnContext.sendActivity('Test Message');
}
);
Related
So I want to make it so that every time a new ticket is made it will add a number example: ticket-1 | ticket-2 | ticket-3, ect. And then I want the bot to send the channel in a chat
module.exports = {
data: {
name: `GT1`
},
async execute(interaction, client, message) {
const guild = client.guilds.cache.get("1057116059750117426");
const ticketId = Math.floor(Math.random() * 9000);
await guild.channels.create({
name: `TICKET-${ticketId}`,
parent: '1057370813357109308',
})
interaction.reply({ephemeral: true, content: `Your ticket has been submited \n You can view it here -> ${guild.channels.id}` });
}
}
What you need is a way to persist data after every command. This would require some sort of data storage. I've listed a few options below:
Use a database, (On the discord.js guide they recommend using an ORM)
Store the files in a JSON object on your file system.
Here is an example for 2:
module.exports = {
data: {
name: 'GT1',
},
async execute(interaction, client, message) {
const guild = client.guilds.cache.get('1057116059750117426');
const storageBuffer = fs.readFileSync('./storage.json'); // you will input your file path here.
const storageData = JSON.parse(storageBuffer.toString());
storageData.ticket_id++; // adds one to the ticket number?
const ticketId = storageData.ticket_id;
await guild.channels.create({
name: `TICKET-${ticketId}`,
parent: '1057370813357109308',
});
interaction.reply({
ephemeral: true,
content: `Your ticket has been submited \n You can view it here -> ${guild.channels.id}`,
});
fs.writeFileSync('./storage.json', JSON.stringify(storageData)); // updates the data to your storage file
},
};
You will need to create the json file before using it.
storage.json
{
ticket_id: 0
}
As for sending to a channel you can take a look at this: https://discord.js.org/#/docs/main/stable/class/Interaction?scrollTo=channel
I'm creating a discord bot using node.js and i want it to create a private text channel on a server and add to it the user sending the command "!create" and the bot itself.
I have found a way to make a text channel using this answer: How to create a text channel
but i can't find a way to make it private and add people to it.
I do it always like this:
const everyoneRole = client.guilds.get('SERVER ID').roles.find('name', '#everyone');
const name = message.author.username;
message.guild.createChannel(name, 'text')
.then(r => {
r.overwritePermissions(message.author.id, { VIEW_CHANNEL: true });
r.overwritePermissions(client.id, { VIEW_CHANNEL: true });
r.overwritePermissions(everyoneRole, { VIEW_CHANNEL: false });
})
.catch(console.error);
First, we define the everyoneRole. Then we use the method overwritePermissions() to overwrite the permissions of the newly created guild textchannel. There we give the message author and the bot the permission to view the channel and we revoke the everyone role the permission to view this channel.
Thanks to #gilles-heinesch for the lead. The API of discord.js got drastically changed over time, so here is an updated version:
const { Client, Permissions } = require('discord.js');
/** #param {string|number} serverId - a "snowflake" ID you can see in address bar */
async function createPrivateChannel(serverId, channelName) {
const guild = await client.guilds.fetch(serverId);
const everyoneRole = guild.roles.everyone;
const channel = await guild.channels.create(channelName, 'text');
await channel.overwritePermissions([
{type: 'member', id: message.author.id, allow: [Permissions.FLAGS.VIEW_CHANNEL]},
{type: 'member', id: client.user.id, allow: [Permissions.FLAGS.VIEW_CHANNEL]},
{type: 'role', id: everyoneRole.id, deny: [Permissions.FLAGS.VIEW_CHANNEL]},
]);
}
https://discord.js.org/#/docs/main/stable/class/ChannelManager
Use cache collection
const channels = message.guild.channels.cache
const myChannel = channels.find(channel => channel.name === 'channel name')
So, I having an issue of sending a DM to a specific person without an author tag, and without a mention of the person. I tried duplicating the mention array:
/*jshint esversion: 6*/
const commando = require('discord.js-commando');
class Msgowner extends commando.Command {
constructor(client) {
super(client, {
name: 'msgowner',
group: 'info',
memberName: 'msgowner',
description: 'Gives rules on mock or legit duels.',
examples: ['ladderrules type'],
});
}
async run(message) {
var user = {
id: '12345',
username: 'Bloodmorphed',
discriminator: '12345',
avatar: 'd510ca3d384a25b55d5ce7f4c259b2d0',
bot: false,
lastMessageID: null,
lastMessage: null,
};
user.send('Test');
}
}
module.exports = Msgowner;
There is a reason why I need to send DMs this way, but I can't seem to figure out how. (The error it gives now is a unknown function). Also replacing id and discriminator with generic numbers, but they are correct in my code.
Try something like this - get the member you're looking for using message.channel.members.find() method:
async run(message) {
// get Collection of members in channel
let members = message.channel.members;
// find specific member in collection - enter user's id in place of '<id number>'
let guildMember = members.find('id', '<id number>');
// send Direct Message to member
guildMember.send('test message');
}
Edit: It looks like it's also possible to find users outside the current channel by doing something like this:
async run(message) {
// get client from message's channel
let client = message.channel.client;
// fetch user via given user id
let user = client.fetchUser('<id number>')
.then(user => {
// once promise returns with user, send user a DM
user.send('Test message');
});
}
Okay, found my answer:
async run(message) {
var user = {
id: '12345,
username: 'Bloodmorphed',
discriminator: '12345',
avatar: 'd510ca3d384a25b55d5ce7f4c259b2d0',
bot: false,
lastMessageID: null,
lastMessage: null,
};
console.log(user);
message.member.user.send('Test');
}
}
module.exports = Msgowner;
EDIT: This was NOT the answer, still looking for one.
I am trying to configure stripe.accounts.create({}) for Stripe custom. My goal here is to create everything in one form so the user fulfills all of the information requirements for their stripe account to transact after the form is compete. When testing the current code using the credit card number Stripe recommended, I am getting the error that is displayed after the following code block. I am wondering if there is a tokenization process that I am missing that isn't referenced in the stripe create account docs. This is my current post method
var knex = require("../models/knex"),
express = require('express'),
middleware = require("../middleware/index"),
stripe = require("stripe")("sk_test_VALUEOFMYTESTKEY"),
router = express.Router({mergeParams:true});
router.post("/formuser",function(req,res){
console.log(req.user[0].user_id);
knex("users.user").select("*").where("user_id",req.user[0].user_id)
.then((user) => {
var today = new Date(Date.now()).toLocaleString();
var accountType = String(req.body.accountType).toLowerCase();
var checkIfCard = accountType=="card";
console.log(req.body.accountType,checkIfCard,String(req.body.cardNumber));
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
console.log(ip);
if(!checkIfCard){
stripe.accounts.create({
email: user.email,
country: "US",
type: "custom",
//Required fields for Custom via... https://stripe.com/docs/connect/required-verification-information
metadata: {
"external_account": {
"object": "bank_account",
"exp_month": req.body.cardExpirationMonth,
"exp_year": req.body.cardExpirationYear,// : null,
"number": req.body.bankNumber,// : null,
}, //external account info... https://stripe.com/docs/api#account_create_bank_account
"city": req.body.city,
"legal_entity.adress.line1": req.body.streetAddress,
"legal_entity.address.postal_code": req.body.zipCode,
"legal_entity.address.state": req.body.state,
"legal_entity.dob.day": req.body.birthDay,
"legal_entity.dob.month": req.body.birthMonth,
"legal_entity.dob.year": req.body.birthYear,
"legal_entity.first_name": req.body.firstName,
"legal_entity.last_name": req.body.lastName,
"legal_entity.ssn_last_4": req.body.ssn_last_4,
"tos_acceptance.date": today,
"tos_acceptance.ip": ip,
}
}).then((acct) => {
res.redirect("/");
})
.catch((e) => {
console.log(e);
});
} else {
stripe.accounts.create({
email: user.email,
country: "US",
type: "custom",
//Required fields for Custom via... https://stripe.com/docs/connect/required-verification-information
metadata: {
"external_account": {
"object": "card", //bank account or cc or dc...
"card": req.body.cardNumber.toString(),
"cvc" : req.body.cvc.toString(),
"currency" : "usd",// : null
}, //external account info... https://stripe.com/docs/api#account_create_bank_account
"city": req.body.city,
"legal_entity.adress.line1": req.body.streetAddress,
"legal_entity.address.postal_code": req.body.zipCode,
"legal_entity.address.state": req.body.state,
"legal_entity.dob.day": req.body.birthDay,
"legal_entity.dob.month": req.body.birthMonth,
"legal_entity.dob.year": req.body.birthYear,
"legal_entity.first_name": req.body.firstName,
"legal_entity.last_name": req.body.lastName,
"legal_entity.ssn_last_4": req.body.ssn_last_4,
"tos_acceptance.date": today,
"tos_acceptance.ip": ip,
}
}).then((acct) => {
res.redirect("/");
})
.catch((e) => {
console.log(e);
});
}});
});
When I enter in the credit card information that Stripe recommends to test, I get the following error
{ [Error: Invalid val: {"object"=>"card", "card"=>"4242 4242 4242 4242", "cvc"=>"111", "currency"=>"usd"} must be a string under 500 characters]
type: 'StripeInvalidRequestError',
stack: 'Error: Invalid val: {"object"=>"card", "card"=>"4242 4242 4242 4242", "cvc"=>"111", "currency"=>"usd"} must be a string under 500 character
when I expected a user to be created.
EDIT: I removed some of the knex database code in this post to shorten it's length as it is not relevant to the current error. The current error is specifically from Stripe's promise.
Your code is trying to pass bank account details in external_account but also passing card data at the same time. This is unlikely to be what you want.
On top of this, you should not be passing this information server-side at all as it's sensitive. Instead, you should be creating a token client-side. For card data, you would use Elements and for bank account data you would build your own form and tokenize with Stripe.js. Once this is done, you get a card token tok_123 or a bank account token btok_123 and can then use this server-side in the external_account parameter.
Then, you should also pass the data as nested hashes. This means that you would not pass "legal_entity.adress.line1" but instead legal_entity[address][line1]. Your code should instead look something like this:
stripe.accounts.create(
{
type: 'custom',
country: 'US',
legal_entity : {
first_name : 'john',
last_name : 'doe',
type : 'individual',
address: {
line1: 'line1',
city: 'city',
state: 'state',
postal_code: '90210',
country: 'US'
}
},
external_account: 'tok_visa_debit',
}).then((acct) => {
console.log('account: ', JSON.stringify(acct));
}).catch((e) => {
console.log(e);
});
Stuck with this.I am trying to test my login API using expect and new version of expect throwing me some error.
That's my testing code.
it('should login user and return auth token', (done) => {
request(app)
.post('/users/login')
.send({
email : users[1].email,
password : users[1].password
})
.expect((res) => {
expect(res.headers['x-auth']).toBeTruthy();
})
.end((error,res) => {
if(error)
{
return done(error);
}
User.findById(users[1]._id).then((user) => {
expect(user.tokens[0]).toMatchObject({
access : 'auth',
token : res.headers['x-auth']
});
done();
}).catch((error) => done(error));
});
});
And error is
1) POST /users/login
should login user and return auth token:
Error: expect(received).toMatchObject(expected)
Expected value to match object:
{"access": "auth", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1OWYwMzM0ZGExMzRmYjFmNzg4NTkzOTciLCJhY2Nlc3MiOiJhdX
RoIiwiaWF0IjoxNTA4OTE0MDEzfQ.S0KCmLADcCLPWTK1khxNPO03tVMTW0HU117xapm56MM"}
Received:
{"_id": "59f0335da134fb1f788593b3", "access": "auth", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1OWYwMzM0ZGExMzR
mYjFmNzg4NTkzOTciLCJhY2Nlc3MiOiJhdXRoIiwiaWF0IjoxNTA4OTE0MDEzfQ.S0KCmLADcCLPWTK1khxNPO03tVMTW0HU117xapm56MM"}
Difference:
- Expected
+ Received
Object {
+ "_id": "59f0335da134fb1f788593b3",
"access": "auth",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1OWYwMzM0ZGExMzRmYjFmNzg4NTkzOTciLCJhY2Nlc3MiOiJhdXRoIiwiaWF0IjoxNTA
4OTE0MDEzfQ.S0KCmLADcCLPWTK1khxNPO03tVMTW0HU117xapm56MM",
}
I am testing two things but the code itself including _id and showing me that error. In previous version of expect (when Jest was not introduced) it was simple using toInclude() assertion,but now both toContain() and toMatchObject() showing same error.
That's my seed file
const{ObjectID} = require('mongodb');
const jwt = require('jsonwebtoken');
const {Todo} = require('./../../models/todo');
const {User} = require('./../../models/user');
const userOneId = new ObjectID();
const userTwoId = new ObjectID();
const users = [{
_id: userOneId,
email: 'adil.aj95#gmail.com',
password : 'userOnePass',
tokens: [{
access : 'auth',
token : jwt.sign({_id : userOneId,access : 'auth'}, 'abc123').toString()
}]
},
{
_id: userTwoId,
email: 'adil2.aj95#gmail.com',
password : 'userTwoPass',
// tokens: [{
// access : 'auth',
// token : jwt.sign({_id : userTwoId,access : 'auth'}, 'abc123').toString()
// }]
}];
You need just a slight change. Instead of using
expect(user.tokens[0]).toMatchObject({
access : 'auth',
token : res.headers['x-auth']
});
include .toObject() after user like this
expect(user.toObject().tokens[0]).toMatchObject({
access : 'auth',
token : res.headers['x-auth']
});
Why? Your user is a mongoose object that has more info than you would expect. You can see that there is an extra _id property in the token (the error that is thrown shows that). What toObject() does is it returns just the object as you would expect it, without all the mongoose-specific properties (stuff like _id, __v etc).
You can use .toHaveProperty(keyPath, value) for the new expect version by jest.
So the code becomes like this:
expect(user.tokens[0]).toHaveProperty('access', 'auth');
expect(user.tokens[0]).toHaveProperty('token', user.tokens[0].token);