-
Notifications
You must be signed in to change notification settings - Fork 1
feat: 引用メッセージのコンテキストへの取り込み #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fa8d84e
f57e448
2f03617
0c872e6
72f2a24
601ef21
ee7a615
6780002
0db4fde
fd201a4
ba14990
6c3f4fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package bot | ||
|
|
||
| import ( | ||
| "context" | ||
| "time" | ||
|
|
||
| "github.com/motoki317/sc" | ||
| ) | ||
|
|
||
| func getChannelPathInternal(ctx context.Context, channelID string) (string, error) { | ||
| bot := GetBot() | ||
|
|
||
| path, _, err := bot.API().ChannelAPI.GetChannelPath(ctx, channelID).Execute() | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| return path.Path, nil | ||
| } | ||
|
|
||
| var channelPathCache = sc.NewMust(getChannelPathInternal, time.Hour, time.Hour) | ||
|
|
||
| func GetChannelPath(channelID string) (string, error) { | ||
| return channelPathCache.Get(context.Background(), channelID) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package bot | ||
|
|
||
| import ( | ||
| "context" | ||
| "time" | ||
|
|
||
| "github.com/motoki317/sc" | ||
| "github.com/traPtitech/go-traq" | ||
| ) | ||
|
|
||
| func getUserInternal(ctx context.Context, userID string) (*traq.UserDetail, error) { | ||
| bot := GetBot() | ||
|
|
||
| user, _, err := bot.API().UserAPI.GetUser(ctx, userID).Execute() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return user, nil | ||
| } | ||
|
|
||
| var userCache = sc.NewMust(getUserInternal, time.Hour, time.Hour) | ||
|
|
||
| func GetUser(userID string) (*traq.UserDetail, error) { | ||
| return userCache.Get(context.Background(), userID) | ||
| } | ||
|
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's check the structure and find the relevant files
find . -name "user.go" -o -name "channel.go" | grep -E "(user|channel)\.go"Repository: traPtitech/BOT_GPT Length of output: 110 🏁 Script executed: # Now let's examine the GetUser function in user.go at lines 24-26
cat -n internal/bot/user.go | head -35Repository: traPtitech/BOT_GPT Length of output: 754 🏁 Script executed: # Let's also check the channel.go file to understand the similar pattern
cat -n internal/bot/channel.go | head -35Repository: traPtitech/BOT_GPT Length of output: 755 🏁 Script executed: # Search for all usages of GetUser to understand the impact
rg "GetUser\(" -A 2 -B 2Repository: traPtitech/BOT_GPT Length of output: 1135
Line 25で 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package formatter | ||
|
|
||
| import "regexp" | ||
|
|
||
| var embedRegex = regexp.MustCompile(`!\{"type":"(\w+?)","raw":"(.+?)","id":"[a-z0-9-]+?"\}`) | ||
|
|
||
| func FormatEmbeds(content string) string { | ||
| return embedRegex.ReplaceAllString(content, "$2") | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package formatter | ||
|
|
||
| import "testing" | ||
|
|
||
| func TestFormatEmbeds(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| input string | ||
| expected string | ||
| }{ | ||
| { | ||
| name: "No embeds", | ||
| input: "Hello, world!", | ||
| expected: "Hello, world!", | ||
| }, | ||
| { | ||
| name: "Single embed", | ||
| input: `Hello, !{"type":"channel","raw":"#general","id":"04ad2c18-fdcb-4c43-beef-82e8ba26ac98"}, world`, | ||
| expected: "Hello, #general, world", | ||
| }, | ||
| { | ||
| name: "Multiple embeds", | ||
| input: `!{"type":"user","raw":"@cp20","id":"be77174f-13c5-4464-8b15-7f45b96d5b18"}!{"type":"channel","raw":"#general","id":"04ad2c18-fdcb-4c43-beef-82e8ba26ac98"}`, | ||
| expected: "@cp20#general", | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| result := FormatEmbeds(tt.input) | ||
| if result != tt.expected { | ||
| t.Errorf("FormatEmbeds(%q) = %q; expected %q", tt.input, result, tt.expected) | ||
| } | ||
| }) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,106 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package formatter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "regexp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "unicode/utf8" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/traPtitech/BOT_GPT/internal/bot" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/traPtitech/go-traq" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const quoteRegexStr = `\bhttps://q\.trap\.jp/messages/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var quoteRegex = regexp.MustCompile(quoteRegexStr) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var allowingPrefixes = []string{"event", "general", "random", "services", "team/SysAd"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func isChannelAllowingQuotes(channelID string) (bool, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channelPath, err := bot.GetChannelPath(channelID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, prefix := range allowingPrefixes { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if strings.HasPrefix(channelPath, prefix) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 許可チャンネル判定が広すぎ、意図しないチャンネルを通す可能性があります。 Line 25 の 🔧 判定ロジックの例-var allowingPrefixes = []string{"event", "general", "random", "services", "team/SysAd"}
+var exactAllowingChannels = map[string]struct{}{
+ "event": {},
+ "general": {},
+ "random": {},
+ "services": {},
+}
func isChannelAllowingQuotes(channelID string) (bool, error) {
channelPath, err := bot.GetChannelPath(channelID)
if err != nil {
return false, err
}
- for _, prefix := range allowingPrefixes {
- if strings.HasPrefix(channelPath, prefix) {
- return true, nil
- }
- }
+ if _, ok := exactAllowingChannels[channelPath]; ok {
+ return true, nil
+ }
+ if channelPath == "team/SysAd" || strings.HasPrefix(channelPath, "team/SysAd/") {
+ return true, nil
+ }
return false, nil
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func isUserAllowingQuotes(userID string, messageUserID string) (bool, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if userID == messageUserID { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messageUser, err := bot.GetUser(messageUserID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if messageUser.Bot { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func getQuoteMarkdown(message *traq.Message) (string, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user, err := bot.GetUser(message.UserId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "", err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "> " + user.Name + ":\n> " + strings.ReplaceAll(message.Content, "\n", "\n> "), nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const maxQuoteLength = 10000 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func FormatQuotedMessage(userID string, content string) (string, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| matches := quoteRegex.FindAllSubmatch([]byte(content), len(content)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messageIDs := make([]string, 0, len(matches)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, match := range matches { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(match) < 2 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messageID := string(match[1]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messageIDs = append(messageIDs, messageID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var formattedContent strings.Builder | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| formattedContent.WriteString(quoteRegex.ReplaceAllString(content, "")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, messageID := range messageIDs { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message := bot.GetMessage(messageID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if message == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if utf8.RuneCountInString(message.Content) > maxQuoteLength { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runes := []rune(message.Content) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.Content = string(runes[:maxQuoteLength]) + "(以下略)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| channelAllowed, err := isChannelAllowingQuotes(message.ChannelId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "", err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| userAllowed, err := isUserAllowingQuotes(userID, message.UserId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "", err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !channelAllowed && !userAllowed { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| quote, err := getQuoteMarkdown(message) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "", err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| formattedContent.WriteString("\n\n" + quote) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return formattedContent.String(), nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: traPtitech/BOT_GPT
Length of output: 1801
🏁 Script executed:
Repository: traPtitech/BOT_GPT
Length of output: 1393
🏁 Script executed:
Repository: traPtitech/BOT_GPT
Length of output: 2410
🏁 Script executed:
Repository: traPtitech/BOT_GPT
Length of output: 2381
🏁 Script executed:
Repository: traPtitech/BOT_GPT
Length of output: 3466
🏁 Script executed:
Repository: traPtitech/BOT_GPT
Length of output: 3610
context.Background()固定では、外部API呼び出しのタイムアウト制御ができません。Line 24 の実装は、呼び出し元がタイムアウトを設定しても反映できません。ネットワーク遅延時にハンドラが長時間ブロックされるリスクがあります。
ただし、修正には複数レイヤーの変更が必要です:
GetChannelPath(channelID string)→GetChannelPath(ctx context.Context, channelID string)isChannelAllowingQuotes(channelID string)→isChannelAllowingQuotes(ctx context.Context, channelID string)FormatQuotedMessage(userID, content string)→FormatQuotedMessage(ctx context.Context, userID, content string)MessageReceived、DirectMessageReceived)にも context を追加現在のハンドラは WebSocket 接続に基づくイベントコールバックであり、context がないため、この変更は各レイヤーで実施が必要です。
💡 変更の概要
🤖 Prompt for AI Agents