Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions packages/api/src/activities/activity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,40 @@ describe('Activity', () => {
});
});

describe('withChannelData feedback normalization', () => {
it('should upgrade legacy feedbackLoopEnabled:true to feedbackLoop default', () => {
const activity = new Activity({ type: 'test' }).withChannelData({ feedbackLoopEnabled: true });

expect(activity.channelData?.feedbackLoop).toEqual({ type: 'default' });
expect(activity.channelData?.feedbackLoopEnabled).toBeUndefined();
});

it('should clear feedbackLoopEnabled when feedbackLoop is already set', () => {
const activity = new Activity({ type: 'test' }).withChannelData({
feedbackLoop: { type: 'custom' },
feedbackLoopEnabled: true,
});

expect(activity.channelData?.feedbackLoop).toEqual({ type: 'custom' });
expect(activity.channelData?.feedbackLoopEnabled).toBeUndefined();
});
});

describe('addFeedback', () => {
it('should add', () => {
it('should add default feedback loop', () => {
const activity = new Activity({ type: 'test' }).addFeedback();

expect(activity.type).toEqual('test');
expect(activity.channelData?.feedbackLoopEnabled).toEqual(true);
expect(activity.channelData?.feedbackLoop).toEqual({ type: 'default' });
expect(activity.channelData?.feedbackLoopEnabled).toBeUndefined();
});

it('should add custom feedback loop', () => {
const activity = new Activity({ type: 'test' }).addFeedback('custom');

expect(activity.type).toEqual('test');
expect(activity.channelData?.feedbackLoop).toEqual({ type: 'custom' });
expect(activity.channelData?.feedbackLoopEnabled).toBeUndefined();
});
});

Expand Down
20 changes: 16 additions & 4 deletions packages/api/src/activities/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,16 @@ export class Activity<T extends string = string> implements IActivity<T> {
}

withChannelData(value: ChannelData) {
this.channelData = { ...this.channelData, ...value };
const merged: ChannelData = { ...this.channelData, ...value };

if (merged.feedbackLoop !== undefined) {
merged.feedbackLoopEnabled = undefined;
} else if (merged.feedbackLoopEnabled === true) {
merged.feedbackLoop = { type: 'default' };
merged.feedbackLoopEnabled = undefined;
}

this.channelData = merged;
return this;
}

Expand Down Expand Up @@ -364,14 +373,17 @@ export class Activity<T extends string = string> implements IActivity<T> {
}

/**
* Enable message feedback
* Enable message feedback.
* @param mode - `'default'` shows Teams' built-in thumbs up/down UI.
* `'custom'` triggers a `message/fetchTask` invoke so the bot can return its own task module dialog.
*/
addFeedback() {
addFeedback(mode: 'default' | 'custom' = 'default') {
if (!this.channelData) {
this.channelData = {};
}

this.channelData.feedbackLoopEnabled = true;
this.channelData.feedbackLoop = { type: mode };
this.channelData.feedbackLoopEnabled = undefined;
return this;
Comment thread
lilyydu marked this conversation as resolved.
}

Expand Down
39 changes: 39 additions & 0 deletions packages/api/src/activities/invoke/message/fetch-task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ConversationReference } from '../../../models';
import { IActivity } from '../../activity';

export interface IMessageFetchTaskInvokeActivity extends IActivity<'invoke'> {
/**
* The name of the operation associated with an invoke or event activity.
*/
name: 'message/fetchTask';

/**
* A value that is associated with the activity.
*/
value: {
/**
* The data payload containing action name and value.
*/
data: {
/**
* The name of the action.
*/
actionName: 'feedback';

/**
* The nested action value containing the user's reaction.
*/
actionValue: {
/**
* The feedback button the user clicked.
*/
reaction: 'like' | 'dislike';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm this didn't exist before? i thought we had some examples showing this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're probably thinking of this one -

but different invoke activity (this is for default)

};
};
};

/**
* A reference to another conversation or activity.
*/
relatesTo?: ConversationReference;
}
4 changes: 3 additions & 1 deletion packages/api/src/activities/invoke/message/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { IMessageFetchTaskInvokeActivity } from './fetch-task';
import { IMessageSubmitActionInvokeActivity } from './submit-action';

export type MessageInvokeActivity = IMessageSubmitActionInvokeActivity;
export type MessageInvokeActivity = IMessageFetchTaskInvokeActivity | IMessageSubmitActionInvokeActivity;

export * from './fetch-task';
export * from './submit-action';
11 changes: 11 additions & 0 deletions packages/api/src/models/channel-data/feedback-loop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Configuration for a feedback loop on a message.
*/
export type FeedbackLoop = {
/**
* The type of feedback loop.
* Use `custom` to trigger a `message/fetchTask` invoke so the bot can return its own task module dialog.
* Use `default` for the standard Teams thumbs up/down UI.
*/
type: 'default' | 'custom';
};
12 changes: 11 additions & 1 deletion packages/api/src/models/channel-data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { MeetingInfo } from '../meeting';
import { MembershipSource } from '../membership-source';

import { ChannelInfo } from './channel-info';
import { FeedbackLoop } from './feedback-loop';
import { NotificationInfo } from './notification-info';
import { OnBehalfOf } from './on-behalf-of';
import { ChannelDataSettings } from './settings';
Expand Down Expand Up @@ -57,10 +58,18 @@ export type ChannelData = {
settings?: ChannelDataSettings;

/**
* Whether or not the feedback loop feature is enabled.
* Legacy feedback loop flag. Setting this to `true` is equivalent to `feedbackLoop: { type: 'default' }`.
* Prefer using `feedbackLoop` directly.
*/
feedbackLoopEnabled?: boolean;
Comment thread
corinagum marked this conversation as resolved.

/**
* Feedback loop configuration.
* Set `type` to `'custom'` to trigger a `message/fetchTask` invoke for a bot-provided task module dialog.
* Set `type` to `'default'` for the standard Teams thumbs up/down UI.
*/
feedbackLoop?: FeedbackLoop;

/**
* ID of the stream.
* @remarks
Expand Down Expand Up @@ -111,3 +120,4 @@ export * from './on-behalf-of';
export * from './settings';
export * from './team-info';
export * from './tenant-info';
export * from './feedback-loop';
Comment thread
lilyydu marked this conversation as resolved.
1 change: 1 addition & 0 deletions packages/api/src/models/invoke-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type InvokeResponseBody = {
'task/submit': TaskModuleResponse;
'tab/fetch': TabResponse;
'tab/submit': TabResponse;
'message/fetchTask': TaskModuleResponse;
'message/submitAction': void;
'handoff/action': void;
'signin/tokenExchange': TokenExchangeInvokeResponse | void;
Expand Down
13 changes: 13 additions & 0 deletions packages/apps/src/router/router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,5 +280,18 @@ describe('Router', () => {
name: 'task/fetch'
} as any)).toHaveLength(1);
});

it('should select message fetch-task routes', () => {
const router = new Router();
const handler = jest.fn();

router.on('invoke', handler);
router.on('message.fetch-task', handler);

expect(router.select({
type: 'invoke',
name: 'message/fetchTask',
} as any)).toHaveLength(2);
});
});
});
2 changes: 2 additions & 0 deletions packages/apps/src/routes/invoke/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type InvokeAliases = {
'task/submit': 'dialog.submit';
'tab/fetch': 'tab.open';
'tab/submit': 'tab.submit';
'message/fetchTask': 'message.fetch-task';
'message/submitAction': 'message.submit';
'handoff/action': 'handoff.action';
'signin/tokenExchange': 'signin.token-exchange';
Expand All @@ -60,6 +61,7 @@ export const INVOKE_ALIASES: InvokeAliases = {
'task/submit': 'dialog.submit',
'tab/fetch': 'tab.open',
'tab/submit': 'tab.submit',
'message/fetchTask': 'message.fetch-task',
'message/submitAction': 'message.submit',
'handoff/action': 'handoff.action',
'signin/tokenExchange': 'signin.token-exchange',
Expand Down
2 changes: 1 addition & 1 deletion packages/devtools/src/stores/ChatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const createMessageBase = (
};

const getFeedbackState = (event: ActivityEvent<any>) => ({
feedbackLoopEnabled: event.body.channelData?.feedbackLoopEnabled ? true : false,
feedbackLoopEnabled: !!(event.body.channelData?.feedbackLoop ?? event.body.channelData?.feedbackLoopEnabled),
});

const clearTimer = (timers: Record<string, NodeJS.Timeout>, id: string) => {
Expand Down
Loading