fix: dialogs tool
This commit is contained in:
parent
5a37b27d7c
commit
421f6874d3
6 changed files with 190 additions and 26 deletions
|
|
@ -46,7 +46,10 @@ tasks:
|
|||
desc: Run linter
|
||||
cmd: golangci-lint run --fix ./...
|
||||
|
||||
# git tag -a v0.1.1 -m "Added releases"
|
||||
# git push origin v0.1.0
|
||||
tag:
|
||||
desc: Create a new tag
|
||||
cmds:
|
||||
- git tag -a v0.1.6 -m "Dialog fix"
|
||||
- git push origin v0.1.6
|
||||
|
||||
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -8,6 +8,7 @@ require (
|
|||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/urfave/cli/v3 v3.1.0
|
||||
golang.org/x/time v0.11.0
|
||||
)
|
||||
|
|
@ -34,7 +35,6 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ogen-go/ogen v1.10.1 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gotd/td/tg"
|
||||
|
|
@ -70,11 +71,16 @@ func (c *Client) GetDialogs(args DialogsArguments) (*mcp.ToolResponse, error) {
|
|||
api := client.API()
|
||||
dialogsClass, err := api.MessagesGetDialogs(ctx, &tg.MessagesGetDialogsRequest{
|
||||
OffsetPeer: &tg.InputPeerEmpty{},
|
||||
Limit: 20,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get dialogs: %w", err)
|
||||
}
|
||||
|
||||
// Debug
|
||||
// jsonData, _ := json.Marshal(dialogsClass)
|
||||
// log.Info().RawJSON("dialogs", cleanJSON(jsonData)).Msg("dialogs")
|
||||
|
||||
var dialogs *tg.MessagesDialogs
|
||||
switch d := dialogsClass.(type) {
|
||||
case *tg.MessagesDialogs:
|
||||
|
|
@ -90,6 +96,25 @@ func (c *Client) GetDialogs(args DialogsArguments) (*mcp.ToolResponse, error) {
|
|||
return errors.New("unexpected dialogs response type")
|
||||
}
|
||||
|
||||
messageMap := make(map[string][]*tg.Message)
|
||||
for _, m := range dialogs.Messages {
|
||||
msg, ok := m.(*tg.Message)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.PeerID == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
messageMap[msg.PeerID.String()] = append(messageMap[msg.PeerID.String()], msg)
|
||||
}
|
||||
|
||||
usersMap := make(map[string]tg.UserClass)
|
||||
for _, u := range dialogs.Users {
|
||||
usersMap["Peer"+u.String()] = u
|
||||
}
|
||||
|
||||
result = make([]DialogInfo, 0, len(dialogs.Dialogs))
|
||||
|
||||
for _, dialog := range dialogs.Dialogs {
|
||||
|
|
@ -103,22 +128,27 @@ func (c *Client) GetDialogs(args DialogsArguments) (*mcp.ToolResponse, error) {
|
|||
info.LastMessageID = dialogItem.TopMessage
|
||||
|
||||
if args.WithLastMessages {
|
||||
for _, msg := range dialogs.Messages {
|
||||
message, ok := msg.(*tg.Message)
|
||||
if !ok {
|
||||
continue
|
||||
msgs := messageMap[dialogItem.Peer.String()]
|
||||
for _, msg := range msgs {
|
||||
var who string
|
||||
if msg.FromID != nil {
|
||||
if u, ok := usersMap[msg.FromID.String()]; ok {
|
||||
who = u.String()
|
||||
}
|
||||
}
|
||||
|
||||
var who string
|
||||
if message.FromID != nil {
|
||||
who = message.FromID.String()
|
||||
// Limit message to 20 words
|
||||
text := msg.Message
|
||||
words := strings.Fields(text)
|
||||
if len(words) > 20 {
|
||||
text = strings.Join(words[:20], " ") + "..."
|
||||
}
|
||||
|
||||
info.LastMessages = append(info.LastMessages, MessageInfo{
|
||||
Who: who,
|
||||
When: time.Unix(int64(message.Date), 0).Format(time.DateTime),
|
||||
Text: message.Message,
|
||||
IsUnread: !message.Out,
|
||||
When: time.Unix(int64(msg.Date), 0).Format(time.DateTime),
|
||||
Text: text,
|
||||
IsUnread: dialogItem.UnreadCount > 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -206,15 +236,6 @@ func (c *Client) GetDialogs(args DialogsArguments) (*mcp.ToolResponse, error) {
|
|||
return nil, errors.Wrap(err, "failed to marshal response")
|
||||
}
|
||||
|
||||
return mcp.NewToolResponse(mcp.NewTextContent(string(jsonData))), nil
|
||||
}
|
||||
|
||||
// Helper function to get user's name
|
||||
func getUserName(user *tg.User) string {
|
||||
name := user.FirstName
|
||||
if user.LastName != "" {
|
||||
name += " " + user.LastName
|
||||
}
|
||||
|
||||
return name
|
||||
cleanedData := cleanJSON(jsonData)
|
||||
return mcp.NewToolResponse(mcp.NewTextContent(string(cleanedData))), nil
|
||||
}
|
||||
|
|
|
|||
88
internal/tg/helpers.go
Normal file
88
internal/tg/helpers.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package tg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func getUserName(user *tg.User) string {
|
||||
if username, ok := user.GetUsername(); ok && username != "" {
|
||||
return "@" + username
|
||||
}
|
||||
|
||||
name := user.FirstName
|
||||
if user.LastName != "" {
|
||||
name += " " + user.LastName
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// cleanJSON removes empty/default fields from JSON
|
||||
func cleanJSON(data []byte) []byte {
|
||||
result := gjson.ParseBytes(data)
|
||||
cleaned := cleanValue(result)
|
||||
if cleaned == nil {
|
||||
return data // Return original if cleaning failed
|
||||
}
|
||||
|
||||
cleanedJSON, err := json.Marshal(cleaned)
|
||||
if err != nil {
|
||||
return data // Return original if marshaling failed
|
||||
}
|
||||
|
||||
return cleanedJSON
|
||||
}
|
||||
|
||||
func cleanValue(v gjson.Result) interface{} {
|
||||
switch v.Type {
|
||||
case gjson.String:
|
||||
if v.String() == "" {
|
||||
return nil
|
||||
}
|
||||
return v.String()
|
||||
case gjson.Number:
|
||||
// return nil
|
||||
if v.Int() == 0 && v.Float() == 0 {
|
||||
return nil
|
||||
}
|
||||
return v.Value()
|
||||
case gjson.True:
|
||||
return nil
|
||||
// return true
|
||||
case gjson.False:
|
||||
return nil
|
||||
case gjson.Null:
|
||||
return nil
|
||||
case gjson.JSON:
|
||||
if v.IsArray() {
|
||||
arr := make([]interface{}, 0)
|
||||
v.ForEach(func(_, item gjson.Result) bool {
|
||||
if cleaned := cleanValue(item); cleaned != nil {
|
||||
arr = append(arr, cleaned)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(arr) == 0 {
|
||||
return nil
|
||||
}
|
||||
return arr
|
||||
}
|
||||
if v.IsObject() {
|
||||
obj := make(map[string]interface{})
|
||||
v.ForEach(func(key, val gjson.Result) bool {
|
||||
if cleaned := cleanValue(val); cleaned != nil {
|
||||
obj[key.String()] = cleaned
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(obj) == 0 {
|
||||
return nil
|
||||
}
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
47
resourse.go
Normal file
47
resourse.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
mcp "github.com/metoro-io/mcp-golang"
|
||||
)
|
||||
|
||||
func sampleResource() (*mcp.ResourceResponse, error) {
|
||||
type Chat struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
UnreadCount int `json:"unread_count"`
|
||||
}
|
||||
|
||||
chats := []Chat{
|
||||
{
|
||||
ID: 123456789,
|
||||
Type: "channel",
|
||||
Title: "Sample Channel",
|
||||
UnreadCount: 5,
|
||||
},
|
||||
{
|
||||
ID: 987654321,
|
||||
Type: "group",
|
||||
Title: "Test Group",
|
||||
UnreadCount: 2,
|
||||
},
|
||||
}
|
||||
|
||||
rss := make([]*mcp.EmbeddedResource, 0, len(chats))
|
||||
for _, chat := range chats {
|
||||
chat.ID = 0
|
||||
uri := fmt.Sprintf("telegram://chats/%d", chat.ID)
|
||||
|
||||
content, err := json.Marshal(chat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rss = append(rss, mcp.NewTextEmbeddedResource(uri, string(content), "application/json"))
|
||||
}
|
||||
|
||||
return mcp.NewResourceResponse(rss...), nil
|
||||
}
|
||||
9
serve.go
9
serve.go
|
|
@ -56,16 +56,21 @@ func serve(ctx context.Context, cmd *cli.Command) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
err = server.RegisterTool("me", "Get current Telegram account info", client.GetMe)
|
||||
err = server.RegisterTool("tg_me", "Get current account info", client.GetMe)
|
||||
if err != nil {
|
||||
return fmt.Errorf("register tool: %w", err)
|
||||
}
|
||||
|
||||
err = server.RegisterTool("dialogs", "Get list of dialogs (chats, channels, groups)", client.GetDialogs)
|
||||
err = server.RegisterTool("tg_dialogs", "Get list of dialogs (chats, channels, groups)", client.GetDialogs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("register dialogs tool: %w", err)
|
||||
}
|
||||
|
||||
err = server.RegisterResource("telegram://chats", "tg_chats", "List of telegram chats", "application/json", sampleResource)
|
||||
if err != nil {
|
||||
return fmt.Errorf("register chats resource: %w", err)
|
||||
}
|
||||
|
||||
if err := server.Serve(); err != nil {
|
||||
return fmt.Errorf("serve: %w", err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue