first commit
This commit is contained in:
+578
@@ -0,0 +1,578 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cdle/sillyplus/core/common"
|
||||
"github.com/cdle/sillyplus/core/storage"
|
||||
"github.com/cdle/sillyplus/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
var cgs []CarryGroup
|
||||
|
||||
type CarryGroupsResult struct {
|
||||
Success bool `json:"success"`
|
||||
Data []CarryGroup `json:"data"`
|
||||
Page int `json:"page"`
|
||||
Total int `json:"total"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
var CarryGroups = MakeBucket("CarryGroups")
|
||||
|
||||
// LOGIC
|
||||
func init() {
|
||||
AddCommand([]*common.Function{
|
||||
{
|
||||
Rules: []string{`raw [\s\S]+`},
|
||||
Hidden: true,
|
||||
Handle: func(s common.Sender) interface{} {
|
||||
var bot_id = s.GetBotID()
|
||||
var platform = s.GetImType()
|
||||
var chat_id = s.GetChatID()
|
||||
var user_id = s.GetUserID()
|
||||
var content = s.GetContent()
|
||||
var from *CarryGroup //判断当前消息来自采集源
|
||||
var cgs = cgs
|
||||
var uuid = utils.GenUUID()
|
||||
for i := range cgs {
|
||||
if chat_id == cgs[i].ID && cgs[i].In {
|
||||
from = &cgs[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if nil == from { //非采集群
|
||||
s.Continue()
|
||||
return nil
|
||||
}
|
||||
bots_id := GetAdapterBotsID(platform)
|
||||
if len(from.BotsID) == 0 && len(bots_id) != 0 {
|
||||
from.BotsID = bots_id
|
||||
}
|
||||
//判断是否来自指定采集机器人
|
||||
var from_right_bot bool
|
||||
if len(from.BotsID) != 0 && from.BotsID[0] == bot_id {
|
||||
from_right_bot = true
|
||||
}
|
||||
//检测指定机器人是否离线,离线则使用其他第一个机器人,否则忽略消息
|
||||
if !from_right_bot {
|
||||
if len(from.BotsID) != 0 {
|
||||
if Contains(bots_id, bot_id) {
|
||||
console.Debug("%s 忽略机器人(%s)消息非采集指定机器人(%s)消息", uuid, bot_id, from.BotsID[0])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if len(bots_id) != 0 && bots_id[0] != bot_id { //不是第一个机器人
|
||||
console.Debug("%s 忽略机器人(%s)消息非其他第一个机器人(%s)的消息", uuid, bot_id, bots_id[0])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
console.Debug("%s 当前采集群 %s", uuid, chat_id)
|
||||
//预测采集白名单、黑名单
|
||||
if len(from.Allowed) != 0 { //白名单
|
||||
if !Contains(from.Allowed, user_id) {
|
||||
console.Debug("%s 用户(%s)不在采集群白名单 %v", uuid, chat_id)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
if Contains(from.Prohibited, user_id) {
|
||||
console.Debug("%s 用户(%s)在采集群黑名单 %v", uuid, chat_id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
//预测采集包含、排除词
|
||||
if len(from.Include) != 0 { //包含
|
||||
if word := Include(content, from.Include); word == "" {
|
||||
console.Debug("%s 消息中无采集包含词", uuid)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if len(from.Exclude) != 0 { //排除
|
||||
if word := Include(content, from.Exclude); word != "" {
|
||||
console.Debug("%s 消息中有采集排除词 %s", uuid, word)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var outs []CarryGroup //预测转发群
|
||||
for i := range cgs {
|
||||
if cgs[i].Out && cgs[i].ID != chat_id {
|
||||
for j := range cgs[i].From {
|
||||
if cgs[i].From[j] == chat_id {
|
||||
if len(cgs[i].Allowed) != 0 { //白名单
|
||||
if !Contains(cgs[i].Allowed, user_id) {
|
||||
console.Debug("%s 用户(%s)不在转发群(%s)白名单 %v", uuid, user_id, cgs[i].ID)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if Contains(cgs[i].Prohibited, user_id) {
|
||||
console.Debug("%s 用户(%s)在转发群(%s)黑名单 %v", uuid, user_id, cgs[i].ID)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(cgs[i].Include) != 0 { //包含
|
||||
if word := Include(content, cgs[i].Include); word == "" {
|
||||
console.Debug("%s 消息中无转发(s)包含词", uuid, cgs[i].ID)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(cgs[i].Exclude) != 0 { //排除
|
||||
if word := Include(content, cgs[i].Exclude); word != "" {
|
||||
console.Debug("%s 消息中有转发(s)排除词 %s", uuid, cgs[i].ID, word)
|
||||
continue
|
||||
}
|
||||
}
|
||||
outs = append(outs, cgs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.Debug("%s 预测转发群数目 %v", uuid, len(outs))
|
||||
if len(outs) == 0 {
|
||||
return nil
|
||||
}
|
||||
var scripts = []string{}
|
||||
//执行采集脚本
|
||||
fs := Functions
|
||||
for i := range fs {
|
||||
for j := range from.Scripts {
|
||||
if fs[i].UUID == from.Scripts[j] && !Contains(scripts, fs[i].UUID) {
|
||||
fs[i].Handle(s)
|
||||
content = s.GetContent()
|
||||
if content == "" {
|
||||
return nil
|
||||
}
|
||||
scripts = append(scripts, fs[i].UUID)
|
||||
}
|
||||
}
|
||||
}
|
||||
//执行转发脚本
|
||||
for i := range outs {
|
||||
var scripts = scripts
|
||||
var content = content
|
||||
for j := range outs[i].Scripts {
|
||||
for k := range fs {
|
||||
if fs[k].UUID == outs[i].Scripts[j] && !Contains(scripts, fs[k].UUID) { //
|
||||
fs[k].Handle(s)
|
||||
content = s.GetContent()
|
||||
fmt.Println(content)
|
||||
if content == "" {
|
||||
goto HELL
|
||||
}
|
||||
scripts = append(scripts, fs[k].UUID)
|
||||
}
|
||||
}
|
||||
}
|
||||
HELL:
|
||||
if content != "" { //选择机器人
|
||||
adapter, err := GetAdapter(append([]string{platform}, outs[i].BotsID...)...)
|
||||
if adapter == nil {
|
||||
console.Warn("%s 转发群(%s)相关机器人%v都不在线", uuid, outs[i].ID, outs[i].BotsID)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
console.Debug("%s 指定机器人都不在线,转发群(%s)已选择其他机器人(%s)推送", uuid, outs[i].ID, adapter.botid)
|
||||
}
|
||||
if adapter != nil {
|
||||
adapter.Push(map[string]string{
|
||||
CONETNT: content,
|
||||
CHAT_ID: chat_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// VAR Watch
|
||||
func init() {
|
||||
setCgs()
|
||||
storage.Watch(CarryGroups, nil, func(old, new, key string) *storage.Final {
|
||||
console.Log("已更新搬运数据。")
|
||||
ocg := CarryGroup{}
|
||||
ncg := CarryGroup{}
|
||||
json.Unmarshal([]byte(old), &ocg)
|
||||
json.Unmarshal([]byte(new), &ncg)
|
||||
tmp := cgs
|
||||
if old != "" {
|
||||
if new == "" { // 删除
|
||||
if ocg.ID != "" {
|
||||
for i, cg := range tmp {
|
||||
if cg.ID == ocg.ID {
|
||||
tmp = append(tmp[:i], tmp[i+1:]...)
|
||||
RemListenOnGroup(cg.ID, fmt.Sprintf("已为采集群(%s)关闭监听模式。", cg.ID))
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else { // 修改
|
||||
if ocg.ID != "" {
|
||||
for i, cg := range tmp {
|
||||
if cg.ID == ocg.ID {
|
||||
tmp[i] = ncg
|
||||
if ncg.In {
|
||||
if ncg.Enable {
|
||||
AddListenOnGroup(ncg.ID, fmt.Sprintf("已为采集群(%s)开启监听模式。", ncg.ID))
|
||||
} else {
|
||||
RemListenOnGroup(ncg.ID, fmt.Sprintf("已为采集群(%s)关闭监听模式。", ncg.ID))
|
||||
}
|
||||
} else {
|
||||
RemListenOnGroup(ncg.ID, fmt.Sprintf("已为采集群(%s)关闭监听模式。", ncg.ID))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else { //创建
|
||||
if ncg.ID != "" {
|
||||
tmp = append(tmp, ncg)
|
||||
if ncg.In && ncg.Enable {
|
||||
AddListenOnGroup(ncg.ID, fmt.Sprintf("已为采集群(%s)开启监听模式。", ncg.ID))
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
sort.Sort(byCreatedAt(tmp))
|
||||
for i := range tmp {
|
||||
tmp[i].Index = i + 1
|
||||
}
|
||||
cgs = tmp
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func setCgs() {
|
||||
CarryGroups.Foreach(func(b1, b2 []byte) error {
|
||||
cg := CarryGroup{}
|
||||
err := json.Unmarshal(b2, &cg)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if cg.In {
|
||||
AddListenOnGroup(cg.ID, fmt.Sprintf("已为采集群(%s)开启监听模式。", cg.ID))
|
||||
}
|
||||
cgs = append(cgs, cg)
|
||||
return nil
|
||||
})
|
||||
sort.Sort(byCreatedAt(cgs))
|
||||
for i := range cgs {
|
||||
cgs[i].Index = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
type CarryGroup struct {
|
||||
Index int `json:"id"` //编号 顺序编号
|
||||
In bool `json:"in"` //搬进来 勾选按钮
|
||||
Out bool `json:"out"` //运出去 勾选按钮
|
||||
From []string `json:"from"` //采集源
|
||||
Allowed []string `json:"allowed"` //白名单模式
|
||||
Prohibited []string `json:"prohibited"` //黑名单模式 Select选择器多选
|
||||
ID string `json:"chat_id"` //群组ID 文字表单
|
||||
ChatName string `json:"chat_name"` //群昵称 文字表单
|
||||
Remark string `json:"remark"` //备注
|
||||
Platform string `json:"platform"` //平台 Select选择器单选
|
||||
Enable bool `json:"enable"` //启用状态 开关
|
||||
Include []string `json:"include"` //包含关键词 多个关键词用逗号隔开 用户复制粘贴过去后自动转换成多彩标签
|
||||
Exclude []string `json:"exclude"` //排除关键词 包含关键词
|
||||
CreatedAt int `json:"created_at"` //创建时间戳(秒)转换成日期
|
||||
BotsID []string `json:"bots_id"` //工作机器人 多选
|
||||
Scripts []string `json:"scripts"` //处理脚本
|
||||
}
|
||||
|
||||
// CARRY API
|
||||
func init() {
|
||||
GinApi(GET, "/api/carry/groups", RequireAuth, func(ctx *gin.Context) {
|
||||
current := utils.Int(ctx.Query("current"))
|
||||
pageSize := utils.Int(ctx.Query("pageSize"))
|
||||
rr := CarryGroupsResult{
|
||||
Success: true,
|
||||
}
|
||||
cgs := cgs
|
||||
rr.Total = len(cgs)
|
||||
if current == 0 {
|
||||
current = 1
|
||||
}
|
||||
if pageSize == 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
begin := (current - 1) * pageSize
|
||||
end := (current) * pageSize
|
||||
if end > rr.Total {
|
||||
end = rr.Total
|
||||
}
|
||||
if begin > end {
|
||||
begin = end
|
||||
}
|
||||
rr.Data = cgs[begin:end]
|
||||
for i := range rr.Data {
|
||||
gn := &Nickname{
|
||||
ID: rr.Data[i].ID,
|
||||
}
|
||||
nickname.First(gn)
|
||||
if gn.Value != "" {
|
||||
rr.Data[i].ChatName = gn.Value
|
||||
}
|
||||
}
|
||||
ctx.JSON(200, rr)
|
||||
})
|
||||
GinApi(GET, "/api/carry/group_names", RequireAuth, func(ctx *gin.Context) {
|
||||
cgs := cgs
|
||||
var names = map[string]string{}
|
||||
for _, cg := range cgs {
|
||||
names[cg.ID] = cg.ChatName
|
||||
}
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": true,
|
||||
"data": names,
|
||||
})
|
||||
})
|
||||
GinApi(GET, "/api/carry/group_selects", RequireAuth, func(ctx *gin.Context) {
|
||||
chat_id := ctx.Query("chat_id")
|
||||
platform := ctx.Query("platform")
|
||||
cgs := cgs
|
||||
var names = map[string]string{}
|
||||
var bots_id = []string{}
|
||||
var users = []string{}
|
||||
for _, cg := range cgs {
|
||||
names[cg.ID] = cg.ChatName
|
||||
if cg.ID == chat_id {
|
||||
users = append(users, cg.Allowed...)
|
||||
users = append(users, cg.Prohibited...)
|
||||
if platform == "" {
|
||||
platform = cg.Platform
|
||||
}
|
||||
}
|
||||
}
|
||||
bots_id = GetAdapterBotsID(platform)
|
||||
var scripts = map[string]string{}
|
||||
functions := Functions
|
||||
for _, function := range functions {
|
||||
if function.UUID != "" && len(function.Rules) == 0 && !function.OnStart && !function.Module {
|
||||
scripts[function.UUID] = function.Title + ".js"
|
||||
}
|
||||
}
|
||||
var user_names = []NicklabeL{}
|
||||
nickname.Foreach(func(b1, b2 []byte) error {
|
||||
v := &Nickname{}
|
||||
code := string(b1)
|
||||
err := json.Unmarshal(b2, v)
|
||||
if err == nil {
|
||||
platforms = append(platforms, v.Platform)
|
||||
if Contains(users, code) {
|
||||
user_names = append(user_names, NicklabeL{
|
||||
Label: fmt.Sprintf("%s(%s)", v.Value, code),
|
||||
Value: code,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": true,
|
||||
"data": map[string]interface{}{
|
||||
"user_names": user_names,
|
||||
"group_names": names,
|
||||
"bots_id": bots_id,
|
||||
"platforms": getPltsArray(),
|
||||
"scripts": scripts,
|
||||
},
|
||||
})
|
||||
|
||||
})
|
||||
GinApi(POST, "/api/carry/group", RequireAuth, func(ctx *gin.Context) {
|
||||
// 将请求的 JSON 数据解析为一个 map[string]interface{} 类型的变量
|
||||
var updateData map[string]interface{}
|
||||
err := ctx.BindJSON(&updateData)
|
||||
if err != nil {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": false,
|
||||
"errorMessage": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
v, ok := updateData["chat_id"]
|
||||
if !ok {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": false,
|
||||
"errorMessage": "群号不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
chat_id := v.(string)
|
||||
var cg = CarryGroup{
|
||||
ID: chat_id,
|
||||
}
|
||||
err = CarryGroups.First(&cg)
|
||||
if err != nil {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": false,
|
||||
"errorMessage": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
for key, value := range updateData {
|
||||
switch key {
|
||||
case "in":
|
||||
if in, ok := value.(bool); ok {
|
||||
cg.In = in
|
||||
}
|
||||
case "out":
|
||||
if out, ok := value.(bool); ok {
|
||||
cg.Out = out
|
||||
}
|
||||
case "from":
|
||||
if from, ok := value.([]interface{}); ok {
|
||||
cg.From = toStringSlice(from)
|
||||
}
|
||||
case "allowed":
|
||||
if allowed, ok := value.([]interface{}); ok {
|
||||
cg.Allowed = toStringSlice(allowed)
|
||||
}
|
||||
case "prohibited":
|
||||
if prohibited, ok := value.([]interface{}); ok {
|
||||
cg.Prohibited = toStringSlice(prohibited)
|
||||
}
|
||||
case "chat_name":
|
||||
if chatName, ok := value.(string); ok {
|
||||
cg.ChatName = chatName
|
||||
}
|
||||
case "remark":
|
||||
if remark, ok := value.(string); ok {
|
||||
cg.Remark = remark
|
||||
}
|
||||
case "platform":
|
||||
if platform, ok := value.(string); ok {
|
||||
cg.Platform = platform
|
||||
}
|
||||
case "enable":
|
||||
if disable, ok := value.(bool); ok {
|
||||
cg.Enable = disable
|
||||
}
|
||||
case "include":
|
||||
if include, ok := value.([]interface{}); ok {
|
||||
cg.Include = toStringSlice(include)
|
||||
}
|
||||
case "exclude":
|
||||
if exclude, ok := value.([]interface{}); ok {
|
||||
cg.Exclude = toStringSlice(exclude)
|
||||
}
|
||||
case "bots_id":
|
||||
if botsID, ok := value.([]interface{}); ok {
|
||||
cg.BotsID = toStringSlice(botsID)
|
||||
}
|
||||
case "scripts":
|
||||
if scripts, ok := value.([]interface{}); ok {
|
||||
cg.Scripts = toStringSlice(scripts)
|
||||
}
|
||||
}
|
||||
}
|
||||
if cg.CreatedAt == 0 {
|
||||
cg.CreatedAt = int(time.Now().Unix())
|
||||
}
|
||||
CarryGroups.Set(chat_id, utils.JsonMarshal(cg))
|
||||
if err != nil {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": false,
|
||||
"errorMessage": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": true,
|
||||
})
|
||||
})
|
||||
GinApi(DELETE, "/api/carry/group", RequireAuth, func(ctx *gin.Context) {
|
||||
cg := &CarryGroup{}
|
||||
err := ctx.BindJSON(cg)
|
||||
if err != nil {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": false,
|
||||
"errorMessage": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
if cg.ID == "" {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": false,
|
||||
"errorMessage": "群号不为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
CarryGroups.Set(cg.ID, "")
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"success": true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type byCreatedAt []CarryGroup
|
||||
|
||||
func (s byCreatedAt) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s byCreatedAt) Less(i, j int) bool {
|
||||
return s[i].CreatedAt > s[j].CreatedAt
|
||||
}
|
||||
|
||||
func (s byCreatedAt) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// 将 []interface{} 转为 []string 的工具函数
|
||||
func toStringSlice(intfSlice []interface{}) []string {
|
||||
stringSlice := make([]string, len(intfSlice))
|
||||
for i, intf := range intfSlice {
|
||||
if str, ok := intf.(string); ok {
|
||||
stringSlice[i] = str
|
||||
}
|
||||
}
|
||||
return stringSlice
|
||||
}
|
||||
|
||||
func Contains(strs []string, str ...string) bool {
|
||||
for _, s := range str {
|
||||
for _, str := range strs {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Include(content string, includes []string) string {
|
||||
for _, include := range includes {
|
||||
if len(include) > 2 && include[0] == '/' && include[len(include)-1] == '/' {
|
||||
pattern := include[1 : len(include)-1]
|
||||
_, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
console.Error("包含词/排除词正则表达式 %s 错误 %s", include, err.Error())
|
||||
continue
|
||||
}
|
||||
match, err := regexp.MatchString(pattern, content)
|
||||
if err == nil && match {
|
||||
return include
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(content, include) {
|
||||
return include
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user