mirror of
https://github.com/HugeFrog24/go-telegram-bot.git
synced 2026-03-02 00:14:34 +00:00
Handle business messages
This commit is contained in:
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
13
anthropic.go
Normal file → Executable file
13
anthropic.go
Normal file → Executable file
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/liushuangls/go-anthropic/v2"
|
"github.com/liushuangls/go-anthropic/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *Bot) getAnthropicResponse(ctx context.Context, messages []anthropic.Message, isNewChat, isAdminOrOwner bool) (string, error) {
|
func (b *Bot) getAnthropicResponse(ctx context.Context, messages []anthropic.Message, isNewChat, isAdminOrOwner, isEmojiOnly bool) (string, error) {
|
||||||
// Use prompts from config
|
// Use prompts from config
|
||||||
var systemMessage string
|
var systemMessage string
|
||||||
if isNewChat {
|
if isNewChat {
|
||||||
@@ -23,6 +23,10 @@ func (b *Bot) getAnthropicResponse(ctx context.Context, messages []anthropic.Mes
|
|||||||
systemMessage += " " + b.config.SystemPrompts["avoid_sensitive"]
|
systemMessage += " " + b.config.SystemPrompts["avoid_sensitive"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isEmojiOnly {
|
||||||
|
systemMessage += " " + b.config.SystemPrompts["respond_with_emojis"]
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the roles are correct
|
// Ensure the roles are correct
|
||||||
for i := range messages {
|
for i := range messages {
|
||||||
switch messages[i].Role {
|
switch messages[i].Role {
|
||||||
@@ -36,13 +40,10 @@ func (b *Bot) getAnthropicResponse(ctx context.Context, messages []anthropic.Mes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
model := anthropic.ModelClaude3Dot5Sonnet20240620
|
model := anthropic.Model(b.config.Model)
|
||||||
if !isAdminOrOwner {
|
|
||||||
model = anthropic.ModelClaudeInstant1Dot2
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := b.anthropicClient.CreateMessages(ctx, anthropic.MessagesRequest{
|
resp, err := b.anthropicClient.CreateMessages(ctx, anthropic.MessagesRequest{
|
||||||
Model: model,
|
Model: model, // Now `model` is of type anthropic.Model
|
||||||
Messages: messages,
|
Messages: messages,
|
||||||
System: systemMessage,
|
System: systemMessage,
|
||||||
MaxTokens: 1000,
|
MaxTokens: 1000,
|
||||||
|
|||||||
56
bot.go
Normal file → Executable file
56
bot.go
Normal file → Executable file
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -162,10 +163,17 @@ func (b *Bot) prepareContextMessages(chatMemory *ChatMemory) []anthropic.Message
|
|||||||
if !msg.IsUser {
|
if !msg.IsUser {
|
||||||
role = anthropic.RoleAssistant
|
role = anthropic.RoleAssistant
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textContent := strings.TrimSpace(msg.Text)
|
||||||
|
if textContent == "" {
|
||||||
|
// Skip empty messages
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
contextMessages = append(contextMessages, anthropic.Message{
|
contextMessages = append(contextMessages, anthropic.Message{
|
||||||
Role: role,
|
Role: role,
|
||||||
Content: []anthropic.MessageContent{
|
Content: []anthropic.MessageContent{
|
||||||
anthropic.NewTextMessageContent(msg.Text),
|
anthropic.NewTextMessageContent(textContent),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -196,27 +204,37 @@ func initTelegramBot(token string, handleUpdate func(ctx context.Context, tgBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendResponse sends a message to the specified chat.
|
// sendResponse sends a message to the specified chat.
|
||||||
func (b *Bot) sendResponse(ctx context.Context, chatID int64, text string) {
|
// Returns an error if sending the message fails.
|
||||||
_, err := b.tgBot.SendMessage(ctx, &bot.SendMessageParams{
|
func (b *Bot) sendResponse(ctx context.Context, chatID int64, text string, businessConnectionID string) error {
|
||||||
|
params := &bot.SendMessageParams{
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
Text: text,
|
Text: text,
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("[%s] [ERROR] Error sending message: %v", b.config.ID, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if businessConnectionID != "" {
|
||||||
|
params.BusinessConnectionID = businessConnectionID
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := b.tgBot.SendMessage(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[%s] [ERROR] Error sending message to chat %d with BusinessConnectionID %s: %v",
|
||||||
|
b.config.ID, chatID, businessConnectionID, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendStats sends the bot statistics to the specified chat.
|
// sendStats sends the bot statistics to the specified chat.
|
||||||
func (b *Bot) sendStats(ctx context.Context, chatID int64) {
|
func (b *Bot) sendStats(ctx context.Context, chatID int64, businessConnectionID string) {
|
||||||
totalUsers, totalMessages, err := b.getStats()
|
totalUsers, totalMessages, err := b.getStats()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error fetching stats: %v\n", err)
|
fmt.Printf("Error fetching stats: %v\n", err)
|
||||||
b.sendResponse(ctx, chatID, "Sorry, I couldn't retrieve the stats at this time.")
|
b.sendResponse(ctx, chatID, "Sorry, I couldn't retrieve the stats at this time.", businessConnectionID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
statsMessage := fmt.Sprintf("📊 **Bot Statistics:**\n\n- Total Users: %d\n- Total Messages: %d", totalUsers, totalMessages)
|
statsMessage := fmt.Sprintf("📊 **Bot Statistics:**\n\n- Total Users: %d\n- Total Messages: %d", totalUsers, totalMessages)
|
||||||
b.sendResponse(ctx, chatID, statsMessage)
|
b.sendResponse(ctx, chatID, statsMessage, businessConnectionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStats retrieves the total number of users and messages from the database.
|
// getStats retrieves the total number of users and messages from the database.
|
||||||
@@ -233,3 +251,23 @@ func (b *Bot) getStats() (int64, int64, error) {
|
|||||||
|
|
||||||
return totalUsers, totalMessages, nil
|
return totalUsers, totalMessages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isOnlyEmojis checks if the string consists solely of emojis.
|
||||||
|
func isOnlyEmojis(s string) bool {
|
||||||
|
for _, r := range s {
|
||||||
|
if !isEmoji(r) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEmoji determines if a rune is an emoji.
|
||||||
|
// This is a simplistic check and can be expanded based on requirements.
|
||||||
|
func isEmoji(r rune) bool {
|
||||||
|
return (r >= 0x1F600 && r <= 0x1F64F) || // Emoticons
|
||||||
|
(r >= 0x1F300 && r <= 0x1F5FF) || // Misc Symbols and Pictographs
|
||||||
|
(r >= 0x1F680 && r <= 0x1F6FF) || // Transport and Map
|
||||||
|
(r >= 0x2600 && r <= 0x26FF) || // Misc symbols
|
||||||
|
(r >= 0x2700 && r <= 0x27BF) // Dingbats
|
||||||
|
}
|
||||||
|
|||||||
41
config.go
Normal file → Executable file
41
config.go
Normal file → Executable file
@@ -5,16 +5,37 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/liushuangls/go-anthropic/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BotConfig struct {
|
type BotConfig struct {
|
||||||
ID string `json:"id"` // Unique identifier for the bot
|
ID string `json:"id"` // Unique identifier for the bot
|
||||||
|
TelegramToken string `json:"telegram_token"` // Telegram Bot Token
|
||||||
MemorySize int `json:"memory_size"`
|
MemorySize int `json:"memory_size"`
|
||||||
MessagePerHour int `json:"messages_per_hour"`
|
MessagePerHour int `json:"messages_per_hour"`
|
||||||
MessagePerDay int `json:"messages_per_day"`
|
MessagePerDay int `json:"messages_per_day"`
|
||||||
TempBanDuration string `json:"temp_ban_duration"`
|
TempBanDuration string `json:"temp_ban_duration"`
|
||||||
|
Model anthropic.Model `json:"model"` // Changed from string to anthropic.Model
|
||||||
SystemPrompts map[string]string `json:"system_prompts"`
|
SystemPrompts map[string]string `json:"system_prompts"`
|
||||||
TelegramToken string `json:"telegram_token"` // Telegram Bot Token
|
}
|
||||||
|
|
||||||
|
// Custom unmarshalling to handle anthropic.Model
|
||||||
|
func (c *BotConfig) UnmarshalJSON(data []byte) error {
|
||||||
|
type Alias BotConfig
|
||||||
|
aux := &struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(c),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &aux); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Model = anthropic.Model(aux.Model)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAllConfigs(dir string) ([]BotConfig, error) {
|
func loadAllConfigs(dir string) ([]BotConfig, error) {
|
||||||
@@ -57,6 +78,11 @@ func loadAllConfigs(dir string) ([]BotConfig, error) {
|
|||||||
}
|
}
|
||||||
tokens[config.TelegramToken] = true
|
tokens[config.TelegramToken] = true
|
||||||
|
|
||||||
|
// Validate Model
|
||||||
|
if config.Model == "" {
|
||||||
|
return nil, fmt.Errorf("config %s is missing 'model' field", configPath)
|
||||||
|
}
|
||||||
|
|
||||||
configs = append(configs, config)
|
configs = append(configs, config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,14 +103,6 @@ func loadConfig(filename string) (BotConfig, error) {
|
|||||||
return config, fmt.Errorf("failed to decode JSON from %s: %w", filename, err)
|
return config, fmt.Errorf("failed to decode JSON from %s: %w", filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optionally override telegram_token with environment variable if set
|
|
||||||
// Uncomment the following lines if you choose to use environment variables for tokens
|
|
||||||
/*
|
|
||||||
if envToken := os.Getenv(fmt.Sprintf("TELEGRAM_TOKEN_%s", config.ID)); envToken != "" {
|
|
||||||
config.TelegramToken = envToken
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,5 +118,8 @@ func (c *BotConfig) Reload(filename string) error {
|
|||||||
return fmt.Errorf("failed to decode JSON from %s: %w", filename, err)
|
return fmt.Errorf("failed to decode JSON from %s: %w", filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure the Model is correctly casted
|
||||||
|
c.Model = anthropic.Model(c.Model)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
4
config/default.json
Normal file → Executable file
4
config/default.json
Normal file → Executable file
@@ -5,10 +5,12 @@
|
|||||||
"messages_per_hour": 20,
|
"messages_per_hour": 20,
|
||||||
"messages_per_day": 100,
|
"messages_per_day": 100,
|
||||||
"temp_ban_duration": "24h",
|
"temp_ban_duration": "24h",
|
||||||
|
"model": "claude-3-5-sonnet-20240620",
|
||||||
"system_prompts": {
|
"system_prompts": {
|
||||||
"default": "You are a helpful assistant.",
|
"default": "You are a helpful assistant.",
|
||||||
"custom_instructions": "Please follow these guidelines:\n- Your name is Atom.\n- If a user asks about buying apples, inform them that we don't sell apples.\n- When asked for a joke, tell a clean, family-friendly joke about programming or technology.\n- If someone inquires about our services, explain that we offer AI-powered chatbot solutions.\n- For any questions about pricing, direct users to contact our sales team at sales@example.com.\n- If asked about your capabilities, be honest about what you can and cannot do.\nAlways maintain a friendly and professional tone.",
|
"custom_instructions": "Please follow these guidelines:\n- Your name is Atom.\n- If a user asks about buying apples, inform them that we don't sell apples.\n- When asked for a joke, tell a clean, family-friendly joke about programming or technology.\n- If someone inquires about our services, explain that we offer AI-powered chatbot solutions.\n- For any questions about pricing, direct users to contact our sales team at sales@example.com.\n- If asked about your capabilities, be honest about what you can and cannot do.\nAlways maintain a friendly and professional tone.",
|
||||||
"continue_conversation": "Continuing our conversation. Remember previous context if relevant.",
|
"continue_conversation": "Continuing our conversation. Remember previous context if relevant.",
|
||||||
"avoid_sensitive": "Avoid discussing sensitive topics or providing harmful information."
|
"avoid_sensitive": "Avoid discussing sensitive topics or providing harmful information.",
|
||||||
|
"respond_with_emojis": "Since the user sent only emojis, respond using emojis only."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0
database.go
Normal file → Executable file
0
database.go
Normal file → Executable file
6
go.mod
Normal file → Executable file
6
go.mod
Normal file → Executable file
@@ -3,7 +3,7 @@ module github.com/HugeFrog24/thatsky-telegram-bot
|
|||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-telegram/bot v1.8.4
|
github.com/go-telegram/bot v1.9.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/liushuangls/go-anthropic/v2 v2.8.1
|
github.com/liushuangls/go-anthropic/v2 v2.8.1
|
||||||
golang.org/x/time v0.7.0
|
golang.org/x/time v0.7.0
|
||||||
@@ -14,6 +14,6 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
12
go.sum
Normal file → Executable file
12
go.sum
Normal file → Executable file
@@ -1,5 +1,5 @@
|
|||||||
github.com/go-telegram/bot v1.8.4 h1:7viEUESakK29aiCumq6ui5jTPqJLLDeFubTsQzE07Kg=
|
github.com/go-telegram/bot v1.9.0 h1:z9g0Fgk9B7G/xoVMqji30hpJPlr3Dz3aVW2nzSGfPuI=
|
||||||
github.com/go-telegram/bot v1.8.4/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM=
|
github.com/go-telegram/bot v1.9.0/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
@@ -8,10 +8,10 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/liushuangls/go-anthropic/v2 v2.8.1 h1:pxFl88IgkG7e8Z1XwOYu48LcmEN0+6UdO58HF9altw0=
|
github.com/liushuangls/go-anthropic/v2 v2.8.1 h1:pxFl88IgkG7e8Z1XwOYu48LcmEN0+6UdO58HF9altw0=
|
||||||
github.com/liushuangls/go-anthropic/v2 v2.8.1/go.mod h1:8BKv/fkeTaL5R9R9bGkaknYBueyw2WxY20o7bImbOek=
|
github.com/liushuangls/go-anthropic/v2 v2.8.1/go.mod h1:8BKv/fkeTaL5R9R9bGkaknYBueyw2WxY20o7bImbOek=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
|
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
|
||||||
|
|||||||
125
handlers.go
Normal file → Executable file
125
handlers.go
Normal file → Executable file
@@ -11,35 +11,63 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (b *Bot) handleUpdate(ctx context.Context, tgBot *bot.Bot, update *models.Update) {
|
func (b *Bot) handleUpdate(ctx context.Context, tgBot *bot.Bot, update *models.Update) {
|
||||||
if update.Message == nil {
|
var message *models.Message
|
||||||
|
|
||||||
|
if update.Message != nil {
|
||||||
|
message = update.Message
|
||||||
|
} else if update.BusinessMessage != nil {
|
||||||
|
message = update.BusinessMessage
|
||||||
|
} else {
|
||||||
|
// No message to process
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chatID := update.Message.Chat.ID
|
chatID := message.Chat.ID
|
||||||
userID := update.Message.From.ID
|
userID := message.From.ID
|
||||||
|
|
||||||
|
// Extract businessConnectionID if available
|
||||||
|
var businessConnectionID string
|
||||||
|
if update.BusinessConnection != nil {
|
||||||
|
businessConnectionID = update.BusinessConnection.ID
|
||||||
|
} else if message.BusinessConnectionID != "" {
|
||||||
|
businessConnectionID = message.BusinessConnectionID
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the message is a command
|
// Check if the message is a command
|
||||||
if update.Message.Entities != nil {
|
if message.Entities != nil {
|
||||||
for _, entity := range update.Message.Entities {
|
for _, entity := range message.Entities {
|
||||||
if entity.Type == "bot_command" {
|
if entity.Type == "bot_command" {
|
||||||
command := strings.TrimSpace(update.Message.Text[entity.Offset : entity.Offset+entity.Length])
|
command := strings.TrimSpace(message.Text[entity.Offset : entity.Offset+entity.Length])
|
||||||
switch command {
|
switch command {
|
||||||
case "/stats":
|
case "/stats":
|
||||||
b.sendStats(ctx, chatID)
|
b.sendStats(ctx, chatID, businessConnectionID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Existing rate limit and message handling
|
// Check if the message contains a sticker
|
||||||
if !b.checkRateLimits(userID) {
|
if message.Sticker != nil {
|
||||||
b.sendRateLimitExceededMessage(ctx, chatID)
|
b.handleStickerMessage(ctx, chatID, userID, message, businessConnectionID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
username := update.Message.From.Username
|
// Existing rate limit and message handling
|
||||||
text := update.Message.Text
|
if !b.checkRateLimits(userID) {
|
||||||
|
b.sendRateLimitExceededMessage(ctx, chatID, businessConnectionID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username := message.From.Username
|
||||||
|
text := message.Text
|
||||||
|
|
||||||
|
// Proceed only if the message contains text
|
||||||
|
if text == "" {
|
||||||
|
// Optionally, handle other message types or ignore
|
||||||
|
log.Printf("Received a non-text message from user %d in chat %d", userID, chatID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
user, err := b.getOrCreateUser(userID, username)
|
user, err := b.getOrCreateUser(userID, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -56,19 +84,84 @@ func (b *Bot) handleUpdate(ctx context.Context, tgBot *bot.Bot, update *models.U
|
|||||||
|
|
||||||
contextMessages := b.prepareContextMessages(chatMemory)
|
contextMessages := b.prepareContextMessages(chatMemory)
|
||||||
|
|
||||||
response, err := b.getAnthropicResponse(ctx, contextMessages, b.isNewChat(chatID), b.isAdminOrOwner(userID))
|
isEmojiOnly := isOnlyEmojis(text) // Ensure you have this variable defined
|
||||||
|
response, err := b.getAnthropicResponse(ctx, contextMessages, b.isNewChat(chatID), b.isAdminOrOwner(userID), isEmojiOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting Anthropic response: %v", err)
|
log.Printf("Error getting Anthropic response: %v", err)
|
||||||
response = "I'm sorry, I'm having trouble processing your request right now."
|
response = "I'm sorry, I'm having trouble processing your request right now."
|
||||||
}
|
}
|
||||||
|
|
||||||
b.sendResponse(ctx, chatID, response)
|
b.sendResponse(ctx, chatID, response, businessConnectionID)
|
||||||
|
|
||||||
assistantMessage := b.createMessage(chatID, 0, "", string(anthropic.RoleAssistant), response, false)
|
assistantMessage := b.createMessage(chatID, 0, "", string(anthropic.RoleAssistant), response, false)
|
||||||
b.storeMessage(assistantMessage)
|
b.storeMessage(assistantMessage)
|
||||||
b.addMessageToChatMemory(chatMemory, assistantMessage)
|
b.addMessageToChatMemory(chatMemory, assistantMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bot) sendRateLimitExceededMessage(ctx context.Context, chatID int64) {
|
func (b *Bot) sendRateLimitExceededMessage(ctx context.Context, chatID int64, businessConnectionID string) {
|
||||||
b.sendResponse(ctx, chatID, "Rate limit exceeded. Please try again later.")
|
b.sendResponse(ctx, chatID, "Rate limit exceeded. Please try again later.", businessConnectionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) handleStickerMessage(ctx context.Context, chatID, userID int64, message *models.Message, businessConnectionID string) {
|
||||||
|
username := message.From.Username
|
||||||
|
|
||||||
|
// Create and store the sticker message
|
||||||
|
userMessage := b.createMessage(chatID, userID, username, "user", "Sent a sticker.", true)
|
||||||
|
userMessage.StickerFileID = message.Sticker.FileID
|
||||||
|
|
||||||
|
// Safely store the Thumbnail's FileID if available
|
||||||
|
if message.Sticker.Thumbnail != nil {
|
||||||
|
userMessage.StickerPNGFile = message.Sticker.Thumbnail.FileID
|
||||||
|
}
|
||||||
|
|
||||||
|
b.storeMessage(userMessage)
|
||||||
|
|
||||||
|
// Update chat memory
|
||||||
|
chatMemory := b.getOrCreateChatMemory(chatID)
|
||||||
|
b.addMessageToChatMemory(chatMemory, userMessage)
|
||||||
|
|
||||||
|
// Generate AI response about the sticker
|
||||||
|
response, err := b.generateStickerResponse(ctx, userMessage)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error generating sticker response: %v", err)
|
||||||
|
// Provide a fallback dynamic response based on sticker type
|
||||||
|
if message.Sticker.IsAnimated {
|
||||||
|
response = "Wow, that's a cool animated sticker!"
|
||||||
|
} else if message.Sticker.IsVideo {
|
||||||
|
response = "Interesting video sticker!"
|
||||||
|
} else {
|
||||||
|
response = "That's a cool sticker!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.sendResponse(ctx, chatID, response, businessConnectionID)
|
||||||
|
|
||||||
|
assistantMessage := b.createMessage(chatID, 0, "", string(anthropic.RoleAssistant), response, false)
|
||||||
|
b.storeMessage(assistantMessage)
|
||||||
|
b.addMessageToChatMemory(chatMemory, assistantMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) generateStickerResponse(ctx context.Context, message Message) (string, error) {
|
||||||
|
// Example: Use the sticker type to generate a response
|
||||||
|
if message.StickerFileID != "" {
|
||||||
|
// Prepare context with information about the sticker
|
||||||
|
contextMessages := []anthropic.Message{
|
||||||
|
{
|
||||||
|
Role: anthropic.RoleUser,
|
||||||
|
Content: []anthropic.MessageContent{
|
||||||
|
anthropic.NewTextMessageContent("User sent a sticker."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since this is a sticker message, isEmojiOnly is false
|
||||||
|
response, err := b.getAnthropicResponse(ctx, contextMessages, false, false, false)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Hmm, that's interesting!", nil
|
||||||
}
|
}
|
||||||
|
|||||||
23
models.go
Normal file → Executable file
23
models.go
Normal file → Executable file
@@ -28,19 +28,22 @@ type ConfigModel struct {
|
|||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
BotID uint
|
BotID uint
|
||||||
ChatID int64
|
ChatID int64
|
||||||
UserID int64
|
UserID int64
|
||||||
Username string
|
Username string
|
||||||
UserRole string
|
UserRole string
|
||||||
Text string
|
Text string
|
||||||
Timestamp time.Time
|
StickerFileID string `json:"sticker_file_id,omitempty"` // New field to store Sticker File ID
|
||||||
IsUser bool
|
StickerPNGFile string `json:"sticker_png_file,omitempty"` // Optionally store PNG file ID if needed
|
||||||
|
Timestamp time.Time
|
||||||
|
IsUser bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatMemory struct {
|
type ChatMemory struct {
|
||||||
Messages []Message
|
Messages []Message
|
||||||
Size int
|
Size int
|
||||||
|
BusinessConnectionID string // New field to store the business connection ID
|
||||||
}
|
}
|
||||||
|
|
||||||
type Role struct {
|
type Role struct {
|
||||||
|
|||||||
0
rate_limiter.go
Normal file → Executable file
0
rate_limiter.go
Normal file → Executable file
0
rate_limiter_test.go
Normal file → Executable file
0
rate_limiter_test.go
Normal file → Executable file
Reference in New Issue
Block a user