Merge remote-tracking branch 'origin/main' into HEAD

This commit is contained in:
S0yKaf 2025-01-18 13:55:15 -05:00
commit 0e137c0f2d
1759 changed files with 864109 additions and 314186 deletions

View file

@ -26,7 +26,7 @@ type AccountSettings struct {
AccountID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // AccountID that owns this settings.
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created.
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item was last updated.
Privacy Visibility `bun:",nullzero"` // Default post privacy for this account
Privacy Visibility `bun:",nullzero,default:3"` // Default post privacy for this account
Sensitive *bool `bun:",nullzero,notnull,default:false"` // Set posts from this account to sensitive by default?
Language string `bun:",nullzero,notnull,default:'en'"` // What language does this account post in?
StatusContentType string `bun:",nullzero"` // What is the default format for statuses posted by this account (only for local accounts).
@ -35,7 +35,7 @@ type AccountSettings struct {
EnableRSS *bool `bun:",nullzero,notnull,default:false"` // enable RSS feed subscription for this account's public posts at [URL]/feed
HideBoosts *bool `bun:",nullzero,notnull,default:false"` // Hide boosts from this accounts profile page.
HideCollections *bool `bun:",nullzero,notnull,default:false"` // Hide this account's followers/following collections.
WebVisibility Visibility `bun:",nullzero,notnull,default:public"` // Visibility level of statuses that visitors can view via the web profile.
WebVisibility Visibility `bun:",nullzero,notnull,default:3"` // Visibility level of statuses that visitors can view via the web profile.
InteractionPolicyDirect *InteractionPolicy `bun:""` // Interaction policy to use for new direct visibility statuses by this account. If null, assume default policy.
InteractionPolicyMutualsOnly *InteractionPolicy `bun:""` // Interaction policy to use for new mutuals only visibility statuses. If null, assume default policy.
InteractionPolicyFollowersOnly *InteractionPolicy `bun:""` // Interaction policy to use for new followers only visibility statuses. If null, assume default policy.

View file

@ -19,6 +19,7 @@ package gtsmodel
import (
"path"
"strings"
"time"
)
@ -46,8 +47,8 @@ func (c AdminActionCategory) String() string {
}
}
func NewAdminActionCategory(in string) AdminActionCategory {
switch in {
func ParseAdminActionCategory(in string) AdminActionCategory {
switch strings.ToLower(in) {
case "account":
return AdminActionCategoryAccount
case "domain":
@ -96,8 +97,8 @@ func (t AdminActionType) String() string {
}
}
func NewAdminActionType(in string) AdminActionType {
switch in {
func ParseAdminActionType(in string) AdminActionType {
switch strings.ToLower(in) {
case "disable":
return AdminActionDisable
case "reenable":

View file

@ -0,0 +1,24 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gtsmodel
// enumType is the type we (at least, should) use
// for database enum types. it is the largest size
// supported by a PostgreSQL SMALLINT, since an
// SQLite SMALLINT is actually variable in size.
type enumType int16

View file

@ -22,7 +22,7 @@ import (
"strings"
"time"
"github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
)
// Conversation represents direct messages between the owner account and a set of other accounts.
@ -62,7 +62,7 @@ type Conversation struct {
// ConversationOtherAccountsKey creates an OtherAccountsKey from a list of OtherAccountIDs.
func ConversationOtherAccountsKey(otherAccountIDs []string) string {
otherAccountIDs = util.Deduplicate(otherAccountIDs)
otherAccountIDs = xslices.Deduplicate(otherAccountIDs)
slices.Sort(otherAccountIDs)
return strings.Join(otherAccountIDs, ",")
}

View file

@ -45,6 +45,10 @@ func (d *DomainAllow) GetUpdatedAt() time.Time {
return d.UpdatedAt
}
func (d *DomainAllow) SetUpdatedAt(i time.Time) {
d.UpdatedAt = i
}
func (d *DomainAllow) GetDomain() string {
return d.Domain
}
@ -53,26 +57,54 @@ func (d *DomainAllow) GetCreatedByAccountID() string {
return d.CreatedByAccountID
}
func (d *DomainAllow) SetCreatedByAccountID(i string) {
d.CreatedByAccountID = i
}
func (d *DomainAllow) GetCreatedByAccount() *Account {
return d.CreatedByAccount
}
func (d *DomainAllow) SetCreatedByAccount(i *Account) {
d.CreatedByAccount = i
}
func (d *DomainAllow) GetPrivateComment() string {
return d.PrivateComment
}
func (d *DomainAllow) SetPrivateComment(i string) {
d.PrivateComment = i
}
func (d *DomainAllow) GetPublicComment() string {
return d.PublicComment
}
func (d *DomainAllow) SetPublicComment(i string) {
d.PublicComment = i
}
func (d *DomainAllow) GetObfuscate() *bool {
return d.Obfuscate
}
func (d *DomainAllow) SetObfuscate(i *bool) {
d.Obfuscate = i
}
func (d *DomainAllow) GetSubscriptionID() string {
return d.SubscriptionID
}
func (d *DomainAllow) SetSubscriptionID(i string) {
d.SubscriptionID = i
}
func (d *DomainAllow) GetType() DomainPermissionType {
return DomainPermissionAllow
}
func (d *DomainAllow) IsOrphan() bool {
return d.SubscriptionID == ""
}

View file

@ -45,6 +45,10 @@ func (d *DomainBlock) GetUpdatedAt() time.Time {
return d.UpdatedAt
}
func (d *DomainBlock) SetUpdatedAt(i time.Time) {
d.UpdatedAt = i
}
func (d *DomainBlock) GetDomain() string {
return d.Domain
}
@ -53,26 +57,54 @@ func (d *DomainBlock) GetCreatedByAccountID() string {
return d.CreatedByAccountID
}
func (d *DomainBlock) SetCreatedByAccountID(i string) {
d.CreatedByAccountID = i
}
func (d *DomainBlock) GetCreatedByAccount() *Account {
return d.CreatedByAccount
}
func (d *DomainBlock) SetCreatedByAccount(i *Account) {
d.CreatedByAccount = i
}
func (d *DomainBlock) GetPrivateComment() string {
return d.PrivateComment
}
func (d *DomainBlock) SetPrivateComment(i string) {
d.PrivateComment = i
}
func (d *DomainBlock) GetPublicComment() string {
return d.PublicComment
}
func (d *DomainBlock) SetPublicComment(i string) {
d.PublicComment = i
}
func (d *DomainBlock) GetObfuscate() *bool {
return d.Obfuscate
}
func (d *DomainBlock) SetObfuscate(i *bool) {
d.Obfuscate = i
}
func (d *DomainBlock) GetSubscriptionID() string {
return d.SubscriptionID
}
func (d *DomainBlock) SetSubscriptionID(i string) {
d.SubscriptionID = i
}
func (d *DomainBlock) GetType() DomainPermissionType {
return DomainPermissionBlock
}
func (d *DomainBlock) IsOrphan() bool {
return d.SubscriptionID == ""
}

View file

@ -17,22 +17,36 @@
package gtsmodel
import "time"
import (
"strings"
"time"
)
// DomainPermission models a domain
// permission entry (block/allow).
// DomainPermission models a domain permission
// entry -- block / allow / draft / exclude.
type DomainPermission interface {
GetID() string
GetCreatedAt() time.Time
GetUpdatedAt() time.Time
SetUpdatedAt(i time.Time)
GetDomain() string
GetCreatedByAccountID() string
SetCreatedByAccountID(i string)
GetCreatedByAccount() *Account
SetCreatedByAccount(i *Account)
GetPrivateComment() string
SetPrivateComment(i string)
GetPublicComment() string
SetPublicComment(i string)
GetObfuscate() *bool
SetObfuscate(i *bool)
GetSubscriptionID() string
SetSubscriptionID(i string)
GetType() DomainPermissionType
// Return true if this DomainPermission
// does not have a subscription id set.
IsOrphan() bool
}
// Domain permission type.
@ -55,8 +69,8 @@ func (p DomainPermissionType) String() string {
}
}
func NewDomainPermissionType(in string) DomainPermissionType {
switch in {
func ParseDomainPermissionType(in string) DomainPermissionType {
switch strings.ToLower(in) {
case "block":
return DomainPermissionBlock
case "allow":

View file

@ -0,0 +1,110 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gtsmodel
import "time"
type DomainPermissionDraft struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Time when this item was created.
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Time when this item was last updated.
PermissionType DomainPermissionType `bun:",notnull,unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // Permission type of the draft.
Domain string `bun:",nullzero,notnull,unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // Domain to block or allow. Eg. 'whatever.com'.
CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this subscription.
CreatedByAccount *Account `bun:"-"` // Account corresponding to createdByAccountID.
PrivateComment string `bun:",nullzero"` // Private comment on this perm, viewable to admins.
PublicComment string `bun:",nullzero"` // Public comment on this perm, viewable (optionally) by everyone.
Obfuscate *bool `bun:",nullzero,notnull,default:false"` // Obfuscate domain name when displaying it publicly.
SubscriptionID string `bun:"type:CHAR(26),unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // ID of the subscription that created this draft, if any.
}
func (d *DomainPermissionDraft) GetID() string {
return d.ID
}
func (d *DomainPermissionDraft) GetCreatedAt() time.Time {
return d.CreatedAt
}
func (d *DomainPermissionDraft) GetUpdatedAt() time.Time {
return d.UpdatedAt
}
func (d *DomainPermissionDraft) SetUpdatedAt(i time.Time) {
d.UpdatedAt = i
}
func (d *DomainPermissionDraft) GetDomain() string {
return d.Domain
}
func (d *DomainPermissionDraft) GetCreatedByAccountID() string {
return d.CreatedByAccountID
}
func (d *DomainPermissionDraft) SetCreatedByAccountID(i string) {
d.CreatedByAccountID = i
}
func (d *DomainPermissionDraft) GetCreatedByAccount() *Account {
return d.CreatedByAccount
}
func (d *DomainPermissionDraft) SetCreatedByAccount(i *Account) {
d.CreatedByAccount = i
}
func (d *DomainPermissionDraft) GetPrivateComment() string {
return d.PrivateComment
}
func (d *DomainPermissionDraft) SetPrivateComment(i string) {
d.PrivateComment = i
}
func (d *DomainPermissionDraft) GetPublicComment() string {
return d.PublicComment
}
func (d *DomainPermissionDraft) SetPublicComment(i string) {
d.PublicComment = i
}
func (d *DomainPermissionDraft) GetObfuscate() *bool {
return d.Obfuscate
}
func (d *DomainPermissionDraft) SetObfuscate(i *bool) {
d.Obfuscate = i
}
func (d *DomainPermissionDraft) GetSubscriptionID() string {
return d.SubscriptionID
}
func (d *DomainPermissionDraft) SetSubscriptionID(i string) {
d.SubscriptionID = i
}
func (d *DomainPermissionDraft) GetType() DomainPermissionType {
return d.PermissionType
}
func (d *DomainPermissionDraft) IsOrphan() bool {
return d.SubscriptionID == ""
}

View file

@ -0,0 +1,93 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gtsmodel
import (
"time"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
// DomainPermissionExclude represents one domain that should be excluded
// when domain permission (excludes) are created from subscriptions.
type DomainPermissionExclude struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Time when this item was created.
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Time when this item was last updated.
Domain string `bun:",nullzero,notnull,unique"` // Domain to exclude. Eg. 'whatever.com'.
CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this exclude.
CreatedByAccount *Account `bun:"-"` // Account corresponding to createdByAccountID.
PrivateComment string `bun:",nullzero"` // Private comment on this exclude, viewable to admins.
}
func (d *DomainPermissionExclude) GetID() string {
return d.ID
}
func (d *DomainPermissionExclude) GetCreatedAt() time.Time {
return d.CreatedAt
}
func (d *DomainPermissionExclude) GetUpdatedAt() time.Time {
return d.UpdatedAt
}
func (d *DomainPermissionExclude) SetUpdatedAt(i time.Time) {
d.UpdatedAt = i
}
func (d *DomainPermissionExclude) GetDomain() string {
return d.Domain
}
func (d *DomainPermissionExclude) GetCreatedByAccountID() string {
return d.CreatedByAccountID
}
func (d *DomainPermissionExclude) SetCreatedByAccountID(i string) {
d.CreatedByAccountID = i
}
func (d *DomainPermissionExclude) GetCreatedByAccount() *Account {
return d.CreatedByAccount
}
func (d *DomainPermissionExclude) SetCreatedByAccount(i *Account) {
d.CreatedByAccount = i
}
func (d *DomainPermissionExclude) GetPrivateComment() string {
return d.PrivateComment
}
func (d *DomainPermissionExclude) SetPrivateComment(i string) {
d.PrivateComment = i
}
/*
Stubbed functions for interface purposes.
*/
func (d *DomainPermissionExclude) GetPublicComment() string { return "" }
func (d *DomainPermissionExclude) SetPublicComment(_ string) {}
func (d *DomainPermissionExclude) GetObfuscate() *bool { return util.Ptr(false) }
func (d *DomainPermissionExclude) SetObfuscate(_ *bool) {}
func (d *DomainPermissionExclude) GetSubscriptionID() string { return "" }
func (d *DomainPermissionExclude) SetSubscriptionID(_ string) {}
func (d *DomainPermissionExclude) GetType() DomainPermissionType { return DomainPermissionUnknown }
func (d *DomainPermissionExclude) IsOrphan() bool { return true }

View file

@ -0,0 +1,74 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gtsmodel
import "time"
type DomainPermissionSubscription struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
Priority uint8 `bun:""` // Priority of this subscription compared to others of the same permission type. 0-255 (higher = higher priority).
Title string `bun:",nullzero,unique"` // Moderator-set title for this list.
PermissionType DomainPermissionType `bun:",nullzero,notnull"` // Permission type of the subscription.
AsDraft *bool `bun:",nullzero,notnull,default:true"` // Create domain permission entries resulting from this subscription as drafts.
AdoptOrphans *bool `bun:",nullzero,notnull,default:false"` // Adopt orphaned domain permissions present in this subscription's entries.
CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this subscription.
CreatedByAccount *Account `bun:"-"` // Account corresponding to createdByAccountID.
URI string `bun:",nullzero,notnull,unique"` // URI of the domain permission list.
ContentType DomainPermSubContentType `bun:",nullzero,notnull"` // Content type to expect from the URI.
FetchUsername string `bun:",nullzero"` // Username to send when doing a GET of URI using basic auth.
FetchPassword string `bun:",nullzero"` // Password to send when doing a GET of URI using basic auth.
FetchedAt time.Time `bun:"type:timestamptz,nullzero"` // Time when fetch of URI was last attempted.
SuccessfullyFetchedAt time.Time `bun:"type:timestamptz,nullzero"` // Time when the domain permission list was last *successfuly* fetched, to be transmitted as If-Modified-Since header.
ETag string `bun:"etag,nullzero"` // Etag last received from the server (if any) on successful fetch.
Error string `bun:",nullzero"` // If latest fetch attempt errored, this field stores the error message. Cleared on latest successful fetch.
}
type DomainPermSubContentType enumType
const (
DomainPermSubContentTypeUnknown DomainPermSubContentType = 0 // ???
DomainPermSubContentTypeCSV DomainPermSubContentType = 1 // text/csv
DomainPermSubContentTypeJSON DomainPermSubContentType = 2 // application/json
DomainPermSubContentTypePlain DomainPermSubContentType = 3 // text/plain
)
func (p DomainPermSubContentType) String() string {
switch p {
case DomainPermSubContentTypeCSV:
return "text/csv"
case DomainPermSubContentTypeJSON:
return "application/json"
case DomainPermSubContentTypePlain:
return "text/plain"
default:
panic("unknown content type")
}
}
func NewDomainPermSubContentType(in string) DomainPermSubContentType {
switch in {
case "text/csv":
return DomainPermSubContentTypeCSV
case "application/json":
return DomainPermSubContentTypeJSON
case "text/plain":
return DomainPermSubContentTypePlain
default:
return DomainPermSubContentTypeUnknown
}
}

View file

@ -26,20 +26,20 @@ import (
// Filter stores a filter created by a local account.
type Filter struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
ExpiresAt time.Time `bun:"type:timestamptz,nullzero"` // Time filter should expire. If null, should not expire.
AccountID string `bun:"type:CHAR(26),notnull,nullzero"` // ID of the local account that created the filter.
Title string `bun:",nullzero,notnull,unique"` // The name of the filter.
Action FilterAction `bun:",nullzero,notnull"` // The action to take.
Keywords []*FilterKeyword `bun:"-"` // Keywords for this filter.
Statuses []*FilterStatus `bun:"-"` // Statuses for this filter.
ContextHome *bool `bun:",nullzero,notnull,default:false"` // Apply filter to home timeline and lists.
ContextNotifications *bool `bun:",nullzero,notnull,default:false"` // Apply filter to notifications.
ContextPublic *bool `bun:",nullzero,notnull,default:false"` // Apply filter to home timeline and lists.
ContextThread *bool `bun:",nullzero,notnull,default:false"` // Apply filter when viewing a status's associated thread.
ContextAccount *bool `bun:",nullzero,notnull,default:false"` // Apply filter when viewing an account profile.
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
ExpiresAt time.Time `bun:"type:timestamptz,nullzero"` // Time filter should expire. If null, should not expire.
AccountID string `bun:"type:CHAR(26),notnull,nullzero,unique:filters_account_id_title_uniq"` // ID of the local account that created the filter.
Title string `bun:",nullzero,notnull,unique:filters_account_id_title_uniq"` // The name of the filter.
Action FilterAction `bun:",nullzero,notnull"` // The action to take.
Keywords []*FilterKeyword `bun:"-"` // Keywords for this filter.
Statuses []*FilterStatus `bun:"-"` // Statuses for this filter.
ContextHome *bool `bun:",nullzero,notnull,default:false"` // Apply filter to home timeline and lists.
ContextNotifications *bool `bun:",nullzero,notnull,default:false"` // Apply filter to notifications.
ContextPublic *bool `bun:",nullzero,notnull,default:false"` // Apply filter to home timeline and lists.
ContextThread *bool `bun:",nullzero,notnull,default:false"` // Apply filter when viewing a status's associated thread.
ContextAccount *bool `bun:",nullzero,notnull,default:false"` // Apply filter when viewing an account profile.
}
// Expired returns whether the filter has expired at a given time.

View file

@ -34,6 +34,7 @@ type Instance struct {
ShortDescriptionText string `bun:""` // Raw text version of short description (before parsing).
Description string `bun:""` // Longer description of this instance.
DescriptionText string `bun:""` // Raw text version of long description (before parsing).
CustomCSS string `bun:",nullzero"` // Custom CSS for the instance.
Terms string `bun:""` // Terms and conditions of this instance.
TermsText string `bun:""` // Raw text version of terms (before parsing).
ContactEmail string `bun:""` // Contact email address for this instance

View file

@ -69,25 +69,29 @@ type InteractionRequest struct {
Like *StatusFave `bun:"-"` // Not stored in DB. Only set if InteractionType = InteractionLike.
Reply *Status `bun:"-"` // Not stored in DB. Only set if InteractionType = InteractionReply.
Announce *Status `bun:"-"` // Not stored in DB. Only set if InteractionType = InteractionAnnounce.
URI string `bun:",nullzero,unique"` // ActivityPub URI of the Accept (if accepted) or Reject (if rejected). Null/empty if currently neither accepted not rejected.
AcceptedAt time.Time `bun:"type:timestamptz,nullzero"` // If interaction request was accepted, time at which this occurred.
RejectedAt time.Time `bun:"type:timestamptz,nullzero"` // If interaction request was rejected, time at which this occurred.
// ActivityPub URI of the Accept (if accepted) or Reject (if rejected).
// Field may be empty if currently neither accepted not rejected, or if
// acceptance/rejection was implicit (ie., not resulting from an Activity).
URI string `bun:",nullzero,unique"`
}
// IsHandled returns true if interaction
// request has been neither accepted or rejected.
func (ir *InteractionRequest) IsPending() bool {
return ir.URI == "" && ir.AcceptedAt.IsZero() && ir.RejectedAt.IsZero()
return !ir.IsAccepted() && !ir.IsRejected()
}
// IsAccepted returns true if this
// interaction request has been accepted.
func (ir *InteractionRequest) IsAccepted() bool {
return ir.URI != "" && !ir.AcceptedAt.IsZero()
return !ir.AcceptedAt.IsZero()
}
// IsRejected returns true if this
// interaction request has been rejected.
func (ir *InteractionRequest) IsRejected() bool {
return ir.URI != "" && !ir.RejectedAt.IsZero()
return !ir.RejectedAt.IsZero()
}

View file

@ -224,7 +224,7 @@ func DefaultInteractionPolicyFor(v Visibility) *InteractionPolicy {
case VisibilityDirect:
return DefaultInteractionPolicyDirect()
default:
panic("visibility " + v + " not recognized")
panic("invalid visibility")
}
}

View file

@ -26,7 +26,6 @@ import (
type MediaAttachment struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
StatusID string `bun:"type:CHAR(26),nullzero"` // ID of the status to which this is attached
URL string `bun:",nullzero"` // Where can the attachment be retrieved on *this* server
RemoteURL string `bun:",nullzero"` // Where can the attachment be retrieved on a remote server (empty for local media)

View file

@ -26,7 +26,6 @@ import (
type Mention struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the status this mention originates from
Status *Status `bun:"rel:belongs-to"` // status referred to by statusID
OriginAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // ID of the mention creator account

View file

@ -17,7 +17,10 @@
package gtsmodel
import "time"
import (
"strings"
"time"
)
// Notification models an alert/notification sent to an account about something like a reblog, like, new follow request, etc.
type Notification struct {
@ -34,20 +37,82 @@ type Notification struct {
Read *bool `bun:",nullzero,notnull,default:false"` // Notification has been seen/read
}
// NotificationType describes the reason/type of this notification.
type NotificationType string
// NotificationType describes the
// reason/type of this notification.
type NotificationType enumType
// Notification Types
const (
NotificationFollow NotificationType = "follow" // NotificationFollow -- someone followed you
NotificationFollowRequest NotificationType = "follow_request" // NotificationFollowRequest -- someone requested to follow you
NotificationMention NotificationType = "mention" // NotificationMention -- someone mentioned you in their status
NotificationReblog NotificationType = "reblog" // NotificationReblog -- someone boosted one of your statuses
NotificationFave NotificationType = "favourite" // NotificationFave -- someone faved/liked one of your statuses
NotificationPoll NotificationType = "poll" // NotificationPoll -- a poll you voted in or created has ended
NotificationStatus NotificationType = "status" // NotificationStatus -- someone you enabled notifications for has posted a status.
NotificationSignup NotificationType = "admin.sign_up" // NotificationSignup -- someone has submitted a new account sign-up to the instance.
NotificationPendingFave NotificationType = "pending.favourite" // Someone has faved a status of yours, which requires approval by you.
NotificationPendingReply NotificationType = "pending.reply" // Someone has replied to a status of yours, which requires approval by you.
NotificationPendingReblog NotificationType = "pending.reblog" // Someone has boosted a status of yours, which requires approval by you.
// Notification Types
NotificationUnknown NotificationType = 0 // NotificationUnknown -- unknown notification type, error if this occurs
NotificationFollow NotificationType = 1 // NotificationFollow -- someone followed you
NotificationFollowRequest NotificationType = 2 // NotificationFollowRequest -- someone requested to follow you
NotificationMention NotificationType = 3 // NotificationMention -- someone mentioned you in their status
NotificationReblog NotificationType = 4 // NotificationReblog -- someone boosted one of your statuses
NotificationFave NotificationType = 5 // NotificationFave -- someone faved/liked one of your statuses
NotificationPoll NotificationType = 6 // NotificationPoll -- a poll you voted in or created has ended
NotificationStatus NotificationType = 7 // NotificationStatus -- someone you enabled notifications for has posted a status.
NotificationSignup NotificationType = 8 // NotificationSignup -- someone has submitted a new account sign-up to the instance.
NotificationPendingFave NotificationType = 9 // Someone has faved a status of yours, which requires approval by you.
NotificationPendingReply NotificationType = 10 // Someone has replied to a status of yours, which requires approval by you.
NotificationPendingReblog NotificationType = 11 // Someone has boosted a status of yours, which requires approval by you.
)
// String returns a stringified, frontend API compatible form of NotificationType.
func (t NotificationType) String() string {
switch t {
case NotificationFollow:
return "follow"
case NotificationFollowRequest:
return "follow_request"
case NotificationMention:
return "mention"
case NotificationReblog:
return "reblog"
case NotificationFave:
return "favourite"
case NotificationPoll:
return "poll"
case NotificationStatus:
return "status"
case NotificationSignup:
return "admin.sign_up"
case NotificationPendingFave:
return "pending.favourite"
case NotificationPendingReply:
return "pending.reply"
case NotificationPendingReblog:
return "pending.reblog"
default:
panic("invalid notification type")
}
}
// ParseNotificationType returns a notification type from the given value.
func ParseNotificationType(in string) NotificationType {
switch strings.ToLower(in) {
case "follow":
return NotificationFollow
case "follow_request":
return NotificationFollowRequest
case "mention":
return NotificationMention
case "reblog":
return NotificationReblog
case "favourite":
return NotificationFave
case "poll":
return NotificationPoll
case "status":
return NotificationStatus
case "admin.sign_up":
return NotificationSignup
case "pending.favourite":
return NotificationPendingFave
case "pending.reply":
return NotificationPendingReply
case "pending.reblog":
return NotificationPendingReblog
default:
return NotificationUnknown
}
}

View file

@ -20,13 +20,15 @@ package gtsmodel
import (
"slices"
"time"
"github.com/superseriousbusiness/gotosocial/internal/util/xslices"
)
// Status represents a user-created 'post' or 'status' in the database, either remote or local
type Status struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // id of this item in the database
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item last updated
EditedAt time.Time `bun:"type:timestamptz,nullzero"` // when this status was last edited (if set)
FetchedAt time.Time `bun:"type:timestamptz,nullzero"` // when was item (remote) last fetched.
PinnedAt time.Time `bun:"type:timestamptz,nullzero"` // Status was pinned by owning account at this time.
URI string `bun:",unique,nullzero,notnull"` // activitypub URI of this status
@ -55,6 +57,8 @@ type Status struct {
BoostOf *Status `bun:"-"` // status that corresponds to boostOfID
BoostOfAccount *Account `bun:"rel:belongs-to"` // account that corresponds to boostOfAccountID
ThreadID string `bun:"type:CHAR(26),nullzero"` // id of the thread to which this status belongs; only set for remote statuses if a local account is involved at some point in the thread, otherwise null
EditIDs []string `bun:"edits,array"` //
Edits []*StatusEdit `bun:"-"` //
PollID string `bun:"type:CHAR(26),nullzero"` //
Poll *Poll `bun:"-"` //
ContentWarning string `bun:",nullzero"` // cw string for this status
@ -92,7 +96,8 @@ func (s *Status) GetBoostOfAccountID() string {
return s.BoostOfAccountID
}
// AttachmentsPopulated returns whether media attachments are populated according to current AttachmentIDs.
// AttachmentsPopulated returns whether media attachments
// are populated according to current AttachmentIDs.
func (s *Status) AttachmentsPopulated() bool {
if len(s.AttachmentIDs) != len(s.Attachments) {
// this is the quickest indicator.
@ -106,7 +111,8 @@ func (s *Status) AttachmentsPopulated() bool {
return true
}
// TagsPopulated returns whether tags are populated according to current TagIDs.
// TagsPopulated returns whether tags are
// populated according to current TagIDs.
func (s *Status) TagsPopulated() bool {
if len(s.TagIDs) != len(s.Tags) {
// this is the quickest indicator.
@ -120,7 +126,8 @@ func (s *Status) TagsPopulated() bool {
return true
}
// MentionsPopulated returns whether mentions are populated according to current MentionIDs.
// MentionsPopulated returns whether mentions are
// populated according to current MentionIDs.
func (s *Status) MentionsPopulated() bool {
if len(s.MentionIDs) != len(s.Mentions) {
// this is the quickest indicator.
@ -134,7 +141,8 @@ func (s *Status) MentionsPopulated() bool {
return true
}
// EmojisPopulated returns whether emojis are populated according to current EmojiIDs.
// EmojisPopulated returns whether emojis are
// populated according to current EmojiIDs.
func (s *Status) EmojisPopulated() bool {
if len(s.EmojiIDs) != len(s.Emojis) {
// this is the quickest indicator.
@ -148,6 +156,21 @@ func (s *Status) EmojisPopulated() bool {
return true
}
// EditsPopulated returns whether edits are
// populated according to current EditIDs.
func (s *Status) EditsPopulated() bool {
if len(s.EditIDs) != len(s.Edits) {
// this is quickest indicator.
return false
}
for i, id := range s.EditIDs {
if s.Edits[i].ID != id {
return false
}
}
return true
}
// EmojissUpToDate returns whether status emoji attachments of receiving status are up-to-date
// according to emoji attachments of the passed status, by comparing their emoji URIs. We don't
// use IDs as this is used to determine whether there are new emojis to fetch.
@ -247,6 +270,44 @@ func (s *Status) IsLocalOnly() bool {
return s.Federated == nil || !*s.Federated
}
// AllAttachmentIDs gathers ALL media attachment IDs from both the
// receiving Status{}, and any historical Status{}.Edits. Note that
// this function will panic if Status{}.Edits is not populated.
func (s *Status) AllAttachmentIDs() []string {
var total int
if len(s.EditIDs) != len(s.Edits) {
panic("status edits not populated")
}
// Get count of attachment IDs.
total += len(s.AttachmentIDs)
for _, edit := range s.Edits {
total += len(edit.AttachmentIDs)
}
// Start gathering of all IDs with *current* attachment IDs.
attachmentIDs := make([]string, len(s.AttachmentIDs), total)
copy(attachmentIDs, s.AttachmentIDs)
// Append IDs of historical edits.
for _, edit := range s.Edits {
attachmentIDs = append(attachmentIDs, edit.AttachmentIDs...)
}
// Deduplicate these IDs in case of shared media.
return xslices.Deduplicate(attachmentIDs)
}
// UpdatedAt returns latest time this status
// was updated, either EditedAt or CreatedAt.
func (s *Status) UpdatedAt() time.Time {
if s.EditedAt.IsZero() {
return s.CreatedAt
}
return s.EditedAt
}
// StatusToTag is an intermediate struct to facilitate the many2many relationship between a status and one or more tags.
type StatusToTag struct {
StatusID string `bun:"type:CHAR(26),unique:statustag,nullzero,notnull"`
@ -263,27 +324,58 @@ type StatusToEmoji struct {
Emoji *Emoji `bun:"rel:belongs-to"`
}
// Visibility represents the visibility granularity of a status.
type Visibility string
// Visibility represents the
// visibility granularity of a status.
type Visibility enumType
const (
// VisibilityNone means nobody can see this.
// It's only used for web status visibility.
VisibilityNone Visibility = "none"
// VisibilityPublic means this status will be visible to everyone on all timelines.
VisibilityPublic Visibility = "public"
// VisibilityUnlocked means this status will be visible to everyone, but will only show on home timeline to followers, and in lists.
VisibilityUnlocked Visibility = "unlocked"
VisibilityNone Visibility = 1
// VisibilityPublic means this status will
// be visible to everyone on all timelines.
VisibilityPublic Visibility = 2
// VisibilityUnlocked means this status will be visible to everyone,
// but will only show on home timeline to followers, and in lists.
VisibilityUnlocked Visibility = 3
// VisibilityFollowersOnly means this status is viewable to followers only.
VisibilityFollowersOnly Visibility = "followers_only"
// VisibilityMutualsOnly means this status is visible to mutual followers only.
VisibilityMutualsOnly Visibility = "mutuals_only"
// VisibilityDirect means this status is visible only to mentioned recipients.
VisibilityDirect Visibility = "direct"
VisibilityFollowersOnly Visibility = 4
// VisibilityMutualsOnly means this status
// is visible to mutual followers only.
VisibilityMutualsOnly Visibility = 5
// VisibilityDirect means this status is
// visible only to mentioned recipients.
VisibilityDirect Visibility = 6
// VisibilityDefault is used when no other setting can be found.
VisibilityDefault Visibility = VisibilityUnlocked
)
// String returns a stringified, frontend API compatible form of Visibility.
func (v Visibility) String() string {
switch v {
case VisibilityNone:
return "none"
case VisibilityPublic:
return "public"
case VisibilityUnlocked:
return "unlocked"
case VisibilityFollowersOnly:
return "followers_only"
case VisibilityMutualsOnly:
return "mutuals_only"
case VisibilityDirect:
return "direct"
default:
panic("invalid visibility")
}
}
// Content models the simple string content
// of a status along with its ContentMap,
// which contains content entries keyed by

View file

@ -0,0 +1,62 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gtsmodel
import "time"
// StatusEdit represents a **historical** view of a Status
// after a received edit. The Status itself will always
// contain the latest up-to-date information.
//
// Note that stored status edits may not exactly match that
// of the origin server, they are a best-effort by receiver
// to store version history. There is no AP history endpoint.
type StatusEdit struct {
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
Content string `bun:""` // Content of status at time of edit; likely html-formatted but not guaranteed.
ContentWarning string `bun:",nullzero"` // Content warning of status at time of edit.
Text string `bun:""` // Original status text, without formatting, at time of edit.
Language string `bun:",nullzero"` // Status language at time of edit.
Sensitive *bool `bun:",nullzero,notnull,default:false"` // Status sensitive flag at time of edit.
AttachmentIDs []string `bun:"attachments,array"` // Database IDs of media attachments associated with status at time of edit.
AttachmentDescriptions []string `bun:",array"` // Previous media descriptions of media attachments associated with status at time of edit.
Attachments []*MediaAttachment `bun:"-"` // Media attachments relating to .AttachmentIDs field (not always populated).
PollOptions []string `bun:",array"` // Poll options of status at time of edit, only set if status contains a poll.
PollVotes []int `bun:",array"` // Poll vote count at time of status edit, only set if poll votes were reset.
StatusID string `bun:"type:CHAR(26),nullzero,notnull"` // The originating status ID this is a historical edit of.
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // The creation time of this version of the status content (according to receiving server).
// We don't bother having a *gtsmodel.Status model here
// as the StatusEdit is always just attached to a Status,
// so it doesn't need a self-reference back to it.
}
// AttachmentsPopulated returns whether media attachments
// are populated according to current AttachmentIDs.
func (e *StatusEdit) AttachmentsPopulated() bool {
if len(e.AttachmentIDs) != len(e.Attachments) {
// this is the quickest indicator.
return false
}
for i, id := range e.AttachmentIDs {
if e.Attachments[i].ID != id {
return false
}
}
return true
}