Parcourir la source

feat: 添加语音合成功能(/tts),调用阿里云API

能够自动更新过期的token,支持私聊以及群聊
JamZYM il y a 5 mois
Parent
commit
a3f56a73ff
6 fichiers modifiés avec 206 ajouts et 2 suppressions
  1. 3 1
      .gitignore
  2. 1 1
      api/send.go
  3. 129 0
      function/slash.go
  4. 8 0
      go.mod
  5. 48 0
      go.sum
  6. 17 0
      main.go

+ 3 - 1
.gitignore

@@ -1 +1,3 @@
-.env
+.env
+main
+temp/*

+ 1 - 1
api/send.go

@@ -26,7 +26,7 @@ func (conn *WsBot) Send_group_msg(group_id int, message string) error {
 		{
 			"action":"send_group_msg",
 			"params":{
-				"user_id":%d,
+				"group_id":%d,
 				"message":"%s"
 			}
 		}

+ 129 - 0
function/slash.go

@@ -6,12 +6,16 @@ import (
 	"io"
 	"log"
 	"net/http"
+	"net/url"
 	"os"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
 
 	"github.com/JamZYM/golagrange/api"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
 	"github.com/shirou/gopsutil/v4/cpu"
 	"github.com/shirou/gopsutil/v4/disk"
 	"github.com/shirou/gopsutil/v4/mem"
@@ -141,3 +145,128 @@ func Func_serverStatus(connBot api.WsBot, user_id int) {
 		connBot.Send_private_msg(user_id, response_text)
 	}
 }
+
+type TokenResult struct {
+	ErrMsg string
+	Token  struct {
+		UserId     string
+		Id         string
+		ExpireTime int64
+	}
+}
+
+func Func_TTS(connBot api.WsBot, id int, msg_text string, isGroup bool, file_id int) {
+	msg_text = strings.Replace(msg_text, "/tts ", "", 1)
+	currentTime := time.Now().Unix()
+	expireTime, _ := strconv.Atoi(os.Getenv("TOKENEXPIRE"))
+	if currentTime > int64(expireTime) {
+		_tts_getToken()
+	}
+	var appkey string = os.Getenv("APPKEY")
+	var token string = os.Getenv("TOKEN")
+	var text string = msg_text
+	var textUrlEncode = text
+	textUrlEncode = url.QueryEscape(textUrlEncode)
+	textUrlEncode = strings.Replace(textUrlEncode, "+", "%20", -1)
+	textUrlEncode = strings.Replace(textUrlEncode, "*", "%2A", -1)
+	textUrlEncode = strings.Replace(textUrlEncode, "%7E", "~", -1)
+	var audioSaveFile string = "temp/audio/" + strconv.Itoa(file_id) + ".wav"
+	absoluteDir, _ := os.Getwd()
+	var absoluteFile string = "file://" + absoluteDir + "/" + audioSaveFile
+	var format string = "wav"
+	var sampleRate int = 16000
+	_tts_processGETRequest(appkey, token, textUrlEncode, audioSaveFile, format, sampleRate)
+	if isGroup {
+		connBot.Send_group_msg(id, "[CQ:record,file="+absoluteFile+"]")
+	} else {
+		connBot.Send_private_msg(id, "[CQ:record,file="+absoluteFile+"]")
+	}
+}
+
+func _tts_processGETRequest(appkey string, token string, text string, audioSaveFile string, format string, sampleRate int) {
+	/**
+	 * 设置HTTPS GET请求:
+	 * 1.使用HTTPS协议
+	 * 2.语音识别服务域名:nls-gateway-cn-shanghai.aliyuncs.com
+	 * 3.语音识别接口请求路径:/stream/v1/tts
+	 * 4.设置必须请求参数:appkey、token、text、format、sample_rate
+	 * 5.设置可选请求参数:voice、volume、speech_rate、pitch_rate
+	 */
+	var url string = "https://nls-gateway-cn-shanghai.aliyuncs.com/stream/v1/tts"
+	url = url + "?appkey=" + appkey
+	url = url + "&token=" + token
+	url = url + "&text=" + text
+	url = url + "&format=" + format
+	url = url + "&sample_rate=" + strconv.Itoa(sampleRate)
+	// voice 发音人,可选,默认是xiaoyun。
+	// url = url + "&voice=" + "xiaoyun"
+	// volume 音量,范围是0~100,可选,默认50。
+	// url = url + "&volume=" + strconv.Itoa(50)
+	// speech_rate 语速,范围是-500~500,可选,默认是0。
+	// url = url + "&speech_rate=" + strconv.Itoa(0)
+	// pitch_rate 语调,范围是-500~500,可选,默认是0。
+	// url = url + "&pitch_rate=" + strconv.Itoa(0)
+	/**
+	 * 发送HTTPS GET请求,处理服务端的响应。
+	 */
+	response, err := http.Get(url)
+	if err != nil {
+		fmt.Println("The GET request failed!")
+		panic(err)
+	}
+	defer response.Body.Close()
+	contentType := response.Header.Get("Content-Type")
+	body, _ := io.ReadAll(response.Body)
+	if contentType == "audio/mpeg" {
+		file, _ := os.Create(audioSaveFile)
+		defer file.Close()
+		file.Write([]byte(body))
+		// fmt.Println("The GET request succeed!")
+	} else {
+		// ContentType 为 null 或者为 "application/json"
+		statusCode := response.StatusCode
+		fmt.Println("The HTTP statusCode: " + strconv.Itoa(statusCode))
+		fmt.Println("The GET request failed: " + string(body))
+	}
+}
+
+func _tts_getToken() {
+	client, err := sdk.NewClientWithAccessKey("cn-shanghai", os.Getenv("AKID"), os.Getenv("AKKEY"))
+	if err != nil {
+		panic(err)
+	}
+	request := requests.NewCommonRequest()
+	request.Method = "POST"
+	request.Domain = "nls-meta.cn-shanghai.aliyuncs.com"
+	request.ApiName = "CreateToken"
+	request.Version = "2019-02-28"
+	response, err := client.ProcessCommonRequest(request)
+	if err != nil {
+		panic(err)
+	}
+
+	var tr TokenResult
+	err = json.Unmarshal([]byte(response.GetHttpContentString()), &tr)
+	if err == nil {
+		fmt.Println(tr.Token.Id)
+		fmt.Println(tr.Token.ExpireTime)
+	} else {
+		fmt.Println(err)
+	}
+
+	envContent, err := os.ReadFile(".env")
+	if err != nil {
+		fmt.Println("Error reading .env file")
+		return
+	}
+	re1 := regexp.MustCompile(`TOKEN=".*"`)
+	newContent := re1.ReplaceAllString(string(envContent), fmt.Sprintf(`TOKEN="%s"`, tr.Token.Id))
+	re := regexp.MustCompile(`TOKENEXPIRE=".*"`)
+	newContent = re.ReplaceAllString(string(newContent), fmt.Sprintf(`TOKENEXPIRE="%d"`, tr.Token.ExpireTime))
+
+	err = os.WriteFile(".env", []byte(newContent), 0644)
+	if err != nil {
+		fmt.Println("Error reading .env file")
+		return
+	}
+}

+ 8 - 0
go.mod

@@ -5,10 +5,17 @@ go 1.22.2
 require github.com/gorilla/websocket v1.5.1
 
 require (
+	github.com/aliyun/alibaba-cloud-sdk-go v1.61.1376 // indirect
+	github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1 // indirect
 	github.com/go-ole/go-ole v1.2.6 // indirect
+	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/joho/godotenv v1.5.1 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
+	github.com/satori/go.uuid v1.2.0 // indirect
 	github.com/shirou/gopsutil/v4 v4.24.5 // indirect
 	github.com/shoenig/go-m1cpu v0.1.6 // indirect
 	github.com/tklauser/go-sysconf v0.3.12 // indirect
@@ -16,4 +23,5 @@ require (
 	github.com/yusufpapurcu/wmi v1.2.4 // indirect
 	golang.org/x/net v0.17.0 // indirect
 	golang.org/x/sys v0.20.0 // indirect
+	gopkg.in/ini.v1 v1.66.2 // indirect
 )

+ 48 - 0
go.sum

@@ -1,30 +1,78 @@
+github.com/aliyun/alibaba-cloud-sdk-go v1.61.1376 h1:lExo7heZgdFn5AbaNJEllbA0KSJ/Z8T7MphvMREJOOo=
+github.com/aliyun/alibaba-cloud-sdk-go v1.61.1376/go.mod h1:9CMdKNL3ynIGPpfTcdwTvIm8SGuAZYYC4jFVSSvE1YQ=
+github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1 h1:LjItoNZuu5xHlsByFo+kr3nGa4LRIESCGWhfurayxBg=
+github.com/aliyun/alibabacloud-nls-go-sdk v1.1.1/go.mod h1:4BDMUKpEaP/Ct79w0ozR0nbnEj49g1k3mrgX/IKG5I4=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
 github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
 github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
 github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/shirou/gopsutil/v4 v4.24.5 h1:gGsArG5K6vmsh5hcFOHaPm87UD003CaDMkAOweSQjhM=
 github.com/shirou/gopsutil/v4 v4.24.5/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA=
 github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
 github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
 github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
 github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
 github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
 github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
 github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
 golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
+gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 17 - 0
main.go

@@ -11,6 +11,8 @@ import (
 	"github.com/joho/godotenv"
 )
 
+var TTS_id int = 0
+
 func msgProccess(msg map[string]interface{}, connBot api.WsBot) {
 	// fmt.Println(msg)
 	if msg["message_type"].(string) == "private" {
@@ -22,6 +24,21 @@ func msgProccess(msg map[string]interface{}, connBot api.WsBot) {
 				function.Func_bvdl(msg_text, connBot, user_id)
 			} else if msg_text == "/serverStatus" {
 				function.Func_serverStatus(connBot, user_id)
+			} else if strings.HasPrefix(msg_text, "/tts ") {
+				TTS_id++
+				function.Func_TTS(connBot, user_id, msg_text, false, TTS_id-1)
+				TTS_id = TTS_id % 10
+			}
+		}
+	} else if msg["message_type"].(string) == "group" {
+		group_id := int(msg["group_id"].(float64))
+		msg_type := msg["message"].([]interface{})[0].(map[string]interface{})["type"].(string)
+		if msg_type == "text" {
+			msg_text := msg["message"].([]interface{})[0].(map[string]interface{})["data"].(map[string]interface{})["text"].(string)
+			if strings.HasPrefix(msg_text, "/tts ") {
+				TTS_id++
+				function.Func_TTS(connBot, group_id, msg_text, true, TTS_id-1)
+				TTS_id = TTS_id % 10
 			}
 		}
 	}