Skip to content

Commit 73c78cf

Browse files
committed
Support command parse
1 parent 9d1b047 commit 73c78cf

9 files changed

+941
-785
lines changed

README.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ function eventHandler ({
1515
group, // the group object, can get chat group id from group.id
1616
userId, // message creator's id
1717
isPrivateChat, // if it is a private chat
18-
message // message object, check ringcentral api document for detail
18+
message, // message object, check ringcentral api document for detail
19+
commandLineOptions, // only if set commandLineConfigs in botConfig, would get parse result from text, check doc/command-line-parser.md for detail
1920
}) {
2021
console.log(
2122
type,
@@ -71,7 +72,18 @@ const botConfig = {
7172
botRoute: '/bot', // optional
7273
models: { // optional
7374
Bot: 'your bot data model defination' // check src/models/Bot.ts as a example, optional
75+
},
76+
commandLineConfigs: { // optional
77+
{
78+
command: 'add'
79+
options: [
80+
['-f, --float <number>', 'float argument'],
81+
['-i, --integer <number>', 'integer argument'],
82+
['-v, --verbose', 'verbosity that can be increased']
83+
]
84+
}
7485
}
86+
// if set commandLineConfigs in botConfig, would get parsed commandLineOptions object from text, check doc/command-line-parser.md for detail(use commander module)
7587
}
7688

7789
let app = express()

doc/command-line-parser.md

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
```js
2+
function myParseInt(value, dummyPrevious) {
3+
// parseInt takes a string and a radix
4+
const parsedValue = parseInt(value, 10);
5+
if (isNaN(parsedValue)) {
6+
throw new commander.InvalidArgumentError('Not a number.');
7+
}
8+
return parsedValue;
9+
}
10+
11+
function increaseVerbosity(dummyValue, previous) {
12+
return previous + 1;
13+
}
14+
15+
function collect(value, previous) {
16+
return previous.concat([value]);
17+
}
18+
19+
function commaSeparatedList(value, dummyPrevious) {
20+
return value.split(',');
21+
}
22+
23+
const botConfig = {
24+
adminRoute: '/admin', // optional
25+
botRoute: '/bot', // optional
26+
models: { // optional
27+
Bot: 'your bot data model defination' // check src/models/Bot.ts as a example, optional
28+
},
29+
commandLineConfigs: { // optional
30+
{
31+
command: 'adjust'
32+
options: [
33+
['-f, --float <number>', 'float argument', parseFloat],
34+
['-i, --integer <number>', 'integer argument', myParseInt],
35+
['-v, --verbose', 'verbosity that can be increased', increaseVerbosity, 0],
36+
['-c, --collect <value>', 'repeatable value', collect, []],
37+
['-l, --list <items>', 'comma separated list', commaSeparatedList]
38+
]
39+
},
40+
{
41+
command: 'list' // this command no option
42+
},
43+
{
44+
command: 'add' // this command no option
45+
}
46+
}
47+
// if set commandLineConfigs in botConfig, would get parsed options object from text, check doc/command-line-parser.md for detail
48+
}
49+
50+
51+
function eventHandler ({
52+
type, // could be 'BotRemoved', 'Message4Bot', 'Message4Others', 'BotGroupLeft', 'BotJoinGroup', 'Maintain', 'SetupDatabase'
53+
bot, // the bot instance, check src/models/Bot.ts for instance methods
54+
text, // the text message user posted in chat group
55+
group, // the group object, can get chat group id from group.id
56+
userId, // message creator's id
57+
isPrivateChat, // if it is a private chat
58+
message, // message object, check ringcentral api document for detail
59+
commandLineOptions, // only if set commandLineConfigs in botConfig, would get parse result from text, check doc/command-line-parser.md for detail
60+
}) {
61+
console.log(
62+
type,
63+
bot,
64+
text,
65+
group,
66+
userId,
67+
message
68+
)
69+
// when text === 'list'
70+
expect(commandLineOptions).tobeEqual({
71+
command: 'list',
72+
rest: ''
73+
})
74+
// when text = 'add more text desc'
75+
expect(commandLineOptions).tobeEqual({
76+
command: 'add',
77+
rest: 'more text desc',
78+
options: {
79+
verbose: 0,
80+
collect: []
81+
}
82+
})
83+
// when text = 'adjust -f 5.8 -i 5654 some other not related text '
84+
expect(commandLineOptions).tobeEqual({
85+
command: 'add',
86+
rest: ' -f 5.8 -i 5654 some other not related text ',
87+
options: {
88+
verbose: 0,
89+
collect: [],
90+
integer: 5654,
91+
float: 5.8
92+
}
93+
})
94+
}
95+
```

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ringcentral-chatbot-core",
3-
"version": "1.4.0",
3+
"version": "1.5.1",
44
"license": "MIT",
55
"scripts": {
66
"test": "RINGCENTRAL_CHATBOT_DATABASE_CONNECTION_URI=sqlite:// jest -c test/jest.config.ts",
@@ -13,6 +13,7 @@
1313
"@types/supertest": "^2.0.11",
1414
"@types/validator": "^13.6.3",
1515
"axios": "^0.24.0",
16+
"commander": "^9.2.0",
1617
"dynamo-sequelize": "^3.0.0",
1718
"express": "^4.17.1",
1819
"express-basic-auth": "^1.2.0",
@@ -28,10 +29,10 @@
2829
"jest": "^27.0.6",
2930
"ramda": "^0.27.1",
3031
"sqlite3": "^5.0.2",
32+
"supertest": "^6.1.4",
3133
"ts-jest": "^27.0.4",
3234
"ts-node": "^10.1.0",
3335
"typescript": "^4.3.5",
34-
"supertest": "^6.1.4",
3536
"yarn-upgrade-all": "^0.5.4"
3637
},
3738
"files": [

src/apps/bot.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const createApp = (handle: Function, conf: BotConfig) => {
3434
break;
3535
}
3636
case 'PostAdded': {
37-
const result = await postAdded(Bot, message);
37+
const result = await postAdded(Bot, message, conf);
3838
if (result) {
3939
await handle({type: 'Message4Bot', ...result});
4040
}

src/handlers/command-line-parser.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* parse text to command line
3+
*/
4+
5+
import { program } from 'commander'
6+
7+
export default (text: string, commandLineConfigs: any []): any => {
8+
const arr = text.split(' ')
9+
const cmd: any = arr[0]
10+
if (!cmd) {
11+
return {}
12+
}
13+
const conf: any = commandLineConfigs.find(c => c.command === cmd)
14+
if (!conf) {
15+
return {}
16+
}
17+
const rest = arr.slice(1)
18+
const res: any = {
19+
command: cmd,
20+
rest: rest.join(' ')
21+
}
22+
if (conf.options && rest.length) {
23+
for (const opt of conf.options) {
24+
(program.option as any)(...opt)
25+
}
26+
program.parse(rest, { from: 'user' })
27+
const options = program.opts()
28+
res.options = options
29+
}
30+
return res
31+
}

src/handlers/postAdded.ts

+22-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import axios from 'axios';
2+
import {BotType, Message, BotConfig} from '../types';
3+
import commandParser from './command-line-parser'
24

3-
import {BotType, Message} from '../types';
4-
5-
export const postAdded = async (Bot: any, message: Message) => {
6-
let text = message.body.text;
7-
if (!text) {
5+
export const postAdded = async (Bot: any, message: Message, conf: BotConfig) => {
6+
const originalText = message.body.text;
7+
if (!originalText) {
88
return; // not a text message
99
}
1010
const botId = message.ownerId;
@@ -25,7 +25,7 @@ export const postAdded = async (Bot: any, message: Message) => {
2525
!message.body.mentions.some(m => m.type === 'Person' && m.id === botId))
2626
) {
2727
return {
28-
text,
28+
text: originalText,
2929
group,
3030
bot,
3131
userId,
@@ -35,7 +35,7 @@ export const postAdded = async (Bot: any, message: Message) => {
3535
};
3636
}
3737
const regex = new RegExp(`!\\[:Person\\]\\(${bot.id}\\)`);
38-
text = text.replace(regex, ' ').trim();
38+
const text = originalText.replace(regex, ' ').trim();
3939
if (text.startsWith('__rename__')) {
4040
await bot.rename(text.substring(10).trim());
4141
return;
@@ -55,5 +55,19 @@ export const postAdded = async (Bot: any, message: Message) => {
5555
await bot.updateToken(text.substring(15).trim());
5656
return;
5757
}
58-
return {text, group, bot, userId, isPrivateChat, message: message.body};
58+
const result: any = {
59+
originalText,
60+
text,
61+
group,
62+
bot,
63+
userId,
64+
isPrivateChat,
65+
message:
66+
message.body
67+
};
68+
if (conf.commandLineConfigs) {
69+
const commandLineOptions = commandParser(text, conf.commandLineConfigs)
70+
result.commandLineOptions = commandLineOptions
71+
}
72+
return result
5973
};

src/index.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ export const extendApp = (
1313
adminRoute: '/admin',
1414
botRoute: '/bot',
1515
models: {}
16-
}
16+
},
17+
commandLineConfigs?: any [],
1718
) => {
1819
const conf = {
20+
commandLineConfigs,
1921
...config,
2022
models: Object.assign({}, defaultModels, config.models || {})
2123
}
@@ -42,12 +44,12 @@ export const extendApp = (
4244

4345
if (process.env.RINGCENTRAL_CHATBOT_ADMIN_USERNAME && process.env.RINGCENTRAL_CHATBOT_ADMIN_PASSWORD) {
4446
app.use(
45-
config.adminRoute,
47+
conf.adminRoute,
4648
adminApp(mergedHandle, conf)
4749
);
4850
}
4951
app.use(
50-
config.botRoute,
52+
conf.botRoute,
5153
botApp(mergedHandle, conf)
5254
);
5355
for (const skill of skills) {

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export type AttachmentType = {
4444
};
4545

4646
export interface BotConfig {
47+
commandLineConfigs?: any [],
4748
adminRoute: string,
4849
botRoute: string,
4950
models?: any,

0 commit comments

Comments
 (0)