Skip to content

Commit

Permalink
[MM-53432] Calls transcriptions (#549)
Browse files Browse the repository at this point in the history
* handleBotGetProfileForSession

* handleBotPostTranscriptions

* Refactor job api, part I

* Updates

* Job status updates

* Couple transcribing job with recording

* Update offloader

* Updates

* Remove event

* Bump Go version

* Add some docs

* Add e2e test for call transcriptions

* Use leaner API request context

* Verify transcription content

* Bump calls-common

* Fix error message

* Rename param

* Init transcriber runner only if enabled

* Allow status failure report even after job has ended

* Add public.JobInfo

* Bump public module

* Update recorder and transcriber deps

* Update recording ended banner text

* Add processing message

* Update job info types

* Fix transcription file access issues

* Cache current date to avoid refetching

* Update comment

* [MM-53432] Improvements (#565)

* Add Dismiss button to recording job post-processing message

* Implement more human friendly filename for recordings and transcriptions

* Support setting transcriber threads

* Improvements

* Include transcription post ID in metadata

* Add config option to select model size (#566)

* Update transcribing job max duration

* Update strings

* Update deps

* Fix e2e imports

* Use underscore to replace spaces in filenames
  • Loading branch information
streamer45 authored Nov 29, 2023
1 parent ff80fe2 commit 53ab625
Show file tree
Hide file tree
Showing 53 changed files with 1,761 additions and 261 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ jobs:
plugin-cd:
uses: mattermost/actions-workflows/.github/workflows/plugin-cd.yml@main
with:
golang-version: "1.19"
golang-version: "1.21"
secrets: inherit
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ jobs:
uses: mattermost/actions-workflows/.github/workflows/plugin-ci.yml@main
with:
golangci-lint-version: "v1.54.2"
golang-version: "1.19"
golang-version: "1.21"
secrets: inherit
8 changes: 2 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,6 @@ ifneq ($(HAS_WEBAPP),)
endif

golangci-lint: ## Run golangci-lint on codebase
# https://stackoverflow.com/a/677212/1027058 (check if a command exists or not)
@if ! [ -x "$$(command -v golangci-lint)" ]; then \
echo "golangci-lint is not installed. Please see https://github.com/golangci/golangci-lint#install for installation instructions."; \
exit 1; \
fi; \
ifneq ($(HAS_SERVER),)
@if ! [ -x "$$(command -v golangci-lint)" ]; then \
echo "golangci-lint is not installed. Please see https://github.com/golangci/golangci-lint#install for installation instructions."; \
Expand All @@ -87,6 +81,7 @@ ifneq ($(HAS_SERVER),)
@echo Running golangci-lint
golangci-lint run ./server/... ./lt/...
cd server/public && golangci-lint run ./...
endif

## Builds the server, if it exists, for all supported architectures, unless MM_SERVICESETTINGS_ENABLEDEVELOPER is set
Expand Down Expand Up @@ -291,6 +286,7 @@ else
test: apply webapp/node_modules standalone/node_modules gotestsum
ifneq ($(HAS_SERVER),)
$(GOBIN)/gotestsum -- -v $(GO_TEST_FLAGS) ./server/...
cd ./server/public && $(GOBIN)/gotestsum -- -v $(GO_TEST_FLAGS) ./...
endif
ifneq ($(HAS_WEBAPP),)
cd webapp && $(NPM) run test;
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ This plugin demands some network configuration changes to allow audio/video comm
> **_Note_**
>
> Building the plugin requires the following:
> - Golang: version >= **1.18**
> - Golang: version >= **1.21**
> - NodeJS: version **16.x**
> - NPM: version **8.x**
Expand Down
Binary file modified e2e/assets/sample.wav
Binary file not shown.
43 changes: 43 additions & 0 deletions e2e/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {expect, request} from '@playwright/test';
import {APIRequestContext} from 'playwright-core';

import {adminState, baseURL, pluginID} from './constants';
import {headers, newUserPage} from './utils';

type CallsConfig = {
enabletranscriptions: boolean;
};

export const apiPatchConfig = async (cfg: CallsConfig) => {
const adminContext = await request.newContext({
baseURL,
storageState: adminState.storageStatePath,
});
const serverConfig = await (await adminContext.get(`${baseURL}/api/v4/config`)).json();

serverConfig.PluginSettings.Plugins = {
...serverConfig.PluginSettings.Plugins,
[`${pluginID}`]: {
...serverConfig.PluginSettings.Plugins[pluginID],
...cfg,
},
};

const resp = await adminContext.put(`${baseURL}/api/v4/config`, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
data: serverConfig,
});
await expect(resp.status()).toEqual(200);
};

export const apiEnableTranscriptions = async () => {
return apiPatchConfig({
enabletranscriptions: true,
});
};

export const apiDisableTranscriptions = async () => {
return apiPatchConfig({
enabletranscriptions: false,
});
};
4 changes: 4 additions & 0 deletions e2e/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,8 @@ export default class PlaywrightDevPage {
await expect(notificationsSoundedAt.length).toEqual(numNotificationsSounded);
await expect(notificationsSoundStoppedAt.length).toEqual(numNotificationsStoppedAt);
}

async unmute() {
await this.page.locator('#voice-mute-unmute').click();
}
}
72 changes: 68 additions & 4 deletions e2e/tests/recordings.spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import {chromium, expect, test} from '@playwright/test';

import {baseURL, defaultTeam, pluginID} from '../constants';
import {apiDisableTranscriptions, apiEnableTranscriptions} from '../config';
import {adminState, baseURL, defaultTeam, pluginID} from '../constants';
import PlaywrightDevPage from '../page';
import {getUserStoragesForTest} from '../utils';
import {getUserStoragesForTest, newUserPage} from '../utils';

test.beforeEach(async ({page, context}) => {
const devPage = new PlaywrightDevPage(page);
await devPage.goto();
});

test.describe('call recordings', () => {
test.describe('call recordings and transcriptions', () => {
test.use({storageState: getUserStoragesForTest()[0]});

test('recording - slash command', async ({page}) => {
test('recording - slash command', async ({page, request}) => {
test.setTimeout(150000);

await apiDisableTranscriptions();

// start call
const devPage = new PlaywrightDevPage(page);

await devPage.startCall();

// start recording
Expand Down Expand Up @@ -52,5 +58,63 @@ test.describe('call recordings', () => {

// leave call
await devPage.leaveCall();

// Transcriptions test, we need to keep it here or it would conflict
// with the above as tests are run concurrently.

await page.reload();
await apiEnableTranscriptions();

// start call
await devPage.startCall();

// start recording
await page.locator('#post_textbox').fill('/call recording start');
await page.getByTestId('SendMessageButton').click();

// very recording start prompt renders correctly
await expect(page.getByTestId('calls-widget-banner-recording')).toBeVisible();
await expect(page.getByTestId('calls-widget-banner-recording')).toContainText('Recording and transcription has started');

// Unmute
await devPage.unmute();

// Give it a few of seconds to produce a decent recording
await devPage.wait(4000);

// stop recording
await page.locator('#post_textbox').fill('/call recording stop');
await page.getByTestId('SendMessageButton').click();

// very recording ended prompt renders correctly
await expect(page.getByTestId('calls-widget-banner-recording')).toBeVisible();
await expect(page.getByTestId('calls-widget-banner-recording')).toContainText('Recording and transcription has stopped. Processing…');

// verify transcription file has been posted by the bot (assumes CRT enabled)
await page.locator('.post__body').last().locator('.ThreadFooter button.ReplyButton').click();
await expect(page.locator('.ThreadViewer').locator('.post__header').last()).toContainText('calls');
await expect(page.locator('.ThreadViewer').locator('.post__header').last()).toContainText('BOT');
await expect(page.locator('.ThreadViewer').locator('.post__body').last().filter({has: page.locator('.post-message__text')})).toContainText('Here\'s the call transcription');
await expect(page.locator('.ThreadViewer').locator('.post__body').last().filter({has: page.getByTestId('fileAttachmentList')})).toBeVisible();

// open recording's preview
await page.locator('.ThreadViewer').locator('.post__body').nth(1).filter({has: page.getByTestId('fileAttachmentList')}).click();
await expect(page.locator('.file-preview-modal__content')).toBeVisible();

// verify transcription track exists
await expect(page.getByTestId('calls-recording-transcription')).toHaveAttribute('label', 'Transcription');

// fetch transcription file and verify it has the expected content
const src = await page.getByTestId('calls-recording-transcription').getAttribute('src');
const resp = await request.get(`${baseURL}${src}`);
expect(resp.status()).toEqual(200);
const transcriptionData = await resp.body();
await expect(transcriptionData.toString()).toContain('This is a test transcription sample');

// exit preview
await page.keyboard.press('Escape');

// leave call
await devPage.leaveCall();
});
});
4 changes: 2 additions & 2 deletions e2e/users.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {expect} from '@playwright/test';
import {APIRequestContext} from 'playwright-core';

import {baseURL} from './constants';
import {headers} from './utils';
import {adminState, baseURL} from './constants';
import {headers, newUserPage} from './utils';

export const apiPatchNotifyProps = async (request: APIRequestContext, newProps: Record<string, string>) => {
let resp = await request.get(`${baseURL}/api/v4/users/me`);
Expand Down
13 changes: 7 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/mattermost/mattermost-plugin-calls

go 1.19
go 1.21.4

require (
github.com/pion/ice/v2 v2.3.11 // indirect
Expand All @@ -13,12 +13,13 @@ require (
require (
github.com/Masterminds/semver v1.5.0
github.com/gorilla/websocket v1.5.0
github.com/mattermost/calls-offloader v0.3.2
github.com/mattermost/calls-recorder v0.4.2
github.com/mattermost/calls-offloader v0.5.0
github.com/mattermost/calls-recorder v0.6.0
github.com/mattermost/calls-transcriber v0.1.0
github.com/mattermost/logr/v2 v2.0.16
github.com/mattermost/mattermost-plugin-calls/server/public v0.0.1
github.com/mattermost/mattermost-plugin-calls/server/public v0.0.3-0.20231103204030-06bd54bcfa67
github.com/mattermost/mattermost/server/public v0.0.11-0.20231115180603-759bb70b2f44
github.com/mattermost/rtcd v0.12.0
github.com/mattermost/rtcd v0.12.1-0.20231121174414-6a5686281335
github.com/mattermost/squirrel v0.2.0
github.com/pion/interceptor v0.1.25
github.com/pion/rtp v1.8.3
Expand All @@ -28,7 +29,7 @@ require (
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
)

replace github.com/pion/interceptor v0.1.25 => github.com/streamer45/interceptor v0.0.0-20231109204624-da39785ad37e
replace github.com/pion/interceptor => github.com/streamer45/interceptor v0.0.0-20230202152215-57f3ac9e7696

replace github.com/mattermost/mattermost-plugin-calls/server/public => ./server/public

Expand Down
Loading

0 comments on commit 53ab625

Please sign in to comment.