feat: add stickers
This commit is contained in:
@@ -418,6 +418,9 @@ type SendRequest struct {
|
||||
PreviewURL *string
|
||||
// Entity is something else in the app — a post — shared as a card.
|
||||
Entity *EntityAttachment
|
||||
// StickerKey sends a piece of reward artwork. Ownership is checked
|
||||
// server-side; the key alone proves nothing.
|
||||
StickerKey *string
|
||||
// Quote is a passage shared from the library, rendered as a quote the
|
||||
// reader can tap to return to the exact phrase.
|
||||
Quote *QuoteAttachment
|
||||
@@ -458,7 +461,8 @@ func (s *ChatService) Send(ctx context.Context, userID, conversationID string, r
|
||||
body := strings.TrimSpace(req.Body)
|
||||
// A photo or a shared passage with no caption is an ordinary message; only
|
||||
// a message with nothing at all in it is empty.
|
||||
if body == "" && len(req.Attachments) == 0 && req.Quote == nil && req.Entity == nil {
|
||||
if body == "" && len(req.Attachments) == 0 && req.Quote == nil &&
|
||||
req.Entity == nil && req.StickerKey == nil {
|
||||
return nil, ErrChatEmptyBody
|
||||
}
|
||||
if len([]rune(body)) > maxBodyRunes {
|
||||
@@ -493,6 +497,12 @@ func (s *ChatService) Send(ctx context.Context, userID, conversationID string, r
|
||||
return s.hydrateSent(ctx, userID, conversationID, msg), nil
|
||||
}
|
||||
|
||||
if req.StickerKey != nil {
|
||||
if err := s.attachSticker(ctx, conversationID, userID, msg, *req.StickerKey); err != nil {
|
||||
s.logger.Warn("chat: sticker refused",
|
||||
zap.String("user_id", userID), zap.Error(err))
|
||||
}
|
||||
}
|
||||
if err := s.attachEntity(ctx, conversationID, userID, msg, req.Entity); err != nil {
|
||||
s.logger.Warn("chat: entity share refused", zap.String("user_id", userID), zap.Error(err))
|
||||
}
|
||||
@@ -1624,6 +1634,40 @@ func (s *ChatService) Message(ctx context.Context, userID, conversationID, messa
|
||||
return s.withAttachments(ctx, userID, msg)
|
||||
}
|
||||
|
||||
// Stickers lists what the caller can send: sādhana rewards they earned, plus
|
||||
// stickers they saved from a chat.
|
||||
func (s *ChatService) Stickers(ctx context.Context, userID string) ([]model.Sticker, error) {
|
||||
return s.chat.Stickers(ctx, userID)
|
||||
}
|
||||
|
||||
// SaveSticker keeps one someone sent, after checking it really is on that
|
||||
// message and the caller is in the thread.
|
||||
func (s *ChatService) SaveSticker(ctx context.Context, userID, messageID, key string) error {
|
||||
return s.chat.SaveSticker(ctx, userID, key, messageID)
|
||||
}
|
||||
|
||||
// attachSticker stores the artwork on a message.
|
||||
//
|
||||
// Ownership is checked here, not trusted from the client: the key comes over
|
||||
// the wire, and without this anyone could send any sticker in the catalog by
|
||||
// guessing its key. The image and name are denormalized because the catalog
|
||||
// is static art — there is nothing viewer-dependent to re-resolve on read.
|
||||
func (s *ChatService) attachSticker(ctx context.Context, conversationID, userID string, msg *model.Message, key string) error {
|
||||
sticker, err := s.chat.Sticker(ctx, userID, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entityType := model.EntitySticker
|
||||
if _, err := s.chat.AddAttachment(ctx, repository.AddAttachmentParams{
|
||||
MessageID: msg.ID, ConversationID: conversationID, Ord: 0,
|
||||
Kind: model.AttachmentSticker, EntityType: &entityType,
|
||||
EntityID: &sticker.Key, ImageURL: &sticker.ImageURL, Title: &sticker.Name,
|
||||
}); err != nil {
|
||||
s.logger.Warn("chat: attach sticker failed", zap.Error(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Typing broadcasts that someone is composing.
|
||||
//
|
||||
// Never persisted and never pushed: a typing indicator is only meaningful to
|
||||
|
||||
Reference in New Issue
Block a user