diff --git a/internal/transport/dereference_test.go b/internal/transport/dereference_test.go index 836ba2ba5..4c284700a 100644 --- a/internal/transport/dereference_test.go +++ b/internal/transport/dereference_test.go @@ -185,7 +185,7 @@ func (suite *DereferenceTestSuite) TestDerefLocalStatus() { "replies": { "first": { "id": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true", - "next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?only_other_accounts=false\u0026page=true", + "next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true\u0026only_other_accounts=false", "partOf": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies", "type": "CollectionPage" }, diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go index ce1501e1a..7cf736993 100644 --- a/internal/typeutils/internaltoas.go +++ b/internal/typeutils/internaltoas.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" "net/url" + "strconv" "strings" "code.superseriousbusiness.org/activity/pub" @@ -1042,7 +1043,7 @@ func (c *Converter) MentionToAS(ctx context.Context, m *gtsmodel.Mention) (vocab domain = m.TargetAccount.Domain } username := m.TargetAccount.Username - nameString := fmt.Sprintf("@%s@%s", username, domain) + nameString := "@" + username + "@" + domain nameProp := streams.NewActivityStreamsNameProperty() nameProp.AppendXMLSchemaString(nameString) mention.SetActivityStreamsName(nameProp) @@ -1104,7 +1105,7 @@ func (c *Converter) EmojiToAS(ctx context.Context, e *gtsmodel.Emoji) (vocab.Too emoji.SetJSONLDId(idProp) nameProp := streams.NewActivityStreamsNameProperty() - nameString := fmt.Sprintf(":%s:", e.Shortcode) + nameString := ":" + e.Shortcode + ":" nameProp.AppendXMLSchemaString(nameString) emoji.SetActivityStreamsName(nameProp) @@ -1490,7 +1491,7 @@ func (c *Converter) BlockToAS(ctx context.Context, b *gtsmodel.Block) (vocab.Act // } // } func (c *Converter) StatusToASRepliesCollection(ctx context.Context, status *gtsmodel.Status, onlyOtherAccounts bool) (vocab.ActivityStreamsCollection, error) { - collectionID := fmt.Sprintf("%s/replies", status.URI) + collectionID := status.URI + "/replies" collectionIDURI, err := url.Parse(collectionID) if err != nil { return nil, err @@ -1509,7 +1510,7 @@ func (c *Converter) StatusToASRepliesCollection(ctx context.Context, status *gts // first.id firstPageIDProp := streams.NewJSONLDIdProperty() - firstPageID, err := url.Parse(fmt.Sprintf("%s?page=true", collectionID)) + firstPageID, err := url.Parse(collectionID + "?page=true") if err != nil { return nil, gtserror.NewErrorInternalError(err) } @@ -1518,7 +1519,8 @@ func (c *Converter) StatusToASRepliesCollection(ctx context.Context, status *gts // first.next nextProp := streams.NewActivityStreamsNextProperty() - nextPropID, err := url.Parse(fmt.Sprintf("%s?only_other_accounts=%t&page=true", collectionID, onlyOtherAccounts)) + nextPropIDStr := collectionID + "?page=true&only_other_accounts=" + strconv.FormatBool(onlyOtherAccounts) + nextPropID, err := url.Parse(nextPropIDStr) if err != nil { return nil, gtserror.NewErrorInternalError(err) } @@ -1553,15 +1555,15 @@ func (c *Converter) StatusToASRepliesCollection(ctx context.Context, status *gts // ] // } func (c *Converter) StatusURIsToASRepliesPage(ctx context.Context, status *gtsmodel.Status, onlyOtherAccounts bool, minID string, replies map[string]*url.URL) (vocab.ActivityStreamsCollectionPage, error) { - collectionID := fmt.Sprintf("%s/replies", status.URI) + collectionID := status.URI + "/replies" page := streams.NewActivityStreamsCollectionPage() // .id pageIDProp := streams.NewJSONLDIdProperty() - pageIDString := fmt.Sprintf("%s?page=true&only_other_accounts=%t", collectionID, onlyOtherAccounts) + pageIDString := collectionID + "?page=true&only_other_accounts=" + strconv.FormatBool(onlyOtherAccounts) if minID != "" { - pageIDString = fmt.Sprintf("%s&min_id=%s", pageIDString, minID) + pageIDString += "&min_id=" + minID } pageID, err := url.Parse(pageIDString) @@ -1593,9 +1595,9 @@ func (c *Converter) StatusURIsToASRepliesPage(ctx context.Context, status *gtsmo // .next nextProp := streams.NewActivityStreamsNextProperty() - nextPropIDString := fmt.Sprintf("%s?only_other_accounts=%t&page=true", collectionID, onlyOtherAccounts) + nextPropIDString := collectionID + "?page=true&only_other_accounts=" + strconv.FormatBool(onlyOtherAccounts) if highestID != "" { - nextPropIDString = fmt.Sprintf("%s&min_id=%s", nextPropIDString, highestID) + nextPropIDString += "&min_id=" + minID } nextPropID, err := url.Parse(nextPropIDString) @@ -1643,12 +1645,12 @@ func (c *Converter) StatusesToASOutboxPage(ctx context.Context, outboxID string, // .id pageIDProp := streams.NewJSONLDIdProperty() - pageID := fmt.Sprintf("%s?page=true", outboxID) + pageID := outboxID + "?page=true" if minID != "" { - pageID = fmt.Sprintf("%s&minID=%s", pageID, minID) + pageID += "&min_id=" + minID } if maxID != "" { - pageID = fmt.Sprintf("%s&maxID=%s", pageID, maxID) + pageID += "&max_id=" + maxID } pageIDURI, err := url.Parse(pageID) if err != nil { @@ -1691,7 +1693,7 @@ func (c *Converter) StatusesToASOutboxPage(ctx context.Context, outboxID string, // .next if lowest != "" { nextProp := streams.NewActivityStreamsNextProperty() - nextPropIDString := fmt.Sprintf("%s?page=true&max_id=%s", outboxID, lowest) + nextPropIDString := outboxID + "?page=true&max_id=" + lowest nextPropIDURI, err := url.Parse(nextPropIDString) if err != nil { return nil, err @@ -1703,7 +1705,7 @@ func (c *Converter) StatusesToASOutboxPage(ctx context.Context, outboxID string, // .prev if highest != "" { prevProp := streams.NewActivityStreamsPrevProperty() - prevPropIDString := fmt.Sprintf("%s?page=true&min_id=%s", outboxID, highest) + prevPropIDString := outboxID + "?page=true&min_id=" + highest prevPropIDURI, err := url.Parse(prevPropIDString) if err != nil { return nil, err @@ -1854,7 +1856,7 @@ func (c *Converter) PollVoteToASCreates( ap.AppendTo(create, pollAuthorIRI) // Create ID formatted as: {$voterIRI}/activity#vote{$index}/{$statusIRI}. - createID := fmt.Sprintf("%s/activity#vote%d/%s", author.URI, i, poll.Status.URI) + createID := author.URI + "/activity#vote" + strconv.Itoa(i) + "/" + poll.Status.URI ap.MustSet(ap.SetJSONLDIdStr, ap.WithJSONLDId(create), createID) // Set Create actor appropriately. @@ -1867,7 +1869,7 @@ func (c *Converter) PollVoteToASCreates( note := streams.NewActivityStreamsNote() // For AP IRI generate from author URI + poll ID + vote choice. - id := fmt.Sprintf("%s#%s/votes/%d", author.URI, poll.ID, choice) + id := author.URI + "#" + poll.ID + "/votes/" + strconv.Itoa(choice) ap.MustSet(ap.SetJSONLDIdStr, ap.WithJSONLDId(note), id) // Attach new name property to note with vote choice. diff --git a/internal/typeutils/internaltoas_test.go b/internal/typeutils/internaltoas_test.go index f3e19bb81..5c3e52ddb 100644 --- a/internal/typeutils/internaltoas_test.go +++ b/internal/typeutils/internaltoas_test.go @@ -600,7 +600,7 @@ func (suite *InternalToASTestSuite) TestStatusToAS() { "replies": { "first": { "id": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true", - "next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?only_other_accounts=false\u0026page=true", + "next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true\u0026only_other_accounts=false", "partOf": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies", "type": "CollectionPage" }, @@ -702,7 +702,7 @@ func (suite *InternalToASTestSuite) TestStatusWithTagsToASWithIDs() { "replies": { "first": { "id": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R/replies?page=true", - "next": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R/replies?only_other_accounts=false\u0026page=true", + "next": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R/replies?page=true\u0026only_other_accounts=false", "partOf": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R/replies", "type": "CollectionPage" }, @@ -822,7 +822,7 @@ func (suite *InternalToASTestSuite) TestStatusWithTagsToASFromDB() { "replies": { "first": { "id": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R/replies?page=true", - "next": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R/replies?only_other_accounts=false\u0026page=true", + "next": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R/replies?page=true\u0026only_other_accounts=false", "partOf": "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R/replies", "type": "CollectionPage" }, @@ -927,7 +927,7 @@ func (suite *InternalToASTestSuite) TestStatusToASWithMentions() { "replies": { "first": { "id": "http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0/replies?page=true", - "next": "http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0/replies?only_other_accounts=false\u0026page=true", + "next": "http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0/replies?page=true\u0026only_other_accounts=false", "partOf": "http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0/replies", "type": "CollectionPage" }, diff --git a/internal/typeutils/wrap_test.go b/internal/typeutils/wrap_test.go index c0c51b37a..4f630124b 100644 --- a/internal/typeutils/wrap_test.go +++ b/internal/typeutils/wrap_test.go @@ -130,7 +130,7 @@ func (suite *WrapTestSuite) TestWrapNoteInCreate() { "replies": { "first": { "id": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true", - "next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?only_other_accounts=false\u0026page=true", + "next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true\u0026only_other_accounts=false", "partOf": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies", "type": "CollectionPage" }, diff --git a/internal/uris/helpers.go b/internal/uris/helpers.go new file mode 100644 index 000000000..410bb47e2 --- /dev/null +++ b/internal/uris/helpers.go @@ -0,0 +1,44 @@ +// 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 . + +package uris + +// helper functions for string building that are designed +// to be easily inlineable, and much faster than fmt.Sprintf(). +// +// you can check this by ensuring these funcs are not output in: +// go build -gcflags='-m=2' ./internal/uris/ 2>&1 | grep 'cannot inline' + +func buildURL1(proto, host, path1 string) string { + return proto + "://" + host + "/" + path1 +} + +func buildURL2(proto, host, path1, path2 string) string { + return proto + "://" + host + "/" + path1 + "/" + path2 +} + +func buildURL4(proto, host, path1, path2, path3, path4 string) string { + return proto + "://" + host + "/" + path1 + "/" + path2 + "/" + path3 + "/" + path4 +} + +func buildURL5(proto, host, path1, path2, path3, path4, path5 string) string { + return proto + "://" + host + "/" + path1 + "/" + path2 + "/" + path3 + "/" + path4 + "/" + path5 +} + +func buildPath4(path1, path2, path3, path4 string) string { + return path1 + "/" + path2 + "/" + path3 + "/" + path4 +} diff --git a/internal/uris/uri.go b/internal/uris/uri.go index d4bc2d829..d7ae9fe75 100644 --- a/internal/uris/uri.go +++ b/internal/uris/uri.go @@ -50,73 +50,127 @@ const ( RejectsPath = "rejects" // RejectsPath represents the activitypub Reject's location ) -// UserURIs contains a bunch of UserURIs and URLs for a user, host, account, etc. +// UserURIs contains a bunch of UserURIs +// and URLs for a user, host, account, etc. type UserURIs struct { - // The web URL of the instance host, eg https://example.org + + // The web URL of the instance + // host, eg https://example.org HostURL string - // The web URL of the user, eg., https://example.org/@example_user + + // The web URL of the user, + // eg., https://example.org/@example_user UserURL string - // The web URL for statuses of this user, eg., https://example.org/@example_user/statuses + + // The web URL for statuses of this user, + // eg., https://example.org/@example_user/statuses StatusesURL string - // The activitypub URI of this user, eg., https://example.org/users/example_user + // The activitypub URI of this user, + // eg., https://example.org/users/example_user UserURI string - // The activitypub URI for this user's statuses, eg., https://example.org/users/example_user/statuses + + // The activitypub URI for this user's statuses, + // eg., https://example.org/users/example_user/statuses StatusesURI string - // The activitypub URI for this user's activitypub inbox, eg., https://example.org/users/example_user/inbox + + // The activitypub URI for this user's activitypub inbox, + // eg., https://example.org/users/example_user/inbox InboxURI string - // The activitypub URI for this user's activitypub outbox, eg., https://example.org/users/example_user/outbox + + // The activitypub URI for this user's activitypub outbox, + // eg., https://example.org/users/example_user/outbox OutboxURI string - // The activitypub URI for this user's followers, eg., https://example.org/users/example_user/followers + + // The activitypub URI for this user's followers, + // eg., https://example.org/users/example_user/followers FollowersURI string - // The activitypub URI for this user's following, eg., https://example.org/users/example_user/following + + // The activitypub URI for this user's following, + // eg., https://example.org/users/example_user/following FollowingURI string - // The activitypub URI for this user's liked posts eg., https://example.org/users/example_user/liked + + // The activitypub URI for this user's liked posts. + // eg., https://example.org/users/example_user/liked LikedURI string - // The activitypub URI for this user's featured collections, eg., https://example.org/users/example_user/collections/featured + + // The activitypub URI for this user's featured collections, + // eg., https://example.org/users/example_user/collections/featured FeaturedCollectionURI string - // The URI for this user's public key, eg., https://example.org/users/example_user/publickey + + // The URI for this user's public key, + // eg., https://example.org/users/example_user/publickey PublicKeyURI string } // GenerateURIForFollow returns the AP URI for a new follow -- something like: // https://example.org/users/whatever_user/follow/01F7XTH1QGBAPMGF49WJZ91XGC func GenerateURIForFollow(username string, thisFollowID string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, FollowPath, thisFollowID) + return buildURL4(proto, + host, + UsersPath, + username, + FollowPath, + thisFollowID, + ) } // GenerateURIForLike returns the AP URI for a new like/fave -- something like: // https://example.org/users/whatever_user/liked/01F7XTH1QGBAPMGF49WJZ91XGC func GenerateURIForLike(username string, thisFavedID string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, LikedPath, thisFavedID) + return buildURL4(proto, + host, + UsersPath, + username, + LikedPath, + thisFavedID, + ) } // GenerateURIForUpdate returns the AP URI for a new update activity -- something like: // https://example.org/users/whatever_user#updates/01F7XTH1QGBAPMGF49WJZ91XGC func GenerateURIForUpdate(username string, thisUpdateID string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s#%s/%s", protocol, host, UsersPath, username, UpdatePath, thisUpdateID) + return buildURL4(proto, + host, + UsersPath, + username, + UpdatePath, + thisUpdateID, + ) } // GenerateURIForBlock returns the AP URI for a new block activity -- something like: // https://example.org/users/whatever_user/blocks/01F7XTH1QGBAPMGF49WJZ91XGC func GenerateURIForBlock(username string, thisBlockID string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, BlocksPath, thisBlockID) + return buildURL4(proto, + host, + UsersPath, + username, + BlocksPath, + thisBlockID, + ) } // GenerateURIForMove returns the AP URI for a new Move activity -- something like: // https://example.org/users/whatever_user/moves/01F7XTH1QGBAPMGF49WJZ91XGC func GenerateURIForMove(username string, thisMoveID string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, MovesPath, thisMoveID) + return buildURL4(proto, + host, + UsersPath, + username, + MovesPath, + thisMoveID, + ) } // GenerateURIForReport returns the API URI for a new Flag activity -- something like: @@ -125,57 +179,74 @@ func GenerateURIForMove(username string, thisMoveID string) string { // This path specifically doesn't contain any info about the user who did the reporting, // to protect their privacy. func GenerateURIForReport(thisReportID string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s", protocol, host, ReportsPath, thisReportID) + return buildURL2(proto, + host, + ReportsPath, + thisReportID, + ) } // GenerateURIForEmailConfirm returns a link for email confirmation -- something like: // https://example.org/confirm_email?token=490e337c-0162-454f-ac48-4b22bb92a205 func GenerateURIForEmailConfirm(token string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s?token=%s", protocol, host, ConfirmEmailPath, token) + return buildURL1(proto, host, ConfirmEmailPath) + "?token=" + token } // GenerateURIForAccept returns the AP URI for a new Accept activity -- something like: // https://example.org/users/whatever_user/accepts/01F7XTH1QGBAPMGF49WJZ91XGC func GenerateURIForAccept(username string, thisAcceptID string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, AcceptsPath, thisAcceptID) + return buildURL4(proto, + host, + UsersPath, + username, + AcceptsPath, + thisAcceptID, + ) } // GenerateURIForReject returns the AP URI for a new Reject activity -- something like: // https://example.org/users/whatever_user/rejects/01F7XTH1QGBAPMGF49WJZ91XGC func GenerateURIForReject(username string, thisRejectID string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, RejectsPath, thisRejectID) + return buildURL4(proto, + host, + UsersPath, + username, + RejectsPath, + thisRejectID, + ) } -// GenerateURIsForAccount throws together a bunch of URIs for the given username, with the given protocol and host. -func GenerateURIsForAccount(username string) *UserURIs { - protocol := config.GetProtocol() +// GenerateURIsForAccount throws together a bunch of URIs +// for the given username, with the given protocol and host. +func GenerateURIsForAccount(username string) UserURIs { + proto := config.GetProtocol() host := config.GetHost() - // The below URLs are used for serving web requests - hostURL := fmt.Sprintf("%s://%s", protocol, host) - userURL := fmt.Sprintf("%s/@%s", hostURL, username) - statusesURL := fmt.Sprintf("%s/%s", userURL, StatusesPath) + // URLs for serving web requests. + hostURL := proto + "://" + host + userURL := hostURL + "/@" + username + statusesURL := userURL + "/" + StatusesPath // the below URIs are used in ActivityPub and Webfinger - userURI := fmt.Sprintf("%s/%s/%s", hostURL, UsersPath, username) - statusesURI := fmt.Sprintf("%s/%s", userURI, StatusesPath) - inboxURI := fmt.Sprintf("%s/%s", userURI, InboxPath) - outboxURI := fmt.Sprintf("%s/%s", userURI, OutboxPath) - followersURI := fmt.Sprintf("%s/%s", userURI, FollowersPath) - followingURI := fmt.Sprintf("%s/%s", userURI, FollowingPath) - likedURI := fmt.Sprintf("%s/%s", userURI, LikedPath) - collectionURI := fmt.Sprintf("%s/%s/%s", userURI, CollectionsPath, FeaturedPath) - publicKeyURI := fmt.Sprintf("%s/%s", userURI, PublicKeyPath) + userURI := hostURL + "/" + UsersPath + "/" + username + statusesURI := userURI + "/" + StatusesPath + inboxURI := userURI + "/" + InboxPath + outboxURI := userURI + "/" + OutboxPath + followersURI := userURI + "/" + FollowersPath + followingURI := userURI + "/" + FollowingPath + likedURI := userURI + "/" + LikedPath + collectionURI := userURI + "/" + CollectionsPath + "/" + FeaturedPath + publicKeyURI := userURI + "/" + PublicKeyPath - return &UserURIs{ + return UserURIs{ HostURL: hostURL, UserURL: userURL, StatusesURL: statusesURL, @@ -205,19 +276,16 @@ func URIForAttachment( mediaID string, extension string, ) string { - const format = "%s://%s/%s/%s/%s/%s/%s.%s" - - return fmt.Sprintf( - format, - config.GetProtocol(), - config.GetHost(), + proto := config.GetProtocol() + host := config.GetHost() + return buildURL5(proto, + host, FileserverPath, accountID, mediaType, mediaSize, mediaID, - extension, - ) + ) + "." + extension } // StoragePathForAttachment generates a storage @@ -233,16 +301,12 @@ func StoragePathForAttachment( mediaID string, extension string, ) string { - const format = "%s/%s/%s/%s.%s" - - return fmt.Sprintf( - format, + return buildPath4( accountID, mediaType, mediaSize, mediaID, - extension, - ) + ) + "." + extension } // URIForEmoji generates an @@ -252,12 +316,10 @@ func StoragePathForAttachment( // // "https://example.org/emoji/01FPST9QK4V5XWS3F9Z4F2G1X7" func URIForEmoji(emojiID string) string { - const format = "%s://%s/%s/%s" - - return fmt.Sprintf( - format, - config.GetProtocol(), - config.GetHost(), + proto := config.GetProtocol() + host := config.GetHost() + return buildURL2(proto, + host, EmojiPath, emojiID, ) @@ -265,9 +327,14 @@ func URIForEmoji(emojiID string) string { // URIForTag generates an activitypub uri for a tag. func URIForTag(name string) string { - protocol := config.GetProtocol() + proto := config.GetProtocol() host := config.GetHost() - return fmt.Sprintf("%s://%s/%s/%s", protocol, host, TagsPath, strings.ToLower(name)) + name = strings.ToLower(name) + return buildURL2(proto, + host, + TagsPath, + name, + ) } // IsUserPath returns true if the given URL path corresponds to eg /users/example_username diff --git a/internal/util/paging.go b/internal/util/paging.go index 712498313..69bacd2cd 100644 --- a/internal/util/paging.go +++ b/internal/util/paging.go @@ -18,8 +18,8 @@ package util import ( - "fmt" "net/url" + "strconv" "strings" apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model" @@ -71,7 +71,7 @@ func PackagePageableResponse(params PageableResponseParams) (*apimodel.PageableR nextRaw := params.NextMaxIDKey + "=" + params.NextMaxIDValue if params.Limit != 0 { - nextRaw = fmt.Sprintf("limit=%d&", params.Limit) + nextRaw + nextRaw = "limit=" + strconv.Itoa(params.Limit) + "&" + nextRaw } for _, p := range params.ExtraQueryParams { @@ -96,7 +96,7 @@ func PackagePageableResponse(params PageableResponseParams) (*apimodel.PageableR prevRaw := params.PrevMinIDKey + "=" + params.PrevMinIDValue if params.Limit != 0 { - prevRaw = fmt.Sprintf("limit=%d&", params.Limit) + prevRaw + prevRaw = "limit=" + strconv.Itoa(params.Limit) + "&" + prevRaw } for _, p := range params.ExtraQueryParams { diff --git a/internal/webpush/realsender.go b/internal/webpush/realsender.go index 99472c815..831c5e4f2 100644 --- a/internal/webpush/realsender.go +++ b/internal/webpush/realsender.go @@ -20,7 +20,6 @@ package webpush import ( "context" "encoding/json" - "fmt" "io" "net/http" "slices" @@ -299,41 +298,37 @@ func formatNotificationTitle( switch notification.NotificationType { case gtsmodel.NotificationFollow: - return fmt.Sprintf("%s followed you", displayNameOrAcct) + return displayNameOrAcct + " followed you" case gtsmodel.NotificationFollowRequest: - return fmt.Sprintf("%s requested to follow you", displayNameOrAcct) + return displayNameOrAcct + " requested to followed you" case gtsmodel.NotificationMention: - return fmt.Sprintf("%s mentioned you", displayNameOrAcct) + return displayNameOrAcct + " mentioned you" case gtsmodel.NotificationReblog: - return fmt.Sprintf("%s boosted your post", displayNameOrAcct) + return displayNameOrAcct + " boosted your post" case gtsmodel.NotificationFavourite: - return fmt.Sprintf("%s faved your post", displayNameOrAcct) + return displayNameOrAcct + " faved your post" case gtsmodel.NotificationPoll: if subscription.AccountID == notification.TargetAccountID { return "Your poll has ended" } - return fmt.Sprintf("%s's poll has ended", displayNameOrAcct) + return displayNameOrAcct + "'s poll has ended" case gtsmodel.NotificationStatus: - return fmt.Sprintf("%s posted", displayNameOrAcct) + return displayNameOrAcct + " posted" case gtsmodel.NotificationAdminSignup: - return fmt.Sprintf("%s requested to sign up", displayNameOrAcct) + return displayNameOrAcct + " requested to sign up" case gtsmodel.NotificationPendingFave: - return fmt.Sprintf("%s faved your post, which requires your approval", displayNameOrAcct) + return displayNameOrAcct + " faved your post, which requires your approval" case gtsmodel.NotificationPendingReply: - return fmt.Sprintf("%s mentioned you, which requires your approval", displayNameOrAcct) + return displayNameOrAcct + " mentioned you, which requires your approval" case gtsmodel.NotificationPendingReblog: - return fmt.Sprintf("%s boosted your post, which requires your approval", displayNameOrAcct) + return displayNameOrAcct + " boosted your post, which requires your approval" case gtsmodel.NotificationAdminReport: - return fmt.Sprintf("%s submitted a report", displayNameOrAcct) + return displayNameOrAcct + " submitted a report" case gtsmodel.NotificationUpdate: - return fmt.Sprintf("%s updated their post", displayNameOrAcct) + return displayNameOrAcct + " updated their post" default: log.Warnf(ctx, "Unknown notification type: %d", notification.NotificationType) - return fmt.Sprintf( - "%s did something (unknown notification type %d)", - displayNameOrAcct, - notification.NotificationType, - ) + return displayNameOrAcct + " did something (unknown notification type)" } }