From de5376c66f4880c3804e020e2a1708942182f746 Mon Sep 17 00:00:00 2001 From: Benny Date: Mon, 21 Aug 2023 16:09:58 -0700 Subject: [PATCH] optimsitic and new comment/admire push notifications (#1159) * optimsitic and new comment/admire push notifications * truncate long names * wrong join var --- db/gen/coredb/query.sql.go | 26 ++++++++++ db/queries/core/query.sql | 3 ++ service/notifications/notifications.go | 66 ++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/db/gen/coredb/query.sql.go b/db/gen/coredb/query.sql.go index 00eb56703..1fa1db266 100644 --- a/db/gen/coredb/query.sql.go +++ b/db/gen/coredb/query.sql.go @@ -3447,6 +3447,32 @@ func (q *Queries) GetTokenByTokenIdentifiers(ctx context.Context, arg GetTokenBy return i, err } +const getTokenMediaByTokenId = `-- name: GetTokenMediaByTokenId :one +select tm.id, tm.created_at, tm.last_updated, tm.version, tm.contract_id, tm.token_id, tm.chain, tm.active, tm.metadata, tm.media, tm.name, tm.description, tm.processing_job_id, tm.deleted from tokens join token_medias tm on tokens.token_media_id = tm.id where tokens.id = $1 and tokens.displayable and not tokens.deleted and not tm.deleted +` + +func (q *Queries) GetTokenMediaByTokenId(ctx context.Context, id persist.DBID) (TokenMedia, error) { + row := q.db.QueryRow(ctx, getTokenMediaByTokenId, id) + var i TokenMedia + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.LastUpdated, + &i.Version, + &i.ContractID, + &i.TokenID, + &i.Chain, + &i.Active, + &i.Metadata, + &i.Media, + &i.Name, + &i.Description, + &i.ProcessingJobID, + &i.Deleted, + ) + return i, err +} + const getTokenOwnerByID = `-- name: GetTokenOwnerByID :one select u.id, u.deleted, u.version, u.last_updated, u.created_at, u.username, u.username_idempotent, u.wallets, u.bio, u.traits, u.universal, u.notification_settings, u.email_verified, u.email_unsubscriptions, u.featured_gallery, u.primary_wallet_id, u.user_experiences, u.profile_image_id from tokens t join users u on u.id = t.owner_user_id diff --git a/db/queries/core/query.sql b/db/queries/core/query.sql index f76a33774..1ff7502c2 100644 --- a/db/queries/core/query.sql +++ b/db/queries/core/query.sql @@ -82,6 +82,9 @@ SELECT c.* FROM galleries g, unnest(g.collections) -- name: GetTokenById :one select * from tokens where id = $1 and displayable and deleted = false; +-- name: GetTokenMediaByTokenId :one +select tm.* from tokens join token_medias tm on tokens.token_media_id = tm.id where tokens.id = $1 and tokens.displayable and not tokens.deleted and not tm.deleted; + -- name: GetTokenByIdBatch :batchone select * from tokens where id = $1 and displayable and deleted = false; diff --git a/service/notifications/notifications.go b/service/notifications/notifications.go index 0f47f6f1a..dd739d4d6 100644 --- a/service/notifications/notifications.go +++ b/service/notifications/notifications.go @@ -48,6 +48,7 @@ type pushLimiter struct { comments *limiters.KeyRateLimiter admires *limiters.KeyRateLimiter follows *limiters.KeyRateLimiter + tokens *limiters.KeyRateLimiter } func newPushLimiter() *pushLimiter { @@ -57,6 +58,7 @@ func newPushLimiter() *pushLimiter { comments: limiters.NewKeyRateLimiter(ctx, cache, "comments", 5, time.Minute), admires: limiters.NewKeyRateLimiter(ctx, cache, "admires", 1, time.Minute*10), follows: limiters.NewKeyRateLimiter(ctx, cache, "follows", 1, time.Minute*10), + tokens: limiters.NewKeyRateLimiter(ctx, cache, "tokens", 1, time.Minute*10), } } @@ -101,6 +103,18 @@ func (p *pushLimiter) tryFollow(ctx context.Context, sendingUserID persist.DBID, } } +func (p *pushLimiter) tryTokens(ctx context.Context, sendingUserID persist.DBID, tokenID persist.DBID) error { + key := fmt.Sprintf("%s:%s", sendingUserID, tokenID) + if p.isActionAllowed(ctx, p.tokens, key) { + return nil + } + + return errRateLimited{ + limiterName: p.follows.Name(), + senderID: sendingUserID, + } +} + func (p *pushLimiter) isActionAllowed(ctx context.Context, limiter *limiters.KeyRateLimiter, key string) bool { canContinue, _, err := limiter.ForKey(ctx, key) if err != nil { @@ -493,7 +507,7 @@ func createPushMessage(ctx context.Context, notif db.Notification, queries *db.Q }, } - if notif.Action == persist.ActionAdmiredFeedEvent { + if notif.Action == persist.ActionAdmiredFeedEvent || notif.Action == persist.ActionAdmiredPost { admirer, err := queries.GetUserById(ctx, notif.Data.AdmirerIDs[0]) if err != nil { return task.PushNotificationMessage{}, err @@ -507,11 +521,16 @@ func createPushMessage(ctx context.Context, notif db.Notification, queries *db.Q return task.PushNotificationMessage{}, fmt.Errorf("user with ID=%s has no username", admirer.ID) } - message.Body = fmt.Sprintf("%s admired your activity", admirer.Username.String) + ac := "activity" + if notif.Action == persist.ActionAdmiredPost { + ac = "post" + } + + message.Body = fmt.Sprintf("%s admired your %s", admirer.Username.String, ac) return message, nil } - if notif.Action == persist.ActionCommentedOnFeedEvent { + if notif.Action == persist.ActionCommentedOnFeedEvent || notif.Action == persist.ActionCommentedOnPost { comment, err := queries.GetCommentByCommentID(ctx, notif.CommentID) if err != nil { return task.PushNotificationMessage{}, err @@ -530,7 +549,12 @@ func createPushMessage(ctx context.Context, notif db.Notification, queries *db.Q return task.PushNotificationMessage{}, fmt.Errorf("user with ID=%s has no username", commenter.ID) } - message.Body = fmt.Sprintf("%s commented on your activity", commenter.Username.String) + ac := "activity" + if notif.Action == persist.ActionCommentedOnPost { + ac = "post" + } + + message.Body = fmt.Sprintf("%s commented on your %s", commenter.Username.String, ac) return message, nil } @@ -558,6 +582,34 @@ func createPushMessage(ctx context.Context, notif db.Notification, queries *db.Q message.Body = body return message, nil } + if notif.Action == persist.ActionNewTokensReceived { + + tm, err := queries.GetTokenMediaByTokenId(ctx, notif.Data.NewTokenID) + if err != nil { + return task.PushNotificationMessage{}, err + } + name := tm.Name + if name == "" { + to, err := queries.GetTokenById(ctx, notif.Data.NewTokenID) + if err != nil { + return task.PushNotificationMessage{}, err + } + name = to.Name.String + } + + name = util.TruncateWithEllipsis(name, 20) + + if err := limiter.tryTokens(ctx, notif.OwnerID, notif.Data.NewTokenID); err != nil { + return task.PushNotificationMessage{}, err + } + amount := notif.Data.NewTokenQuantity + i := amount.BigInt().Uint64() + if i > 1 { + message.Body = fmt.Sprintf("You received %d new %s tokens", i, name) + } else { + message.Body = fmt.Sprintf("You received a new %s token", name) + } + } return task.PushNotificationMessage{}, fmt.Errorf("unsupported notification action: %s", notif.Action) } @@ -570,6 +622,12 @@ func actionSupportsPushNotifications(action persist.Action) bool { return true case persist.ActionUserFollowedUsers: return true + case persist.ActionNewTokensReceived: + return true + case persist.ActionCommentedOnPost: + return true + case persist.ActionAdmiredPost: + return true default: return false }