How to do something as soon as Skill is created? - alexa

In my Alexa Skill i set dynamic entities in 'addRequestInterceptors' as the entities must be available as soon as possible, the issue is that if the user says 'Alexa, open SKILLNAME' the entities are initialized correctly, but if the user instead says 'Alexa, ask SKILLNAME where is DYNAMICENTITY' at this point the skill fails as the DYNAMICENTITY is not yet set.
So my question is, how can i set my dynamic entities as soon as possible like when the skill is created? I've tryed the LaunchIntent but as in the case above it's avoided if the user make the question inside the invocation..
So LaunchIntent and addRequestInterceptors aren't good to do it..
Here is my code which i should run as soon as possible:
const LoadListaPuntiVendita = {
async process(handlerInput) {
const {attributesManager, requestEnvelope} = handlerInput;
const sessionAttributes = attributesManager.getSessionAttributes();
const attributes = await attributesManager.getPersistentAttributes() || {};
const piva = attributes.hasOwnProperty('piva') ? attributes.piva : null;
sessionAttributes["piva"] = piva;
if (!piva || piva === null) {
return;
}
let negozi = sessionAttributes['negozi']
if (!negozi) {
const response = await logic.fetchNegozi(piva);
sessionAttributes['negozi'] = response;
addDynamicEntities(handlerInput.responseBuilder, 'PuntoVenditaType', response);
}
}
};

Here's one possible bug on first glance. This will only run as expected after you've had at least one exchange in which "piva" had been saved and set as a persistent value for the customer.
If you're running this the moment the skill is initialized, it still must be run after the persistence adapter has been initialized and retrieved the value of "piva" from your store AND there must be have been a value stored for "piva" or the code returns early without setting the entity.

Related

DJS - Member count excluding bots

I want to create a member count that excludes all bots.
I've talked to a friend that told me the code looks to be correct. But it only gives me a count of 0. Perhaps you have better eyes than him!
module.exports = async(client) => {
const guild = client.guilds.cache.get('912706237806829598');
setInterval(() => {
const memberCount = guild.members.cache.filter(member => !member.user.bot).size;
const channel = guild.channels.cache.get('959556115383861298');
channel.setName(`╭👥・Members: ${memberCount.toLocaleString()}`);
console.log('Updating Member Count');
console.log(memberCount);
}, 5000);
}
Thanks.
Tried my code and expect a value representing the amount of humans on my server, but only got 0.
There are two potential problems with your code. First, you're not fetching the member collection and are instead interacting with the cache; if I remember correctly the cache usually only shows you online members and not offline ones. Second, the dreaded issue of rate limits.
The first issue is an easy fix. You just need to fetch the members instead of using the cache before all of your setInterval code:
setInterval(async () => {
const memberCount = (await guild.members.fetch()).filter(member => !member.user.bot).size;
const channel = guild.channels.cache.get('959556115383861298');
channel.setName(`╭👥・Members: ${memberCount.toLocaleString()}`);
console.log('Updating Member Count');
console.log(memberCount);
}, 5000);
That was probably causing the issue you were having. However, there's an additional problem here, one you will run into if you use this code. Rate limits. The normal rate limit for most actions in the Discord API is, if I remember correctly, 10,000 requests per 10 mins. However, the rate limit for changing the name of a channel is just 2 requests per 10 mins (last I checked; I don't know if they've increased this limit since last year). Your code is changing the name of a channel every 5 seconds. In just 10-15 seconds, you'll surpass the rate limit.
I have created channels that keep track of server stats for my bots in the past as well, and ran into this issue when I was doing so. Luckily, I brewed up a way to bypass the rate limit issue back when I was creating my own stats system. Though changing channel names has an incredibly low rate limit, fully creating channels has the normal rate limit of 10,000 requests per 10 mins. Therefore, instead of changing the channel name directly, you could instead: a) clone the channel; b) change the clone's name while creating it; c) set the clone's position in the channel list to the same position as the actual channel; d) delete the original channel. The cloned channel will keep all of the perms, settings, etc of the original. This all assumes, of course, that the original channel isn't a text channel with important messages or such inside, as those messages would all disappear with this method. I used voice channels for my system.
Here's how this bypass could look:
setInterval(async () => {
const memberCount = (await guild.members.fetch()).filter(member => !member.user.bot).size;
const channel = guild.channels.cache.get('959556115383861298');
const pos = channel.position;
const clone = await channel.clone({
name: `╭👥・Members: ${memberCount.toLocaleString()}`
});
await clone.setPosition(pos);
console.log('Updating Member Count');
console.log(memberCount);
await channel.delete();
}, 5000);
With this approach, rate limits are no longer an issue. Note that this can still be further improved, however. For example, inside the interval you could check if the member count has changed from the previous count, and only modify the channel name if and only if the count has changed. The entire approach potentially could be changed as well; perhaps you could use guildMemberAdd and guildMemberRemove events to track when the member count increases or decreases, and only modify the name when one of those happens (and only if the member being added/removed is not a bot). But whether to add either of those optional potential improvements is up to you.
Note: Please try this code out on a test channel before using it on the actual channel you are using, just to be safe.
If you wanted to get to all members including the bot you can use memberCount
const totalCount = guild.memberCount
If you wanted to get only the members size
const memberCount = guild.members.cache.filter(member => !member.user.bot).size;
And if you only wanted to get the bot size
const botCount = guild.members.cache.filter(member => member.user.bot).size;
Full code:
const guild = client.guilds.cache.get('912706237806829598')
const memberCount = guild.members.cache.filter(member => !member.user.bot).size;
const botCount = guild.members.cache.filter(member => member.user.bot).size;
const totalCount = guild.memberCount
console.log(memberCount, botCount, totalCount)

Give Role when a member joins the voice channel. discord.js

Im trying to make discord.js code. When a member joins the voice channnel I want the bot to give them a role, when the member leaves, i want the role to be removed. thanks for your help.
const newChannelID = newState.channelID;
const oldChannelID = oldState.channelID;
if (oldChannelID === "835280102764838942"){
member.send('role given');
let role = message.guild.roles.cache.find(r => r.id === "835279162293747774");
member.roles.add(835279162293747774);
} else if(newChannelID === "835280102764838942"){
member.send('role removed');
member.roles.remove(835279162293747774);
}
})
There's a couple of things you need to clean up in your code. I'm going to assume you're using the voiceStateUpdate event to trigger your command.
Most importantly, (logic-wise) you actually need to flip-flop the way you add and remove roles. Currently, the general code states that if the user left the voice channel (thus oldChannelID = actual vc ID), then it would actually give the user a role. This seems to be the opposite of your intention, thus you'd simply swap out the code in each if/else if statement.
Second, the way you're assigning roles and adding them is incorrect. I'd recommend this resource for more info.
Third, you cannot access message if you were indeed using voiceStateUpdate as your event, since it only emits an oldState and a newState.
Lastly, you need to designate a text channel to send the message to. In my code, I manually grabbed the ID of the channel I wanted and plugged it into the code. You'll have to do the same by replacing the number strings I have with your own specific ones.
With that being said, here's the correct modified code:
client.on('voiceStateUpdate', (oldState, newState) => {
const txtChannel = client.channels.cache.get('803359668054786118'); //manually input your own channel
const newChannelID = newState.channelID;
const oldChannelID = oldState.channelID;
if (oldChannelID === "800743802074824747") { //manually put the voice channel ID
txtChannel.send('role removed');
let role = newState.guild.roles.cache.get("827306356842954762"); //added this
newState.member.roles.remove(role).catch(console.error);
} else if (newChannelID === "800743802074824747") {
txtChannel.send('role given');
let role = oldState.guild.roles.cache.get("827306356842954762"); //change this somewhat
oldState.member.roles.add(role).catch(console.error); //adding a catch method is always good practice
}
})

How can i add role when member has 10 invites?

im making discord bot with discord.js v12 and im trying to give role when member has 10 invites
i found invitation code from here and modified it:
if(message.channel.type != "dm" && message.channel.id === '768475421953915093') {
message.delete();
var user = message.author;
message.guild.fetchInvites()
.then
(invites =>
{
const userInvites = invites.array().filter(o => o.inviter.id === user.id);
var userInviteCount = 0;
for(var i=0; i < userInvites.length; i++)
{
var invite = userInvites[i];
userInviteCount += invite['uses'];
}
if(userInviteCount >= 10){
const guildMember = message.member;
guildMember.addRole('767542365812886656');
message.author.send(`you have access now`)
}else {
message.author.send(`sry u have ${userInviteCount} invitations. u need at least five`)
}
}
)
}
yes, it works but you can trick the system easily. If you keep rejoining from the server, the invite counter increases. How can i stop that?
To do this, you will need an external database since Discord API doesn't provide any information about who used that invite and how many times.
ALERT: It's a simple feature but can be very hard for people that don't have any coding experience, so if it's your first project, don't give up and keep trying! :D
You can use MongoDB Atlas, which is very simples and free.
And the logic will be very simple:
1 - Create a collection to store the invites data, you can do something like in this example
2 - Create a collection (you can give any name, but in this example, I will refer to this collection as new_members) to store the new members data, it should have the fields serverId (to store the guild ID), userId (to store the user ID) and inviteCode (to store the invite code or url)
3 - When a user joins the discord server, it will trigger the "guildMemberAdd" event, and you will get the invite that was used (with the example that I give in the first step), and create a new doc in the new_members collection.
4 - After creating this new data, you will search for all the docs from the same invite, and filter them by userId (there are many ways to do this, this is one of them).
5 - With this array of docs, you can use his length to determine how many people were invited, and give the role.
VERY SIMPLE EXAMPLE, THAT SHOULDN'T BE USED:
DiscordClient.on("guildMemberAdd", async (member) => {
/**
* https://github.com/AnIdiotsGuide/discordjs-bot-guide/blob/master/coding-guides/tracking-used-invites.md
* Here you will get the invite that was used, and store in the "inviteCode" variable
*/
const inviteCode = await getInviteUsed()
await NewMemberRepository.insert({
serverId: member.guild.id,
userId: member.id,
inviteCode: inviteCode
});
const joinedMembersFromTheInvite = await NewMemberRepository.find({
inviteCode: inviteCode
});
/**
* https://stackoverflow.com/questions/43374112/filter-unique-values-from-an-array-of-objects
* Here you will filter the results by userId
*/
const invitesWithUniqueUsers = filterJoinedMembersByUserId(joinedMembers)
if (invitesWithUniqueUsers >= REQUIRED_INVITES_QTD) {
/**
* Gives the role to the creator of the invite
*/
}
});
Hope it helps, good luck! :D
OBS:
Yes, Discord API provides how many times an invite was used, but not how many a specific person used this invite.
In the examples, I used something like TypeORM, but if you are using JavaScript instead TypeScript, you can use Mongoose

Unreliable Google Firebase transactions

In my (greatly simplified) model I have users, accounts and account_types. Each user can have multiple accounts of each account_type. When an account of type TT is created I'm updating the "users" field of that object so it keeps the users which have accounts of that types, and the number of such accounts they have.
users: {
some fields
},
accounts: {
userID: UU,
type: TT
},
account_type:
users: { UU: 31 }
}
I use the onCreate and onDelete cloud triggers for accounts to update the account_type object. Since multiple accounts can be created simultaneously I have to use transactions:
exports.onCreateAccount = functions.firestore
.document('accounts/{accountID}')
.onCreate((account, context) => {
const acc_user = account.data().userID;
const acc_type = account.data().type;
return admin.firestore().runTransaction(transaction => {
// This code may get re-run multiple times if there are conflicts.
const accountTypeRef = admin.firestore().doc("account_types/"+acc_type);
return transaction.get(accountTypeRef).then(accTypeDoc => {
var users = accTypeDoc.data().users;
if (users === undefined) {
users = {};
}
if (users[acc_user] === undefined) {
users[acc_user] = 1;
} else {
users[acc_user]++;
}
transaction.update(accountTypeRef, {users: users});
return;
})
})
.catch(error => {
console.log("AccountType create transaction failed. Error: "+error);
});
});
In my tests I'm first populating the database with some data so I'm also adding a user and 30 accounts of the same type. With the local emulator this works just fine and at the end of the addition I see that the account_type object contains the user with the counter at 30. But when deployed to Firebase and running the same functions the counter gets to less than 30. My suspicion is that since Firebase is much slower and transactions take longer, more of them are conflicted and fail and eventually don't execute at all. The transaction failure documentation (https://firebase.google.com/docs/firestore/manage-data/transactions) says:
"The transaction read a document that was modified outside of the transaction. In this case, the transaction automatically runs again. The transaction is retried a finite number of times."
So my questions:
What does "finite" mean?
Any way to control this number?
How can I make sure my transactions are executed at some point and don't get dropped like that so my data is consistent?
Any other idea as to why I'm not getting the correct results when deployed to the cloud?
What does "finite" mean?
It's the opposite of "unlimited". It will retry no more than a set number of times.
Any way to control this number?
Other than modifying the source code of the SDK, no. The SDK itself advertise a specific number, as it might change.
How can I make sure my transactions are executed at some point and don't get dropped like that so my data is consistent?
Detect the error and retry in your app. If you aren't seeing the transaction fail with an error, then nothing went wrong.
Any other idea as to why I'm not getting the correct results when deployed to the cloud?
Since we can't see what exactly you're doing to trigger the function, and have no specific expected results to compare to, it's not really possible to say.

Alexa check if a new session was started by accessing session.new

This is the data that is being send by alexa to my skills backend. In the backend code I would like to test if the session is new or not and based on this information I would like to produce a different output. I try to access the session.new, but I don't know how and I could now find anything about it online so far.
const { attributesManager } = handlerInput;
const requestAttributes = attributesManager.getRequestAttributes();
requestAttributes.session.new
//this leads to the error "cannot read property new of undefined"
const { attributesManager } = handlerInput;
const requestAttributes = attributesManager.getSessionAttributes();
sessionAttributes.session.new
//this leads to the same error
I finally found out that this is not possible using the AttributesManager because this only allows access to request, session and permanent attributes. But session.new is not part of those. If you want to check if the Alexa session is new you have to use the so called RequestEnvelopeUtils.
Using those you can access if the session is new or not by using the following statement.
(Alexa.isNewSession(handlerInput.requestEnvelope))

Resources