- {this.props.mode === 'livechat' && (
+ {mode === 'livechat' && (
-
+ {debugMode === DebugMode.Normal && (
+
+ )}
+ {/*{debugMode === DebugMode.Sidecar && (*/}
+ {/* */}
+ {/*)}*/}
)}
@@ -370,13 +380,22 @@ export class Emulator extends React.Component
{
break;
}
};
-
- private onExportClick = (): void => {
- if (this.props.document.directLine) {
- CommandServiceImpl.remoteCall(
- SharedConstants.Commands.Emulator.SaveTranscriptToFile,
- this.props.document.directLine.conversationId
- );
+ // Uncomment when ready to export bot state
+ // private onExportBotStateClick = async (): Promise => {
+ // try {
+ // await this.props.exportItems(ValueTypesMask.BotState, this.props.conversationId);
+ // } catch (e) {
+ // const notification = newNotification(e.message);
+ // this.props.createErrorNotification(notification);
+ // }
+ // };
+
+ private onExportTranscriptClick = async (): Promise => {
+ try {
+ await this.props.exportItems(ValueTypesMask.Activity, this.props.conversationId);
+ } catch (e) {
+ const notification = newNotification(e.message);
+ this.props.createErrorNotification(notification);
}
};
diff --git a/packages/app/client/src/ui/editor/emulator/emulatorContainer.ts b/packages/app/client/src/ui/editor/emulator/emulatorContainer.ts
index d58a69b94..5ff098987 100644
--- a/packages/app/client/src/ui/editor/emulator/emulatorContainer.ts
+++ b/packages/app/client/src/ui/editor/emulator/emulatorContainer.ts
@@ -32,6 +32,7 @@
//
import { connect } from 'react-redux';
import { Notification, SharedConstants } from '@bfemulator/app-shared';
+import { ValueTypesMask } from '@bfemulator/app-shared/src';
import { RootState } from '../../../data/store';
import * as PresentationActions from '../../../data/action/presentationActions';
@@ -65,6 +66,8 @@ const mapDispatchToProps = (dispatch): EmulatorProps => ({
createErrorNotification: (notification: Notification) => dispatch(beginAdd(notification)),
trackEvent: (name: string, properties?: { [key: string]: any }) =>
CommandServiceImpl.remoteCall(SharedConstants.Commands.Telemetry.TrackEvent, name, properties).catch(),
+ exportItems: (valueTypes: ValueTypesMask, conversationId: string) =>
+ CommandServiceImpl.remoteCall(SharedConstants.Commands.Emulator.SaveTranscriptToFile, valueTypes, conversationId),
});
export const EmulatorContainer = connect(
diff --git a/packages/app/client/src/ui/helpers/activeBotHelper.ts b/packages/app/client/src/ui/helpers/activeBotHelper.ts
index 4d79d35a5..e180f5b16 100644
--- a/packages/app/client/src/ui/helpers/activeBotHelper.ts
+++ b/packages/app/client/src/ui/helpers/activeBotHelper.ts
@@ -83,7 +83,7 @@ export const ActiveBotHelper = new (class {
/** Sets a bot as active
* @param bot Bot to set as active
*/
- async setActiveBot(bot: BotConfigWithPath): Promise {
+ async setActiveBot(bot: BotConfigWithPath): Promise {
try {
// set the bot as active on the server side
const botDirectory = await CommandServiceImpl.remoteCall(SharedConstants.Commands.Bot.SetActive, bot);
@@ -104,21 +104,19 @@ export const ActiveBotHelper = new (class {
}
/** tell the server-side the active bot is now closed */
- closeActiveBot(): Promise {
- return CommandServiceImpl.remoteCall(Bot.Close)
- .then(() => {
- store.dispatch(BotActions.closeBot());
- CommandServiceImpl.remoteCall(SharedConstants.Commands.Electron.SetTitleBar, '');
- })
- .catch(err => {
- const errMsg = `Error while closing active bot: ${err}`;
- const notification = newNotification(errMsg);
- store.dispatch(beginAdd(notification));
- throw new Error(errMsg);
- });
+ async closeActiveBot(): Promise {
+ try {
+ await CommandServiceImpl.remoteCall(Bot.Close);
+ store.dispatch(BotActions.closeBot());
+ await CommandServiceImpl.remoteCall(SharedConstants.Commands.Electron.SetTitleBar, '');
+ } catch (err) {
+ const errMsg = `Error while closing active bot: ${err}`;
+ const notification = newNotification(errMsg);
+ store.dispatch(beginAdd(notification));
+ }
}
- async botAlreadyOpen(): Promise {
+ async botAlreadyOpen(): Promise {
// TODO - localization
return await CommandServiceImpl.remoteCall(Electron.ShowMessageBox, true, {
buttons: ['OK'],
@@ -131,7 +129,7 @@ export const ActiveBotHelper = new (class {
});
}
- async confirmAndCreateBot(botToCreate: BotConfigWithPath, secret: string): Promise {
+ async confirmAndCreateBot(botToCreate: BotConfigWithPath, secret: string): Promise {
// prompt the user to confirm the switch
const result = await this.confirmSwitchBot();
diff --git a/packages/app/main/src/botHelpers.ts b/packages/app/main/src/botHelpers.ts
index 6c15ca8d7..b4b1976c3 100644
--- a/packages/app/main/src/botHelpers.ts
+++ b/packages/app/main/src/botHelpers.ts
@@ -186,14 +186,9 @@ export async function removeBotFromList(botPath: string): Promise {
}
export function getTranscriptsPath(activeBot: BotConfigWithPath, conversation: Conversation): string {
- if (conversation.mode === 'livechat-url') {
+ if (!activeBot || conversation.mode === 'livechat-url') {
return path.join(electron.app.getPath('downloads'), './transcripts');
}
-
- if (activeBot) {
- const dirName = path.dirname(activeBot.path);
- return path.join(dirName, './transcripts');
- }
-
- return '/';
+ const dirName = path.dirname(activeBot.path);
+ return path.join(dirName, './transcripts');
}
diff --git a/packages/app/main/src/commands/emulatorCommands.spec.ts b/packages/app/main/src/commands/emulatorCommands.spec.ts
index 9d9a4161d..2db6fec29 100644
--- a/packages/app/main/src/commands/emulatorCommands.spec.ts
+++ b/packages/app/main/src/commands/emulatorCommands.spec.ts
@@ -40,6 +40,7 @@ import { BotConfigWithPathImpl, CommandRegistryImpl } from '@bfemulator/sdk-shar
import { BotConfiguration } from 'botframework-config';
import { newBot, newEndpoint, SharedConstants } from '@bfemulator/app-shared';
import { Conversation } from '@bfemulator/emulator-core';
+import { ValueTypesMask } from '@bfemulator/app-shared';
import * as store from '../botData/store';
import { getStore as getSettingsStore } from '../settingsData/store';
@@ -422,7 +423,7 @@ describe('The emulatorCommands', () => {
const patchBotJsonSpy = jest.spyOn((botHelpers as any).default, 'patchBotsJson').mockResolvedValue(true);
const command = mockCommandRegistry.getCommand(SharedConstants.Commands.Emulator.SaveTranscriptToFile);
- await command.handler('1234');
+ await command.handler(ValueTypesMask.Activity, '1234');
expect(getActiveBotSpy).toHaveBeenCalled();
expect(conversationByIdSpy).toHaveBeenCalledWith('1234');
diff --git a/packages/app/main/src/commands/emulatorCommands.ts b/packages/app/main/src/commands/emulatorCommands.ts
index 082e4de2d..4667b8679 100644
--- a/packages/app/main/src/commands/emulatorCommands.ts
+++ b/packages/app/main/src/commands/emulatorCommands.ts
@@ -59,7 +59,7 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) {
// Saves the conversation to a transcript file, with user interaction to set filename.
commandRegistry.registerCommand(
Commands.SaveTranscriptToFile,
- async (conversationId: string): Promise => {
+ async (valueTypes: number, conversationId: string): Promise => {
const activeBot: BotConfigWithPath = getActiveBot();
const conversation = Emulator.getInstance().framework.server.botEmulator.facilities.conversations.conversationById(
conversationId
@@ -85,7 +85,7 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) {
if (filename && filename.length) {
mkdirpSync(path.dirname(filename));
- const transcripts = await conversation.getTranscript();
+ const transcripts = await conversation.getTranscript(valueTypes);
writeFile(filename, transcripts);
TelemetryService.trackEvent('transcript_save');
}
diff --git a/packages/app/shared/src/enums/ValueTypes.ts b/packages/app/shared/src/enums/ValueTypes.ts
index f698a8ede..b33378e85 100644
--- a/packages/app/shared/src/enums/ValueTypes.ts
+++ b/packages/app/shared/src/enums/ValueTypes.ts
@@ -37,3 +37,17 @@ export enum ValueTypes {
Error = 'https://www.botframework.com/schemas/error',
Activity = 'https://www.botframework.com/schemas/activity',
}
+
+export class ValueTypesMask {
+ public static [ValueTypes.BotState] = 0b1;
+ public static [ValueTypes.Debug] = 0b10;
+ public static [ValueTypes.Diff] = 0b100;
+ public static [ValueTypes.Error] = 0b1000;
+ public static [ValueTypes.Activity] = 0b10000;
+ public static BotState = 0b1;
+ public static Debug = 0b10;
+ public static Diff = 0b100;
+ public static Error = 0b1000;
+ public static Activity = 0b10000;
+ private constructor() {}
+}
diff --git a/packages/emulator/core/src/directLine/middleware/startConversation.ts b/packages/emulator/core/src/directLine/middleware/startConversation.ts
index 0de95ae7b..7ac27f57c 100644
--- a/packages/emulator/core/src/directLine/middleware/startConversation.ts
+++ b/packages/emulator/core/src/directLine/middleware/startConversation.ts
@@ -65,9 +65,8 @@ export default function startConversation(botEmulator: BotEmulator) {
// Sends "user added to conversation"
await conversation.sendConversationUpdate([currentUser], undefined);
created = true;
- } else {
- const botIsNotInConversation = conversation.members.findIndex(user => user.id === botEndpoint.botId) === -1;
- if (botEndpoint && botIsNotInConversation) {
+ } else if (botEndpoint && !conversationId.endsWith('transcript')) {
+ if (conversation.members.findIndex(user => user.id === botEndpoint.botId) === -1) {
// Adds bot to conversation and sends "bot added to conversation"
conversation.addMember(botEndpoint.botId, 'Bot');
} else {
diff --git a/packages/emulator/core/src/facility/conversation.ts b/packages/emulator/core/src/facility/conversation.ts
index 32b5e4a5e..859d34d39 100644
--- a/packages/emulator/core/src/facility/conversation.ts
+++ b/packages/emulator/core/src/facility/conversation.ts
@@ -42,6 +42,8 @@ import {
externalLinkItem,
isLocalHostUrl,
LogLevel,
+ networkRequestItem,
+ networkResponseItem,
PaymentOperations,
PaymentRequest,
PaymentRequestComplete,
@@ -54,14 +56,14 @@ import {
import {
Activity,
Attachment,
+ ChannelAccount,
ConversationAccount,
IContactRelationUpdateActivity,
IInvokeActivity,
IMessageActivity,
- ChannelAccount,
} from 'botframework-schema';
-import { networkRequestItem, networkResponseItem } from '@bfemulator/sdk-shared';
import { ChatMode } from '@bfemulator/app-shared';
+import { ValueTypesMask } from '@bfemulator/app-shared';
import { BotEmulator } from '../botEmulator';
import { TokenCache } from '../userToken/tokenCache';
@@ -667,10 +669,23 @@ export default class Conversation extends EventEmitter {
});
}
- public async getTranscript(): Promise {
- // Currently, we only export transcript of activities
- // TODO: Think about "member join/left", "typing", "activity update/delete", etc.
- const activities = this.transcript.filter(record => record.type === 'activity add').map(record => record.activity);
+ /**
+ * Gets the transcript, extracting values based on
+ * the (optional) valueTypesToExtract bitmask. If the valueTypesToExtract is
+ * not included in the bitmask, the entire activity is
+ * included. Otherwise, the value of the activity is extracted.
+ *
+ * @param valueTypesToExtract a bitmask representing the value Types to extract from the activity
+ */
+ public async getTranscript(valueTypesToExtract: number = 0): Promise {
+ const activities = this.transcript
+ .filter(record => record.type === 'activity add')
+ .map(record => {
+ const { activity } = record;
+ const extractValue =
+ valueTypesToExtract && activity.valueType && !!(valueTypesToExtract & ValueTypesMask[activity.valueType]); // bitwise intentional
+ return extractValue ? activity.value : activity;
+ });
for (let i = 0; i < activities.length; i++) {
await this.processActivityForDataUrls(activities[i]);
}