summaryrefslogtreecommitdiff
path: root/internal/typeutils/defaulticons.go
blob: 1076d3476dc1048966735542c3c32d219cbc5514 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// 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 typeutils

import (
	"math/rand"
	"os"
	"path/filepath"
	"strings"

	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
	"github.com/superseriousbusiness/gotosocial/internal/config"
	"github.com/superseriousbusiness/gotosocial/internal/log"
)

const defaultHeaderPath = "/assets/default_header.webp"

// populateDefaultAvatars returns a slice of standard avatars found
// in the path [web-assets-base-dir]/default_avatars. The slice
// entries correspond to the relative url via which they can be
// retrieved from the server.
//
// So for example, an avatar called default.jpeg would be returned
// in the slice as "/assets/default_avatars/default.jpeg".
func populateDefaultAvatars() (defaultAvatars []string) {
	webAssetsAbsFilePath, err := filepath.Abs(config.GetWebAssetBaseDir())
	if err != nil {
		log.Panicf(nil, "error getting abs path for web assets: %s", err)
	}

	defaultAvatarsAbsFilePath := filepath.Join(webAssetsAbsFilePath, "default_avatars")
	defaultAvatarFiles, err := os.ReadDir(defaultAvatarsAbsFilePath)
	if err != nil {
		log.Warnf(nil, "error reading default avatars at %s: %s", defaultAvatarsAbsFilePath, err)
		return
	}

	for _, f := range defaultAvatarFiles {
		// ignore directories
		if f.IsDir() {
			continue
		}

		// ignore files bigger than 50kb
		if i, err := f.Info(); err != nil || i.Size() > 50000 {
			continue
		}

		// get the name of the file, eg avatar.jpeg
		fileName := f.Name()

		// get just the .jpeg, for example, from avatar.jpeg
		extensionWithDot := filepath.Ext(fileName)

		// remove the leading . to just get, eg, jpeg
		extension := strings.TrimPrefix(extensionWithDot, ".")

		// take only files with simple extensions
		// that we know will work OK as avatars
		switch strings.ToLower(extension) {
		case "jpeg", "jpg", "gif", "png", "webp":
			avatarURL := config.GetProtocol() + "://" + config.GetHost() + "/assets/default_avatars/" + fileName
			defaultAvatars = append(defaultAvatars, avatarURL)
		default:
			continue
		}
	}

	return
}

// ensureAvatar ensures that the given account has a value set
// for the avatar URL.
//
// If no value is set, an avatar will be selected at random from
// the available default avatars. This selection is 'sticky', so
// the same account will get the same result on subsequent calls.
//
// If a value for the avatar URL is already set, this function is
// a no-op.
//
// If there are no default avatars available, this function is a
// no-op.
func (c *Converter) ensureAvatar(account *apimodel.Account) {
	if (account.Avatar != "" && account.AvatarStatic != "") || len(c.defaultAvatars) == 0 {
		return
	}

	var avatar string
	if avatarI, ok := c.randAvatars.Load(account.ID); ok {
		// we already have a default avatar stored for this account
		avatar, ok = avatarI.(string)
		if !ok {
			panic("avatarI was not a string")
		}
	} else {
		// select + store a default avatar for this account at random
		randomIndex := rand.Intn(len(c.defaultAvatars)) //nolint:gosec
		avatar = c.defaultAvatars[randomIndex]
		c.randAvatars.Store(account.ID, avatar)
	}

	account.Avatar = avatar
	account.AvatarStatic = avatar

	const defaultAviDesc = "Grayed-out line drawing of a cute sloth (default avatar)."
	account.AvatarDescription = defaultAviDesc
}

// ensureHeader ensures that the given account has a value set
// for the header URL.
//
// If no value is set, the default header will be set.
//
// If a value for the header URL is already set, this function is
// a no-op.
func (c *Converter) ensureHeader(account *apimodel.Account) {
	if account.Header != "" && account.HeaderStatic != "" {
		return
	}

	h := config.GetProtocol() + "://" + config.GetHost() + defaultHeaderPath
	account.Header = h
	account.HeaderStatic = h

	const defaultHeaderDesc = "Flat gray background (default header)."
	account.HeaderDescription = defaultHeaderDesc
}