2023-03-12 15:00:57 +00:00
// 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/>.
2021-03-05 17:31:12 +00:00
2021-04-19 17:42:19 +00:00
// Package gtsmodel contains types used *internally* by GoToSocial and added/removed/selected from the database.
2021-03-15 15:15:14 +00:00
// These types should never be serialized and/or sent out via public APIs, as they contain sensitive information.
2021-08-25 13:34:33 +00:00
// The annotation used on these structs is for handling them via the bun-db ORM.
// See here for more info on bun model annotations: https://bun.uptrace.dev/guide/models.html
2021-04-19 17:42:19 +00:00
package gtsmodel
2021-03-05 17:31:12 +00:00
import (
2021-04-01 18:46:45 +00:00
"crypto/rsa"
2023-02-03 20:03:05 +00:00
"strings"
2021-03-05 17:31:12 +00:00
"time"
2023-02-03 20:03:05 +00:00
"github.com/superseriousbusiness/gotosocial/internal/config"
[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
2023-03-28 13:03:14 +00:00
"github.com/superseriousbusiness/gotosocial/internal/log"
2021-03-05 17:31:12 +00:00
)
2021-08-31 17:27:02 +00:00
// Account represents either a local or a remote fediverse account, gotosocial or otherwise (mastodon, pleroma, etc).
2021-03-15 22:05:24 +00:00
type Account struct {
2023-08-06 10:22:40 +00:00
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 was last updated.
FetchedAt time . Time ` bun:"type:timestamptz,nullzero" ` // when was item (remote) last fetched.
Username string ` bun:",nullzero,notnull,unique:usernamedomain" ` // Username of the account, should just be a string of [a-zA-Z0-9_]. Can be added to domain to create the full username in the form ``[username]@[domain]`` eg., ``user_96@example.org``. Username and domain should be unique *with* each other
Domain string ` bun:",nullzero,unique:usernamedomain" ` // Domain of the account, will be null if this is a local account, otherwise something like ``example.org``. Should be unique with username.
AvatarMediaAttachmentID string ` bun:"type:CHAR(26),nullzero" ` // Database ID of the media attachment, if present
AvatarMediaAttachment * MediaAttachment ` bun:"rel:belongs-to" ` // MediaAttachment corresponding to avatarMediaAttachmentID
AvatarRemoteURL string ` bun:",nullzero" ` // For a non-local account, where can the header be fetched?
HeaderMediaAttachmentID string ` bun:"type:CHAR(26),nullzero" ` // Database ID of the media attachment, if present
HeaderMediaAttachment * MediaAttachment ` bun:"rel:belongs-to" ` // MediaAttachment corresponding to headerMediaAttachmentID
HeaderRemoteURL string ` bun:",nullzero" ` // For a non-local account, where can the header be fetched?
DisplayName string ` bun:"" ` // DisplayName for this account. Can be empty, then just the Username will be used for display purposes.
EmojiIDs [ ] string ` bun:"emojis,array" ` // Database IDs of any emojis used in this account's bio, display name, etc
Emojis [ ] * Emoji ` bun:"attached_emojis,m2m:account_to_emojis" ` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation
Fields [ ] * Field // A slice of of fields that this account has added to their profile.
FieldsRaw [ ] * Field // The raw (unparsed) content of fields that this account has added to their profile, without conversion to HTML, only available when requester = target
Note string ` bun:"" ` // A note that this account has on their profile (ie., the account's bio/description of themselves)
NoteRaw string ` bun:"" ` // The raw contents of .Note without conversion to HTML, only available when requester = target
Memorial * bool ` bun:",default:false" ` // Is this a memorial account, ie., has the user passed away?
AlsoKnownAs string ` bun:"type:CHAR(26),nullzero" ` // This account is associated with x account id (TODO: migrate to be AlsoKnownAsID)
MovedToAccountID string ` bun:"type:CHAR(26),nullzero" ` // This account has moved this account id in the database
Bot * bool ` bun:",default:false" ` // Does this account identify itself as a bot?
Reason string ` bun:"" ` // What reason was given for signing up when this account was created?
Locked * bool ` bun:",default:true" ` // Does this account need an approval for new followers?
Discoverable * bool ` bun:",default:false" ` // Should this account be shown in the instance's profile directory?
Privacy Visibility ` bun:",nullzero" ` // Default post privacy for this account
Sensitive * bool ` bun:",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).
CustomCSS string ` bun:",nullzero" ` // Custom CSS that should be displayed for this Account's profile and statuses.
URI string ` bun:",nullzero,notnull,unique" ` // ActivityPub URI for this account.
URL string ` bun:",nullzero,unique" ` // Web URL for this account's profile
InboxURI string ` bun:",nullzero,unique" ` // Address of this account's ActivityPub inbox, for sending activity to
SharedInboxURI * string ` bun:"" ` // Address of this account's ActivityPub sharedInbox. Gotcha warning: this is a string pointer because it has three possible states: 1. We don't know yet if the account has a shared inbox -- null. 2. We know it doesn't have a shared inbox -- empty string. 3. We know it does have a shared inbox -- url string.
OutboxURI string ` bun:",nullzero,unique" ` // Address of this account's activitypub outbox
FollowingURI string ` bun:",nullzero,unique" ` // URI for getting the following list of this account
FollowersURI string ` bun:",nullzero,unique" ` // URI for getting the followers list of this account
FeaturedCollectionURI string ` bun:",nullzero,unique" ` // URL for getting the featured collection list of this account
ActorType string ` bun:",nullzero,notnull" ` // What type of activitypub actor is this account?
2023-09-12 09:43:12 +00:00
PrivateKey * rsa . PrivateKey ` bun:"" ` // Privatekey for signing activitypub requests, will only be defined for local accounts
PublicKey * rsa . PublicKey ` bun:",notnull" ` // Publickey for authorizing signed activitypub requests, will be defined for both local and remote accounts
2023-08-06 10:22:40 +00:00
PublicKeyURI string ` bun:",nullzero,notnull,unique" ` // Web-reachable location of this account's public key
2023-09-12 09:43:12 +00:00
PublicKeyExpiresAt time . Time ` bun:"type:timestamptz,nullzero" ` // PublicKey will expire/has expired at given time, and should be fetched again as appropriate. Only ever set for remote accounts.
2023-08-06 10:22:40 +00:00
SensitizedAt time . Time ` bun:"type:timestamptz,nullzero" ` // When was this account set to have all its media shown as sensitive?
SilencedAt time . Time ` bun:"type:timestamptz,nullzero" ` // When was this account silenced (eg., statuses only visible to followers, not public)?
SuspendedAt time . Time ` bun:"type:timestamptz,nullzero" ` // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account)
HideCollections * bool ` bun:",default:false" ` // Hide this account's collections
SuspensionOrigin string ` bun:"type:CHAR(26),nullzero" ` // id of the database entry that caused this account to become suspended -- can be an account ID or a domain block ID
EnableRSS * bool ` bun:",default:false" ` // enable RSS feed subscription for this account's public posts at [URL]/feed
2021-03-05 17:31:12 +00:00
}
2023-02-03 20:03:05 +00:00
// IsLocal returns whether account is a local user account.
[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
2023-03-28 13:03:14 +00:00
func ( a * Account ) IsLocal ( ) bool {
2023-02-03 20:03:05 +00:00
return a . Domain == "" || a . Domain == config . GetHost ( ) || a . Domain == config . GetAccountDomain ( )
}
// IsRemote returns whether account is a remote user account.
[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
2023-03-28 13:03:14 +00:00
func ( a * Account ) IsRemote ( ) bool {
2023-02-03 20:03:05 +00:00
return ! a . IsLocal ( )
}
// IsInstance returns whether account is an instance internal actor account.
[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
2023-03-28 13:03:14 +00:00
func ( a * Account ) IsInstance ( ) bool {
2023-05-09 15:05:35 +00:00
if a . IsLocal ( ) {
// Check if our instance account.
return a . Username == config . GetHost ( )
}
// Check if remote instance account.
2023-02-03 20:03:05 +00:00
return a . Username == a . Domain ||
a . FollowersURI == "" ||
a . FollowingURI == "" ||
2023-06-21 16:26:40 +00:00
( a . Username == "internal.fetch" && strings . Contains ( a . Note , "internal service actor" ) ) ||
a . Username == "instance.actor" // <- misskey
2023-02-03 20:03:05 +00:00
}
[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
2023-03-28 13:03:14 +00:00
// EmojisPopulated returns whether emojis are populated according to current EmojiIDs.
func ( a * Account ) EmojisPopulated ( ) bool {
if len ( a . EmojiIDs ) != len ( a . Emojis ) {
// this is the quickest indicator.
return false
}
// Emojis must be in same order.
for i , id := range a . EmojiIDs {
if a . Emojis [ i ] == nil {
log . Warnf ( nil , "nil emoji in slice for account %s" , a . URI )
continue
}
if a . Emojis [ i ] . ID != id {
return false
}
}
return true
}
2023-09-12 09:43:12 +00:00
// PubKeyExpired returns true if the account's public key
// has been marked as expired, and the expiry time has passed.
func ( a * Account ) PubKeyExpired ( ) bool {
if a == nil {
return false
}
return ! a . PublicKeyExpiresAt . IsZero ( ) &&
a . PublicKeyExpiresAt . Before ( time . Now ( ) )
}
2022-09-26 09:56:01 +00:00
// AccountToEmoji is an intermediate struct to facilitate the many2many relationship between an account and one or more emojis.
type AccountToEmoji struct {
2023-08-06 10:22:40 +00:00
AccountID string ` bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull" `
Account * Account ` bun:"rel:belongs-to" `
EmojiID string ` bun:"type:CHAR(26),unique:accountemoji,nullzero,notnull" `
Emoji * Emoji ` bun:"rel:belongs-to" `
2022-09-26 09:56:01 +00:00
}
2021-04-01 18:46:45 +00:00
// Field represents a key value field on an account, for things like pronouns, website, etc.
// VerifiedAt is optional, to be used only if Value is a URL to a webpage that contains the
// username of the user.
type Field struct {
2023-08-06 10:22:40 +00:00
Name string // Name of this field.
Value string // Value of this field.
VerifiedAt time . Time ` bun:",nullzero" ` // This field was verified at (optional).
2021-03-07 12:05:33 +00:00
}
2021-09-01 16:29:25 +00:00
// Relationship describes a requester's relationship with another account.
type Relationship struct {
ID string // The account id.
Following bool // Are you following this user?
ShowingReblogs bool // Are you receiving this user's boosts in your home timeline?
Notifying bool // Have you enabled notifications for this user?
FollowedBy bool // Are you followed by this user?
Blocking bool // Are you blocking this user?
BlockedBy bool // Is this user blocking you?
Muting bool // Are you muting this user?
MutingNotifications bool // Are you muting notifications from this user?
Requested bool // Do you have a pending follow request for this user?
DomainBlocking bool // Are you blocking this user's domain?
Endorsed bool // Are you featuring this user on your profile?
Note string // Your note on this account.
}