Blog Copilot Studio

Neil HaddleyMarch 24, 2024

Blog Copilot

I created a Business Central extension that allows Business Central users to "chat to" this blog.

I created a "Blog Copilot" Copilot

I created a "Blog Copilot" Copilot

I watched the animation

I watched the animation

The Copilot was provisioned

The Copilot was provisioned

I switched to No authentication

I switched to No authentication

I clicked Save

I clicked Save

I published the Copilot

I published the Copilot

I copied the Token Endpoint

I copied the Token Endpoint

With the Business Central extension deployed I navigated to the Blog Copilot page using the search function

With the Business Central extension deployed I navigated to the Blog Copilot page using the search function

I asked the Blog Copilot "What is NGRX"?

I asked the Blog Copilot "What is NGRX"?

I asked the Blog Copilot "What is an API Gateway" and "What is Java"?

I asked the Blog Copilot "What is an API Gateway" and "What is Java"?

BlogCopilotExtension.al

TEXT
1controladdin BlogCopilotControlAddin
2{
3    RequestedHeight = 400;
4    MaximumHeight = 700;
5    VerticalStretch = true;
6    VerticalShrink = true;
7    HorizontalStretch = true;
8    HorizontalShrink = true;
9
10    Scripts = 'https://cdn.botframework.com/botframework-webchat/latest/webchat.js', 'BlogCopilotFunctions.js';
11    Stylesheets = 'BlogCopilotStyles.css';
12
13
14    event ControlReady()
15    procedure CreateCopilot();
16}
17
18page 99110 "Blog Copilot"
19{
20    PageType=Card;
21    Caption='Blog Copilot';
22
23
24    ApplicationArea = All;
25    UsageCategory = Tasks;
26
27    layout
28    {
29        area(Content)
30        {
31            usercontrol(Foo; BlogCopilotControlAddin)
32            {
33                ApplicationArea = All;
34
35            }
36        }
37    }
38
39}

BlogCopilotFunctions.js

TEXT
1async function CreateCopilot() {
2
3    var __div = document.getElementById('controlAddIn');
4
5    // Specifies style options to customize the Web Chat canvas.
6    // Please visit https://microsoft.github.io/BotFramework-WebChat for customization samples.
7    const styleOptions = {
8        accent: '#00809d',
9        botAvatarBackgroundColor: '#FFFFFF',
10        // botAvatarImage: 'https://learn.microsoft.com/azure/bot-service/v4sdk/media/logo_bot.svg',
11        botAvatarImage: 'data:image;base64,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',
12        botAvatarInitials: 'BT',
13        userAvatarImage: 'https://content.powerapps.com/resource/makerx/static/media/user.336bbce3.svg',
14        userAvatarInitials: 'USER',
15        hideUploadButton: true,
16        primaryFont: fontFamily(['Segoe UI', 'Segoe WP', 'Segoe', 'device-segoe', 'Tahoma', 'Helvetica', 'Arial', 'sans-serif']),
17    };
18
19
20    // Specifies the token endpoint URL.
21    // To get this value, visit Copilot Studio > Settings > Channels > Mobile app page.
22    const tokenEndpointURL = new URL('https://b838e0443024ea32b2f47862b85e99.03.environment.api.powerplatform.com/powervirtualagents/botsbyschema/cr74e_blogCopilot/directline/token?api-version=2022-03-01-preview');
23
24    const locale = document.documentElement.lang || 'en'; // Uses language specified in <html> element and fallback to English (United States).
25
26    const apiVersion = tokenEndpointURL.searchParams.get('api-version');
27
28    const [directLineURL, token] = await Promise.all([
29        fetch(new URL(`/powervirtualagents/regionalchannelsettings?api-version=${apiVersion}`, tokenEndpointURL))
30            .then(response => {
31                if (!response.ok) {
32                    throw new Error('Failed to retrieve regional channel settings.');
33                }
34
35                return response.json();
36            })
37            .then(({ channelUrlsById: { directline } }) => directline),
38        fetch(tokenEndpointURL)
39            .then(response => {
40                if (!response.ok) {
41                    throw new Error('Failed to retrieve Direct Line token.');
42                }
43
44                return response.json();
45            })
46            .then(({ token }) => token)
47    ]);
48
49    const directLine = WebChat.createDirectLine({ domain: new URL('v3/directline', directLineURL), token });
50
51    // Sends "startConversation" event when the connection is established.
52
53    const subscription = directLine.connectionStatus$.subscribe({
54        next(value) {
55            if (value === 2) {
56                directLine
57                    .postActivity({
58                        localTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
59                        locale,
60                        name: 'startConversation',
61                        type: 'event',
62                    })
63                    .subscribe();
64
65                // Only send the event once, unsubscribe after the event is sent.
66                subscription.unsubscribe();
67            }
68        }
69    });
70
71    WebChat.renderWebChat({ directLine, locale, styleOptions }, __div);
72
73    Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("ControlReady");
74
75}
76
77function fontFamily(fonts) {
78    return fonts.map(font => `'${font}'`).join(', ');
79}
80
81// call the setup function
82function sleep(ms) {
83    return new Promise(resolve => setTimeout(resolve, ms));
84}
85
86sleep(1000).then(() => { CreateCopilot(); });

BlogCopilotStyles.css

TEXT
1* {
2    font-size: 10.5pt
3}