diff --git a/models.go b/models.go index d2c3c8b..67ebc0f 100644 --- a/models.go +++ b/models.go @@ -95,6 +95,96 @@ type Event struct { Data map[string]any `json:"data"` } +// Channel identifies the delivery channel for inbox messages. +type Channel string + +// InboxStatus filters inbox messages by status. +type InboxStatus string + +const ( + InboxStatusUnread InboxStatus = "unread" + InboxStatusOpened InboxStatus = "opened" + InboxStatusArchived InboxStatus = "archived" +) + +// UserInboxQuery holds filters for querying user inbox messages. +type UserInboxQuery struct { + Source string + ExternalID string + Status *InboxStatus + Tags []string + MessageSource *string + Priority *int + Channel Channel + Limit *int + Offset *int +} + +// PostUserInboxMessagesRequest is the payload for creating user inbox messages. +type PostUserInboxMessagesRequest struct { + Source string `json:"source"` + ExternalID string `json:"external_id"` + Messages []map[string]any `json:"messages"` +} + +// UserInboxMessageEvents is the payload for marking user inbox messages as opened or archived. +type UserInboxMessageEvents struct { + Source string `json:"source"` + ExternalID string `json:"external_id"` + MessageIDs []string `json:"message_ids"` +} + +// InboxMessage is a single inbox message as returned by the API. +type InboxMessage struct { + ID string `json:"id"` + Data map[string]any `json:"data"` + Status InboxStatus `json:"status"` + Channel Channel `json:"channel"` + Priority int `json:"priority"` + Tags []string `json:"tags"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// InboxMessageList is the response payload for inbox message queries. +type InboxMessageList struct { + Data []InboxMessage `json:"data"` + Total int `json:"total"` +} + +// InboxCount is the response payload for inbox count queries. +type InboxCount struct { + Unread int `json:"unread"` + Total int `json:"total"` +} + +// OrganizationInboxQuery holds filters for querying organization inbox messages. +type OrganizationInboxQuery struct { + Source string + ExternalID string + Status *InboxStatus + Tags []string + MessageSource *string + Priority *int + Channel Channel + Limit *int + Offset *int +} + +// PostOrganizationInboxMessagesRequest is the payload for creating organization inbox messages. +type PostOrganizationInboxMessagesRequest struct { + Source string `json:"source"` + ExternalID string `json:"external_id"` + Messages []map[string]any `json:"messages"` +} + +// OrganizationInboxMessageEvents is the payload for marking organization inbox messages as opened or archived. +type OrganizationInboxMessageEvents struct { + Source string `json:"source"` + ExternalID string `json:"external_id"` + MessageIDs []string `json:"message_ids"` +} + // UpsertOrganizationRequest creates or updates an organization. type UpsertOrganizationRequest struct { Identifier []ExternalID `json:"identifier"` diff --git a/organizations.go b/organizations.go index 651ecd4..5ebda39 100644 --- a/organizations.go +++ b/organizations.go @@ -3,6 +3,9 @@ package lunogram import ( "context" "net/http" + "net/url" + "strconv" + "strings" ) // UpsertOrganization creates or updates an organization by external identifier(s). @@ -36,3 +39,81 @@ func (c *Client) RemoveOrganizationUser(ctx context.Context, req *RemoveOrganiza func (c *Client) PostOrganizationEvents(ctx context.Context, events []OrganizationEvent) error { return c.do(ctx, http.MethodPost, "/api/client/organizations/events", events, nil) } + +// PostOrganizationInboxMessages creates one or more inbox messages for organizations. +func (c *Client) PostOrganizationInboxMessages(ctx context.Context, req *PostOrganizationInboxMessagesRequest) error { + return c.do(ctx, http.MethodPost, "/api/client/organizations/inbox", req, nil) +} + +// GetOrganizationInbox retrieves visible, non-expired inbox messages for an organization. +func (c *Client) GetOrganizationInbox(ctx context.Context, query *OrganizationInboxQuery) (*InboxMessageList, error) { + path := "/api/client/organizations/inbox" + if values := organizationInboxQueryValues(query); len(values) > 0 { + path += "?" + values.Encode() + } + + var list InboxMessageList + if err := c.do(ctx, http.MethodGet, path, nil, &list); err != nil { + return nil, err + } + return &list, nil +} + +// GetOrganizationInboxCount retrieves unread and total inbox message counts for an organization. +func (c *Client) GetOrganizationInboxCount(ctx context.Context, query *OrganizationInboxQuery) (*InboxCount, error) { + path := "/api/client/organizations/inbox/count" + if values := organizationInboxQueryValues(query); len(values) > 0 { + path += "?" + values.Encode() + } + + var count InboxCount + if err := c.do(ctx, http.MethodGet, path, nil, &count); err != nil { + return nil, err + } + return &count, nil +} + +// PostOrganizationInboxOpened marks one or more organization inbox messages as opened. +func (c *Client) PostOrganizationInboxOpened(ctx context.Context, events *OrganizationInboxMessageEvents) error { + return c.do(ctx, http.MethodPost, "/api/client/organizations/inbox/opened", events, nil) +} + +// PostOrganizationInboxArchived marks one or more organization inbox messages as archived. +func (c *Client) PostOrganizationInboxArchived(ctx context.Context, events *OrganizationInboxMessageEvents) error { + return c.do(ctx, http.MethodPost, "/api/client/organizations/inbox/archived", events, nil) +} + +func organizationInboxQueryValues(query *OrganizationInboxQuery) url.Values { + values := url.Values{} + if query == nil { + return values + } + if query.Source != "" { + values.Set("source", query.Source) + } + if query.ExternalID != "" { + values.Set("external_id", query.ExternalID) + } + if query.Status != nil { + values.Set("status", string(*query.Status)) + } + if len(query.Tags) > 0 { + values.Set("tags", strings.Join(query.Tags, ",")) + } + if query.MessageSource != nil { + values.Set("message_source", *query.MessageSource) + } + if query.Priority != nil { + values.Set("priority", strconv.Itoa(*query.Priority)) + } + if query.Channel != "" { + values.Set("channel", string(query.Channel)) + } + if query.Limit != nil { + values.Set("limit", strconv.Itoa(*query.Limit)) + } + if query.Offset != nil { + values.Set("offset", strconv.Itoa(*query.Offset)) + } + return values +} diff --git a/users.go b/users.go index c26b552..c232713 100644 --- a/users.go +++ b/users.go @@ -3,6 +3,9 @@ package lunogram import ( "context" "net/http" + "net/url" + "strconv" + "strings" ) // UpsertUser creates or updates a user profile. The user is identified by @@ -28,3 +31,81 @@ func (c *Client) DeleteUser(ctx context.Context, req *DeleteUserRequest) error { func (c *Client) PostUserEvents(ctx context.Context, events []Event) error { return c.do(ctx, http.MethodPost, "/api/client/users/events", events, nil) } + +// PostUserInboxMessages creates one or more inbox messages for users. +func (c *Client) PostUserInboxMessages(ctx context.Context, req *PostUserInboxMessagesRequest) error { + return c.do(ctx, http.MethodPost, "/api/client/users/inbox", req, nil) +} + +// GetUserInbox retrieves visible, non-expired inbox messages for a user. +func (c *Client) GetUserInbox(ctx context.Context, query *UserInboxQuery) (*InboxMessageList, error) { + path := "/api/client/users/inbox" + if values := userInboxQueryValues(query); len(values) > 0 { + path += "?" + values.Encode() + } + + var list InboxMessageList + if err := c.do(ctx, http.MethodGet, path, nil, &list); err != nil { + return nil, err + } + return &list, nil +} + +// GetUserInboxCount retrieves unread and total inbox message counts for a user. +func (c *Client) GetUserInboxCount(ctx context.Context, query *UserInboxQuery) (*InboxCount, error) { + path := "/api/client/users/inbox/count" + if values := userInboxQueryValues(query); len(values) > 0 { + path += "?" + values.Encode() + } + + var count InboxCount + if err := c.do(ctx, http.MethodGet, path, nil, &count); err != nil { + return nil, err + } + return &count, nil +} + +// PostUserInboxOpened marks one or more inbox messages as opened. +func (c *Client) PostUserInboxOpened(ctx context.Context, events *UserInboxMessageEvents) error { + return c.do(ctx, http.MethodPost, "/api/client/users/inbox/opened", events, nil) +} + +// PostUserInboxArchived marks one or more inbox messages as archived. +func (c *Client) PostUserInboxArchived(ctx context.Context, events *UserInboxMessageEvents) error { + return c.do(ctx, http.MethodPost, "/api/client/users/inbox/archived", events, nil) +} + +func userInboxQueryValues(query *UserInboxQuery) url.Values { + values := url.Values{} + if query == nil { + return values + } + if query.Source != "" { + values.Set("source", query.Source) + } + if query.ExternalID != "" { + values.Set("external_id", query.ExternalID) + } + if query.Status != nil { + values.Set("status", string(*query.Status)) + } + if len(query.Tags) > 0 { + values.Set("tags", strings.Join(query.Tags, ",")) + } + if query.MessageSource != nil { + values.Set("message_source", *query.MessageSource) + } + if query.Priority != nil { + values.Set("priority", strconv.Itoa(*query.Priority)) + } + if query.Channel != "" { + values.Set("channel", string(query.Channel)) + } + if query.Limit != nil { + values.Set("limit", strconv.Itoa(*query.Limit)) + } + if query.Offset != nil { + values.Set("offset", strconv.Itoa(*query.Offset)) + } + return values +}