Commit f0ecb243 authored by xuchaozou's avatar xuchaozou

feat: 初步完成打开语音模块

parent 7ccb01fc
Pipeline #85693 failed with stages
...@@ -118,6 +118,7 @@ ...@@ -118,6 +118,7 @@
"@wisdom-utils/components": "0.1.349", "@wisdom-utils/components": "0.1.349",
"@wisdom-utils/runtime": "0.0.48", "@wisdom-utils/runtime": "0.0.48",
"@wisdom-utils/utils": "0.1.388", "@wisdom-utils/utils": "0.1.388",
"@congcongcai/jieba.js" : "0.0.3",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"antd": "4.21.2", "antd": "4.21.2",
"long": "^5.2.3", "long": "^5.2.3",
......
...@@ -9,13 +9,13 @@ import { Directive } from '../../core' ...@@ -9,13 +9,13 @@ import { Directive } from '../../core'
const Dialog = props => { const Dialog = props => {
const {history , pandaRecordWebSocketRef} = props const { pandaRecordWebSocketRef} = props
const ulRef = useRef(null) const ulRef = useRef(null)
const directiveRef = useRef(null) const directiveRef = useRef(null)
const [data , setData] = useState([]) const [data , setData] = useState([])
const finish = useCallback(async ({resultText}) => { const finish = useCallback(async ({resultText}) => {
pandaRecordWebSocketRef.setStatus("pause") pandaRecordWebSocketRef.current.setStatus("pause")
const directive = directiveRef.current.parse({ const directive = directiveRef.current.parse({
text : resultText text : resultText
}) })
...@@ -25,16 +25,19 @@ const Dialog = props => { ...@@ -25,16 +25,19 @@ const Dialog = props => {
content : "系统正在思考中,请稍后..." content : "系统正在思考中,请稍后..."
}) })
const msg = await directive.excute() const msg = await directive.excute()
setTimeout(() => {
updateLastData({ updateLastData({
content : msg content : msg
}) })
}, 1000)
pandaRecordWebSocketRef.current.setStatus("playing")
return return
} }
sendContent({ sendContent({
role : "system", role : "system",
content : "亲,我暂时还未理解你的意思,请换一种方式表达或者描述更加具体" content : "亲,我暂时还未理解你的意思,请换一种方式表达或者描述更加具体"
}) })
pandaRecordWebSocketRef.setStatus("playing") pandaRecordWebSocketRef.current.setStatus("playing")
}, []) }, [])
const frame = useCallback((data) => { const frame = useCallback((data) => {
...@@ -68,7 +71,6 @@ const Dialog = props => { ...@@ -68,7 +71,6 @@ const Dialog = props => {
useEffect(() => { useEffect(() => {
directiveRef.current = new Directive({ directiveRef.current = new Directive({
params : { params : {
history
} }
}) })
}, []) }, [])
...@@ -92,14 +94,14 @@ const Dialog = props => { ...@@ -92,14 +94,14 @@ const Dialog = props => {
} }
const updateLastData = ({content , ...params }) => { const updateLastData = ({content , ...params }) => {
setData(data => data.map((data , index) => { setData(data => data.map((item , index) => {
if(index < length -1) { if(index < data.length -1) {
return { return {
...data ...item
} }
} else { } else {
return { return {
...data, ...item,
content, content,
...params, ...params,
} }
......
...@@ -20,12 +20,11 @@ const PandaTip = props => { ...@@ -20,12 +20,11 @@ const PandaTip = props => {
} else if(directive instanceof ByeDirective && status == "open") { } else if(directive instanceof ByeDirective && status == "open") {
setStatus("close") setStatus("close")
} }
}, []) }, [status])
useEffect(() => { useEffect(() => {
directiveRef.current = new Directive({ directiveRef.current = new Directive({
// directiveTypes : [AwakenDirective , ByeDirective], directiveTypes : [AwakenDirective , ByeDirective],
directiveTypes : []
}) })
}, []) }, [])
......
...@@ -3,6 +3,8 @@ class BaseDirective { ...@@ -3,6 +3,8 @@ class BaseDirective {
this.options = options this.options = options
this.text = this.options.text this.text = this.options.text
this.directives = options.directives this.directives = options.directives
this.params = options.params || {}
this.data = null
} }
match () { match () {
......
...@@ -3,7 +3,7 @@ import BaseDirective from "./BaseDirective"; ...@@ -3,7 +3,7 @@ import BaseDirective from "./BaseDirective";
class ByeDirective extends BaseDirective { class ByeDirective extends BaseDirective {
match () { match () {
const {text} = this const {text} = this
if(/再见再见|拜拜/.test(text)) { if(/再见|拜拜|关闭/.test(text)) {
return true return true
} }
return false return false
......
import BaseDirective from "./BaseDirective";
class CloseMenuDirective extends BaseDirective {
match () {
const { text } = this
let result = null
result = text.match(/关闭(.*)(?=(?:菜单|功能|路径))/)
if (!result) {
result = text.match(/关闭(.*)(?=(?:,|,|。|))/)
}
if (result && result.length >= 2) {
this.matchResult = result[1]
}
return !!(result && result.length >= 2)
}
async excute () {
let widgets = null
if(this.data) {
} else {
widgets = this.getCompleteMenus()
}
}
getCompleteMenus () {
const result = Utils.findMenus({
name: this.matchResult
})
const { widgets } = result
// return widgets.length > 0 ? widgets : null
}
}
export default CloseMenuDirective
\ No newline at end of file
import Utils from '../utils/Utils'
import BaseDirective from './BaseDirective'
class HomePageDirective extends BaseDirective {
match () {
const {text} = this
if(/首页/.test(text)) {
return true
}
return false
}
async excute () {
if(!window.globalConfig.homepage) {
return "没有配置首页"
}
Utils.openMenu({url : window.globalConfig.homepage})
return "返回首页成功"
}
}
export default HomePageDirective
\ No newline at end of file
import BaseDirective from "./BaseDirective"; import BaseDirective from "./BaseDirective";
import { cut, extract } from '@congcongcai/jieba.js'
import Utils from "../utils/Utils";
import { uniq } from "lodash";
class OpenMenuDirective extends BaseDirective { class OpenMenuDirective extends BaseDirective {
match () { match() {
const { text } = this
let result = null
result = text.match(/打开(.*)(?=(?:菜单|功能|路径))/)
if (!result) {
result = text.match(/打开(.*)(?=(?:,|,|。|))/)
}
if (result && result.length >= 2) {
this.matchResult = result[1]
}
return !!(result && result.length >= 2)
}
async excute() {
let widgets = null
if (this.data) {
} else {
widgets = this.getCompleteMenus()
if (!widgets) {
widgets = this.getPatternMenus()
}
if (!widgets) {
return "没有查找到此菜单,请检查语音是否正确"
}
if (widgets.length == 1) {
return this.handleOneSingleMenu(widgets[0])
} else {
this.data = {}
this.data.type = "widgets"
this.data.widgets = widgets
return `${this.widgetsToMsg(widgets)}`
}
}
//先判断一下是否能完全匹配一个菜单
} }
async excute () { handleOneSingleMenu(widget) {
if (widget.url) {
this.openSingleMenu(widget)
return `打开${widget.label}成功`
} else {
this.data = {}
this.data.type = "widgets"
this.data.widgets = widgets
return `你打开的${widget.label}是一个菜单组,${this.widgetsToMsg(widget.widgets)}`
}
}
widgetsToMsg (widgets) {
return `有以下${widgets.length - 1}个菜单\n${widgets.map((widget, index) => `${index + 1}.${widget.label}`).join("\n")},\n请语音确认一下打开第几个菜单`
}
getCompleteMenus() {
const result = Utils.findMenus({
name: this.matchResult
})
const { widgets } = result
return widgets.length > 0 ? widgets : null
}
getPatternMenus() {
const weights = extract(this.matchResult, 4)
let data = []
if (weights.length == 0) return null
weights.map(({ word }) => {
const name = new RegExp(word)
const result = Utils.findMenus({
name
})
if(result.widgets.length > 0) {
data = data.concat(result.widgets)
}
})
data = uniq(data)
return data.length > 0 ? data : null
}
openSingleMenu(widget) {
Utils.openMenu({widget})
} }
} }
......
import AwakenDirective from "./AwakenDirective" import AwakenDirective from "./AwakenDirective"
import OpenMenuDirective from "./OpenMenuDirective" import OpenMenuDirective from "./OpenMenuDirective"
import HomePageDirective from "./HomePageDirective"
const DirectiveTypes = [ const DirectiveTypes = [
AwakenDirective, AwakenDirective,
HomePageDirective,
OpenMenuDirective OpenMenuDirective
] ]
class Directive { class Directive {
......
...@@ -56,6 +56,32 @@ const Utils = { ...@@ -56,6 +56,32 @@ const Utils = {
} }
return realLength; return realLength;
}, },
findMenus ({ name }) {
const result = {
widgets : [],
}
window.globalConfig.widgets.map(widget => this._findMenus({widget, name , result}))
return result
},
_findMenus({widget, name , result}) {
if(widget.hideInMenu) return
if( name instanceof RegExp) {
const isPattern = name.test(widget.label)
if(isPattern) {
result.widgets.push(widget)
}
} else if(name == widget.label){
result.widgets.push(widget)
}
if(widget.widgets && Array.isArray(widget.widgets)) {
return widget.widgets.map(widget => this._findMenus({widget , name , result}))
}
},
openMenu ({widget = {}, url}) {
if(!((widget.url && widget.product) || url)) return
const _url = url || `${widget.product}/${widget.url}`
window.history.pushState(null, '', `/civbase/${_url}`);
}
}; };
export default Utils; export default Utils;
...@@ -3,22 +3,24 @@ import { useCallback, useEffect, useRef, useState } from 'react' ...@@ -3,22 +3,24 @@ import { useCallback, useEffect, useRef, useState } from 'react'
import { PandaRecordWebSocket } from './core' import { PandaRecordWebSocket } from './core'
import { message } from 'antd' import { message } from 'antd'
import PandaTip from './Ui/PandaTip' import PandaTip from './Ui/PandaTip'
import { init, cut, extract, ready } from "@congcongcai/jieba.js";
const SoundAi = props => { const SoundAi = props => {
const pandaRecordWebSocketRef = useRef(null) const pandaRecordWebSocketRef = useRef(null)
const keyRef = useRef(null) const keyRef = useRef(null)
const [isStartRecorder , setIsStartRecorder] = useState(false) const [isStartRecorder, setIsStartRecorder] = useState(false)
useEffect(async () => { useEffect(async () => {
try { try {
await getIsOpenConfig() await getIsOpenConfig()
await getKey() await getKey()
await initJieBa()
event.emit("record:changeVisible", { event.emit("record:changeVisible", {
visible : true visible: true
}) })
event.on("record:changedStaus", changeSwitch) event.on("record:changedStaus", changeSwitch)
} catch (error){ } catch (error) {
console.warn(error) console.warn(error)
} }
return () => { return () => {
...@@ -30,34 +32,34 @@ const SoundAi = props => { ...@@ -30,34 +32,34 @@ const SoundAi = props => {
const changeSwitch = useCallback(async ({ recordData, e: checked }) => { const changeSwitch = useCallback(async ({ recordData, e: checked }) => {
if (checked) { if (checked) {
emitSwitch({ emitSwitch({
loading : true loading: true
}) })
createPandaRecordWebSocket() createPandaRecordWebSocket()
const data = await pandaRecordWebSocketRef.current.start() const data = await pandaRecordWebSocketRef.current.start()
emitSwitch({ emitSwitch({
loading : false loading: false
}) })
if(!data.isSuccess) { if (!data.isSuccess) {
return message.info(data.msg) return message.info(data.msg)
} }
emitSwitch({ emitSwitch({
checked : true checked: true
}) })
setIsStartRecorder(true) setIsStartRecorder(true)
} else { } else {
emitSwitch({ emitSwitch({
loading : true loading: true
}) })
await destroyPandaRecorderWebSocket() await destroyPandaRecorderWebSocket()
emitSwitch({ emitSwitch({
loading : false, loading: false,
checked : false checked: false
}) })
setIsStartRecorder(false) setIsStartRecorder(false)
} }
}, []) }, [])
const createPandaRecordWebSocket = () => { const createPandaRecordWebSocket = async () => {
destroyPandaRecorderWebSocket() destroyPandaRecorderWebSocket()
pandaRecordWebSocketRef.current = new PandaRecordWebSocket({ pandaRecordWebSocketRef.current = new PandaRecordWebSocket({
config : keyRef.current.shortSound, config : keyRef.current.shortSound,
...@@ -70,10 +72,28 @@ const SoundAi = props => { ...@@ -70,10 +72,28 @@ const SoundAi = props => {
} }
} }
}) })
// await testText("我需要打开铁山全景图菜单")
// await testText("我需要返回首页")
} }
const testText = text => new Promise((resolve , reject) => {
setTimeout(() => {
event.emit("aiSound:frame", {
resultTextTemp: text
})
setTimeout(() => {
event.emit("aiSound:finish", {
resultText: text
})
setTimeout(() => {
resolve()
}, 500)
}, 500)
}, 500)
})
const destroyPandaRecorderWebSocket = async () => { const destroyPandaRecorderWebSocket = async () => {
if(pandaRecordWebSocketRef.current) { if (pandaRecordWebSocketRef.current) {
await pandaRecordWebSocketRef.current.destroy() await pandaRecordWebSocketRef.current.destroy()
pandaRecordWebSocketRef.current = null pandaRecordWebSocketRef.current = null
} }
...@@ -110,9 +130,9 @@ const SoundAi = props => { ...@@ -110,9 +130,9 @@ const SoundAi = props => {
}) })
}) })
const getKey = () => new Promise((resolve , reject) => { const getKey = () => new Promise((resolve, reject) => {
fetch("https://civgis.panda-water.cn/system/pandaAiSoundWorker/key.json",{ fetch("https://civgis.panda-water.cn/system/pandaAiSoundWorker/key.json", {
mode : "cors" mode: "cors"
}).then(res => res.json()) }).then(res => res.json())
.then(res => { .then(res => {
keyRef.current = res keyRef.current = res
...@@ -121,9 +141,18 @@ const SoundAi = props => { ...@@ -121,9 +141,18 @@ const SoundAi = props => {
reject(error) reject(error)
}) })
}) })
const initJieBa = () => new Promise((resolve, reject) => {
ready().then(() => {
init()
resolve()
}).catch(() => {
reject()
})
})
return (<> return (<>
{ {
isStartRecorder ? <PandaTip pandaRecordWebSocketRef = {pandaRecordWebSocketRef} /> : null isStartRecorder ? <PandaTip pandaRecordWebSocketRef={pandaRecordWebSocketRef} /> : null
} }
</>) </>)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment