Alexa (ASK) : How to play multiple audio from different URL? - alexa

I'm a beginner at ASK development and currently stuck on playing audio from multiple different URLs using .addAudioPlayerPlayDirective from handlerInput.responseBuilder. So if I add multiple addAudioPlayerPlayDirective to the responseBuilder it returns error instead. For example :
handlerInput.responseBuilder
.speak(speakOutput)
.addAudioPlayerPlayDirective('REPLACE_ALL', podcastBeginning.url, podcastBeginning.token, 0, null, podcastBeginning.metadata)
.addAudioPlayerPlayDirective('REPLACE_ALL', podcastBeginning.url, podcastBeginning.token, 0, null, podcastBeginning.metadata);
Also, I added the method of the issue below.
const LatestHeadlinesIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest' ||
handlerInput.requestEnvelope.request.type === 'IntentRequest' &&
(
handlerInput.requestEnvelope.request.intent.name === 'LatestHeadlinesIntent' ||
handlerInput.requestEnvelope.request.intent.name === 'AMAZON.ResumeIntent' ||
handlerInput.requestEnvelope.request.intent.name === 'AMAZON.LoopOnIntent' ||
handlerInput.requestEnvelope.request.intent.name === 'AMAZON.NextIntent' ||
handlerInput.requestEnvelope.request.intent.name === 'AMAZON.PreviousIntent' ||
handlerInput.requestEnvelope.request.intent.name === 'AMAZON.RepeatIntent' ||
handlerInput.requestEnvelope.request.intent.name === 'AMAZON.ShuffleOnIntent' ||
handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StartOverIntent'
);
},
async handle(handlerInput) {
const response = await httpGet();
let responseBuilder = handlerInput.responseBuilder;
let speakOutput;
let podcastBeginning = audio[0];
let podcastNext = audio[1];
let podcastEnd = audio[2];
let audioCount = 0;
let podcast = {
"token": response.audios[audioCount].intId,
"url": response.audios[audioCount].url,
"metadata" : {
"title": response.audios[audioCount].title,
}
}
if (sucessfulGet === true){
speakOutput = 'Alright, reading you the latest headlines!';
handlerInput.responseBuilder
.speak(speakOutput)
.addAudioPlayerPlayDirective('REPLACE_ALL', podcastBeginning.url, podcastBeginning.token, 0, null, podcastBeginning.metadata)
}else {
speakOutput = "No stories found. Response Code: " + responseCode;
}
return handlerInput.responseBuilder
.getResponse();
}
};
Any help would be appreciated. Thanks.

The AudioPlayer interface can help you achieve this functionality. It provides directives and requests (i.e next, previous, pause) for streaming audio and monitoring playback progression. For more information :
https://developer.amazon.com/en-US/docs/alexa/custom-skills/audioplayer-interface-reference.html
Also there is a an Alexa skill sample (https://github.com/alexa-samples/skill-sample-nodejs-multistream-audio-player/blob/main/lambda/index.js) which handles multiple audio files. The audio data is contained in an array found in /lambda/constants and imported into /lambda/index.js, which contains the logic to handle requests to manage the audio (play, pause, play next, etc) from your skill.
Please also note that each audio item URL must meet the following requirements listed here: https://developer.amazon.com/en-US/docs/alexa/custom-skills/audioplayer-interface-reference.html#audio-stream-requirements

Related

How do i keep taking inputs from multiple users until a specific user says a particular keyword (discord.js)?

So I've been trying to make an auction bot on discord. I've figured out how to handle a single item, but I'm having issues regarding bundles.
My goal - Let the user info any/all items when auctioning a bundle till he says the keyword 'done'. The bot stores the url of the first message (embed sent by the bot) and then does nothing till the user says done. The two users are 1. the user himself, 2. the bot who's item(s) he wants to auction.
I tried using the message collector but the bot straight up ignores it. After searching around on the web and trying to adapt to my situation, the closest thing i found is using async/await with awaitMessages while inside a for loop. After that failed too, i tried to use a chain of then() with the awaitMessages. Here's my code snippet. Any help is appreciated.
let flg =0
if(arguments[3] === 'bundle'){
message.channel.send('Say `done` when you have info-ed all your pokemons')
await message.channel.awaitMessages(m => m.author.id === '666956518511345684' || m.author.id === message.author.id, {time : 30000, errors : ['time']}).then(col1 => {
if (col1.first().author.id === '666956518511345684')
details.poke = col1.first().url
}).then(col2 => {
if (col2.first().author.id === message.author.id)
while(flg = 0){
if (col2.first().content.toLowerCase() === 'done')
flg = 1
}
})
Have the collector ignore bot messages, take multiple inputs and check for message content. Once time runs out, or the max input has been reached, the collection of messages will be returned.
if(arguments[3] === 'bundle'){
message.channel.send('Say `done` when you have info-ed all your pokemons')
const filter = m => {
return !m.user.bot && ['666956518511345684', message.author.id].includes(m.author.id) && m.content.toLowerCase() === 'done';
}
message.channel.awaitMessages(filter, {
// Take a max of 5 inputs for example
max: 5
time: 30000,
errors: ['time']
}).then(collected => {
// Your code
})
}
Documentation on TextChannel#awaitMessages()
I tried message collector once again and i made it work somehow. Here's how :
let i = 0
let filter = m => {m.author.id === '<Bot's id>' || m.author.id === message.author.id}
let collector = message.channel.createMessageCollector(filter, {time : 30000})
collector.on('collect', m => {
if (m.author.id === '<bot's id>' && i === 0){
details.poke = m.url
i = 1
}
if (m.author.id === message.author.id && m.content.toLowerCase() === 'done'){
collector.stop()
}
})
collector.on('end', col => { //code here })
The variable i is a flag, since i only want the url of the first message sent by the bot. It can be removed/more flags can be added up to your needs/requirements.
Lemme know if you need anymore help regarding this.

SlowMode for one person is possible ? discord.js

I would like to know if it is possible to do some sort of "SlowMode" for a specific person on Discord.
The reason is that I have a "spammer" friend, and I would like to calm him down with a command that might slow him down when he speaks for "x" secondes.
So I would like to know if this is possible? and if yes, how?
Thank you for your kindness =) (and sorry for this english i use GoogleTraductor)
Here's how I'd do that.
let ratelimits = [];
client.on("message", (msg) => {
// APPLYING RATELIMITS
const appliedRatelimit = ratelimits.find(
(value) =>
value.user === msg.author.id && value.channel === msg.channel.id
);
if (appliedRatelimit) {
// Can they post the message?
const canPostMessage =
msg.createdAt.getTime() - appliedRatelimit.ratelimit >=
appliedRatelimit.lastMessage;
// They can
if (canPostMessage)
return (ratelimits[
ratelimits.indexOf(appliedRatelimit)
].lastMessage = msg.createdAt.getTime());
// They can't
msg.delete({ reason: "Enforcing ratelimit." });
}
// SET RATELIMIT
if (msg.content === "!ratelimit") {
// Checking it's you
if (msg.author.id !== "your id") return msg.reply("You can't do that.");
// You can change these values in function of the received message
const targetedUserId = "whatever id you want";
const targetedChannelId = msg.channel.id;
const msRateLimit = 2000; // 2 seconds
// Delete existant ratelimit if any for this user on this channel
ratelimits = ratelimits.filter(
(value) =>
!(
value.user === targetedUserId &&
value.channel === targetedChannelId
)
);
// Add ratelimit
ratelimits.push({
user: targetedUserId,
channel: targetedChannelId,
ratelimit: msRateLimit,
lastMessage: 0,
});
}
// CLEAR RATELIMITS
if (msg.content === "!clearRatelimits") {
// Checking it's you
if (msg.author.id !== "your id") return msg.reply("You can't do that.");
// Clearing all ratelimits
ratelimits = [];
}
});

I'm trying to make a second input into a bot

I'm trying to make a command where you input the command and the bot says: Are you sure? Then you type yes or no but I can't figure out how I can make it so the user can reply. Can someone help please?
have some ways to do that, the easier is to use MessageCollector to collect the user response.
Example:
message.channel.send("Are you sure?") // Ask the user
const filter = (m) => m.author.id === message.author.id && (m.content.toLowerCase() === "yes" || m.content.toLowerCase() === "no") // Create a filter, only accept messages from the user that used the command and the message includes "yes" or "no"
const collector = message.channel.createMessageCollector(filter, {time: 30000})
collector.once("collect", msg => {
if(msg.content.toLowerCase() === "yes") {
// User sent yes
} else {
// User sent "no"
}
})
collector.once("stop", (collected, reason) => {
if(reason === "time") {
// User took so long to anwser
}
})
You can use TextChannel.awaitMessages too, it returns a Promise with messages.
Example:
message.channel.send("Are you sure?")
const filter = (m) => m.author.id === message.author.id && (m.content.toLowerCase() === "yes" || m.content.toLowerCase() === "no") // Create a filter, only accept messages from the user that used the command and the message includes "yes" or "no"
message.channel.awaitMessages(filter, {max: 1, time: 30000})
.then(collected => {
const msg = collected.first()
if(msg.content.toLowerCase() === "yes") {
// User sent yes
} else {
// User sent "no"
}
})

How to iterate through an enmap?

I'm trying to iterate through an enmap for my discord.js bot, I've managed to set and get values from a single entry but I'm trying to set up a command that adds people to a newsletter like DM about minor major updates.
if (args[0] === 'minor') {
if (devlog.updates === 'minor') return message.channel.send('You are already recieving minor updates.').then(m => m.delete(5000))
await client.devlog.set(userID, "yes", 'subscribed');
await client.devlog.set(userID, "minor", 'updates');
return message.channel.send('You will now recieve minor and major updates.').then(m => m.delete(5000))
}
if (args[0] === 'major') {
if (devlog.updates === 'major') return message.channel.send('You are already recieving major updates.').then(m => m.delete(5000))
await client.devlog.set(userID, "yes", 'subscribed');
await client.devlog.set(userID, "major", 'updates');
return message.channel.send('You will now recieve only major updates.').then(m => m.delete(5000))
}
if (!args[0]) {
if (devlog.subscribed === 'yes') {
await client.devlog.set(userID, "no", 'subscribed');
await client.devlog.set(userID, "none", 'updates');
return message.channel.send('You will stop recieving updates about RoboTurtle all together').then(m => m.delete(5000))
}
if (devlog.subscribed === 'no') {
return message.channel.send(`Please choose wether you\'d like to recieve minor or major updates! (minor has both) **devlog minor/major**`).then(m => m.delete(10000))
}
}
It kind of works but it won't trigger the message if they already are subscribed to the same type of update and if they do just !devlog it's meant to either set them to not receive updates if they already are, or tell them to choose between the two if they aren't, however it just sends the last message either way.
I tried setting up my enmap iteration for DMing all subscribed people with a for...of function based off the .map related docs (since they're meant to be just "fancier" maps) but to no avail, since they don't really show discord style use cases.
if (args[0] === 'minor') {
for (let entry of client.devlog) {
if (entry.updates === 'minor') {
let user = client.users.get(entry)
user.send(`**[Minor Update]\n${args.slice(1)}`)
}
}
}
if (args[0] === 'major') {
for (let entry of client.devlog) {
if (entry.subscribed === 'yes') {
let user = client.users.get(entry)
user.send(`**[Major Update!]\n${args.slice(1)}`)
}
}
}
In case anyone wanted to look at the full code to get a better idea of what Im trying to do here ya go: https://pastebin.com/bCML6EQ5
Since they are just an extension of the normal Map class, I would iterate through them with Map.forEach():
let yourEnmap = new Enmap();
yourEnmap.set('user_id', 'your_values');
yourEnmap.forEach((value, key, map) => {
...
});
In your case it would be something like:
client.devlog.forEach((values, user_id) => {
// you can check your subscription here, the use the user_id to send the DM
client.users.get(user_id).send("Your message.");
});

Alexa skill - retrieve customer mail address node.js

I can't understand the way to use the the method to retrieve the customer mail in Alexa
The doc says to make a request with a token.
https://developer.amazon.com/fr/docs/custom-skills/request-customer-contact-information-for-use-in-your-skill.html
Then I have done that :
function AlexaRequest(accessToken){
Host: api.amazonalexa.com
Accept: application/json
Authorization: Bearer +accessToken+
GET https://api.amazonalexa.com/v2/accounts/~current/settings/Profile.email
}
2 questions
1 - Why do I get a "Parsing Error" : Unexpected Token https ?
2 - How do I get the response from Alexa ?
Thanks !
You can use UpsServiceClient methods to get user's Personal Information.
Follow the steps below or watch detailed tutorial HERE
Step 1: Write this code in your intent handler
const GreetMeIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'GreetMeIntent';
},
async handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
const token = requestEnvelope.context.System.user.permissions &&
requestEnvelope.context.System.user.permissions.consentToken;
if (!token) {
return handlerInput.responseBuilder
.speak('Please Provide Permissions!')
.withAskForPermissionsConsentCard(['alexa::profile:email:read'])
.getResponse();
}
let {deviceId} = requestEnvelope.context.System.device;
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
const email = await upsServiceClient.getProfileEmail();
let speechText = `Hello! Your email is ${email}`;
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Hello World', speechText)
.getResponse();
},
}
Step 2: Add withApiClient in your exports
exports.handler = skillBuilder
.addRequestHandlers(
GreetMeIntentHandler
)
.addErrorHandlers(ErrorHandler)
.withApiClient(new Alexa.DefaultApiClient())
.lambda();

Resources