I am creating a new extension. And I add a context menu option via an extension in webpages.
But chrome developer mode throws an error, that is 'unchecked.runtime.lastError: cannot create an item with duplicate id my id '. but I gave that in unique id. how to fix that.?
this is my context creation method.
chrome.contextMenus.create({
id: "zm_mark_down_preview_beta",
title: 'preview and edit',
contexts: ["editable"]
});
In Chrome, you should create the context menu just once after install/update.
Use onInstalled event:
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "zm_mark_down_preview_beta",
title: 'preview and edit',
contexts: ["editable"]
});
});
Alternatively, you can simply suppress the error by accessing lastError in the callback:
chrome.contextMenus.create({
id: "zm_mark_down_preview_beta",
title: 'preview and edit',
contexts: ["editable"]
}, () => chrome.runtime.lastError);
Related
i have a small problem with my code that at a given action must create a text channel in a category that is accessed by the server administrator, the user who invoked the command, and the user tagged in the command. It all works I manage to create the channel I can even add it to the right cateory but I can't change its permissions. I have tried a variety of methods including:
updateOverwrite
createOverwrite
overwritePermissions
updateOvermissions (Recommended by ChatGPT I think it was a bit gone when he recommended it)
The problem is that with all these methods I get back the error function not found
Any ideas?
Ecco il codice:
guild.channels.create(
{
name: "》《🆔》" + user.user.username,
type: 0,
})
.then(channel => {
// set the parent category
channel.setParent({
id: "1067910662644314153"
});
channel.updateOvermissions([
{
id: user.user.id,
allow: ["VIEW_CHANNEL", "SEND_MESSAGES"]
},
{
id: author.id,
allow: ["VIEW_CHANNEL", "SEND_MESSAGES"]
}
]);
});
I have a solution for you.
Try creating the channel and directly assign it the parent and permissions as in the code I left you below. Also by default it doesn't take category permissions so I also recommend you add deny permissions for everyone. Hope that helped you out
let everyoneRole = guild.roles.cache.find(r => r.name === '#everyone');
guild.channels.create(
{
name: "》《🆔》" + user.user.username,
type: 0,
permissionOverwrites: [
{
id: user.user.id,
allow: [PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages]
},
{
id: author.id,
allow: [PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages]
},
{
id: everyoneRole.id,
deny: [PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.SendMessages]
}
],
parent: "1067910662644314153"
})
While developing contextmenu based extension for Chrome, i'm facing a situation where invoking chrome.contextMenus.onClicked.addListener in a loop adds action cumulatively for each subcontext menu item.
So, when subcontextmenu is clicked it triggers event listener for all subcontext menus, instead for the context menu which was clicked.
var parent;
chrome.runtime.onInstalled.addListener(() => {
parent = chrome.contextMenus.create({"title": "CCM", "id":"CCM", "contexts":["selection"]});
});
Object.keys(msgs).forEach(function(key) {
chrome.runtime.onInstalled.addListener(() => {
var createCM = chrome.contextMenus.create(
{"title": "subcontextmenu"+key, "id":"scm"+key, "parentId": parent, "contexts":["selection"]});
});
chrome.contextMenus.onClicked.addListener(function(info,tab){openAction(info,tab,JSON.stringify(msgs[key]['msg']));}
);
});
In the above example, msgs is a JSON object containing message to be displayed when each subcontextmenu is clicked. Also, the msgs JSON context is bound to change dynamically. So, we can't tweak openAction to hardcode the numbers and associate the messages.
Hope my question is clear. Any help for clearing this confusion will be a great time saver for me.
Use one listener for onInstalled and onClicked (it provides menuItemId in its parameter).
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({title: 'CCM', id: 'CCM', contexts: ['selection']});
Object.keys(msgs).forEach(key => {
chrome.contextMenus.create({
title: 'subcontextmenu' + key,
id: 'scm' + key,
parentId: 'CCM',
contexts: ['selection'],
});
});
});
chrome.contextMenus.onClicked.addListener((info, tab) => {
openAction(info, tab, JSON.stringify(msgs[info.menuItemId].msg));
});
I have a message extension running in MS Teams.
The search list displays the Hero Card.
On selecting one of them it displays something like below in the chat.
Part of code that displays the button:
...
heroCard.content.buttons = [{
type: 'invoke',
title: 'Open Attachment',
value: {
type: "task/fetch",
messageId: "12345",
}
}];
I am looking to run my angular app on clicking Open Attachment which displays the documents.
I figured out the way to proceed using Microsoft Docs: Use task modules from bots
First I need to tweak the hero card button to pass my data.
...
heroCard.content.buttons = [{
type: 'invoke',
title: 'Open Attachment',
value: {
type: "task/fetch",
messageId: "12345",
data: attachments
}
}];
The second thing is to handle the fetch request:
async handleTeamsTaskModuleFetch(context, action) {
var attachments = action.data.data
return {
task: {
type: 'continue',
value: {
height: 400,
width: 400,
title: 'View Documents',
url: `https://example.io?data=${attachments}`
}
}
};
}
Note: the URL must be in the valid domain of the manifest - otherwise you'll see the blank page.
The below is the final output:
I have to add multiple buttons on Card or Basic card. Is it possible ?
In dialog flow documentation, its mentioned there is one element buttons which takes array of element. Based on this I have added buttons like:
agent.add(new BasicCard({
title: body.hits.hits[i]._source.name,
formattedText: '',
image: {
url: body.hits.hits[i]._source.images ? body.hits.hits[i]._source.images[0].src : '',
accessibilityText: 'Logo',
},
buttons: [{
title: "Buy",
openUrlAction: {
url: body.hits.hits[i]._source.buy,
}
},{
title: "Add to Cart",
openUrlAction: {
url: body.hits.hits[i]._source.aad_to_card,
}
}
],
}));
But its throws error as below:
throw new Error(`Unknown response type: "${JSON.stringify(response)}"`);
Some places its mentioned buttons takes only one element. So what's the point of making it array ?
A BasicCard can only have one button. That is the current rule. I can't give a good reason on why it is in an array even if it only accepts one element.
I've created a simple Google Assistant interface using DialogFlow with several Carousels that I want to be able to chain together. Whenever I touch a carousel option though, it always goes to the first Intent that has the actions_intent_OPTION event specified. I can get to all of my screens using voice commands, but I'm not sure how to process the touch commands to send the user to right Intent.
Current code in webhook:
const party = 'party';
const cocktail = 'cocktail';
const SELECTED_ITEM_RESPONSES = {
[party]: 'You selected party',
[cocktail]: 'You selected cocktail',
};
function carousel(agent) {
//agent.add(`Item selected`);
app.intent('actions.intent.OPTION', (conv, params, option) => {
let response = 'You did not select any item from the list or carousel';
if (option && SELECTED_ITEM_RESPONSES.hasOwnProperty(option)) {
response = SELECTED_ITEM_RESPONSES[option];
} else {
response = 'You selected an unknown item from the list or carousel';
}
conv.ask(response);
});
}
If I leave the agent.add() line in, then I get "Item selected"... but if I try to use the app.intent code, it says I'm just getting an empty speech response.
I was trying to create 1 intent called CarouselHandler to process all the menu selections. I used the sample code to call the carousel() function when that intent gets hit by the event.
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('CarouselHandler', carousel);
agent.handleRequest(intentMap);
You have several questions in here about using options. Let's try to clear a few things up.
Can I get a different Intent triggered for each option?
No. The way options are reported to Dialogflow is that all options will trigger the same Intent. You're responsible for looking at the option string sent and calling another function if you wish.
As you've noted, you need to create an Intent with the Event actions_intent_OPTION.
Your code to handle this might look something like this, although there are other ways to handle it:
app.intent('list.reply.click', (conv, params, option) => {
// Get the user's selection
// Compare the user's selections to each of the item's keys
if (!option) {
conv.ask('You did not select any item from the list or carousel');
} else if (option === 'OPTION_1') {
handleOption1( conv );
} else if (option === 'OPTION_2') {
handleOption2Or3( conv );
} else if (option === 'OPTION_3') {
handleOption2Or3( conv );
} else {
conv.ask('You selected an unknown item from the list, or carousel');
}
});
Can I get a different Intent triggered for each carousel?
Yes. To do this, when you send the carousel you will set an OutgoingContext and delete any other OutgoingContexts you created for a carousel (set their lifespan to 0). Then you will create an Intent that has this Context as an IncomingContext.
The code to send a carousel might look something like this if you're using the actions-on-google library
conv.ask("Here is menu 2");
conv.ask(new List({
title: "Menu 2",
items: {
"OPTION_1": {
title: "Option 1",
description: "Description 1"
},
"OPTION_2": {
title: "Option 2",
description: "Description 2"
},
"OPTION_3": {
title: "Option 3",
description: "Description 3"
},
}
});
conv.contexts.set("menu_2",99);
conv.contexts.delete("menu_1");
conv.contexts.delete("menu_3");
// Don't forget to add suggestions, too
If you're using the dialogflow-fulfillment library, it would be similar, although there are a few differences:
let conv = agent.conv();
conv.ask("Here is menu 2");
conv.ask(new List({
title: "Menu 2",
items: {
"OPTION_1": {
title: "Option 1",
description: "Description 1"
},
"OPTION_2": {
title: "Option 2",
description: "Description 2"
},
"OPTION_3": {
title: "Option 3",
description: "Description 3"
},
}
});
agent.add(conv);
agent.setContext({name:"menu_1", lifespan:0});
agent.setContext({name:"menu_2", lifespan:99});
agent.setContext({name:"menu_3", lifespan:0});
If you were using multivocal, the response configuration might look something like this:
{
Template: {
Text: "Here is menu 2",
Option: {
Type: "carousel",
Title: "Menu 2",
Items: [
{
Title: "Option 1",
Body: "Description 1"
},
{
Title: "Option 2",
Body: "Description 2"
},
{
Title: "Option 3",
Body: "Description 3"
}
]
}
},
Context: [
{
name: "menu_1",
lifetime: 0
},
{
name: "menu_2",
lifetime: 99
},
{
name: "menu_3",
lifetime: 0
}
]
}
The Intent that would capture this option suggestion might look something like this:
Your code to handle this would be similar as above, except using the different Intent name.
If there are overlapping options between the handlers, they could call the same function that actually does the work (again, as illustrated above).
How can I handle voice and option responses the same way?
AoG, in some cases, will use the voice response to trigger the option. This is what the aliases are for. But even beyond this, if you have Intents that catch phrases from the user and an Intent that works with the Options, all you need to do is have the fulfillment code call the same function.
Why doesn't the code work?
The line
app.intent('actions.intent.OPTION', (conv, params, option) => {
Probably doesn't do what you think it does. Unless this is the name for the Intent in Dialogflow, the string actions.intent.OPTION won't be seen in your handler. It is also how you register an Intent handler with the actions-on-google library.
It also looks like you're mixing the dialogflow-fulfillment library way of registering Intent handlers with the actions-on-google library way of registering Intent handlers through your carousel() function. Don't do this. (This may also be part of the cause about why replies aren't getting back correctly.)