Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit 1939d0b

Browse files
authored
Merge pull request #112 from microsoft/chatdown-util-refactor
Making read file and read stdin data available through command class
2 parents 6d795cb + ea390d5 commit 1939d0b

File tree

10 files changed

+422
-72
lines changed

10 files changed

+422
-72
lines changed

packages/chatdown/README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,13 @@ USAGE
3838
$ bf chatdown
3939
4040
OPTIONS
41-
-c, --chat=chat The path of the chat file to be parsed. If omitted, stdin will be used.
42-
43-
-f, --folder=folder Path to directory and/or all subdirectories containing chat files to be processed all at
44-
once, ex. ./**/*.chat. If an output directory is not present (-o), it will default the
45-
output to the current working directory.
41+
-i, --in=in The path of the chat file or directory to be parsed. A glob expression may be passed containing
42+
chat files to be processed all at once, ex. ./**/*.chat. If flag is omitted, stdin will be used.
43+
If an output directory is not present (-o), it will default the output to the current working directory.
4644
4745
-h, --help Chatdown command help
4846
49-
-o, --out_folder=out_folder Path to the directory where the output of the multiple chat file processing (-f) will be
47+
-o, --out=out Path to the directory where the output of the multiple chat file processing (-f) will be
5048
placed.
5149
5250
-p, --prefix Prefix stdout with package name.
@@ -56,8 +54,8 @@ OPTIONS
5654
EXAMPLE
5755
5856
$ bf chatdown
59-
$ bf chatdown --chat=./path/to/file/sample.chat
60-
$ bf chatdown -f ./test/utils/*.sample.chat -o ./
57+
$ bf chatdown --in=./path/to/file/sample.chat
58+
$ bf chatdown --in ./test/utils/*.sample.chat -o ./
6159
$ (echo user=Joe && [ConversationUpdate=MembersAdded=Joe]) | bf chatdown --static
6260
```
6361

packages/chatdown/cli.transcript

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
[
2+
{
3+
"conversation": {
4+
"id": "4cGMIt3k9czIfv/SOnqxgNNY+LU="
5+
},
6+
"id": "1",
7+
"recipient": {
8+
"id": "feCFNCKOpzAtVMMV7Mm1l+GUZt0=",
9+
"name": "LulaBot",
10+
"role": "bot"
11+
},
12+
"from": {
13+
"id": "iWYRSfG2L/R91ab+T5ecn1P2GbY=",
14+
"name": "Joe",
15+
"role": "user"
16+
},
17+
"timestamp": "2019-09-06T22:51:37.100Z",
18+
"type": "conversationUpdate",
19+
"channelId": "chatdown",
20+
"membersAdded": [
21+
{
22+
"id": "feCFNCKOpzAtVMMV7Mm1l+GUZt0=",
23+
"name": "LulaBot",
24+
"role": "bot"
25+
}
26+
],
27+
"membersRemoved": []
28+
},
29+
{
30+
"conversation": {
31+
"id": "4cGMIt3k9czIfv/SOnqxgNNY+LU="
32+
},
33+
"id": "2",
34+
"text": "[ConversationUpdate:\r\n membersadded=Joe,Susan]",
35+
"timestamp": "2019-09-06T22:51:39.100Z",
36+
"type": "message",
37+
"channelId": "chatdown"
38+
},
39+
{
40+
"conversation": {
41+
"id": "4cGMIt3k9czIfv/SOnqxgNNY+LU="
42+
},
43+
"id": "3",
44+
"recipient": {
45+
"id": "iWYRSfG2L/R91ab+T5ecn1P2GbY=",
46+
"name": "Joe",
47+
"role": "user"
48+
},
49+
"from": {
50+
"id": "feCFNCKOpzAtVMMV7Mm1l+GUZt0=",
51+
"name": "LulaBot",
52+
"role": "bot"
53+
},
54+
"text": "Hi Joe!\r\nLulaBot->Susan: Hi Susan!\r\n\r\n[ConversationUpdate:membersremoved=Susan]",
55+
"timestamp": "2019-09-06T22:51:41.100Z",
56+
"type": "message",
57+
"channelId": "chatdown"
58+
},
59+
{
60+
"conversation": {
61+
"id": "4cGMIt3k9czIfv/SOnqxgNNY+LU="
62+
},
63+
"id": "4",
64+
"recipient": {
65+
"id": "feCFNCKOpzAtVMMV7Mm1l+GUZt0=",
66+
"name": "LulaBot",
67+
"role": "bot"
68+
},
69+
"from": {
70+
"id": "iWYRSfG2L/R91ab+T5ecn1P2GbY=",
71+
"name": "Joe",
72+
"role": "user"
73+
},
74+
"text": "Hi!",
75+
"timestamp": "2019-09-06T22:51:43.100Z",
76+
"type": "message",
77+
"channelId": "chatdown"
78+
},
79+
{
80+
"conversation": {
81+
"id": "4cGMIt3k9czIfv/SOnqxgNNY+LU="
82+
},
83+
"id": "5",
84+
"recipient": {
85+
"id": "iWYRSfG2L/R91ab+T5ecn1P2GbY=",
86+
"name": "Joe",
87+
"role": "user"
88+
},
89+
"from": {
90+
"id": "feCFNCKOpzAtVMMV7Mm1l+GUZt0=",
91+
"name": "LulaBot",
92+
"role": "bot"
93+
},
94+
"text": "Hello there!",
95+
"timestamp": "2019-09-06T22:51:45.100Z",
96+
"type": "message",
97+
"channelId": "chatdown"
98+
},
99+
{
100+
"conversation": {
101+
"id": "4cGMIt3k9czIfv/SOnqxgNNY+LU="
102+
},
103+
"id": "7",
104+
"recipient": {
105+
"id": "iWYRSfG2L/R91ab+T5ecn1P2GbY=",
106+
"name": "Joe",
107+
"role": "user"
108+
},
109+
"from": {
110+
"id": "feCFNCKOpzAtVMMV7Mm1l+GUZt0=",
111+
"name": "LulaBot",
112+
"role": "bot"
113+
},
114+
"timestamp": "2019-09-06T22:51:45.200Z",
115+
"type": "typing",
116+
"channelId": "chatdown"
117+
},
118+
{
119+
"conversation": {
120+
"id": "4cGMIt3k9czIfv/SOnqxgNNY+LU="
121+
},
122+
"id": "8",
123+
"recipient": {
124+
"id": "iWYRSfG2L/R91ab+T5ecn1P2GbY=",
125+
"name": "Joe",
126+
"role": "user"
127+
},
128+
"from": {
129+
"id": "feCFNCKOpzAtVMMV7Mm1l+GUZt0=",
130+
"name": "LulaBot",
131+
"role": "bot"
132+
},
133+
"text": "# cheese choices\r\n1. cheese 1 \r\n2. cheese 2\r\n3. cheese 3",
134+
"timestamp": "2019-09-06T22:51:52.200Z",
135+
"type": "message",
136+
"channelId": "chatdown",
137+
"suggestedActions": {
138+
"actions": [
139+
{
140+
"title": "option 1",
141+
"type": "imBack",
142+
"value": "option 1"
143+
},
144+
{
145+
"title": "option 2",
146+
"type": "imBack",
147+
"value": "option 2"
148+
},
149+
{
150+
"title": "option 3",
151+
"type": "imBack",
152+
"value": "option 3"
153+
}
154+
]
155+
}
156+
}
157+
]

packages/chatdown/src/commands/chatdown.ts

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
11
import {flags} from '@oclif/command'
2-
import {CLIError, Command} from '@microsoft/bf-cli-command'
3-
2+
import {CLIError, Command, utils} from '@microsoft/bf-cli-command'
43
const chalk = require('chalk')
54
const chatdown = require('../../utils/index')
65
const fs = require('fs-extra')
76
const glob = require('glob')
87
const intercept = require('intercept-stdout')
98
const path = require('path')
10-
const txtfile = require('../../utils/read-text-file')
11-
const piped = require('../../utils/read-piped-data')
129

1310
export default class Chatdown extends Command {
1411
static description = 'Converts chat dialog files in <filename>.chat format into transcript file. Writes corresponding <filename>.transcript for each .chat file'
1512

1613
static examples = [`
1714
$ bf chatdown
18-
$ bf chatdown --chat=./path/to/file/sample.chat
19-
$ bf chatdown -f ./test/utils/*.sample.chat -o ./
15+
$ bf chatdown --in=./path/to/file/sample.chat
16+
$ bf chatdown --in ./test/utils/*.sample.chat -o ./
2017
$ (echo user=Joe && [ConversationUpdate=MembersAdded=Joe]) | bf chatdown --static`]
2118

2219
static flags = {
23-
chat: flags.string({char: 'c', description: 'The path of the chat file to be parsed. If omitted, stdin will be used.'}),
24-
folder: flags.string({char: 'f', description: 'Path to directory and/or all subdirectories containing chat files to be processed all at once, ex. ./**/*.chat. If an output directory is not present (-o), it will default the output to the current working directory. '}),
25-
out_folder: flags.string({char: 'o', description: 'Path to the directory where the output of the multiple chat file processing (-f) will be placed.'}),
20+
in: flags.string({char: 'i', description: 'The path of the chat file or directory to be parsed. A glob expression may be passed containing chat files to be processed all at once, ex. ./**/*.chat. If flag is omitted, stdin will be used. If an output directory is not present (-o), it will default the output to the current working directory.'}),
21+
out: flags.string({char: 'o', description: 'Path to the directory where the output of the multiple chat file processing (-f) will be placed.'}),
2622
static: flags.boolean({char: 's', description: 'Use static timestamps when generating timestamps on activities.'}),
2723
prefix: flags.boolean({char: 'p', description: 'Prefix stdout with package name.'}),
2824
help: flags.help({char: 'h', description: 'Chatdown command help'})
@@ -32,38 +28,41 @@ export default class Chatdown extends Command {
3228
try {
3329
const {flags} = this.parse(Chatdown)
3430

31+
let inputIsDirectory = flags.in ? (flags.in.includes('*') || this.isDir(flags.in)) : false
32+
3533
if (flags.prefix) {
3634
const pkgName = this.config.name
3735
intercept(function (txt: any) {
3836
return `[${pkgName}]\n${txt}`
3937
})
4038
}
4139

42-
if (flags.folder) {
43-
let inputDir = flags.folder.trim()
44-
let outputDir = (flags.out_folder) ? flags.out_folder.trim() : './'
40+
if (inputIsDirectory) {
41+
let inputDir = flags.in ? flags.in.trim() : ''
42+
let outputDir = (flags.out) ? flags.out.trim() : './'
4543
if (outputDir.substr(0, 2) === './') {
4644
outputDir = path.resolve(process.cwd(), outputDir.substr(2))
4745
}
4846
const len = await this.processFiles(inputDir, outputDir)
4947
if (len === 0) {
50-
throw new CLIError('No chat files found at: ' + flags.folder)
48+
throw new CLIError('No chat files found at: ' + flags.in)
5149
}
5250
this.log(chalk.green(`Successfully wrote ${len} files\n`))
5351
return
54-
}
55-
56-
let fileContents = await this.getInput(flags.chat)
57-
if (fileContents) {
58-
const activities = await chatdown(fileContents, flags)
59-
const writeConfirmation = await this.writeOut(activities)
60-
/* tslint:disable:strict-type-predicates */
61-
if (typeof writeConfirmation === 'string') {
62-
process.stdout.write(`${chalk.green('Successfully wrote file:')} ${writeConfirmation}\n`)
63-
}
6452
} else {
65-
return this._help()
53+
const fileContents = await this.getInput(flags.in)
54+
if (fileContents) {
55+
const activities = await chatdown(fileContents, flags)
56+
const writeConfirmation = await this.writeOut(activities)
57+
/* tslint:disable:strict-type-predicates */
58+
if (typeof writeConfirmation === 'string') {
59+
process.stdout.write(`${chalk.green('Successfully wrote file:')} ${writeConfirmation}\n`)
60+
}
61+
} else {
62+
return this._help()
63+
}
6664
}
65+
6766
} catch (err) {
6867
if (err.message.match(/Malformed configurations options detected/)) {
6968
throw new CLIError(err.message)
@@ -72,28 +71,23 @@ export default class Chatdown extends Command {
7271
}
7372
}
7473

74+
private readonly isDir = (path: string) => {
75+
const stats = fs.statSync(path)
76+
return stats.isDirectory()
77+
}
78+
7579
private async getInput(args: any) {
76-
try {
77-
// Check if path passed in --chat
78-
if (args && args.length > 0) {
79-
return txtfile.readSync(args)
80-
} else {
81-
//Check if piped data was sent
82-
const {stdin} = process
83-
if (stdin.isTTY) {
84-
return false
85-
} else {
86-
return await piped.readStdin()
87-
}
88-
}
89-
} catch (err) {
90-
if (err.message.match(/no such file or directory/)) {
91-
throw new CLIError(err.message)
92-
}
93-
if (err.message.match(/No Input/)) {
80+
// Check if path passed in --in
81+
if (args && args.length > 0) {
82+
return utils.readTextFile(args)
83+
} else {
84+
//Check if piped data was sent
85+
const {stdin} = process
86+
if (stdin.isTTY) {
9487
return false
88+
} else {
89+
return this.readStdin()
9590
}
96-
throw err
9791
}
9892
}
9993

@@ -108,7 +102,7 @@ export default class Chatdown extends Command {
108102
fileName = files[i].substr(files[i].lastIndexOf('/'))
109103
}
110104
fileName = fileName.split('.')[0]
111-
let activities = await chatdown(txtfile.readSync(files[i]))
105+
let activities = await chatdown(await utils.readTextFile(files[i]))
112106
let writeFile = `${outputDir}/${fileName}.transcript`
113107
await fs.ensureFile(writeFile)
114108
await fs.writeJson(writeFile, activities, {spaces: 2})

packages/chatdown/test/commands/chatdown.test.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ describe('chatdown', () => {
2222
});
2323
});
2424

25+
it('should output an error if no stdin data passed', done => {
26+
cp.exec(`node ./bin/run chatdown`, (error, stdout, stderr) => {
27+
assert(stderr.includes('No input'));
28+
done();
29+
});
30+
});
31+
2532
it('should throw when a malformed config options is encountered in the input', done => {
2633
cp.exec(`echo bot=LuliBot=joe | node ./bin/run chatdown`, (error, stdout, stderr) => {
2734
assert(stderr.trim().indexOf('Malformed configurations options detected. Options must be in the format optionName=optionValue') >= 0);
@@ -37,43 +44,43 @@ describe('chatdown', () => {
3744
});
3845

3946
it('should read from file when chat file is passed as an argument', done => {
40-
cp.exec(`node ./bin/run chatdown --chat ${path.join(__dirname, '../utils/cli.sample.chat')}`, (error, stdout) => {
47+
cp.exec(`node ./bin/run chatdown --in ${path.join(__dirname, '../utils/cli.sample.chat')}`, (error, stdout) => {
4148
assert.doesNotThrow(() => JSON.parse(stdout));
4249
done();
4350
});
4451
});
4552

46-
it('should process all files when a glob is passed in with the -f argument, and the -o is passed in for the output directory', done => {
47-
cp.exec(`node ./bin/run chatdown -f ./test/utils/*.sample.chat -o ./`, (error, stdout, stderr) => {
53+
it('should process all files when a glob is passed in with the -i argument, and the -o is passed in for the output directory', done => {
54+
cp.exec(`node ./bin/run chatdown -i ./test/utils/*.sample.chat -o ./`, (error, stdout, stderr) => {
4855
assert(stdout.includes('Successfully wrote'));
4956
done();
5057
});
5158
});
5259

53-
it('should process all files when a glob is passed in with the -f argument', done => {
54-
cp.exec(`node ./bin/run chatdown -f ./test/utils/*.sample.chat`, (error, stdout, stderr) => {
60+
it('should process all files when a glob is passed in with the -i argument', done => {
61+
cp.exec(`node ./bin/run chatdown -i ./test/utils/*.sample.chat`, (error, stdout, stderr) => {
5562
assert(stdout.includes('Successfully wrote'));
5663
done();
5764
});
5865
});
5966

6067
it('should not prefix [chatdown] to stdout when --prefix is not passed as an argument', done => {
61-
cp.exec(`echo bot=LuliBot=joe | node ./bin/run chatdown --prefix`, (error, stdout, stderr) => {
68+
cp.exec(`echo bot=LuliBot | node ./bin/run chatdown`, (error, stdout, stderr) => {
6269
assert.notEqual(stdout.startsWith(`[${pkg.name}]`), `It should not show the tag '[${pkg.name}]' when not using the argument --prefix`);
6370
done();
6471
});
6572
});
6673

6774
it('should prefix [chatdown] to stderr when --prefix is passed as an argument', done => {
68-
cp.exec(`echo bot=LuliBot=joe | node ./bin/run chatdown --prefix`, (error, stdout, stderr) => {
69-
assert(stderr.startsWith(`[${pkg.name}]`), `It should show the tag '[${pkg.name}]' when using the argument --prefix`);
75+
cp.exec(`echo bot=LuliBot | node ./bin/run chatdown --prefix`, (error, stdout, stderr) => {
76+
assert(stdout.startsWith(`[${pkg.name}]`), `It should show the tag '[${pkg.name}]' when using the argument --prefix`);
7077
done();
7178
});
7279
});
7380

7481
it('throw error if invalid path in argument', done => {
75-
cp.exec(`node ./bin/run chatdown --chat aaaaa`, (error, stdout, stderr) => {
76-
assert(stderr.includes('no such file or directory'));
82+
cp.exec(`node ./bin/run chatdown --in aaaaa`, (error, stdout, stderr) => {
83+
assert(stderr.includes('no such file or directory') || stderr.includes('error'));
7784
done();
7885
});
7986
});

0 commit comments

Comments
 (0)