Skip to content

Commit

Permalink
fix(teach): fix regexp matching for certain questions, fix #249
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jul 17, 2021
1 parent 616142b commit 20e6522
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 239 deletions.
2 changes: 2 additions & 0 deletions packages/plugin-eval/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ describe('Eval Plugin', () => {
await ses.shouldReply('demo', '123')
await ses.shouldReply('# ^repeat:(.+) ${"$1".repeat(3)} -x', '问答已添加,编号为 2。')
await ses.shouldReply('repeat:123', '123123123')
await ses.shouldReply('# ^我.+ 对,${"$0".replace(/我/, "你")} -x', '问答已添加,编号为 3。')
await ses.shouldReply('我是伞兵', '对,你是伞兵')
})

it('global', async () => {
Expand Down
33 changes: 14 additions & 19 deletions packages/plugin-teach/src/database/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,29 +70,24 @@ export default function apply(ctx: Context) {
ctx.on('dialogue/mongo', ({ regexp, answer, question, original }, conditionals) => {
if (regexp) {
if (answer) conditionals.push({ answer: { $regex: new RegExp(answer, 'i') } })
if (question) conditionals.push({ question: { $regex: new RegExp(original, 'i') } })
if (original) conditionals.push({ original: { $regex: new RegExp(original, 'i') } })
return
}
if (answer) conditionals.push({ answer })
if (question) {
if (regexp === false) {
conditionals.push({ question })
} else {
const $expr = {
body(field: string, question: string, original: string) {
const regex = new RegExp(field, 'i')
return regex.test(question) || regex.test(original)
},
args: ['$name', question, original],
lang: 'js',
}
conditionals.push({
$or: [
{ flag: { $bitsAllClear: Dialogue.Flag.regexp }, question },
{ flag: { $bitsAllSet: Dialogue.Flag.regexp }, $expr },
],
})
if (regexp === false) {
if (question) conditionals.push({ question })
} else if (original) {
const $expr = {
body(field: string, original: string) {
const regex = new RegExp(field, 'i')
return regex.test(original)
},
args: ['$name', original],
lang: 'js',
}
const conds = [{ flag: { $bitsAllSet: Dialogue.Flag.regexp }, $expr } as FilterQuery<Dialogue>]
if (question) conds.push({ flag: { $bitsAllClear: Dialogue.Flag.regexp }, question })
conditionals.push({ $or: conds })
}
})

Expand Down
19 changes: 7 additions & 12 deletions packages/plugin-teach/src/database/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,17 @@ export default function apply(ctx: Context) {

if (regexp) {
if (answer) conditionals.push('`answer` REGEXP ' + escape(answer))
if (question) conditionals.push('`question` REGEXP ' + escape(original))
if (original) conditionals.push('`original` REGEXP ' + escape(original))
return
}

if (answer) conditionals.push('`answer` = ' + escape(answer))
if (question) {
if (regexp === false) {
conditionals.push('`question` = ' + escape(question))
} else {
conditionals.push(`(\
!(\`flag\` & ${Dialogue.Flag.regexp}) && \`question\` = ${escape(question)} ||\
\`flag\` & ${Dialogue.Flag.regexp} && (\
${escape(question)} REGEXP \`question\` || ${escape(original)} REGEXP \`question\`\
)\
)`)
}
if (regexp === false) {
if (question) conditionals.push('`question` = ' + escape(question))
} else if (original) {
const conds = [`\`flag\` & ${Dialogue.Flag.regexp} && ${escape(original)} REGEXP \`original\``]
if (question) conds.push(`!(\`flag\` & ${Dialogue.Flag.regexp}) && \`question\` = ${escape(question)}`)
conditionals.push(`(${conds.join(' || ')})`)
}
})

Expand Down
6 changes: 3 additions & 3 deletions packages/plugin-teach/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ export default function apply(ctx: Context, config: Dialogue.Config) {
} else if (/\[CQ:(?!face)/.test(question)) {
return template('teach.prohibited-cq-code')
}
const { unprefixed, prefixed, appellative } = options.regexp
? { unprefixed: question, prefixed: question, appellative: false }
const { unprefixed, appellative } = options.regexp
? { unprefixed: question, appellative: false }
: config._stripQuestion(question)
defineProperty(options, 'appellative', appellative)
defineProperty(options, '_original', prefixed)
defineProperty(options, '_original', question)
defineProperty(options, 'original', question)
args[0] = unprefixed
args[1] = answer
Expand Down
48 changes: 18 additions & 30 deletions packages/plugin-teach/src/receiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ tokenizer.interpolate('$n', '', (rest) => {
return { rest, tokens: [], source: '' }
})

const halfWidth = ',,.~?!()[]'
const fullWidth = ',、。~?!()【】'
const fullWidthRegExp = new RegExp(`[${fullWidth}]`)

export async function triggerDialogue(ctx: Context, session: Session, next: NextFunction = noop) {
const state = ctx.getSessionState(session)
state.next = next
Expand Down Expand Up @@ -203,8 +207,7 @@ export async function triggerDialogue(ctx: Context, session: Session, next: Next
.replace(/\$0/g, escapeAnswer(session.content))

if (dialogue.flag & Dialogue.Flag.regexp) {
const capture = dialogue._capture || new RegExp(dialogue.question, 'i').exec(state.test.question)
if (!capture) console.log(dialogue.question, state.test.question)
const capture = dialogue._capture || new RegExp(dialogue.original, 'i').exec(state.test.original)
capture.map((segment, index) => {
if (index && index <= 9) {
state.answer = state.answer.replace(new RegExp(`\\$${index}`, 'g'), escapeAnswer(segment || ''))
Expand Down Expand Up @@ -245,7 +248,17 @@ export default function apply(ctx: Context, config: Dialogue.Config) {
ctx.app._dialogueStates = {}

config._stripQuestion = (source) => {
source = prepareSource(source)
source = segment.transform(source, {
text: ({ content }, index, chain) => {
let message = simplify(segment.unescape('' + content))
.toLowerCase()
.replace(/\s+/g, '')
.replace(fullWidthRegExp, $0 => halfWidth[fullWidth.indexOf($0)])
if (index === 0) message = message.replace(/^[()\[\]]*/, '')
if (index === chain.length - 1) message = message.replace(/[\.,?!()\[\]~]*$/, '')
return message
},
})
const original = source
const capture = nicknameRE.exec(source)
if (capture) source = source.slice(capture[0].length)
Expand Down Expand Up @@ -297,12 +310,11 @@ export default function apply(ctx: Context, config: Dialogue.Config) {

ctx.on('dialogue/receive', ({ session, test }) => {
if (session.content.includes('[CQ:image,')) return true
const { unprefixed, prefixed, appellative, activated } = config._stripQuestion(session.content)
const { unprefixed, appellative, activated } = config._stripQuestion(session.content)
test.question = unprefixed
test.original = prefixed
test.original = session.content
test.activated = activated
test.appellative = appellative
if (!test.question) return true
})

// 预判要获取的用户字段
Expand All @@ -322,27 +334,3 @@ export default function apply(ctx: Context, config: Dialogue.Config) {
return triggerDialogue(ctx, session, next)
})
}

function prepareSource(source: string) {
return segment.transform(source, {
text: ({ content }, index, chain) => {
let message = simplify(segment.unescape('' + content))
.toLowerCase()
.replace(/\s+/g, '')
.replace(//g, ',')
.replace(//g, ',')
.replace(//g, '.')
.replace(//g, '?')
.replace(//g, '!')
.replace(//g, '(')
.replace(//g, ')')
.replace(//g, '[')
.replace(//g, ']')
.replace(//g, '~')
.replace(//g, '...')
if (index === 0) message = message.replace(/^[()\[\]]*/, '')
if (index === chain.length - 1) message = message.replace(/[\.,?!()\[\]~]*$/, '')
return message
},
})
}
8 changes: 4 additions & 4 deletions packages/plugin-teach/src/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ async function showSearch(argv: Dialogue.Argv) {
const { regexp, page = 1, original, pipe, recursive, autoMerge } = options
const { itemsPerPage = 30, mergeThreshold = 5 } = argv.config

const test: DialogueTest = { question, answer, regexp, original: options._original }
const test: DialogueTest = { question, answer, regexp, original }
if (app.bail('dialogue/before-search', argv, test)) return
const dialogues = await app.database.getDialoguesByTest(test)

Expand All @@ -182,14 +182,14 @@ async function showSearch(argv: Dialogue.Argv) {
await argv.app.parallel('dialogue/search', argv, test, dialogues)
}

if (!question && !answer) {
if (!original && !answer) {
if (!dialogues.length) return '没有搜索到任何回答,尝试切换到其他环境。'
return sendResult('全部问答如下', formatQuestionAnswers(argv, dialogues))
}

if (!options.regexp) {
const suffix = options.regexp !== false ? ',请尝试使用正则表达式匹配' : ''
if (!question) {
if (!original) {
if (!dialogues.length) return session.send(`没有搜索到回答“${answer}${suffix}。`)
const output = dialogues.map(d => `${formatPrefix(argv, d)}${d.original}`)
return sendResult(`回答“${answer}”的问题如下`, output)
Expand Down Expand Up @@ -227,7 +227,7 @@ async function showSearch(argv: Dialogue.Argv) {
})
}

if (!question) {
if (!original) {
if (!dialogues.length) return `没有搜索到含有正则表达式“${answer}”的回答。`
return sendResult(`回答正则表达式“${answer}”的搜索结果如下`, output)
} else if (!answer) {
Expand Down
Loading

0 comments on commit 20e6522

Please sign in to comment.