I am playing with slack apis to create an integration. I am able to sucessfully create a slack channel using
this.http.post(slackApiHost + '/channels.create', payload, {headers: headers})
.subscribe(
res => {
console.log(res);
resolve(res)
},
err => {
console.log('error:' + err)
}
)
})
the payload looks like
var payload = {
"name" : channelName
};
So, it will fail with name_taken if the channel already exists. which is great. However, I need to find the channel id of the existing channel so that i can then use it for my purpose. how do i go about it?
To get a list of all existing channel you can use conversations.list. This is the most flexible approach and allows you to retrieve any type of channel if you so choose.
If you are looking for a channel by a specific name you will need to first retrieve the list of all channels and then run your own name matching against the full list. An API method which allows you to directly search for a channel by name does not exist.
Note that if you are looking for private channels this method will only retrieve channels that you (the installer of your Slack app / your bot user) has been invited to.
There's currently no direct Slack API to find a slack channel by its name. But you can use conversations.list to handle this and here's the right code to use:
const slack = new SlackWebClient(token);
const channelNameToFind = "test";
for await (const page of slack.paginate("conversations.list", {
limit: 50,
})) {
for (const channel of page.channels) {
if (channel.name === channelNameToFind) {
console.log(`Found ${channelNameToFind} with slack id ${channel.id}`);
break;
}
}
}
This is the right way™️ to do it since it will ensure you stop listing channels as soon as you found the right one.
Good luck!
You can use built-in admin.conversations.search
and then iterate found results.
One more kind of fast tricky solution:
try to send chat.scheduleMessage and then if it was sent chat.deleteScheduledMessage or catch error.
Ruby example:
def channel_exists?(channel)
begin
response = #slack_client.chat_scheduleMessage(channel: channel, text: '', post_at: Time.now.to_i + 100)
#slack_client.chat_deleteScheduledMessage(channel: channel, scheduled_message_id: response.scheduled_message_id)
return true
rescue Slack::Web::Api::Errors::ChannelNotFound
return false
end
end
Related
I'd like to create a simple Firefox extension to write a simple message to the Javascript console when a push notification is received from any site. I see there is a notification show event that seems like it should work although I can't seem to find an example of it in use. Is this possible?
I'm not sure that that API would handle your use case because the event is not global. It is placed on the object e.g.
var notify = new Notification("Hi there!").addEventListener('show', e => console.log("We showed it! ", e));
I can't think of a better solution if you want to watch for global events than what is mentioned in https://stackoverflow.com/a/36868084/4875295 -- Monkey Patching!
Copying the code from that answer for posterity:
function setNotificationCallback(callback) {
const OldNotify = window.Notification;
const newNotify = (title, opt) => {
callback(title, opt);
return new OldNotify(title, opt);
};
newNotify.requestPermission = OldNotify.requestPermission.bind(OldNotify);
Object.defineProperty(newNotify, 'permission', {
get: () => {
return OldNotify.permission;
}
});
window.Notification = newNotify;
}
Working on a Teams chatbot (V4/Node) and need to address GDPR.
In short, users of the chatbot need to be able to export or delete their personal data stored by the chatbot. Personal data is any information which is related to an identified or identifiable natural person. So also a user-ID in a state object.
I read a blog about GDPR and bots but this one does not address the Teams channel. And it is about V3
The personal data given by the user in dialogs (written by me) is
the easy part. I will write some dialogs to show and delete them
(like Bill does in his answer).
The content in the actual conversations is part of the Teams platform and will\should be adressed in Teams itself.
The bit I don't know how to address is the data for the bot to actually run (Bot state etc). What if a user needs to delete the fact that he or she participated in a certain conversation. That is probably stored in some state objects (in my case in Blob storage). But which ones?
I would appreciate some ideas\guidance in how to address this.
Disclaimer: I'm not a GDPR expert but I believe the following to be sufficient.
From a bot standpoint the data stored is the same in Teams channel. You have the conversation state and user state data which is typically (and in most of the examples) set up using Blob storage. I use the conversationState and userState nomenclature for these items.
In my use case, I am storing account number in userState and user name/email in conversationState. Note that there are other things that the bot stores (particularly in conversationState I believe) around the state of the dialog and other bot specific things that are rather meaningless generally but I don't know if they would be considered part of GDPR. Regardless we will be wiping these entire objects out.
To do that, I created a dialog to manage the user profile which displays the key information stored (I'm specifically accessing account number, user name, and email) and then prompts the user for if they want to delete the information. It looks like this in nodejs.
const { ConfirmPrompt, ComponentDialog, WaterfallDialog } = require('botbuilder-dialogs');
const { ActivityTypes } = require('botbuilder');
const WATERFALL_DIALOG = 'waterfallDialog';
const CONFIRM_PROMPT = 'confirmPrompt';
class manageProfileDialog extends ComponentDialog {
constructor(dialogId, userDialogStateAccessor, userState, appInsightsClient, dialogState, conversationState) {
super(dialogId);
this.dialogs.add(new ConfirmPrompt(CONFIRM_PROMPT));
this.dialogs.add(new WaterfallDialog(WATERFALL_DIALOG, [
this.showInfoAndPrompt.bind(this),
this.confirmDelete.bind(this)
]));
this.initialDialogId = WATERFALL_DIALOG;
// State accessors
this.userDialogStateAccessor = userDialogStateAccessor;
this.userState = userState;
this.dialogState = dialogState;
this.conversationState = conversationState;
this.appInsightsClient = appInsightsClient;
} // End constructor
async showInfoAndPrompt(step) {
this.appInsightsClient.trackEvent({name:'manageProfileDialog', properties:{instanceId:step._info.values.instanceId, channel: step.context.activity.channelId}});
this.appInsightsClient.trackMetric({name: 'showInfoAndPrompt', value: 1});
const userProfile = await this.userDialogStateAccessor.get(step.context, {});
const conversationData = await this.dialogState.get(step.context, {});
if (!userProfile.accountNumber & !conversationData.userEmail & !conversationData.userFullName & !conversationData.orderType) {
this.appInsightsClient.trackEvent({name:'manageProfileDialogEnd', properties:{instanceId:step._info.values.instanceId, channel: step.context.activity.channelId}});
this.appInsightsClient.trackMetric({name: 'confirmDelete', value: 1});
await step.context.sendActivity(`I don't have any of your information stored.`);
return await step.endDialog();
} else {
var storedData = '';
if (userProfile.accountNumber) {
storedData += ` \n**Account Number:** ${userProfile.accountNumber}`;
}
if (conversationData.userFullName) {
storedData += ` \n**Name:** ${conversationData.userFullName}`;
}
if (conversationData.userEmail) {
storedData += ` \n**Email:** ${conversationData.userEmail}`;
}
if (conversationData.orderType) {
storedData += ` \n**Default order type:** ${conversationData.orderType}`;
}
await step.context.sendActivity(`Here is the informaiton I have stored: \n ${storedData} \n\n I will forget everything except your account number after the end of this conversation.`);
await step.context.sendActivity({ type: ActivityTypes.Typing });
await new Promise(resolve => setTimeout(resolve, process.env.DIALOG_DELAY));
return await step.prompt(CONFIRM_PROMPT, `I can clear your information if you don't want me to store it or if you want to reneter it. Would you like me to clear your information now?`,['Yes','No']);
}
}
async confirmDelete(step) {
this.appInsightsClient.trackEvent({name:'manageProfileDialogEnd', properties:{instanceId:step._info.values.instanceId, channel: step.context.activity.channelId}});
if (step.result) {
const userProfile = await this.userDialogStateAccessor.delete(step.context, {});
const conversationData = await this.dialogState.delete(step.context, {});
await step.context.sendActivity(`OK, I have cleared your information.`);
return await step.endDialog();
} else {
await step.context.sendActivity(`OK, I won't clear your information. You can ask again at any time.`);
this.appInsightsClient.trackMetric({name: 'confirmDelete', value: 1});
return await step.endDialog();
}
}
}
module.exports.ManageProfileDialog = manageProfileDialog;
One thing I am uncertain of regarding GDPR is if you are storing transcripts or activity data elsewhere in the course of running the bot. For example, I am storing conversation transcripts in CosmosDB, which could include things like names and email addresses if they were provided during the course of the conversation. I don't have a good way to clear this information even if I wanted to. Also, I am storing LUIS traces and other information in Application Insights, which in many cases includes the activity which may have things like user name or ID attached. I'm not even sure it would be possible to delete those traces from Application Insights. I do not know if these fall under the realm of GDPR since they are operational, but if that is a potential concern just be careful about what you are storing in your logging and/or transcript applications.
I am using code based on https://github.com/Microsoft/BotFramework-WebChat/blob/master/samples/15.d.backchannel-send-welcome-event/index.html
When I load the web page I get two of the welcome messages. Looking at the console output of my bot I can see two conversation updates happening.
This doesn't happen with the Bot framework emulator, which only shows one welcome message.
The only place where my code differs from the sample is in rendering:
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
styleOptions,
userID: guid(),
}, document.getElementById('webchat'));
Why is this hapening? Why is the web channel sending two "join" events for the user?
My code handling conversation updates looks like this:
} else if (turnContext.activity.type === ActivityTypes.ConversationUpdate) {
if (DEBUG) { console.log("ConversationUpdate"); }
// Do we have any new members added to the conversation?
if (turnContext.activity.membersAdded.length !== 0) {
// Iterate over all new members added to the conversation
for (var idx in turnContext.activity.membersAdded) {
console.log(turnContext.activity.membersAdded);
// Greet anyone that was not the target (recipient) of this message
// the 'bot' is the recipient for events from the channel,
// turnContext.activity.membersAdded == turnContext.activity.recipient.Id indicates the
// bot was added to the conversation.
if (turnContext.activity.membersAdded[idx].id != turnContext.activity.recipient.id) {
if (DEBUG) {console.log("Starting MASTER_DIALOG");}
const user = await this.userProfile.get(turnContext, {});
user.id = this.guid();
await this.userProfile.set(turnContext, user);
await this.userState.saveChanges(turnContext);
return await dialogContext.beginDialog(MASTER_DIALOG)
}
}
}
}
Using the ConversationUpdate event for sending a welcome message is not recommended. Read more about how to properly send a greeting message.
There will be two ConversationUpdate events per connection. One for when bot joins the conversation and one for when a (human) user joins the conversation. In your current code you are iterating over all new members, where you have to filter out the bot itself.
A better option would be to make use of a custom event sent using the backchannel. In the example you mention, you already have this functionality. It will sent a new event webchat/join to your bot, which even includes the browser language by default.
i would like to implement a way of managing a user sending many messages in a time interval (for example 3 seconds), so that the chatbot only responds to the last one.
Example of inputs (in a gap of 3 seconds):
-Hi
-Hi
-Hi
-Help
Result: The chatbot only responds to the Help message.
Thanks in advance.
You can leverage Middleware feature to intercept every message, with which you can store every user's every message in cache, when your bot receive a new message, you can compaire with those info in cache, then dicide whether the flow needs to go forward.
Npde.js code snippet for quick test:
const moment = require('moment');
let lastMessage = null;
let lastMessageTime = null;
bot.use({
receive: (session, next) => {
let currentMessage = session
if (currentMessage.text !== lastMessage) {
lastMessage = currentMessage.text;
lastMessageTime = currentMessage.timestamp;
next();
} else {
if (moment(currentMessage.timestamp) - moment(lastMessageTime) >= 3000) {
lastMessageTime = currentMessage.timestamp;
next();
}
}
}
})
What needs you paying attention is that, in production env, you need to store the message with session/user id. E.G. Using session/user id as prefix of message and timesamp key in cache.
Please refer to https://learn.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-middleware for how to intercept messages in C#,
and refer to https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-intercept-messages for Node.js version.
Hope it helps.
I'm trying to create a chatbot where in order to avoid the user opening the chat window and not knowing the available options, I want to give some basic instructions when the user opens the chat window.
Is there any trigger available when the user opens a chat window? Maybe then I can check, and if there's not an ongoing conversation I could provide basic instructions.
I did some googling and found nothing about this. Is it possible to do something like this, and if not, is there a way to mitigate this problem, and provide the user with information regarding the chatbot capabilities and supported instructions?
Facebook does not allow bots to initiate a conversation, unlike Skype or other platforms.
There are still some tricks you can do :
Go on the Settings of your Facebook page, then Messaging and check "Show a Messenger Greeting" as below, and write your greeting sentence.
The result will look like this :
You can also set a "Get Started" button to trigger an event.
Here's the doc :
"https://developers.facebook.com/docs/messenger-platform/thread-settings/get-started-button"
You can monitor for two event types: ConversationUpdate and ContactRelationUpdate.
The first one (ConversationUpdate) is called when a user is added or removed from the conversation. So, there's a place where you can introduce available options. It will be each type the new conversation has started. So, it may become annoying, you may add a check - do not show it if the user has been using the bot for some time.
The second (ContactRelationUpdate) is called when a user adds or removes the bot to/from the contacts. In general, it is only called once per user action.
Here's the extract from the Bot-Frameworks examples:
For Node.Js
bot.on('conversationUpdate', function (message) {
// Check for group conversations
if (message.address.conversation.isGroup) {
// Send a hello message when bot is added
if (message.membersAdded) {
message.membersAdded.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
var reply = new builder.Message()
.address(message.address)
.text("Hello everyone!");
bot.send(reply);
}
});
}
// Send a goodbye message when bot is removed
if (message.membersRemoved) {
message.membersRemoved.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
var reply = new builder.Message()
.address(message.address)
.text("Goodbye");
bot.send(reply);
}
});
}
}
});
bot.on('contactRelationUpdate', function (message) {
if (message.action === 'add') {
var name = message.user ? message.user.name : null;
var reply = new builder.Message()
.address(message.address)
.text("Hello %s... Thanks for adding me. Say 'hello' to see some great demos.", name || 'there');
bot.send(reply);
} else {
// delete their data
}
});
For C#
private void HandleMessage(Activity message)
{
if (message.Type == ActivityTypes.ConversationUpdate)
{
if (activity.MembersAdded.Any(m => m.Id == activity.Recipient.Id))
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var response = activity.CreateReply();
response.Text = "Hi! I am Bot. Here's what you can do...";
await connector.Conversations.ReplyToActivityAsync(response);
}
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
if (Activity.AsContactRelationUpdateActivity().Action == ContactRelationUpdateActionTypes.Add)
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var response = activity.CreateReply();
response.Text = "Hi! I am Bot. Thanks for adding me. Here's what you can do...";
}
}
return null;
}
I think the acid answer is not.
But you can intercept the IConversationUpdateActivity type message to know if the user has added the bot to a conversation. In the C# project template you can find a code block that ask for this message type but do nothing.