Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sambetts committed Apr 25, 2024
1 parent f7c28c2 commit 18449bc
Show file tree
Hide file tree
Showing 219 changed files with 13,650 additions and 1 deletion.
46 changes: 46 additions & 0 deletions .github/workflows/buildfunctionsapp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Build and deploy .NET Functions application to App name feedbackbot_AZURE_FUNCTIONS_NAME using profile in secret feedbackbot_PUBLISH_PROFILE_FUNCTIONS
on:
push:
branches:
- main
env:
AZURE_FUNC_PACKAGE_PATH: Functions\publish
CONFIGURATION: Release
DOTNET_CORE_VERSION: 8.0.x
WORKING_DIRECTORY_FUNC: src\Functions
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_CORE_VERSION }}
- name: Restore Functions App
run: dotnet restore "${{ env.WORKING_DIRECTORY_FUNC }}"
- name: Build Functions App
run: dotnet build "${{ env.WORKING_DIRECTORY_FUNC }}" --configuration ${{ env.CONFIGURATION }} --no-restore
- name: Publish Functions App
run: dotnet publish "${{ env.WORKING_DIRECTORY_FUNC }}" --configuration ${{ env.CONFIGURATION }} --no-build --output "${{ env.AZURE_FUNC_PACKAGE_PATH }}"

- name: Publish function
uses: actions/upload-artifact@v3
with:
name: func
path: ${{ env.AZURE_FUNC_PACKAGE_PATH }}
deploy:
runs-on: windows-latest
needs: build
steps:
- name: Download Functions App artifact from build job
uses: actions/download-artifact@v3
with:
name: func
path: ${{ env.AZURE_FUNC_PACKAGE_PATH }}
- name: Deploy Functions App to Azure WebApp
uses: azure/webapps-deploy@v2
with:
app-name: ${{ env.feedbackbot_AZURE_FUNCTIONS_NAME }}
publish-profile: ${{ secrets.feedbackbot_PUBLISH_PROFILE_FUNCTIONS }}
package: ${{ env.AZURE_FUNC_PACKAGE_PATH }}
59 changes: 59 additions & 0 deletions .github/workflows/buildwebandwebjob.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Build and deploy .NET web + webjob to Web App name feedbackbot_AZURE_WEBAPP_NAME using profile found in secret feedbackbot_PUBLISH_PROFILE
on:
push:
branches:
- main
env:
AZURE_WEBAPP_PACKAGE_PATH_WEB: Web\publish
AZURE_WEBAPP_PACKAGE_PATH_WEBJOB: Web\publish\app_data\Jobs\Triggered\ActivityImporter.ConsoleApp
CONFIGURATION: Release
DOTNET_CORE_VERSION: 8.0.x
WORKING_DIRECTORY_WEB: src\Web
WORKING_DIRECTORY_WEBJOB: src\ActivityImporter.ConsoleApp
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_CORE_VERSION }}
- name: Restore Web
run: dotnet restore "${{ env.WORKING_DIRECTORY_WEB }}"
- name: Build Web
run: dotnet build "${{ env.WORKING_DIRECTORY_WEB }}" --configuration ${{ env.CONFIGURATION }} --no-restore
- name: Test
run: dotnet test "${{ env.WORKING_DIRECTORY_WEB }}" --no-build
- name: Publish Web
run: dotnet publish "${{ env.WORKING_DIRECTORY_WEB }}" --configuration ${{ env.CONFIGURATION }} --no-build --output "${{ env.AZURE_WEBAPP_PACKAGE_PATH_WEB }}"

- name: Restore WebJob
run: dotnet restore "${{ env.WORKING_DIRECTORY_WEBJOB }}"
- name: Build WebJob
run: dotnet build "${{ env.WORKING_DIRECTORY_WEBJOB }}" --configuration ${{ env.CONFIGURATION }} --no-restore
- name: Publish WebJob
run: dotnet publish "${{ env.WORKING_DIRECTORY_WEBJOB }}" --configuration ${{ env.CONFIGURATION }} --no-build --output "${{ env.AZURE_WEBAPP_PACKAGE_PATH_WEBJOB }}"

- name: Publish WebJob & Web Artifacts
uses: actions/upload-artifact@v3
with:
name: webjobandweb
path: |
${{ env.AZURE_WEBAPP_PACKAGE_PATH_WEB }}
${{ env.AZURE_WEBAPP_PACKAGE_PATH_WEBJOB }}
deploy:
runs-on: windows-latest
needs: build
steps:
- name: Download webjobandweb artifact from build job
uses: actions/download-artifact@v3
with:
name: webjobandweb
path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH_WEB }}
- name: Deploy webjob and web to Azure WebApp
uses: azure/webapps-deploy@v2
with:
app-name: ${{ env.feedbackbot_AZURE_WEBAPP_NAME }}
publish-profile: ${{ secrets.feedbackbot_PUBLISH_PROFILE }}
package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH_WEB }}
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################

/.github/workflows/feedbackbotdgittest1.yml
/.vs
/src/Functions/Properties/serviceDependencies.copilotfeedback-function-ag - Zip Deploy.json
__azurite*
/Teams App/*.zip
src/Web/Properties/ServiceDependencies/copilotfeedback-ag - Web Deploy/*.*
src/Functions/Properties/ServiceDependencies/copilotfeedback-function-ag - Zip Deploy/*.*
121 changes: 120 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,121 @@
# copilot-feedback-bot
# Copilot Feedback Bot
Get real feedback from your organisations users about how copilot is helping, with "Copilot Feedback Bot".

![Copilot Feedback Bot says 'Hiii!'](imgs/bot-salute-small.png)

This is a bot that collects user feedback about copilot use within M365 so you can see exactly what your users think about it. Do your users love copilot? Does it really save them time? Doing what actions? Do some groups of people like it more than others?

More importantly: is it worth the investment? This is the question this system seeks to quantify in more detail than anything else.

![User Satisfaction Dashboard](imgs/report1.png)

Copilot events are detected automatically, and each user that used copilot will be surveyed about how that interaction went through a Teams bot.

![Teams Prompt from Bot](imgs/botconvo.jpg)

User responses are stored in a database and visualised in Power BI.

![User Satisfaction Report](imgs/report2.png)
This is an automated way to get real feedback from your users about what they think of copilot in Office 365.

All data is stored in your own SQL Server.

## Usage
**Quick version**: run the activity import and let the bot do the rest. The functions app will look for users that have used copilot and then survey them.

When deployed, a web-job will read the Office 365 Activity API to determine copilot interactions - whom, and what. It runs automatically once a day.

A functions app will then find users that have new activity and start a new conversation with them to ask if they could review the activity in question with copilot.

Once surveyed they won't be surveyed again for that specific interaction (even if they don't answer).

If the user doesn't have the bot installed in Teams already, it'll be installed automatically.

If the user says anything to the bot outside the normal dialogue flow, the assumption is they want to leave a copilot review. Surveys don't necessarily have to correlate to a specific interaction, the user can just leave general feedback too.

### Testing the Bot
Copilot activity can be faked by sending a **POST** to ```/api/Triggers/GenerateFakeActivityFor?upn={**UPN**}``` (no body required). This will create a fake meeting & file activity for that user.

The bot can also be forced to check and send for pending surveys to be sent everyone with a **POST** to ```/api/Triggers/SendSurveys``` (no body required).

To test-install the bot for a user and force a "hi!" from the bot, independently of if they have pending surveys or not, **POST** to ```/api/Triggers/InstallBotForUser?upn={**UPN**}``` (no body required).

Otherwise, the longer version is to use copilot in a file or team meeting, wait a few minutes, run (manually or automatically) the import job and then wait for the timer function to run the "_FindAndProcessNewSurveyEventsAllUsers_" method.

## How Does it Work?
Easy. There's a web-job/process that reads O365 audit events for SharePoint and copilot activity. We read SharePoint to know what happened with files (edit/view/etc) and the copilot to know what users have done with copilot. The information we get from copilot events is limited; "User A chatted to copilot in an app, and it involved this file/meeting", so hence we read SP logs too to link what actually was going on.

Note: SharePoint events that are saved can be filtered so we don't accidentally store sensitive file activity data in table "import_url_filter".

In addition we also read aggregated activity reports for users for OneDrive, SharePoint, Outlook and Teams. That way we can find users that are active in O365 but not using Copilot.

Finally the solution reads user metadata from Entra ID (Azure AD) copilot activity & satisfaction by demographics can be analysed - job title, office location, etc.

# Setup Steps
For this, we assume a decent knowledge of Teams apps deployment, .Net, and Azure PaaS.

## Create Bot and Deploy Teams App
Note, that for all these steps you can do them all in PowerShell if you wish. I’m not a sysadmin so this is what works best for me.

You need Teams admin rights and rights to assign sensitive privileges for this setup to work. It's a bot that proactively installs itself and asks users.

1. Go to: https://dev.teams.microsoft.com/bots and create a new bot (or alternatively in the Azure Portal, create a new Azure bot - the 1st link doesn't require an Azure subscription).
2. Create a new client secret for the bot application registration. Note down the client ID & the secret of the bot.
3. Grant permissions (specified below) and have an admin grant consent.
4. Optional: you can create another app registration for the more sensitive privileges if you need (service account). The config supports having x2 accounts, but one can be used for both.

Next, create a Teams app from the template:
4. In "Teams App" root dir, copy file "manifest-template.json" to "manifest.json".
5. Edit "manifest.json" and update all instances of ```<<BOT_APP_ID>>``` with your app registration client ID.
6. Make a zip file from "color.png", "manifest.json", and "outline.png" files in that folder. Make sure all files are in the root of the zip.
7. Deploy that zip file to your apps catalog in Teams admin.
8. Once deployed, copy the "App ID" generated. We'll need that ID for bot configuration.

## Create Azure Resources
Create these resources:

* Required - App service & plan. B1 level recommended for small environments.
* Required - Functions app - consumption plan recommended. May require it's own resource-group.
* Application Insights (link app service & functions app to it)
* Required - Storage account.
* Required - Service bus namespace.
* SQL Database (and server) - the PaaS version. You could use an on-premises SQL if you wanted.

## Required Configuration
These configuration settings are needed in the app service & functions app:

Name | Description
--------------- | -----------
AppCatalogTeamAppId | Teams app ID once deployed to the internal catalog
MicrosoftAppId | ID of bot Azure AD application
MicrosoftAppPassword | Bot app secret
WebAppURL | Root URL of app service
AuthConfig:ClientId | Service account
AuthConfig:ClientSecret | Service account
AuthConfig:TenantId | Service account
ConnectionStrings:Redis | Redis connection string, used for caching delta tokens
ConnectionStrings:ServiceBusRoot | Used for async processing of various things
ConnectionStrings:SQL | The database connection string.
ConnectionStrings:Storage | Connection string. Conversation cache and other table storage

ConnectionStrings go in their own section in App Services (and prefix "ConnectionStrings:" to the name you give there for .Net).

_Important:_ **If any values are missing, the process will crash at start-up**. Check the local VM application log if you get a start-up error (in Kudu for app services).

## Application Permissions
Graph permissions needed (application):
* User.Read.All - for looking up user metadata, allowing activity & survey slicing by demographics (job title, location etc)
* Reports.Read.All - for reading activity data so we can cross-check who's active but not using copilot.
* TeamsAppInstallation.ReadWriteForUser.All - so the bot can proactively install itself into users Teams, to start a new conversation.

Office 365 Management APIs
* ActivityFeed.Read - for detecting copilot events.

All these permissions need administrator consent to be effective.

## Deploy Solution
There are GitHub actions in ".github\workflows\" that will build and deploy the app service & webjob in one WF and the functions app in another.

The workflows require secrets "feedbackbot_PUBLISH_PROFILE" and "feedbackbot_AZURE_FUNCTIONS_NAME" for deploy to work.

Quick hack: publish from Visual Studio is also a (temporary) solution.
Binary file added Reports/Feedback Bot Results.pbix
Binary file not shown.
Binary file added Teams App/color.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions Teams App/manifest-template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
"version": "1.0.0",
"manifestVersion": "1.16",
"id": "<<BOT_APP_ID>>",
"packageName": "com.copilotfeedback.bot",
"name": {
"short": "Copilot Feedback bot",
"full": "Copilot Feedback bot - get real value feedback for copilot usage"
},
"developer": {
"name": "Sam Betts",
"mpnId": "",
"websiteUrl": "https://github.com/sambetts",
"privacyUrl": "https://github.com/sambetts",
"termsOfUseUrl": "https://github.com/sambetts"
},
"description": {
"short": "Copilot Feedback bot - get real value feedback for copilot usage",
"full": "Copilot Feedback bot surveys users how their interactions with M365 Copilot was to compile full and realistic value stats"
},
"icons": {
"outline": "outline.png",
"color": "color.png"
},
"accentColor": "#335484",
"bots": [
{
"botId": "<<BOT_APP_ID>>",
"scopes": [
"personal"
],
"isNotificationOnly": false,
"supportsFiles": false
}
],
"permissions": [
"identity",
"messageTeamMembers"
],
"validDomains": [
"token.botframework.com"
]
}
44 changes: 44 additions & 0 deletions Teams App/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
"version": "1.0.0",
"manifestVersion": "1.16",
"id": "e2cc65ef-2390-4579-b950-83b01dcae815",
"packageName": "com.copilotfeedback.bot",
"name": {
"short": "Copilot Feedback bot",
"full": "Copilot Feedback bot - get real value feedback for copilot usage"
},
"developer": {
"name": "Sam Betts",
"mpnId": "",
"websiteUrl": "https://github.com/sambetts",
"privacyUrl": "https://github.com/sambetts",
"termsOfUseUrl": "https://github.com/sambetts"
},
"description": {
"short": "Copilot Feedback bot - get real value feedback for copilot usage",
"full": "Copilot Feedback bot surveys users how their interactions with M365 Copilot was to compile full and realistic value stats"
},
"icons": {
"outline": "outline.png",
"color": "color.png"
},
"accentColor": "#335484",
"bots": [
{
"botId": "e2cc65ef-2390-4579-b950-83b01dcae815",
"scopes": [
"personal"
],
"isNotificationOnly": false,
"supportsFiles": false
}
],
"permissions": [
"identity",
"messageTeamMembers"
],
"validDomains": [
"token.botframework.com"
]
}
Binary file added Teams App/outline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/bot-meh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/bot-salute-small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/bot-salute.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/bot-thumbs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/botconvo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/report1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/report2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 63 additions & 0 deletions src/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
Loading

0 comments on commit 18449bc

Please sign in to comment.