Skip to content

Martin/feat chat new event#274

Open
loscarepapa wants to merge 4 commits intoasternic:mainfrom
SrAnuncio:martin/feat_chat_new_event
Open

Martin/feat chat new event#274
loscarepapa wants to merge 4 commits intoasternic:mainfrom
SrAnuncio:martin/feat_chat_new_event

Conversation

@loscarepapa
Copy link

No description provided.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the application's eventing capabilities by introducing a ChatNew webhook event, providing users with real-time notifications for new chat initiations. Concurrently, it streamlines the deployment process for EasyPanel users by adding dedicated configuration files and documentation, making it easier to set up and manage the application. Furthermore, the inclusion of a Nix development environment ensures a consistent and reproducible setup for developers.

Highlights

  • New ChatNew Event: A new webhook event, 'ChatNew', has been introduced to detect when a new chat is initiated. This event is triggered for individual chats (1-to-1) upon the first incoming/outgoing message or call with a previously unknown contact or group.
  • EasyPanel Deployment Support: Comprehensive support for deploying the application on EasyPanel has been added, including new configuration files (.env.easypanel.sample, docker-compose-easypanel.yml, easypanel.json) and a detailed EASYPANEL.md guide.
  • Nix Development Environment: Nix Flakes (flake.nix, flake.lock, .envrc) have been integrated to provide a reproducible development environment for Go, including essential tools like go_1_24, gopls, delve, gotools, golangci-lint, and air.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces EasyPanel deployment support with new configuration files and documentation, alongside a new ChatNew event type to detect new chat initiations. The ChatNew event is triggered by incoming/outgoing messages or calls, utilizing a knownChatsCache and database lookups. Review comments suggest addressing potential memory exhaustion in knownChatsCache due to NoExpiration, improving the efficiency of preloadKnownChats and isNewChat functions which currently query the database, and handling database query errors in isNewChat to prevent missed events. Additionally, there's a recommendation to sanitize extraData in fireChatNewEvent to prevent injection vulnerabilities and to optimize the new chat detection logic to avoid performance bottlenecks.

Comment on lines +220 to +227
func fireChatNewEvent(mycli *MyClient, chatJID string, triggerEvent string, extraData map[string]interface{}) {
chatNewPostmap := make(map[string]interface{})
chatNewPostmap["type"] = "ChatNew"
chatNewPostmap["chat"] = chatJID
chatNewPostmap["trigger"] = triggerEvent
for k, v := range extraData {
chatNewPostmap[k] = v
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The fireChatNewEvent function directly passes extraData to the chatNewPostmap. Ensure that all values in extraData are properly sanitized and validated to prevent potential injection attacks or data corruption. Consider defining a strict schema for extraData to enforce type safety and prevent unexpected values.

killchannel = make(map[string](chan bool))
userinfocache = cache.New(5*time.Minute, 10*time.Minute)
lastMessageCache = cache.New(24*time.Hour, 24*time.Hour)
knownChatsCache = cache.New(cache.NoExpiration, cache.NoExpiration) // Tracks known chat JIDs per user for ChatNew event detection
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The knownChatsCache is initialized with cache.NoExpiration. This means the cache will grow indefinitely, potentially leading to memory exhaustion. Consider setting a reasonable expiration time and/or a maximum size for the cache to prevent memory leaks. Also, consider adding a mechanism to evict entries based on least-recently-used (LRU) or other eviction policies.

Comment on lines +161 to +177
func preloadKnownChats(s *server, userID string) {
var chatJIDs []string
var query string
if s.db.DriverName() == "sqlite" {
query = `SELECT DISTINCT chat_jid FROM message_history WHERE user_id = ?`
} else {
query = `SELECT DISTINCT chat_jid FROM message_history WHERE user_id = $1`
}
err := s.db.Select(&chatJIDs, query, userID)
if err != nil {
log.Error().Err(err).Str("userID", userID).Msg("Failed to preload known chats from message_history")
return
}
for _, chatJID := range chatJIDs {
knownChatsCache.Set(userID+":"+chatJID, true, cache.NoExpiration)
}
log.Info().Str("userID", userID).Int("count", len(chatJIDs)).Msg("Preloaded known chats into cache")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function preloadKnownChats directly queries the database, which can be slow, especially with a large message history. Consider using pagination or streaming to process the results in smaller chunks, preventing potential memory issues and improving performance. Also, consider adding a configurable option to disable preloading of known chats, as this might not be needed in all deployments.

Comment on lines +183 to +215
func isNewChat(mycli *MyClient, chatJID string) bool {
cacheKey := mycli.userID + ":" + chatJID

// Check if already known in cache
if _, found := knownChatsCache.Get(cacheKey); found {
return false
}

// Check if chat exists in message_history (persistent storage)
var count int
var query string
if mycli.db.DriverName() == "sqlite" {
query = `SELECT COUNT(*) FROM message_history WHERE user_id = ? AND chat_jid = ? LIMIT 1`
} else {
query = `SELECT COUNT(*) FROM message_history WHERE user_id = $1 AND chat_jid = $2 LIMIT 1`
}
err := mycli.db.Get(&count, query, mycli.userID, chatJID)
if err != nil {
log.Error().Err(err).Str("userID", mycli.userID).Str("chatJID", chatJID).Msg("Failed to check if chat exists in history")
// On error, register it and don't fire the event to avoid false positives
knownChatsCache.Set(cacheKey, true, cache.NoExpiration)
return false
}

if count > 0 {
// Chat already exists in DB, register in cache and return false
knownChatsCache.Set(cacheKey, true, cache.NoExpiration)
return false
}

// Chat is new! Register it in cache
knownChatsCache.Set(cacheKey, true, cache.NoExpiration)
return true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The isNewChat function checks for existing chats by querying the database, which can be inefficient. Consider implementing a more efficient caching mechanism (e.g., using a Bloom filter) to reduce database load. Also, consider adding metrics to track the cache hit rate to evaluate its effectiveness.

Comment on lines +199 to +204
err := mycli.db.Get(&count, query, mycli.userID, chatJID)
if err != nil {
log.Error().Err(err).Str("userID", mycli.userID).Str("chatJID", chatJID).Msg("Failed to check if chat exists in history")
// On error, register it and don't fire the event to avoid false positives
knownChatsCache.Set(cacheKey, true, cache.NoExpiration)
return false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In the isNewChat function, if an error occurs during the database query, the code registers the chat and returns false to avoid false positives. However, this could lead to missed 'ChatNew' events if the database is temporarily unavailable. Consider adding retry logic or a mechanism to re-evaluate these chats later.

Comment on lines +931 to +943
chatJIDStr := evt.Info.Chat.String()
if !evt.Info.IsGroup && strings.HasSuffix(chatJIDStr, "@s.whatsapp.net") && isNewChat(mycli, chatJIDStr) {
extraData := map[string]interface{}{
"phone": evt.Info.Chat.User,
"sender": evt.Info.Sender.String(),
"pushName": evt.Info.PushName,
"isGroup": false,
"isFromMe": evt.Info.IsFromMe,
"timestamp": evt.Info.Timestamp,
"messageID": evt.Info.ID,
}
go fireChatNewEvent(mycli, chatJIDStr, "message", extraData)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic to detect new chats is performed inline within the message processing logic. This could introduce performance bottlenecks, especially under high load. Consider moving this logic to a separate goroutine or using a more efficient event-driven architecture to avoid blocking the main message processing flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant