I'm trying to get the roles of the current user in a specific chat room using Discord's API (using an access_token). I've had a fair look through the docs and can't see how to do this. For example, doing a get request to https://discordapp.com/api/users/#me gives the basic user info as expected. I then tried something along the lines of the following without any luck:
https://discordapp.com/api/users/#me/guilds/${guild.id}/roles
Here's a snippet of the code I'm using:
....get access token then
.then(info => {
console.log(info)
fetch(`https://discordapp.com/api/users/#me`, {
headers: {
authorization: `${info.token_type} ${info.access_token}`,
},
}).then(response => response.json())
.then(info => {
console.log(info)
})
})
Any ideas?
To clarify, the user logs in with discord and my application receives a user access token which I'm then trying to use to get the roles of the user in a specific discord room.
From the documentation you can do this using the endpoint:
GET /guilds/{guild.id}/members/{user.id}
This will return a guild member object that contains the roles of this user.
Example guild member object:
{
"user": {},
"nick": "",
"roles": [],
"joined_at": "",
"deaf": false,
"mute": false
}
There is
GET /users/#me/guilds/{guild.id}/member
Which will get you a JSON object containing
{
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"is_pending": null,
"joined_at": "2023-01-11T23:12:34.423000+00:00",
"nick": null,
"pending": null,
"premium_since": null,
"roles": [
2888141278217623600,
5904405471246944000
],
"user": {
"id": 8285334657500223000,
"username": "your-name",
"display_name": null,
"avatar": null,
"avatar_decoration": null,
"discriminator": 4041,
"public_flags": 0
},
"mute": null,
"deaf": null
}
To use this with OAuth2, you must request the guilds.members.read OAuth2 scope, which is the one that Discord prompts users for their permissions with
Read your member info (nickname, avatar, roles, etc...) for servers you belong to
This was added in sometime in late 2021, judging from this PR.
Related
Please I need help. I don't know what I am doing wrong. I am trying to send push notification from http request but I keep getting this error:
The request was missing an Authentication Key. Please, refer to section "Authentication" of the FCM documentation, at https://firebase.google.com/docs/cloud-messaging/server.
I am currently doing this:
const response = await context.http.post({
url:"https://fcm.googleapis.com/fcm/send",
header:{"Content-Type": "application/json",
"Authorization":"key= Web Server Key"},
body:{
"to": usersPushToken, // From FirebaseMessaging.instance.getToken();
"notification": {
"title": "Title",
"body": "body",
"clickAction": 'FLUTTER_NOTIFICATION_CLICK',
"sound": 'default',
},
}
},
encodeBodyAsJSON: true,
});
My web server key (Cloud Messaging API (Legacy)) I also tried API key:
This was my fault (headers not header). But I will leave this answer for anyone using Flutter, MongoDB, and Firebase Messaging.
const response = await context.http.post({
url:"https://fcm.googleapis.com/fcm/send",
"headers":{"Content-Type": ["application/json"], //Must be in array
"Authorization":["key= Web Server Key"]}, //Must be in array
"body":{
"to": usersPushToken, // From FirebaseMessaging.instance.getToken();
"notification": {
"title": "Title",
"body": "body",
"clickAction": 'FLUTTER_NOTIFICATION_CLICK',
"sound": 'default',
},
}
},
encodeBodyAsJSON: true,
});
Trying to make a simple patch request against a single document, and the request returns
{
"acknowledged": true,
"modifiedCount": 1,
"upsertedId": null,
"upsertedCount": 0,
"matchedCount": 1
}
This is the document I am trying to update
{
"_id": "63843e60079d9cdf9c26505a",
"name": "AKG_HSD271",
"image": "images/Products/AKG_hsd271.png",
"colour": "Black",
"description": "AKG HSD271 over-ear headset",
"price": "165.99",
"startingDateAvailable": "2022-05-10T15:23:28.000Z",
"type": "Over-Ear",
"isOnSale": false,
"stock": 46,
"EndingDateAvailable": "N/A",
"manufacturer": "AKG",
"updatedAt": "2022-12-03T08:48:35.302Z"
}
This is the request body I am sending (via Postman)
{
"price": "100.99"
}
And here is the code for my route handler
router.patch('/Products/:id', (req, res) => {
console.log('/Products/'+req.params.id);
const updates = req.body;
Product.updateOne({_id: ObjectId(req.params.id)}, {$set: updates})
.then(result => {
console.log(result);
res.json(result);
})
.catch(err => {
res.status(500).send(err.message);
});
});
Can't for the love of me figure out what is going wrong and why the price field won't change, and can't find any threads that have a suggestion I haven't tried. Any ideas?
Found the problem. Including the built in json() function in express before my route handlers seemed to do the trick:
router.use(express.json())
Apparently in express, the body property does not exist on the req object unless you include the json middleware. Hope this helps others.
Currently I am using Strapi to build out a custom API and NextJs for the front end, I am trying to use getStaticPaths to create pages based on categories. I have setup a categories collection with a relationship to my papers collection in Strapi and when using Postman to test API routes everything works great. However Next is giving me an error when I attempt to access the getStaticPaths route which should http://localhost:3000/categories/1 but instead I get the error Error: A required parameter (category) was not provided as a string in getStaticPaths for /categories/[category] currently my code looks like this below; However I am confused because I am converting it to a string which should fix the error correct? I am no pro at NextJs btw.
If I manually enter the route in either Postman or my browser it works correctly, responding with the correct json output. And the console for strapi also shows the sent request, However this does not appear in the console when Next tries to load the page I am guessing because it isn't getting that far.
How the F do I fix the above mentioned error I have been here for days and it is getting slightly annoying lol
// pages/categories/[category].js
function Category({ categories }) {
return (
<>
<h1>{categories.name}</h1>
<h2>{categories.papers.name}</h2>
</>
)
}
export async function getStaticPaths() {
const res = await fetch('http://localhost:1337/Categories')
const Categories = await res.json()
await console.log(Categories);
const paths = Categories.map((category) => ({
params: { id: category.id.toString() },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`http://localhost:1337/Categories/${params.id}`)
const categories = await res.json()
console.log(categories)
// Pass post data to the page via props
return { props: { categories } }
}
export default Category
The correct response for http://localhost:1337/Categories/**${params.id}** - which should be 1 meaning the url is http://localhost:1337/Categories/1
{
"id": 2,
"name": "English",
"published_at": "2021-10-08T10:12:08.041Z",
"created_at": "2021-10-08T10:12:04.011Z",
"updated_at": "2021-10-08T10:12:08.061Z",
"papers": [
{
"id": 1,
"name": "2020 English Studies (Testing)",
"description": "# Testing",
"PDF_link": "/uploads/2020_hsc_english_studies_98eabce6e7.pdf",
"published_at": "2021-10-08T10:14:55.816Z",
"created_at": "2021-10-08T10:12:48.714Z",
"updated_at": "2021-10-08T10:14:55.835Z",
"Media_Upload": [
{
"id": 1,
"name": "2020-hsc-english-studies.pdf",
"alternativeText": "",
"caption": "",
"width": null,
"height": null,
"formats": null,
"hash": "2020_hsc_english_studies_98eabce6e7",
"ext": ".pdf",
"mime": "application/pdf",
"size": 4959.79,
"url": "/uploads/2020_hsc_english_studies_98eabce6e7.pdf",
"previewUrl": null,
"provider": "local",
"provider_metadata": null,
"created_at": "2021-10-08T10:14:32.827Z",
"updated_at": "2021-10-08T10:14:32.847Z"
}
]
}
]
}
Keys in params should correspond to your dynamic route name. You pass id key there, but your route is called /categories/[category] so you need to pass category key.
const paths = Categories.map((category) => ({
params: { category: category.id.toString() },
}))
And in getStaticProps obviously also grab category from params.
I'm using Dynamoose to simplify my interactions with DynamoDB in a node.js application. I'm trying to write a query using Dynamoose's Model.query function that will search a table using an index, but it seems like Dynamoose is not including all of the info required to process the query and I'm not sure what I'm doing wrong.
Here's what the schema looks like:
const UserSchema = new dynamoose.Schema({
"user_id": {
"hashKey": true,
"type": String
},
"email": {
"type": String,
"index": {
"global": true,
"name": "email-index"
}
},
"first_name": {
"type": String,
"index": {
"global": true,
"name": "first_name-index"
}
},
"last_name": {
"type": String,
"index": {
"global": true,
"name": "last_name-index"
}
}
)
module.exports = dynamoose.model(config.usersTable, UserSchema)
I'd like to be able to search for users by their email address, so I'm writing a query that looks like this:
Users.query("email").contains(query.email)
.using("email-index")
.all()
.exec()
.then( results => {
res.status(200).json(results)
}).catch( err => {
res.status(500).send("Error searching for users: " + err)
})
I have a global secondary index defined for the email field:
When I try to execute this query, I'm getting the following error:
Error searching for users: ValidationException: Either the KeyConditions or KeyConditionExpression parameter must be specified in the request.
Using the Dynamoose debugging output, I can see that the query winds up looking like this:
aws:dynamodb:query:request - {
"FilterExpression": "contains (#a0, :v0)",
"ExpressionAttributeNames": {
"#a0": "email"
},
"ExpressionAttributeValues": {
":v0": {
"S": "mel"
}
},
"TableName": "user_qa",
"IndexName": "email-index"
}
I note that the actual query sent to DynamoDB does not contain KeyConditions or KeyConditionExpression, as the error message indicates. What am I doing wrong that prevents this query from being written correctly such that it executes the query against the global secondary index I've added for this table?
As it turns out, calls like .contains(text) are used as filters, not query parameters. DynamoDB can't figure out if the text in the index contains the text I'm searching for without looking at every single record, which is a scan, not a query. So it doesn't make sense to try to use .contains(text) in this context, even though it's possible to call it in a chain like the one I constructed. What I ultimately needed to do to make this work is turn my call into a table scan with the .contains(text) filter:
Users.scan({ email: { contains: query.email }}).all().exec().then( ... )
I am not familiar with Dynamoose too much but the following code below will do an update on a record using node.JS and DynamoDB. See the key parameter I have below; by the error message you got it seems you are missing this.
To my knowledge, you must specify a key for an UPDATE request. You can checks the AWS DynamoDB docs to confirm.
var params = {
TableName: table,
Key: {
"id": customerID,
},
UpdateExpression: "set customer_name= :s, customer_address= :p, customer_phone= :u, end_date = :u",
ExpressionAttributeValues: {
":s": customer_name,
":p": customer_address,
":u": customer_phone
},
ReturnValues: "UPDATED_NEW"
};
await docClient.update(params).promise();
I'm trying to read and write from/to an Azure Cosmos DB with two different bots (js, v4, ms botframework).
Chatbot 1:
- Chat with user, save user data and use it later
Chatbot 2:
- Read and display some user data
I use the following client: https://github.com/Microsoft/BotFramework-WebChat
Scenario:
I fixate my userID in the client (which has a directline to bot 1) to let's say "123"
I use Bot 1 and enter my username in the dialog (prompted by bot)
I refresh the Website on which Bot 1 is running with the same id "123"
I see that the bot still has my data stored
I change the ID in my client to "124"
I use Bot 1 and see there is no stored data (which is expected since ID "124" has never chatted with Bot 1)
I change the ID back to "123"
I use bot 1 and see that data from step 2 is still there
I use bot 2 with the id "123"
I see that there is no data ("undefined")
I use bot with ID "123" again
I see that the data from step 2 is gone
Which means that whenever I access the database with my second bot it seems like the data is cleared / deleted.
This is how I access the DB in index.js:
//Add CosmosDB (info in .env file)
const memoryStorage = new CosmosDbStorage({
serviceEndpoint: process.env.ACTUAL_SERVICE_ENDPOINT,
authKey: process.env.ACTUAL_AUTH_KEY,
databaseId: process.env.DATABASE,
collectionId: process.env.COLLECTION
})
// ConversationState and UserState
const conversationState = new ConversationState(memoryStorage);
const userState = new UserState(memoryStorage);
// Use middleware to write/read from DB
adapter.use(new AutoSaveStateMiddleware(conversationState));
adapter.use(new AutoSaveStateMiddleware(userState));
This is how I use the DB in bot.js:
constructor(conversationState, userState, dialogSet, memoryStorage) {
// Creates a new state accessor property.
// See https://aka.ms/about-bot-state-accessors to learn more about the bot state and state accessors
this.conversationState = conversationState;
this.userState = userState;
// Memory storage
this.memoryStorage = memoryStorage;
// Conversation Data Property for ConversationState
this.conversationData = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
// Properties for UserState
this.userData = userState.createProperty(USER_DATA_PROPERTY);
this.investmentData = userState.createProperty(INVESTMENT_DATA_PROPERTY);
}
async displayPayout (step) {
console.log("Display Payout");
// Retrieve user object from UserState storage
const userInvestData = await this.investmentData.get(step.context, {});
const user = await this.userData.get(step.context, {});
await step.context.sendActivity(`Hallo ${user.name}. Am Ausgang kannst du dir deine Bezahlung von ${userInvestData.payout} abholen.` );
}
The code snipped is from bot 2. Bot 1 saves the data in the same way. You can find the repos here:
Bot 1: https://github.com/FRANZKAFKA13/roboadvisoryBot
Bot 2: https://github.com/FRANZKAFKA13/displayBot
Client for Bot 1: https://github.com/FRANZKAFKA13/ra-bot-website-c
Client for Bot 2: https://github.com/FRANZKAFKA13/ra-bot-website-display
I also tried to use the "readOnly" key from CosmosDB in bot 2, which throws an error:
[onTurnError]: [object Object]
(node:1640) UnhandledPromiseRejectionWarning: TypeError: Cannot perform 'set' on a proxy that has been revoked
at adapter.sendActivities.then (C:\Users\X\Implementierung\display_bot\node_modules\botbuilder-core\lib\turnContext.js:175:36)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
(node:1640) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)
Another behavior that I have noticed: When I trigger a "join event" through my client with a redux store, the userdata is not saved as well (every time I refresh the page, the data is gone, despite using the same id "123" all the time)
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
// Event starting bot's conversation
name: 'webchat/join',
value: {}
}
Any ideas? Thanks in advance
Since storage ids (see image) are created automatically based off of user ids (that may also be created automatically and varies by channel) and channel ids, this can be very difficult to do. It can make it very difficult to persist user and conversation data, particularly across bots and channels.
Example ID:
Here's more on how IDs work.
Personally, I would write my own, custom storage, instead of (or in addition to) saving it with UserState.
To write your data, do something like this:
const changes = {};
const userDataToWrite = {
name: step.result,
eTag: '*',
}
// Replace 'UserId' with however you want to set the UserId
changes['UserId'] = userDataToWrite;
this.memoryStorage.write(changes);
This will store a document that looks like this (I set 'UserId' to 'user123':
To read:
const userDataFromStorage = await this.memoryStorage.read(['UserId']);
userDataFromStorage will look like this:
{ UserId:
{ name: 'myName',
eTag: '"0000c700-0000-0000-0000-5c7879d30000"' } }
You'll have to manage userIds yourself, but this will ensure that the data can be read across bots, channels, and users.
Edit: Solved it by adding "[this.userID]" after each "user" call.
I tried your method and whenever I write the data, a new eTag is created which leads to the object being split apart:
"document": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"25781dc4-805d-4e69-bf89-da1f4d72e7cb": {
"name": "",
"age": "",
"gender": "",
"education": "",
"major": "",
"eTag": "\"00003998-0000-0000-0000-5c797fff0000\""
},
"name": "Jane Doe",
"eTag": "\"00003e98-0000-0000-0000-5c7980080000\""
},
"age": 22,
"eTag": "\"00004898-0000-0000-0000-5c7980150000\""
},
"gender": "female",
"eTag": "\"00004d98-0000-0000-0000-5c79801b0000\""
},
"education": "Bachelor",
"eTag": "\"00005498-0000-0000-0000-5c7980200000\""
},
"major": "Business Administration",
"complete": true
How can I prevent this?
My Code:
In Constructor:
this.changes = {};
this.userID = "";
this.userDatax = {
name: "",
age: "",
gender: "",
education: "",
major: "",
eTag: '*',
}
In Dialogs:
async welcomeUser (step) {
console.log("Welcome User Dialog");
//step.context.sendActivity({ type: ActivityTypes.Typing});
// Initialize UserData Object and save it to DB
this.changes[this.userID] = this.userDatax;
await this.memoryStorage.write(this.changes);
}
async promptForAge (step) {
console.log("Age Prompt");
// Read UserData from DB
var user = await this.memoryStorage.read([this.userID]);
console.log(user);
// Before saving entry, check if it already exists
if(!user.name) {
user.name = step.result;
user.eTag = '*';
// Write userData to DB
this.changes[this.userID] = user;
await this.memoryStorage.write(this.changes);
}
}