增加小爱回复 (#100)

* fix:去掉count,修sql

* feat:增加小爱回复

* fix:修一下lint

* fix:修一下lint

* fix:修一下lint

* fix:修一下lint

* fix:修改设置回复模式

* fix:修lint

Co-authored-by: Guohuiyuan <haibaraguo@yeahka.com>
This commit is contained in:
himawari 2022-01-07 19:47:35 +08:00 committed by GitHub
parent ca2f674696
commit ca36038bbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 288 additions and 170 deletions

View File

@ -197,8 +197,9 @@ zerobot -h -t token -u url [-d|w] [-g 监听地址:端口] qq1 qq2 qq3 ...
- [x] 搜卡[xxxx] - [x] 搜卡[xxxx]
- [x] [卡组代码xxx] - [x] [卡组代码xxx]
- 注更多搜卡指令参数https://hs.fbigame.com/misc/searchhelp - 注更多搜卡指令参数https://hs.fbigame.com/misc/searchhelp
- **青云客** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_qingyunke"` - **人工智能回复** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_reply"`
- [x] @Bot 任意文本(任意一句话回复) - [x] @Bot 任意文本(任意一句话回复)
- [x] 设置回复模式[青云客|小爱]
- **关键字搜图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_image_finder"` - **关键字搜图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_image_finder"`
- [x] 来张 [xxx] - [x] 来张 [xxx]
- **拼音首字母释义工具** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nbnhhsh"` - **拼音首字母释义工具** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_nbnhhsh"`

View File

@ -13,9 +13,9 @@ import (
// webctrl "github.com/FloatTech/ZeroBot-Plugin/control/web" // web 后端控制 // webctrl "github.com/FloatTech/ZeroBot-Plugin/control/web" // web 后端控制
// 词库类 // 词库类
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_atri" // ATRI词库 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_reply" // 人工智能回复
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_chat" // 基础词库 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_atri" // ATRI词库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_qingyunke" // 青云客 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_chat" // 基础词库
// 实用类 // 实用类
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_b14" // base16384加解密 _ "github.com/FloatTech/ZeroBot-Plugin/plugin_b14" // base16384加解密

147
plugin_ai_reply/ai_reply.go Normal file
View File

@ -0,0 +1,147 @@
// Package aireply 人工智能回复
package aireply
import (
"errors"
"github.com/FloatTech/ZeroBot-Plugin/control"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
"math/rand"
"time"
)
var (
bucket = rate.NewManager(time.Minute, 20) // 青云客接口回复
engine = control.Register(serviceName, &control.Options{
DisableOnDefault: false,
Help: "人工智能回复\n" +
"- @Bot 任意文本(任意一句话回复)\n- 设置回复模式[青云客|小爱]\n- ",
})
modeMap = map[string]int64{"青云客": 1, "小爱": 2}
)
const (
serviceName = "aireply"
qykURL = "http://api.qingyunke.com/api.php?key=free&appid=0&msg=%s"
qykBotName = "菲菲"
xiaoaiURL = "http://81.70.100.130/api/xiaoai.php?msg=%s&n=text"
xiaoaiBotName = "小爱"
prio = 256
)
// AIReply 公用智能回复类
type AIReply interface {
// DealQuestion 把椛椛替换为各api接口的bot名字
DealQuestion(preMsg string) (msg string)
// GetReply 取得回复消息
GetReply(msg string) (reply string)
// DealReply 处理回复消息
DealReply(reply string) (textReply string, faceReply int)
}
// NewAIReply 智能回复简单工厂
func NewAIReply(mode int64) AIReply {
if mode == 1 {
return &QYKReply{}
} else if mode == 2 {
return &XiaoAiReply{}
}
return &QYKReply{}
}
func init() { // 插件主体
// 回复 @和包括名字
engine.OnMessage(zero.OnlyToMe).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
aireply := NewAIReply(GetReplyMode(ctx))
if !bucket.Load(ctx.Event.UserID).Acquire() {
// 频繁触发,不回复
return
}
msg := ctx.ExtractPlainText()
// 把消息里的椛椛替换成对应接口机器人的名字
msg = aireply.DealQuestion(msg)
reply := aireply.GetReply(msg)
// 挑出 face 表情
textReply, faceReply := aireply.DealReply(reply)
// 回复
time.Sleep(time.Second * 1)
if ctx.Event.MessageType == "group" {
if faceReply != -1 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(textReply), message.Face(faceReply))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(textReply))
}
}
if ctx.Event.MessageType == "private" {
if faceReply != -1 {
ctx.SendChain(message.Text(textReply), message.Face(faceReply))
} else {
ctx.SendChain(message.Text(textReply))
}
}
})
engine.OnPrefix(`设置回复模式`).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
param := ctx.State["args"].(string)
switch param {
case "青云客":
if err := setReplyMode(ctx, modeMap["青云客"]); err != nil {
log.Errorln("[aireply]:", err)
}
ctx.SendChain(message.Text("设置为青云客回复"))
case "小爱":
if err := setReplyMode(ctx, modeMap["小爱"]); err != nil {
log.Errorln("[aireply]:", err)
}
ctx.SendChain(message.Text("设置为小爱回复"))
default:
ctx.SendChain(message.Text("设置失败"))
}
})
}
func setReplyMode(ctx *zero.Ctx, mode int64) error {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
m, ok := control.Lookup(serviceName)
if ok {
return m.SetData(gid, mode)
}
return errors.New("no such plugin")
}
// GetReplyMode 取得回复模式
func GetReplyMode(ctx *zero.Ctx) (mode int64) {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
m, ok := control.Lookup(serviceName)
if ok {
mode = m.GetData(gid)
}
return mode
}
func getAgent() string {
agent := [...]string{
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
"Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)",
"User-Agent,Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"User-Agent, Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
"User-Agent,Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
len1 := len(agent)
return agent[r.Intn(len1)]
}

View File

@ -1,4 +1,4 @@
package qingyunke package aireply
// TODO: 待优化 // TODO: 待优化

View File

@ -0,0 +1,68 @@
package aireply
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
)
// QYKReply 青云客回复类
type QYKReply struct{}
// DealQuestion 把椛椛替换为菲菲
func (*QYKReply) DealQuestion(preMsg string) (msg string) {
msg = strings.ReplaceAll(preMsg, zero.BotConfig.NickName[0], qykBotName)
return msg
}
// GetReply 取得回复消息
func (*QYKReply) GetReply(msg string) (reply string) {
u := fmt.Sprintf(qykURL, url.QueryEscape(msg))
client := &http.Client{}
req, err := http.NewRequest("GET", u, nil)
if err != nil {
log.Errorln("[aireply-qingyunke]:", err)
return ""
}
// 自定义Header
req.Header.Set("User-Agent", getAgent())
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Host", "api.qingyunke.com")
resp, err := client.Do(req)
if err != nil {
log.Errorln("[aireply-qingyunke]:", err)
return
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Errorln("[aireply-qingyunke]:", err)
return
}
reply = gjson.Get(helper.BytesToString(bytes), "content").String()
log.Println("reply:", reply)
return
}
// DealReply 处理回复消息
func (*QYKReply) DealReply(reply string) (textReply string, faceReply int) {
reg := regexp.MustCompile(`\{face:(\d+)\}(.*)`)
faceReply = -1
if reg.MatchString(reply) {
faceReply, _ = strconv.Atoi(reg.FindStringSubmatch(reply)[1])
textReply = reg.FindStringSubmatch(reply)[2]
} else {
textReply = reply
}
textReply = strings.ReplaceAll(textReply, qykBotName, zero.BotConfig.NickName[0])
textReply = strings.ReplaceAll(textReply, "{br}", "\n")
return
}

61
plugin_ai_reply/xiaoai.go Normal file
View File

@ -0,0 +1,61 @@
package aireply
import (
"fmt"
log "github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
// XiaoAiReply 小爱回复类
type XiaoAiReply struct{}
// DealQuestion 把椛椛替换为小爱
func (*XiaoAiReply) DealQuestion(preMsg string) (msg string) {
msg = strings.ReplaceAll(preMsg, zero.BotConfig.NickName[0], xiaoaiBotName)
return msg
}
// GetReply 取得回复消息
func (*XiaoAiReply) GetReply(msg string) (reply string) {
u := fmt.Sprintf(xiaoaiURL, url.QueryEscape(msg))
client := &http.Client{}
req, err := http.NewRequest("GET", u, nil)
if err != nil {
log.Errorln("[aireply-xiaoai]:", err)
return ""
}
// 自定义Header
req.Header.Set("User-Agent", getAgent())
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Host", "81.70.100.130")
resp, err := client.Do(req)
if err != nil {
log.Errorln("[aireply-xiaoai]:", err)
return
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Errorln("[aireply-xiaoai]:", err)
return
}
reply = helper.BytesToString(bytes)
log.Println("reply:", reply)
return
}
// DealReply 处理回复消息
func (*XiaoAiReply) DealReply(reply string) (textReply string, faceReply int) {
textReply = strings.ReplaceAll(reply, xiaoaiBotName, zero.BotConfig.NickName[0])
if textReply == "" {
textReply = zero.BotConfig.NickName[0] + "听不懂你的话了,能再说一遍吗"
}
textReply = strings.ReplaceAll(textReply, "小米智能助理", "电子宠物")
faceReply = -1
return
}

View File

@ -19,7 +19,7 @@ import (
"github.com/wdvxdr1123/ZeroBot/utils/helper" "github.com/wdvxdr1123/ZeroBot/utils/helper"
"github.com/FloatTech/ZeroBot-Plugin/control" "github.com/FloatTech/ZeroBot-Plugin/control"
qingyunke "github.com/FloatTech/ZeroBot-Plugin/plugin_qingyunke" aireply "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_reply"
fileutil "github.com/FloatTech/ZeroBot-Plugin/utils/file" fileutil "github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/web" "github.com/FloatTech/ZeroBot-Plugin/utils/web"
) )
@ -47,14 +47,12 @@ func init() {
engine.OnMessage(zero.OnlyToMe, getAcquire).SetBlock(true).SetPriority(prio). engine.OnMessage(zero.OnlyToMe, getAcquire).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
msg := ctx.ExtractPlainText() msg := ctx.ExtractPlainText()
// 调用青云客接口 AIReply := aireply.NewAIReply(aireply.GetReplyMode(ctx))
reply, err := qingyunke.GetMessage(msg) // 把消息里的椛椛替换成对应接口机器人的名字
if err != nil { msg = AIReply.DealQuestion(msg)
ctx.SendChain(message.Text("ERROR: ", err)) reply := AIReply.GetReply(msg)
return
}
// 挑出 face 表情 // 挑出 face 表情
textReply, _ := qingyunke.DealReply(reply) textReply, _ := AIReply.DealReply(reply)
// 拟声器生成音频 // 拟声器生成音频
syntPath := getSyntPath() syntPath := getSyntPath()
fileName := getWav(textReply, syntPath, vocoderList[1], ctx.Event.UserID) fileName := getWav(textReply, syntPath, vocoderList[1], ctx.Event.UserID)

View File

@ -1,157 +0,0 @@
// Package qingyunke 基于青云客接口的聊天对话功能
package qingyunke
import (
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/ZeroBot-Plugin/control"
)
var (
prio = 256
bucket = rate.NewManager(time.Minute, 20) // 青云客接口回复
engine *zero.Engine
)
func init() { // 插件主体
engine = control.Register("qingyunke", &control.Options{
DisableOnDefault: false,
Help: "青云客\n" +
"- @Bot 任意文本(任意一句话回复)",
})
// 回复 @和包括名字
engine.OnMessage(zero.OnlyToMe).SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
if !bucket.Load(ctx.Event.UserID).Acquire() {
// 频繁触发,不回复
return
}
msg := ctx.ExtractPlainText()
// 调用青云客接口
reply, err := GetMessage(msg)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
// 挑出 face 表情
textReply, faceReply := DealReply(reply)
// 回复
time.Sleep(time.Second * 1)
if ctx.Event.MessageType == "group" {
if faceReply != -1 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(textReply), message.Face(faceReply))
} else {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(textReply))
}
}
if ctx.Event.MessageType == "private" {
if faceReply != -1 {
ctx.SendChain(message.Text(textReply), message.Face(faceReply))
} else {
ctx.SendChain(message.Text(textReply))
}
}
})
// TODO: 待优化
/*
zero.OnRegex("CQ:image,file=|CQ:face,id=", zero.OnlyToMe, switchQYK()).SetBlock(false).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
imageURL := getPicture()
time.Sleep(time.Second * 1)
if ctx.Event.MessageType == "group" {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Image(imageURL))
}
if ctx.Event.MessageType == "private" {
ctx.SendChain(message.Image(imageURL))
}
})
*/
}
// 青云客数据
type dataQYK struct {
Result int `json:"result"`
Content string `json:"content"`
}
const (
qykURL = "http://api.qingyunke.com/api.php"
key = "free"
appid = "0"
)
// GetMessage 青云客取消息
func GetMessage(msg string) (string, error) {
u := fmt.Sprintf(qykURL+"?key=%s&appid=%s&msg=%s", key, appid, url.QueryEscape(msg))
client := &http.Client{}
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return "", err
}
// 自定义Header
req.Header.Set("User-Agent", getAgent())
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Host", "api.qingyunke.com")
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
fmt.Println(string(bytes))
var dataQYK dataQYK
if err := json.Unmarshal(bytes, &dataQYK); err != nil {
return "", err
}
return dataQYK.Content, nil
}
// DealReply 处理青云客返回文本
func DealReply(reply string) (textReply string, faceReply int) {
reg := regexp.MustCompile(`\{face:(\d+)\}(.*)`)
faceReply = -1
if reg.MatchString(reply) {
faceReply, _ = strconv.Atoi(reg.FindStringSubmatch(reply)[1])
textReply = reg.FindStringSubmatch(reply)[2]
} else {
textReply = reply
}
textReply = strings.ReplaceAll(textReply, "菲菲", zero.BotConfig.NickName[0])
textReply = strings.ReplaceAll(textReply, "{br}", "\n")
return
}
func getAgent() string {
agent := [...]string{
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
"Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)",
"User-Agent,Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"User-Agent, Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
"User-Agent,Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
len1 := len(agent)
return agent[r.Intn(len1)]
}