Skip to content

Commit 838da25

Browse files
初始化项目
1 parent be771ed commit 838da25

22 files changed

+1765
-3
lines changed

bot.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,66 @@
11
package bot
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"sync/atomic"
8+
9+
mapset "github.com/deckarep/golang-set/v2"
10+
"github.com/greek-milk-bot/core/models"
11+
"github.com/greek-milk-bot/core/utils"
12+
)
13+
14+
type GreekMilkBot struct {
15+
plugins map[string]*models.PluginInstance
16+
route *Router[models.Packet]
17+
once *atomic.Bool
18+
}
19+
20+
func NewGreekMilkBot(plugins ...models.Plugin) (*GreekMilkBot, error) {
21+
if len(plugins) == 0 {
22+
return nil, errors.New("no plugins")
23+
}
24+
r := &GreekMilkBot{
25+
plugins: make(map[string]*models.PluginInstance),
26+
route: NewRouter[models.Packet](8),
27+
once: new(atomic.Bool),
28+
}
29+
for i, plugin := range plugins {
30+
id := fmt.Sprintf("%d", i)
31+
if plugin == nil {
32+
return nil, errors.New("nil plugin")
33+
}
34+
inst := &models.PluginInstance{
35+
Plugin: plugin,
36+
Meta: utils.NewMap[string, string](),
37+
Tools: mapset.NewSet[string](),
38+
Resources: utils.NewMap[string, models.ResourceProvider](),
39+
}
40+
r.plugins[id] = inst
41+
}
42+
return r, nil
43+
}
44+
func (r *GreekMilkBot) Run(ctx context.Context) error {
45+
if r.once.Swap(true) {
46+
return errors.New("plugin already running")
47+
}
48+
for id, plugin := range r.plugins {
49+
if err := func() error {
50+
bootCtx, cancel := context.WithCancel(ctx)
51+
defer cancel()
52+
plugin.Bus = models.PluginBus{
53+
Context: bootCtx,
54+
ID: id,
55+
}
56+
if err := plugin.Boot(plugin.Bus); err != nil {
57+
return err
58+
}
59+
return nil
60+
}(); err != nil {
61+
return err
62+
}
63+
}
64+
r.route.RunContext(ctx)
65+
return nil
66+
}

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ go 1.25
44

55
require (
66
github.com/deckarep/golang-set/v2 v2.8.0
7-
github.com/google/uuid v1.6.0
87
github.com/stretchr/testify v1.11.1
98
)
109

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
33
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
44
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
5-
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
6-
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
75
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
86
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
97
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=

models/account.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package models
2+
3+
// Guild 群聊
4+
type Guild struct {
5+
Id string `json:"id"`
6+
Name string `json:"name"`
7+
Avatar Resource `json:"avatar"`
8+
}
9+
10+
type GuildMember struct {
11+
*User `json:",inline"`
12+
13+
GuildName string `json:"alias"`
14+
GuildRole []string `json:"role"`
15+
}
16+
17+
type User struct {
18+
Id string `json:"id"`
19+
Name string `json:"name"`
20+
Avatar Resource `json:"avatar"`
21+
}

models/bot.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package models
2+
3+
import (
4+
mapset "github.com/deckarep/golang-set/v2"
5+
"github.com/greek-milk-bot/core/utils"
6+
)
7+
8+
type PluginInstance struct {
9+
Plugin
10+
Bus PluginBus // 每个消息通信上下文
11+
Meta *utils.Map[string, string] // 元数据
12+
Tools mapset.Set[string] // 可用的 RPC 工具包
13+
Resources *utils.Map[string, ResourceProvider] // 支持的资源解析器
14+
}
15+
16+
func NewPluginInstance(p Plugin) *PluginInstance {
17+
return &PluginInstance{
18+
Plugin: p,
19+
Meta: utils.NewMap[string, string](),
20+
Tools: mapset.NewSet[string](),
21+
Resources: utils.NewMap[string, ResourceProvider](),
22+
}
23+
}

models/bus.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package models
2+
3+
import (
4+
"context"
5+
)
6+
7+
type PluginBus struct {
8+
context.Context
9+
ID string
10+
}
11+
12+
func (bus PluginBus) SendPacket(packet Packet) error {
13+
panic("implement me")
14+
}

models/content.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package models
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"reflect"
7+
"strings"
8+
)
9+
10+
var (
11+
covertMap = make(map[string]reflect.Type)
12+
covertMapR = make(map[reflect.Type]string)
13+
)
14+
15+
type (
16+
Contents []Content
17+
RAWContents []RawContent
18+
)
19+
20+
func (contents *Contents) UnmarshalJSON(bytes []byte) error {
21+
var raw RAWContents
22+
err := json.Unmarshal(bytes, &raw)
23+
if err != nil {
24+
return err
25+
}
26+
result := make(Contents, 0, len(raw))
27+
for _, content := range raw {
28+
r := covertMap[content.Type]
29+
if r == nil {
30+
result = append(result,
31+
ContentUnknown{
32+
Type: content.Type,
33+
Value: content.Data,
34+
})
35+
continue
36+
}
37+
i := reflect.New(r).Interface()
38+
if err := json.Unmarshal([]byte(content.Data), i); err != nil {
39+
return err
40+
}
41+
result = append(result, reflect.ValueOf(i).Elem().Interface().(Content))
42+
}
43+
*contents = result
44+
return nil
45+
}
46+
47+
func (contents *Contents) MarshalJSON() ([]byte, error) {
48+
result := make(RAWContents, 0, len(*contents))
49+
for _, content := range *contents {
50+
t := reflect.TypeOf(content)
51+
if t.Kind() == reflect.Ptr {
52+
t = t.Elem()
53+
}
54+
s := covertMapR[t]
55+
if s == "" {
56+
switch typedContent := content.(type) {
57+
case ContentUnknown:
58+
result = append(result, RawContent{
59+
Type: typedContent.Type,
60+
Data: typedContent.Value,
61+
})
62+
continue
63+
default:
64+
return nil, fmt.Errorf("unknown type %T", typedContent)
65+
}
66+
}
67+
data, err := json.Marshal(content)
68+
if err != nil {
69+
return nil, err
70+
}
71+
result = append(result, RawContent{
72+
Type: s,
73+
Data: string(data),
74+
})
75+
}
76+
return json.Marshal(result)
77+
}
78+
79+
func (contents *Contents) String() string {
80+
var data []string
81+
for _, content := range *contents {
82+
data = append(data, content.String())
83+
}
84+
return strings.Join(data, "")
85+
}
86+
87+
type Content interface {
88+
fmt.Stringer
89+
}
90+
91+
type RawContent struct {
92+
Type string `json:"type"`
93+
Data string `json:"data"`
94+
}
95+
96+
var baseType = reflect.TypeOf((*Content)(nil)).Elem()
97+
98+
func RegisterContent(key string, typeOf reflect.Type) {
99+
if _, ok := covertMap[key]; ok {
100+
panic("duplicate key " + key)
101+
}
102+
if !typeOf.Implements(baseType) {
103+
panic("type" + key + " not implements Content")
104+
}
105+
if typeOf.Kind() == reflect.Ptr {
106+
typeOf = typeOf.Elem()
107+
}
108+
covertMap[key] = typeOf
109+
covertMapR[typeOf] = key
110+
}
111+
112+
type ContentUnknown struct {
113+
Type string `json:"type"`
114+
Value string `json:"value"`
115+
}
116+
117+
func (c ContentUnknown) String() string {
118+
return fmt.Sprintf("unknown[type=%s]", c.Type)
119+
}

models/content_types.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package models
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
)
7+
8+
type ContentText struct {
9+
Text string `json:"text"`
10+
}
11+
12+
func (c ContentText) String() string {
13+
return c.Text
14+
}
15+
16+
type ContentAt struct {
17+
Uid string `json:"uid"`
18+
User *User `json:"user"`
19+
}
20+
21+
func (c ContentAt) String() string {
22+
return fmt.Sprintf("@%s", c.Uid)
23+
}
24+
25+
type ContentImage struct {
26+
Resource Resource `json:"data"`
27+
Summary string `json:"summary"`
28+
}
29+
30+
func (c ContentImage) String() string {
31+
return fmt.Sprintf("image[summary=%s,blob]", c.Summary)
32+
}
33+
34+
func init() {
35+
RegisterContent("text", reflect.TypeOf((*ContentText)(nil)))
36+
RegisterContent("at", reflect.TypeOf((*ContentAt)(nil)))
37+
RegisterContent("image", reflect.TypeOf((*ContentImage)(nil)))
38+
}

models/handler.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package models
2+
3+
import (
4+
"context"
5+
"net/url"
6+
)
7+
8+
type PluginHandler func(ctx context.Context, url url.URL) (Plugin, error)
9+
10+
var plugins = make(map[string]PluginHandler)
11+
12+
func GetPlugin(key string) (PluginHandler, bool) {
13+
handler, ok := plugins[key]
14+
return handler, ok
15+
}
16+
17+
func RegisterPlugin(name string, plugin PluginHandler) {
18+
if plugins[name] != nil {
19+
panic("duplicate adapter name: " + name)
20+
}
21+
plugins[name] = plugin
22+
}

models/packet.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package models
2+
3+
import "encoding/json"
4+
5+
type PacketType string
6+
7+
var (
8+
PacketTypeEvent = PacketType("event") // 消息
9+
PacketTypeCall = PacketType("call") // 控制
10+
PacketTypeMeta = PacketType("meta") // 元数据控制
11+
)
12+
13+
type Packet struct {
14+
Src string `json:"src"`
15+
Dest string `json:"dest"`
16+
Type PacketType `json:"type"`
17+
Data any `json:"data"`
18+
}
19+
20+
type WithSrcPacket[T any] struct {
21+
Src string `json:"src"` // src id
22+
Data T `json:"data"` // data
23+
}
24+
25+
type jsonPacket struct {
26+
Src string `json:"src"`
27+
Dest string `json:"dest"`
28+
Type PacketType `json:"type"`
29+
Data json.RawMessage `json:"data"`
30+
}
31+
32+
func (p *Packet) UnmarshalJSON(data []byte) error {
33+
var jp jsonPacket
34+
if err := json.Unmarshal(data, &jp); err != nil {
35+
return err
36+
}
37+
p.Src = jp.Src
38+
p.Dest = jp.Dest
39+
p.Type = jp.Type
40+
switch p.Type {
41+
case PacketTypeEvent:
42+
var e Event
43+
if err := json.Unmarshal(jp.Data, &e); err != nil {
44+
return err
45+
}
46+
p.Data = &e
47+
case PacketTypeCall:
48+
var c PacketCall
49+
if err := json.Unmarshal(jp.Data, &c); err != nil {
50+
return err
51+
}
52+
p.Data = &c
53+
case PacketTypeMeta:
54+
var m PacketMeta
55+
if err := json.Unmarshal(jp.Data, &m); err != nil {
56+
return err
57+
}
58+
p.Data = &m
59+
}
60+
return nil
61+
}

0 commit comments

Comments
 (0)