增加小爱回复 (#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
8 changed files with 288 additions and 170 deletions

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

@@ -0,0 +1,61 @@
package aireply
// TODO: 待优化
/*
import (
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"regexp"
)
var (
reImg = `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
)
//取图片
func getPicture() string {
prefix := "https://fabiaoqing.com/tag/detail/id/5682/page"
url := fmt.Sprintf("%d.html", rand.Intn(11)+1)
url = prefix + url
log.Println("正在" + url + "寻找图片")
urls := getImgs(url)
fmt.Println(urls)
imageURL := urls[rand.Intn(len(urls))]
log.Println("取到" + imageURL)
return imageURL
}
func HandleError(err error, why string) {
if err != nil {
fmt.Println(why, err)
}
}
func getImgs(url string) (urls []string) {
pageStr := GetPageStr(url)
re := regexp.MustCompile(reImg)
results := re.FindAllStringSubmatch(pageStr, -1)
fmt.Printf("共找到%d条结果\n", len(results))
for _, result := range results {
url := result[0]
urls = append(urls, url)
}
return
}
func GetPageStr(url string) (pageStr string) {
resp, err := http.Get(url)
HandleError(err, "http.Get url")
defer resp.Body.Close()
// 2.读取页面内容
pageBytes, err := ioutil.ReadAll(resp.Body)
HandleError(err, "ioutil.ReadAll")
// 字节转字符串
pageStr = string(pageBytes)
return pageStr
}
*/

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
}