Search code examples
c#botframeworkdirect-line-botframework

How to Set [Activity.From.Id] , [member.Id] or [activity.Recipient.Id] using DirectLine?


I'm using MS directline to integrate custom channel with a chatbot based on botframework.

I'm using below functions to generate token & start conversation, but none of them allows to set Context.Activity.From.Id, MembersAdded.FirstOrDefault().Id , nor Activity.Recipient.Id

  • GenerateTokenForNewConversationAsync()
  • StartConversationAsync()

I Know we can control the ID when we send the first user message through directline , but I want to control any of the above IDs before even sending a message from the user. I want to set a specific ID and be able to capture it on the BOT event OnTurnAsync and Activity of type ActivityTypes.ConversationUpdate .. what should I do ?


Solution

  • Regarding my comment, I decided I'd just provide how to do what you want with both packages. Again, if it isn't too much trouble to switch, I highly, highly recommend using Microsoft.Bot.Connector (newer and more frequently updated) over Microsoft.Bot.Connector.DirectLine (older, not updated in 2.5 years, and deprecated until/unless we open-source it Update: This isn't actually deprecated, yet. We're currently working on open-sourcing this, but it's a low-priority task).

    Recommended: Microsoft.Bot.Connector

    Create the conversation with To and From, all-in-one.

    var userAccount = new ChannelAccount(toId,toName);
    var botAccount = new ChannelAccount(fromId, fromName);
    var connector = new ConnectorClient(new Uri(serviceUrl));
    
    IMessageActivity message = Activity.CreateMessageActivity();
    if (!string.IsNullOrEmpty(conversationId) && !string.IsNullOrEmpty(channelId))
    {
        message.ChannelId = channelId;
    }
    else
    {
        conversationId = (await connector.Conversations.CreateDirectConversationAsync( botAccount, userAccount)).Id;
    }
    message.From = botAccount;
    message.Recipient = userAccount;
    message.Conversation = new ConversationAccount(id: conversationId);
    message.Text = "Hello, this is a notification";
    message.Locale = "en-Us";
    await connector.Conversations.SendToConversationAsync((Activity)message);
    

    Credit

    Not Recommended: Microsoft.Bot.Connector.DirectLine

    This is kind of a hacky workaround, but basically, you create the conversation and then send a ConversationUpdate activity.

    //server side, retrieve token from secret
    string directLineSecret = ConfigurationManager.AppSettings["DirectLineSecret"];
    HttpClient httpClient = new HttpClient();
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,$"https://directline.botframework.com/v3/directline/tokens/generate");
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", directLineSecret);
    var fromUser = $"dl_{Guid.NewGuid()}";
    request.Content = new StringContent(
        JsonConvert.SerializeObject(
            new { User = new { Id = fromUser } }),
            Encoding.UTF8,
            "application/json");
    var response = await httpClient.SendAsync(request);
    DirectLineToken dlToken = null;
    if (response.IsSuccessStatusCode)
    {
        var body = await response.Content.ReadAsStringAsync();
        dlToken = JsonConvert.DeserializeObject<DirectLineToken>(body);
    }
    string token = dlToken.token;
    
    //create DirectLineClient from token, client side
    DirectLineClient client = new DirectLineClient(token);            
    var conversation = await client.Conversations.StartConversationAsync();            
    new System.Threading.Thread(async () => await ReadBotMessagesAsync(client, conversation.ConversationId)).Start();
    
    //send conversationUpdate
    var user = new ChannelAccount(fromUser);
    await client.Conversations.PostActivityAsync(conversation.ConversationId,
        new Activity
        {
            From = user,
            Text = string.Empty,
            Type = ActivityTypes.ConversationUpdate,
            MembersAdded = new[] { user }
        }
    );
    TimeSpan delayTime = TimeSpan.FromSeconds(dlToken.expires_in) - TimeSpan.FromMinutes(5);
    Task.Factory.StartNew(async () =>
    {
        while (!_getTokenAsyncCancellation.IsCancellationRequested)
        {
            var t = await client.Tokens.RefreshTokenAsync().ConfigureAwait(false);
    
            await Task.Delay(delayTime, _getTokenAsyncCancellation.Token).ConfigureAwait(false);
        }
    }).ConfigureAwait(false);
    

    Credit