Autocomplete slash command appear only one admin - node.js

Description
I created slash command bot with bot, applications.commands scopes. And create slash command for guild, and command not showed so I create commands as application scope. And still not showing
I tried to kick out my bot and re enter url but not worked...
What is the solution plz! Thank you
Steps to Reproduce
const serverless = require("serverless-http");
const express = require("express");
const app = express();
const { CreateRateUseCase } = require("./core/usecase/createRateUseCase");
const nacl = require("tweetnacl");
const getRawBody = require("raw-body");
const { DiscordBot } = require("./discordBot");
// const { verifyKeyMiddleware } = require("discord-interactions");
require("dotenv").config({ path: `./.env.${process.env.NODE_ENV}` });
app.post(
"/interactions",
// verifyKeyMiddleware(process.env.DISCORD_PUBLIC_KEY),
async (req, res, next) => {
try {
console.log(`req : `);
console.log(req);
console.log(`body ${req.body} ${JSON.stringify(req.body)}`);
const rawBody = await getRawBody(req);
console.log(`rawBody ${rawBody} ${typeof rawBody}`);
const body = JSON.parse(rawBody);
if (
process.env.NODE_ENV === "dev" ||
process.env.NODE_ENV === "production"
) {
const signature = req.get("X-Signature-Ed25519");
console.log(`signature ${signature}`);
const timestamp = req.get("X-Signature-Timestamp");
console.log(`timestamp ${timestamp}`);
const isVerified = nacl.sign.detached.verify(
Buffer.from(timestamp + rawBody),
Buffer.from(signature, "hex"),
Buffer.from(process.env.DISCORD_PUBLIC_KEY, "hex")
);
console.log(`isVerified ${isVerified}`);
if (!isVerified) {
console.log("Failed verification");
return res.status(401).end("invalid request signature");
}
if (body.type === 1) {
console.log("Handling validation test request");
return res.status(200).send({ type: 1 });
}
}
if (body.type === 2) {
if (
body.channel_id !== process.env.DISCORD_CHANNEL_ID_KOR &&
body.channel_id !== process.env.DISCORD_CHANNEL_ID_EN
) {
console.log(`channel id ${body.channel_id}`);
console.log(
"This command is only available in the COMMUNITY category"
);
res.status(200).send({
type: 4,
data: {
content: `This command is only available in the 'COMMUNITY' category. 😒`,
},
});
return;
}
const discordBot = new DiscordBot();
const result = await discordBot.execute(body);
console.log(`result ${JSON.stringify(result)}`);
res.status(200).send(result);
console.log("reply done");
}
return;
} catch (e) {
console.error(e.message);
return res.send("Error handling verification");
}
}
);
deploy server on aws lambda
OAuth2 -> URL Generator, check bot, applications.commands and enter url then select server.
check SERVER MEMBERS INTENT, MESSAGE CONTENT INTENT
enter api gateway url to INTERACTIONS ENDPOINT URL as https://xyz.amazonaws.com/interactions/
create slash commands
const { Client, Intents } = require("discord.js");
const client = new Client({
intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES],
});
console.log(`NODE_ENV ${process.env.NODE_ENV}`);
require("dotenv").config({ path: `./.env.${process.env.NODE_ENV}` });
const korCommandList = {
γ……γ…‡γ„Ή: {
description: "예치 수읡λ₯  응닡",
},
수읡λ₯ : {
description: "예치 수읡λ₯  응닡",
},
예치수읡λ₯ : {
description: "예치 수읡λ₯  응닡",
},
};
const enCommandList = {
depositapy: {
description: "response deposit yield",
},
yield: {
description: "response deposit yield",
},
apy: {
description: "response deposit yield",
},
deposityield: {
description: "response deposit yield",
},
};
client.on("ready", async () => {
const promises = [];
const objs = Object.assign(korCommandList, enCommandList);
console.log(
`process.env.DISCORD_BOT_CLIENT_ID ${process.env.DISCORD_BOT_CLIENT_ID}`
);
console.log(`process.env.DISCORD_GUILD_ID ${process.env.DISCORD_GUILD_ID}`);
for (const key in objs) {
const p = await client.api
.applications(process.env.DISCORD_BOT_CLIENT_ID)
// .guilds(process.env.DISCORD_GUILD_ID)
.commands.post({
data: {
name: key,
description: objs[key].description,
},
});
promises.push(p);
}
const result = await Promise.all(promises);
console.log(result);
client.destroy();
});
client.login(process.env.DISCORD_BOT_TOKEN);
after hours, or day type / on discord app. normal user can not see commad list, but admin can see.
Expected Behavior
Every user on server with bot can see slash command list.
Current Behavior
Admin can see slash command lists, but normal user can not see.
Screenshots/Videos
text box by server admin
text box by server normal user, slash commands not displayed
Client and System Information
browser admin : chrome on mac
user : discord app on mac
lib

Related

Keep getting the 'cannot set headers' with Axios on Firebase Functions which still fully executes regardless of the error

I have a Firebase function that executes on a Stripe webhooks via express. The function executes fine but the sending of the email (using Axios) keeps resulting in an error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
It does work fine on localhost but the error appears when pushed to Firebase staging server. Weirdly the whole function executes fully including the Axios call where I'm getting the header issue (sending the email). It does take about 2-3 minutes to fully execute due to the error.
I've tried a number of different methods using return, and then() promises but it's still flagging this error. My code is as follows:
index.js
// Controllers
const stripeWebhookSubscription = require("./src/controllers/stripe/webhooks/subscription");
// Firebase
const admin = require("firebase-admin");
const functions = require("firebase-functions");
// Express
const express = require("express");
const cors = require("cors");
// Stripe
const stripe = require("stripe")(functions.config().stripe.key_secret);
const serviceAccount = require(functions.config().project.service_account);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: functions.config().project.database_url,
storageBucket: functions.config().project.storage_bucket,
});
const database = admin.firestore();
// -------------------------
// Stripe
// -------------------------
const stripeFunction = express();
stripeFunction.use(cors({origin: true}));
stripeFunction.post("/webhooks", express.raw({type: "application/json"}), (req, res) => {
const sig = req.headers["stripe-signature"];
let event;
try {
event = stripe.webhooks.constructEvent(
req.rawBody,
sig,
functions.config().stripe.webhook_secret
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case "customer.subscription.created":
stripeWebhookSubscription.createSubscription(database, event.data.object, res);
break;
(...)
default:
console.log(`Unhandled event type ${event.type}`);
break;
}
res.json({received: true});
});
exports.stripe = functions.https.onRequest(stripeFunction);
subscription.js
const functions = require("firebase-functions");
const stripe = require("stripe")(functions.config().stripe.key_secret);
const axios = require("axios");
class stripeWebhookSubscription {
static createSubscription(database, subscription, res) {
let barcode;
let plan;
let merchant;
let customer;
database.collection("subscriptions").add({
id: subscription.id,
customer: subscription.customer,
status: subscription.status,
price: {
amount: (subscription.items.data[0].price.unit_amount / 100).toFixed(2),
interval: subscription.items.data[0].price.recurring.interval,
interval_count: subscription.items.data[0].price.recurring.interval_count,
},
product: subscription.items.data[0].price.product,
created: subscription.created,
current_period_start: subscription.current_period_start,
current_period_end: subscription.current_period_end,
cancel_at: subscription.cancel_at,
cancel_at_period_end: subscription.cancel_at_period_end,
payment_gateway: "stripe",
current_usage: 1,
})
.then((doc) => {
barcode = doc.id;
return database.collection("plans").where("stripe.product", "==", subscription.items.data[0].price.product).limit(1).get();
})
.then((docs) => {
docs.forEach((doc) => {
return plan = doc.data();
});
})
.then(() => {
return database.collection("subscriptions").doc(barcode).set({
merchant: plan.merchant,
}, {merge: true});
})
.then(() => {
return database.collection("merchants").doc(plan.merchant).get();
})
.then((doc) => {
return merchant = doc.data();
})
.then(() => {
async function stripeCustomer() {
const stripeData = await stripe.customers.retrieve(subscription.customer);
customer = stripeData;
}
return stripeCustomer().then(() => {
return customer;
});
})
.then(() => {
return database.collection("customers").doc(subscription.customer).set({
name: customer.name,
email: customer.email,
phone: customer.phone,
delinquent: customer.delinquent,
created: customer.created,
livemode: customer.livemode,
merchant: plan.merchant,
subscriptions: [barcode],
}, {merge: true});
})
.then((doc) => {
return axios.request({
url: "https://api.sendinblue.com/v3/smtp/email",
method: "post",
headers: {
"api-key": functions.config().sendinblue.key,
"Content-Type": "application/json",
},
data: {
"to": [
{
"email": customer.email,
"name": customer.name,
},
],
"replyTo": {
"email": "support#scanable.com.au",
"name": "Scanable",
},
"templateId": 2,
"params": {
"plan_name": plan.name,
"interval_count": plan.interval_count,
"interval": plan.interval,
"subscription": barcode,
"merchant_name": merchant.name,
"merchant_email": merchant.email,
},
},
})
.then((response) => {
return console.log("Membership email sent to " + customer.email);
});
})
.then(() => {
res.status(200).send("βœ… Subscription " + subscription.id + " created!");
})
.catch((err) => {
res.status(400).send("⚠️ Error creating subscription (" + subscription.id + "): " + err);
});
}
}
module.exports = stripeWebhookSubscription;
In index.js, you call this line:
stripeWebhookSubscription.createSubscription(database, event.data.object, res);
immediately followed by this line:
res.json({received: true});
By the time the createSubscription path has finished, the response has already been sent. When deployed to Cloud Functions, this will also terminate your function before it's done any of its workload (Note: this termination behaviour is not simulated by the local functions emulator).
Depending on what you are trying to achieve, you can probably just add the missing return to this line so that the res.json({received: true}) never gets called:
return stripeWebhookSubscription.createSubscription(database, event.data.object, res);
Additionally, on this line in subscription.js:
database.collection("subscriptions").add({
you need to add the missing return statement so the asynchronous tasks are properly chained:
return database.collection("subscriptions").add({

Load confirmation URL in Embedded app itself in Shopify

I have an embedded app in shopify which is an paid app ,Once user approves the billing ,i want the app to show the confirmation url in the embedded app itself instead it loads externally.
getsubscriptionurl.js
export const getSubscriptionUrl = async (ctx, shop) => {
const { client } = ctx;
console.log(`process.env.HOST - ${process.env.HOST}`);
console.log(`shop - ${shop}`);
console.log(`${process.env.HOST}/?shop=${shop}`);
const confirmationUrl = await client
.mutate({
mutation: RECURRING_CREATE(),
variables: {
returnUrl: `www.abc.com`,
}
})
.then(response => response.data.appSubscriptionCreate.confirmationUrl);
console.log("me "+ confirmationUrl);
return ctx.redirect(confirmationUrl);
};
server.js
app.prepare().then(async () => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
async afterAuth(ctx) {
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
const host = ctx.query.host;
ACTIVE_SHOPIFY_SHOPS[shop] = {scope:scope,accessToken:accessToken};
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (topic, shop, body) =>
delete ACTIVE_SHOPIFY_SHOPS[shop],
});
if (!response.success) {
console.log(
`Failed to register APP_UNINSTALLED webhook: ${response.result}`
);
}
// Redirect to app with shop parameter upon auth
// ctx.redirect(`/?shop=${shop}&host=${host}`);
server.context.client = await handlers.createClient(shop, accessToken);
await handlers.getSubscriptionUrl(ctx, shop);
},
})
);
You can't basically show the confirmation URL in your app, Shopify won't trust app developers to take sensitive info like payment details, so must open the confirmation URL into a new tab, where the merchant is viewing a Shopify payment page(made by shopify) that contains the payment details to be entered and on confirm the page will redirect the merchant to the return URL as you specified before.
For testing purposes
you can send a test param within the query to allow you to test without entering any payment details
const CREATE_SUB_MUTATION_RECURRING_ONLY = gql`
mutation RecurringSubscription(
$returnUrl: URL!
$test: Boolean!
$planName: String!
$amount: Decimal!
) {
appSubscriptionCreate(
test: $test
name: $planName
returnUrl: $returnUrl
lineItems: [
{
plan: {
appRecurringPricingDetails: {
price: { amount: $amount, currencyCode: USD }
interval: EVERY_30_DAYS
}
}
}
]
) {
userErrors {
field
message
}
confirmationUrl
appSubscription {
id,
currentPeriodEnd
}
}
}
`;
Now to test just pass true to test
result = await graphQlClient?.mutate({
mutation: CREATE_SUB_MUTATION_RECURRING_ONLY,
variables: {
returnUrl,
test,
planName: PLANS_DATA[planName].planName,
amount: PLANS_DATA[planName].price,
},
});

How to resolve 'system:error:invalid-token' In Nexmo Vonage SDK changing to a new app in the same account

I am using "#vonage/server-sdk": "2.10.7-beta-2" package on server to create users in Vonage.
To create the user, I used this API
const Vonage = require('#vonage/server-sdk');
const v = new Vonage({
apiKey: config.voipConfig.key,
apiSecret: config.voipConfig.secret,
applicationId: config.voipConfig.appId,
privateKey: config.voipConfig.privateKeyPath
};
v.users.create({
"name": payload.username,
"display_name": payload.displayName
}, (error: any, result: any) => {
});
Everything was working fine. But when I created a new application in vonage account and used new configs, it started to throw the following error
{
description: 'You did not provide a valid token. Please provide a valid token.',
code: 'system:error:invalid-token'
}
I have checked the details multiple times and not sure what is wrong.
The new credentials are from completely new account.
Any help would be much appreciated.
Looks like you are using the Vonage Conversations API to create a user. In good practice, we usually use dotenv to store our .env variables. If you are using a public github repo, remember to add *.env to your .gitignore.
npm install dotenv
// .env
API_KEY=
API_SECRET=
APPLICATION_ID=
APPLICATION_PRIVATE_KEY_PATH=
TO_NUMBER=<YOUR CELL NUMBER>
VIRTUAL_NUMBER=<VONAGE VIRTUAL NUMBER>
NGROK_URL=https://<URL>.ngrok.io
For testing purposes. Can you make an outbound call with the snippet below? That'll test your credentials.
Make sure to store the private.key in same directory as .env and outbound-call.js
// outbound-call.js
require("dotenv").config();
const API_KEY = process.env.API_KEY;
const API_SECRET = process.env.API_SECRET;
const APPLICATION_ID = process.env.APPLICATION_ID;
const APPLICATION_PRIVATE_KEY_PATH = process.env.APPLICATION_PRIVATE_KEY_PATH;
const TO_NUMBER = process.env.TO_NUMBER;
const VIRTUAL_NUMBER = process.env.VIRTUAL_NUMBER;
if (!API_KEY || !API_SECRET) {
console.log("πŸ”₯ API_KEY or API_SECRET missing");
process.exit(1);
}
if (!APPLICATION_ID || !APPLICATION_PRIVATE_KEY_PATH) {
console.log("πŸ”₯ APPLICATION_ID or APPLICATION_PRIVATE_KEY_PATH missing");
process.exit(1);
}
if (!TO_NUMBER || !VIRTUAL_NUMBER) {
console.log("πŸ”₯ TO_NUMBER or VIRTUAL_NUMBER missing");
process.exit(1);
}
const Vonage = require("#vonage/server-sdk");
const vonage = new Vonage({
apiKey: API_KEY,
apiSecret: API_SECRET,
applicationId: APPLICATION_ID,
privateKey: APPLICATION_PRIVATE_KEY_PATH,
});
vonage.calls.create({
to: [
{
type: "phone",
number: process.env.TO_NUMBER,
},
],
from: {
type: "phone",
number: process.env.VIRTUAL_NUMBER,
},
ncco: [
{
action: "talk",
text: "This is a text to speech call from Vonage",
},
],
});
To test the Conversations API, you will need to enable Voice for your Vonage Application and set the answer Webhook. I use NGROK as well, so it should look like this. https://<URL>.ngrok.io/webhooks/answer
When working with Conversations API, I usually do:
Create a Conversation
Create a User
Create a Member
and put that into the /webhooks/answer
//server.js
require('dotenv').config();
let express = require('express');
let cookieParser = require('cookie-parser');
let logger = require('morgan');
let app = express();
let port = 5001;
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static('public'));
const NGROK_URL = process.env.NGROK_URL;
const Vonage = require('#vonage/server-sdk');
const vonage = new Vonage({
apiKey: process.env.API_KEY,
apiSecret: process.env.API_SECRET,
applicationId: process.env.APPLICATION_ID,
privateKey: process.env.APPLICATION_PRIVATE_KEY_PATH
});
app.post('/webhooks/answer', async (req, res) => {
console.log('🚚 answer', req.body);
let result = req.body;
const createCallConversationPromise = (ConvName, ConvDisplayName) => new Promise((resolve, reject) => {
vonage.conversations.create({
"name": ConvName,
"display_name": ConvDisplayName,
}, (error, result) => error ? reject(error) : resolve(result));
});
const createCallUserPromise = (Name, DisplayName) => new Promise((resolve, reject) => {
vonage.users.create({
name: Name,
display_name: DisplayName,
}, (error, result) => error ? reject(error) : resolve(result));
});
const createCallMemberPromise = (ConvId, UserId) => {
vonage.conversations.members.create(ConvId, {
"action": "join",
"user_id": UserId,
"channel": {
"type": "app"
}
}, (error, result) => {
if(error) {
console.log('\nπŸ”₯ Error Creating Member', error);
}
else {
console.log('\nβœ… Created Member with ConvId', ConvId, 'and UserId',UserId)
console.log(result);
}
})
};
try {
let ConvName = result.from + result.to + result.conversation_uuid;
console.log('nβœ… ConvName', ConvName);
let ConvDisplayName = result.from + result.to + result.conversation_uuid;
const conversation = await createCallConversationPromise(ConvName, ConvDisplayName);
process.env.CONVERSATION_ID = conversation.id;
console.log('\nβœ… CONVERSATION_ID', process.env.CONVERSATION_ID);
let Name = result.from + result.conversation_uuid;
let DisplayName = result.from;
const callUser = await createCallUserPromise(Name, DisplayName);
console.log('\nβœ… UserId', callUser.id)
let ConvId = conversation.id;
let UserId = callUser.id;
const memberUser = await createCallMemberPromise(ConvId, UserId);
let ncco = [
{
action: "talk",
text: "<speak><lang xml:lang='en-GB'>Welcome to Vonage Development inbound call testing</lang></speak>",
voiceName: "Emma"
},
];
res.json(ncco);
} catch (error) {
console.log("πŸ”₯ Error", error);
let ncco = [
{
"action": "talk",
"text": "<speak><lang xml:lang='en-GB'>Error on Process Answer</lang></speak>",
"voiceName": "Emma"
}
];
res.json(ncco);
}
});

socket-io: when a message is sent, how to display a message/notification to the root component / out of the chatroom?

In my chat app when a message is emitted on a chatroom I want to display a message/toaster to the root component so that the person who is out of the chatroom will get notified.
useEffect(() => {
const token = localStorage.getItem('CC_Token');
if (token) {
const payload = JSON.parse(atob(token.split('.')[1]));
setUserId(payload.id);
}
if (socket) {
socket.on('newMessage', (message) => {
const newMessages = [...messages, message];
setMessages(newMessages);
});
}
//eslint-disable-next-line
}, [messages, socket]);
what I have tried is to dispatch an action and tried to update the redux store.
but the problem is reducer is not called.
What would be the best approach to achieve this
You use this code
const notifyMe = () => {
if (!('Notification' in window)) {
alert(
'This browser does not support desktop notification please use Chrome, Brave, Firefox or update your browser'
);
} else if (
Notification.permission === 'granted' ||
Notification.permission === 'default'
) {
var options = {
body: 'This is body of notification',
icon: 'notif.jpg',
dir: 'ltr',
};
var notification = new Notification('Hi there', options);
} else if (Notification.permission === 'denied') {
Notification.requestPermission((permission) => {
if (!('permission' in Notification)) {
Notification.permission = permission;
}
if (permission === 'granted') {
var options = {
body: 'This is body of notification',
icon: 'notif.jpg',
dir: 'ltr',
};
var notification = new Notification('Hi there', options);
}
});
}
};

Bottender - Enabling persistent_menu and get_started button on facebook

I am using NodeJS & Bottender to run my facebook messenger webhook.
So I want to have a get started button and a persistent menu. I checked & patterned my config on the examples provided on the repo like this one but I can't get it to be present on messenger yet.
here's my config.js
require('dotenv').config();
const {
FB_ACCESS_TOKEN,
FB_VERIFY_TOKEN,
APP_ID,
APP_SECRET,
} = process.env;
const { bots } = require('./global-session');
const profile = {
get_started: {
payload: '/start',
},
persistent_menu: [
{
locale: 'default',
composer_input_disabled: false,
call_to_actions: [
{
type: 'postback',
title: 'Menu',
payload: '/menu',
},
],
},
],
};
/* istanbul ignore next */
module.exports = {
messenger: {
bot: {
accessToken: FB_ACCESS_TOKEN,
appId: APP_ID,
appSecret: APP_SECRET,
mapPageToAccessToken: bots.getTokenByPageById,
profile,
},
server: {
verifyToken: FB_VERIFY_TOKEN,
path: '/messenger',
profile,
},
},
};
and here's how I use it
const { MessengerBot } = require('bottender');
const express = require('express');
const bodyParser = require('body-parser');
const { registerRoutes } = require('bottender/express');
const handler = require('./handlers');
const logger = require('./utils/logger');
const { APP_PORT, NODE_ENV } = process.env;
const server = express();
/* istanbul ignore next */
const verify = (req, res, buf) => {
req.rawBody = buf.toString();
};
server.use(bodyParser.json({ verify }));
server.use(require('morgan')('short', { stream: logger.logStream }));
const { messenger } = require('./config');
const bots = {
messenger: new MessengerBot(messenger.bot).onEvent(handler.messenger.execute),
// Define other platform bots here!
};
const initialize = async () => {
try {
registerRoutes(server, bots.messenger, messenger.server);
// Start server
server.listen(APP_PORT, () => logger.info(`ENV[${NODE_ENV}] - server is listening on port ${APP_PORT}...`));
} catch (e) {
logger.error('unable to start server!');
logger.error(e);
throw Error();
}
};
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'test') {
initialize();
}
module.exports = initialize;
Background on my facebook app
- It is already published to public and approved(at least for pages_messaging)
Anything I missed to do? Any help would be appreciated!
We leave messenger profile control to developers, because we can't assume when developers want to set, update or delete bots' messenger profile.
For example, you can have a script like this:
const { MessengerClient } = require('messaging-api-messenger');
const config = require('./bottender.config');
const client = MessengerClient.connect();
const tokens = [ /* */ ]; // get tokens from somewhere
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
client.setMessengerProfile(config.messenger.profile, {
access_token: token, // important! pass token to client api
});
}
Or you can call setMessengerProfile when you register your token to your database:
async function registerPage(page, token) {
// ....put page and token to database
await client.setMessengerProfile(config.messenger.profile, {
access_token: token, // important! pass token to client api
});
}

Resources