this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

delete notifs package

-418
-371
notifs/notifs.go
··· 1 - package notifs 2 - 3 - import ( 4 - "context" 5 - "fmt" 6 - "time" 7 - 8 - appbskytypes "github.com/bluesky-social/indigo/api/bsky" 9 - lexutil "github.com/bluesky-social/indigo/lex/util" 10 - "github.com/bluesky-social/indigo/models" 11 - "github.com/ipfs/go-cid" 12 - cbg "github.com/whyrusleeping/cbor-gen" 13 - "gorm.io/gorm" 14 - "gorm.io/gorm/clause" 15 - ) 16 - 17 - type NotificationManager interface { 18 - GetNotifications(ctx context.Context, user models.Uid) ([]*appbskytypes.NotificationListNotifications_Notification, error) 19 - GetCount(ctx context.Context, user models.Uid) (int64, error) 20 - UpdateSeen(ctx context.Context, usr models.Uid, seen time.Time) error 21 - AddReplyTo(ctx context.Context, user models.Uid, replyid uint, replyto *models.FeedPost) error 22 - AddMention(ctx context.Context, user models.Uid, postid uint, mentioned models.Uid) error 23 - AddUpVote(ctx context.Context, voter models.Uid, postid uint, voteid uint, postauthor models.Uid) error 24 - AddFollow(ctx context.Context, follower, followed models.Uid, recid uint) error 25 - AddRepost(ctx context.Context, op models.Uid, repost uint, reposter models.Uid) error 26 - } 27 - 28 - var _ NotificationManager = (*DBNotifMan)(nil) 29 - 30 - type DBNotifMan struct { 31 - db *gorm.DB 32 - 33 - getRecord GetRecord 34 - } 35 - type GetRecord func(ctx context.Context, user models.Uid, collection string, rkey string, maybeCid cid.Cid) (cid.Cid, cbg.CBORMarshaler, error) 36 - 37 - func NewNotificationManager(db *gorm.DB, getrec GetRecord) *DBNotifMan { 38 - 39 - db.AutoMigrate(&NotifRecord{}) 40 - db.AutoMigrate(&NotifSeen{}) 41 - 42 - return &DBNotifMan{ 43 - db: db, 44 - getRecord: getrec, 45 - } 46 - } 47 - 48 - const ( 49 - NotifKindReply = 1 50 - NotifKindMention = 2 51 - NotifKindUpVote = 3 52 - NotifKindFollow = 4 53 - NotifKindRepost = 5 54 - ) 55 - 56 - type NotifRecord struct { 57 - gorm.Model 58 - For models.Uid 59 - Kind int64 60 - Record uint 61 - Who models.Uid 62 - ReplyTo uint 63 - } 64 - 65 - type NotifSeen struct { 66 - ID uint `gorm:"primarykey"` 67 - Usr models.Uid `gorm:"uniqueIndex"` 68 - LastSeen time.Time 69 - } 70 - 71 - type HydratedNotification struct { 72 - Record any 73 - IsRead bool 74 - IndexedAt time.Time 75 - Uri string 76 - Cid string 77 - Author *appbskytypes.ActorDefs_ProfileViewBasic 78 - Reason string 79 - ReasonSubject *string 80 - } 81 - 82 - func (nm *DBNotifMan) GetNotifications(ctx context.Context, user models.Uid) ([]*appbskytypes.NotificationListNotifications_Notification, error) { 83 - var lastSeen time.Time 84 - if err := nm.db.Model(NotifSeen{}).Where("usr = ?", user).Select("last_seen").Scan(&lastSeen).Error; err != nil { 85 - return nil, err 86 - } 87 - 88 - var notifs []NotifRecord 89 - if err := nm.db.Order("created_at desc").Find(&notifs, "for = ?", user).Error; err != nil { 90 - return nil, err 91 - } 92 - 93 - /* 94 - Record any `json:"record" cborgen:"record"` 95 - IsRead bool `json:"isRead" cborgen:"isRead"` 96 - IndexedAt string `json:"indexedAt" cborgen:"indexedAt"` 97 - Uri string `json:"uri" cborgen:"uri"` 98 - Cid string `json:"cid" cborgen:"cid"` 99 - Author *ActorRef_WithInfo `json:"author" cborgen:"author"` 100 - Reason string `json:"reason" cborgen:"reason"` 101 - ReasonSubject *string `json:"reasonSubject" cborgen:"reasonSubject"` 102 - */ 103 - 104 - out := []*appbskytypes.NotificationListNotifications_Notification{} 105 - 106 - for _, n := range notifs { 107 - hn, err := nm.hydrateNotification(ctx, &n, lastSeen) 108 - if err != nil { 109 - return nil, err 110 - } 111 - 112 - // TODO: muting 113 - hn.Author.Viewer = &appbskytypes.ActorDefs_ViewerState{} 114 - 115 - out = append(out, hn) 116 - } 117 - return out, nil 118 - } 119 - 120 - func (nm *DBNotifMan) hydrateNotification(ctx context.Context, nrec *NotifRecord, lastSeen time.Time) (*appbskytypes.NotificationListNotifications_Notification, error) { 121 - 122 - switch nrec.Kind { 123 - case NotifKindReply: 124 - return nm.hydrateNotificationReply(ctx, nrec, lastSeen) 125 - case NotifKindFollow: 126 - return nm.hydrateNotificationFollow(ctx, nrec, lastSeen) 127 - case NotifKindUpVote: 128 - return nm.hydrateNotificationUpVote(ctx, nrec, lastSeen) 129 - case NotifKindRepost: 130 - return nm.hydrateNotificationRepost(ctx, nrec, lastSeen) 131 - /* 132 - case NotifKindMention: 133 - return nm.hydrateNotificationMention(ctx, nrec, lastSeen) 134 - */ 135 - default: 136 - return nil, fmt.Errorf("attempted to hydrate unknown notif kind: %d", nrec.Kind) 137 - } 138 - } 139 - func (nm *DBNotifMan) getActor(ctx context.Context, act models.Uid) (*models.ActorInfo, error) { 140 - var ai models.ActorInfo 141 - if err := nm.db.First(&ai, "uid = ?", act).Error; err != nil { 142 - return nil, err 143 - } 144 - 145 - return &ai, nil 146 - } 147 - 148 - func (nm *DBNotifMan) hydrateNotificationUpVote(ctx context.Context, nrec *NotifRecord, lastSeen time.Time) (*appbskytypes.NotificationListNotifications_Notification, error) { 149 - var votedOn models.FeedPost 150 - if err := nm.db.First(&votedOn, "id = ?", nrec.Record).Error; err != nil { 151 - return nil, err 152 - } 153 - 154 - voter, err := nm.getActor(ctx, nrec.Who) 155 - if err != nil { 156 - return nil, err 157 - } 158 - 159 - var vote models.VoteRecord 160 - if err := nm.db.First(&vote, "id = ?", nrec.Record).Error; err != nil { 161 - return nil, err 162 - } 163 - 164 - _, rec, err := nm.getRecord(ctx, voter.Uid, "app.bsky.feed.vote", vote.Rkey, cid.Undef) 165 - if err != nil { 166 - return nil, fmt.Errorf("getting vote: %w", err) 167 - } 168 - 169 - postAuthor, err := nm.getActor(ctx, votedOn.Author) 170 - if err != nil { 171 - return nil, err 172 - } 173 - 174 - rsub := "at://" + postAuthor.Did + "/app.bsky.feed.post/" + votedOn.Rkey 175 - 176 - return &appbskytypes.NotificationListNotifications_Notification{ 177 - Record: &lexutil.LexiconTypeDecoder{Val: rec}, 178 - IsRead: nrec.CreatedAt.Before(lastSeen), 179 - IndexedAt: nrec.CreatedAt.Format(time.RFC3339), 180 - Uri: "at://" + voter.Did + "/app.bsky.feed.vote/" + vote.Rkey, 181 - Cid: vote.Cid, 182 - Author: voter.ActorView(), 183 - Reason: "vote", 184 - ReasonSubject: &rsub, 185 - }, nil 186 - } 187 - 188 - func (nm *DBNotifMan) hydrateNotificationRepost(ctx context.Context, nrec *NotifRecord, lastSeen time.Time) (*appbskytypes.NotificationListNotifications_Notification, error) { 189 - var reposted models.FeedPost 190 - if err := nm.db.First(&reposted, "id = ?", nrec.Record).Error; err != nil { 191 - return nil, err 192 - } 193 - 194 - reposter, err := nm.getActor(ctx, nrec.Who) 195 - if err != nil { 196 - return nil, err 197 - } 198 - 199 - var repost models.RepostRecord 200 - if err := nm.db.First(&repost, "id = ?", nrec.Record).Error; err != nil { 201 - return nil, err 202 - } 203 - 204 - _, rec, err := nm.getRecord(ctx, reposter.Uid, "app.bsky.feed.repost", repost.Rkey, cid.Undef) 205 - if err != nil { 206 - return nil, fmt.Errorf("getting repost: %w", err) 207 - } 208 - 209 - postAuthor, err := nm.getActor(ctx, repost.Author) 210 - if err != nil { 211 - return nil, err 212 - } 213 - 214 - rsub := "at://" + postAuthor.Did + "/app.bsky.feed.post/" + reposted.Rkey 215 - 216 - return &appbskytypes.NotificationListNotifications_Notification{ 217 - Record: &lexutil.LexiconTypeDecoder{Val: rec}, 218 - IsRead: nrec.CreatedAt.Before(lastSeen), 219 - IndexedAt: nrec.CreatedAt.Format(time.RFC3339), 220 - Uri: "at://" + reposter.Did + "/app.bsky.feed.repost/" + repost.Rkey, 221 - Cid: repost.RecCid, 222 - Author: reposter.ActorView(), 223 - Reason: "repost", 224 - ReasonSubject: &rsub, 225 - }, nil 226 - } 227 - 228 - func (nm *DBNotifMan) hydrateNotificationReply(ctx context.Context, nrec *NotifRecord, lastSeen time.Time) (*appbskytypes.NotificationListNotifications_Notification, error) { 229 - var fp models.FeedPost 230 - if err := nm.db.First(&fp, "id = ?", nrec.Record).Error; err != nil { 231 - return nil, err 232 - } 233 - 234 - var replyTo models.FeedPost 235 - if err := nm.db.First(&replyTo, "id = ?", nrec.ReplyTo).Error; err != nil { 236 - return nil, err 237 - } 238 - 239 - var author models.ActorInfo 240 - if err := nm.db.First(&author, "id = ?", fp.Author).Error; err != nil { 241 - return nil, err 242 - } 243 - 244 - var opAuthor models.ActorInfo 245 - if err := nm.db.First(&opAuthor, "id = ?", replyTo.Author).Error; err != nil { 246 - return nil, err 247 - } 248 - 249 - _, rec, err := nm.getRecord(ctx, author.Uid, "app.bsky.feed.post", fp.Rkey, cid.Undef) 250 - if err != nil { 251 - return nil, err 252 - } 253 - 254 - rsub := "at://" + opAuthor.Did + "/app.bsky.feed.post/" + replyTo.Rkey 255 - 256 - return &appbskytypes.NotificationListNotifications_Notification{ 257 - Record: &lexutil.LexiconTypeDecoder{Val: rec}, 258 - IsRead: nrec.CreatedAt.Before(lastSeen), 259 - IndexedAt: nrec.CreatedAt.Format(time.RFC3339), 260 - Uri: "at://" + author.Did + "/app.bsky.feed.post/" + fp.Rkey, 261 - Cid: fp.Cid, 262 - Author: author.ActorView(), 263 - Reason: "reply", 264 - ReasonSubject: &rsub, 265 - }, nil 266 - } 267 - 268 - func (nm *DBNotifMan) hydrateNotificationFollow(ctx context.Context, nrec *NotifRecord, lastSeen time.Time) (*appbskytypes.NotificationListNotifications_Notification, error) { 269 - var frec models.FollowRecord 270 - if err := nm.db.First(&frec, "id = ?", nrec.Record).Error; err != nil { 271 - return nil, err 272 - } 273 - 274 - var follower models.ActorInfo 275 - if err := nm.db.First(&follower, "id = ?", nrec.Who).Error; err != nil { 276 - return nil, err 277 - } 278 - 279 - _, rec, err := nm.getRecord(ctx, follower.Uid, "app.bsky.graph.follow", frec.Rkey, cid.Undef) 280 - if err != nil { 281 - return nil, err 282 - } 283 - 284 - return &appbskytypes.NotificationListNotifications_Notification{ 285 - Record: &lexutil.LexiconTypeDecoder{Val: rec}, 286 - IsRead: nrec.CreatedAt.Before(lastSeen), 287 - IndexedAt: nrec.CreatedAt.Format(time.RFC3339), 288 - Uri: "at://" + follower.Did + "/app.bsky.graph.follow/" + frec.Rkey, 289 - Cid: frec.Cid, 290 - Author: follower.ActorView(), 291 - Reason: "follow", 292 - }, nil 293 - 294 - } 295 - 296 - func (nm *DBNotifMan) GetCount(ctx context.Context, user models.Uid) (int64, error) { 297 - // TODO: sql count is inefficient 298 - var lseen time.Time 299 - if err := nm.db.Model(NotifSeen{}).Where("usr = ?", user).Select("last_seen").Scan(&lseen).Error; err != nil { 300 - return 0, err 301 - } 302 - 303 - var c int64 304 - //seen := nm.db.Model(NotifSeen{}).Where("usr = ?", user).Select("last_seen") 305 - if err := nm.db.Model(NotifRecord{}).Where("for = ? AND created_at > ?", user, lseen).Count(&c).Error; err != nil { 306 - return 0, err 307 - } 308 - 309 - return c, nil 310 - } 311 - 312 - func (nm *DBNotifMan) UpdateSeen(ctx context.Context, usr models.Uid, seen time.Time) error { 313 - if err := nm.db.Clauses(clause.OnConflict{ 314 - Columns: []clause.Column{{Name: "usr"}}, 315 - DoUpdates: clause.AssignmentColumns([]string{"last_seen"}), 316 - }).Create(NotifSeen{ 317 - Usr: usr, 318 - LastSeen: seen, 319 - }).Error; err != nil { 320 - return err 321 - } 322 - 323 - return nil 324 - } 325 - 326 - func (nm *DBNotifMan) AddReplyTo(ctx context.Context, user models.Uid, replyid uint, replyto *models.FeedPost) error { 327 - return nm.db.Create(&NotifRecord{ 328 - Kind: NotifKindReply, 329 - For: replyto.Author, 330 - Who: user, 331 - ReplyTo: replyto.ID, 332 - Record: replyid, 333 - }).Error 334 - } 335 - 336 - func (nm *DBNotifMan) AddMention(ctx context.Context, user models.Uid, postid uint, mentioned models.Uid) error { 337 - return nm.db.Create(&NotifRecord{ 338 - For: mentioned, 339 - Kind: NotifKindMention, 340 - Record: postid, 341 - Who: user, 342 - }).Error 343 - } 344 - 345 - func (nm *DBNotifMan) AddUpVote(ctx context.Context, voter models.Uid, postid uint, voteid uint, postauthor models.Uid) error { 346 - return nm.db.Create(&NotifRecord{ 347 - For: postauthor, 348 - Kind: NotifKindUpVote, 349 - ReplyTo: postid, 350 - Record: voteid, 351 - Who: voter, 352 - }).Error 353 - } 354 - 355 - func (nm *DBNotifMan) AddFollow(ctx context.Context, follower, followed models.Uid, recid uint) error { 356 - return nm.db.Create(&NotifRecord{ 357 - Kind: NotifKindFollow, 358 - For: followed, 359 - Who: follower, 360 - Record: recid, 361 - }).Error 362 - } 363 - 364 - func (nm *DBNotifMan) AddRepost(ctx context.Context, op models.Uid, repost uint, reposter models.Uid) error { 365 - return nm.db.Create(&NotifRecord{ 366 - Kind: NotifKindRepost, 367 - For: op, 368 - Record: repost, 369 - Who: reposter, 370 - }).Error 371 - }
-47
notifs/null.go
··· 1 - package notifs 2 - 3 - import ( 4 - "context" 5 - "fmt" 6 - "time" 7 - 8 - appbskytypes "github.com/bluesky-social/indigo/api/bsky" 9 - "github.com/bluesky-social/indigo/models" 10 - ) 11 - 12 - type NullNotifs struct { 13 - } 14 - 15 - var _ NotificationManager = (*NullNotifs)(nil) 16 - 17 - func (nn *NullNotifs) GetNotifications(ctx context.Context, user models.Uid) ([]*appbskytypes.NotificationListNotifications_Notification, error) { 18 - return nil, fmt.Errorf("no notifications engine loaded") 19 - } 20 - 21 - func (nn *NullNotifs) GetCount(ctx context.Context, user models.Uid) (int64, error) { 22 - return 0, fmt.Errorf("no notifications engine loaded") 23 - } 24 - 25 - func (nn *NullNotifs) UpdateSeen(ctx context.Context, usr models.Uid, seen time.Time) error { 26 - return nil 27 - } 28 - 29 - func (nn *NullNotifs) AddReplyTo(ctx context.Context, user models.Uid, replyid uint, replyto *models.FeedPost) error { 30 - return nil 31 - } 32 - 33 - func (nn *NullNotifs) AddMention(ctx context.Context, user models.Uid, postid uint, mentioned models.Uid) error { 34 - return nil 35 - } 36 - 37 - func (nn *NullNotifs) AddUpVote(ctx context.Context, voter models.Uid, postid uint, voteid uint, postauthor models.Uid) error { 38 - return nil 39 - } 40 - 41 - func (nn *NullNotifs) AddFollow(ctx context.Context, follower, followed models.Uid, recid uint) error { 42 - return nil 43 - } 44 - 45 - func (nn *NullNotifs) AddRepost(ctx context.Context, op models.Uid, repost uint, reposter models.Uid) error { 46 - return nil 47 - }